第八章 示例代码(MyBatis)
Sample Code
JPetStore 6 is a full web application built on top of MyBatis 3, Spring 3 and Stripes. It is available for downloading in the downloads section of MyBatis project site. In this section we will walk through this sample to understand how is it built and learn how to run it.
Purpose
This new JPetStore comes with the same idea in mind than its predecessors: keep it simple. The purpose of JPetStore 6 is to demonstrate how to build a web application with very few classes and no advanced coding skills. You just need to know plain Java and SQL.
The 6th version of JPetStore is the smallest one in the family. It uses just 24 java classes while keeping a good design and program structure. As we will see later on, you will find no code for dealing with JDBC, for creating objects or bind them or to handle transactions. What is more impressive is that you will not find any call to the MyBatis API. Although this sounds magical, you will see that the combination of MyBatis mappers and dependency injection lets you build applications without dependencies.
Program Structure
JPetStore 6 follows the typical maven project structure
/jpetstore <-- Maven pom.xml goes here.
/src
/main/
/java <-- Java code goes here.
/org/
/mybatis
/jpetstore
/domain <-- Business domain objects go here.
/persistence <-- Mapper interfaces go here.
/service <-- Application logic goes here.
/web
/actions <-- Presentation logic (actions) goes here.
/resources <-- Non java files go here.
/org
/mybatis
/jpetstore
/persistence <-- Mapper XML files go here.
/database
/webapp
/css
/images
/WEB-INF <-- web.xml and applicationContext.xml go here.
/jsp <-- JSP files go here.
Configuration files
Configuration files are read during application startup. Their purpose is to configure the three frameworks composing the application: Stripes, Spring and MyBatis. We will just need to configure two files: web.xml and applicationContext.xml.
web.xml
First of all we need to start Stripes, so we follow the Stripes manual to do so. The manual says that we should set up a dispatcher servlet and a filter. So let's go.
<filter>
<display-name>Stripes Filter</display-name>
<filter-name>StripesFilter</filter-name>
<filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>StripesFilter</filter-name>
<servlet-name>StripesDispatcher</servlet-name>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<servlet>
<servlet-name>StripesDispatcher</servlet-name>
<servlet-class>net.sourceforge.stripes.controller.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>StripesDispatcher</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
Stripes is able to search for ActionBean classes, for that purpose we must set up the base package it should search in.
<filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
<init-param>
<param-name>ActionResolver.Packages</param-name>
<param-value>org.mybatis.jpetstore.web</param-value>
</init-param>
</filter>
We are done with Stripes. Let's move on to the Spring side. According to Spring's reference manual we should add a Context listener to start up Spring. So let's add it:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
By default Spring will use /WEB-INF/applicationContext.xml if we don't specify a different file. The default is fine for us.
Now we have to let Stripes know that it will be running with Spring. This way we will be able to inject Spring beans directly into Stripes ActionBeans. For that purpose, following once again the Stripes manual, we set up an interceptor as follows below:
<filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
...
<init-param>
<param-name>Interceptor.Classes</param-name>
<param-value>net.sourceforge.stripes.integration.spring.SpringInterceptor</param-value>
</init-param>
</filter>
We are done with web.xml. As you may have notice, we have not set up any MyBatis 3 configuration yet. That configuration goes into the Spring's applicationContext.xml that we will see in the following section.
applicationContext.xml
As you already know applicationContext.xml is the Spring's configuration file. Spring is a dependency injection framework and it has to know which beans it must create and how to bind them together and that is what applicationContext.xml file is for. Let's have a deeper look into it.
The first and easiest thing we have to do is let Spring know where are our service beans. We will let Spring search them in our classpath so we just need to provide it the base package to search in:
<context:component-scan base-package="org.mybatis.jpetstore.service" />
NOTE Spring's component scan feature is not able to find MyBatis mappers. A mapper is not a plain bean and Spring would not know how to instantiate it. We will see how to search for mappers soon.
We will also need a DataSource and a TransactionManager. Given that this is a demo application we will use a test Spring DataSource that will create an HSQL in-memory database and load our database scripts into it and the standard Spring's DataSourceTransactionManager to handle transactions.
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:database/jpetstore-hsqldb-schema.sql"/>
<jdbc:script location="classpath:database/jpetstore-hsqldb-dataload.sql"/>
</jdbc:embedded-database> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
So far, all we have done is standard Stripes and Spring configuration and now it is time to move on to the MyBatis part. As you have learned in this manual to set up MyBatis with Spring you need at least two things: an SqlSessionFactoryBean and a mapper class. So let's go hands on. First define a SqlSessionFactoryBean:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
And now we need to setup our mappers. For that purpose we will use the MapperScannerConfigurer that works similar to Spring standard component scan. It will search our classpath for mapper classes and register them to MyBatis. Similar to Spring's component scan we must configure the base package to search in.
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mybatis.jpetstore.persistence" />
</bean>
To save some writing when building our mapper xml files we would want to be able to use short aliases for beans. The SqlSessionFactoryBean has the capability to search for beans and register their short names as aliases if we setup the typeAliasPackage property like the following:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="org.mybatis.jpetstore.domain" />
</bean>
Our application is now fully configured and ready to run. But before running it lets have a tour through the code to see how it looks like.
Code tour
JPetStore 6 is a typical MVC application with three layers: presentation, logic and data access.
Presentation
The presentation layer is composed by JSP files and Stripes ActionBeans. JSPs just use plain HTML, JSTL tags and Stripes tags so there is nothing especial about them for the sake of this sample. Stripes ActionBeans are like Struts actions or Spring MVC controllers so there is nothing especial with them either.
Given that we have integrated Stripes with Spring, we can inject our services into our ActionsBeans so you can just use them without caring about its creation or lookup. Have a look at CatalogActionBean:
@SessionScope
public class CatalogActionBean extends AbstractActionBean {
...
@SpringBean
private transient CatalogService catalogService;
...
public ForwardResolution viewCategory() {
if (categoryId != null) {
productList = catalogService.getProductListByCategory(categoryId);
category = catalogService.getCategory(categoryId);
}
return new ForwardResolution(VIEW_CATEGORY);
}
...
Note the @SpringBean annotation, that is an Stripes annotation that tells Stripes to look for that bean in Spring and inject it into this ActionBean.
Logic
Application logic is composed by plain Java beans that act as services and plain Java beans that act as domain objects. This layer is in charge of filling domain objects with database data and updating database data with the content of the domain objects. For this purpose this layer must be transactional, that is, it must be able to perform atomic database updates.
Let's have a look at OrderService code to see how all this is achieved:
@Service
public class OrderService { @Autowired
private ItemMapper itemMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private LineItemMapper lineItemMapper; @Transactional
public void insertOrder(Order order) {
order.setOrderId(getNextId("ordernum"));
for (int i = 0; i < order.getLineItems().size(); i++) {
LineItem lineItem = (LineItem) order.getLineItems().get(i);
String itemId = lineItem.getItemId();
Integer increment = new Integer(lineItem.getQuantity());
Map<String, Object> param = new HashMap<String, Object>(2);
param.put("itemId", itemId);
param.put("increment", increment);
itemMapper.updateInventoryQuantity(param);
} orderMapper.insertOrder(order);
orderMapper.insertOrderStatus(order);
for (int i = 0; i < order.getLineItems().size(); i++) {
LineItem lineItem = (LineItem) order.getLineItems().get(i);
lineItem.setOrderId(order.getOrderId());
lineItemMapper.insertLineItem(lineItem);
}
}
The first thing you will notice is that there is no JDBC code in the service, nor it is any MyBatis code in it. You may think that we used the DAO pattern and database access code is in the database layer, but as we will see later, the database layer is built with MyBatis mappers, that are plain java interfaces, and that is why you will not find any call to MyBatis API in the whole application. It is just not needed.
The second thing you may have noticed is that there are no commits or rollbacks. That is because it uses the declarative transaction demarcation feature of Spring that is fully supported by MyBatis-Spring. The Spring's @Transactional annotation indicates that this method is transactional, that means that all updateInventoryQuantity, insertOrder and insertLineItem mapper calls must succeed or none.
Persistence
The persistence layer is composed of MyBatis mappers. Mappers are just plain Java interfaces and mapper XML files containing the SQL statements. There is no custom Java code in this layer. When the getOrder method is called on the OrderMapper interface, MyBatis will execute the getOrder SQL statement in OrderMapper.xml file and will populate the Order domain bean with retrieved data.
public interface OrderMapper {
List<Order> getOrdersByUsername(String username);
Order getOrder(int orderId);
void insertOrder(Order order);
void insertOrderStatus(Order order);
}
<mapper namespace="org.mybatis.jpetstore.persistence.OrderMapper"> <cache /> <select id="getOrder" resultType="Order" parameterType="int">
SELECT
BILLADDR1 AS billAddress1,
BILLADDR2 AS billAddress2,
BILLCITY,
BILLCOUNTRY,
BILLSTATE,
BILLTOFIRSTNAME,
BILLTOLASTNAME,
BILLZIP,
SHIPADDR1 AS shipAddress1,
SHIPADDR2 AS shipAddress2,
SHIPCITY,
SHIPCOUNTRY,
SHIPSTATE,
SHIPTOFIRSTNAME,
SHIPTOLASTNAME,
SHIPZIP,
CARDTYPE,
COURIER,
CREDITCARD,
EXPRDATE AS expiryDate,
LOCALE,
ORDERDATE,
ORDERS.ORDERID,
TOTALPRICE,
USERID AS username,
STATUS
FROM ORDERS, ORDERSTATUS
WHERE ORDERS.ORDERID = #{value}
AND ORDERS.ORDERID = ORDERSTATUS.ORDERID
</select>
...
</mapper>
NOTE You can easily add caching to your queries by adding a <cache /> element to your mapper xml file. Or, if you prefer, Spring lets you cache at a higher level, caching the whole call to a mapper or service method.
Running JPetStore
You may ask. Does all this work? Yes it does! Let's run it.
Let's assume you have a clean computer. These are the steps you should follow to have the sample running on Tomcat with Eclipse:
- Download and install a JDK 6 or later
- Download and upzip Eclipse
- Download and unzip Tomcat
- Run Eclipse
- Go to Git tab
- Clone the repo https://github.com/mybatis/jpetstore-6.git
- Select working directory, right click and select Import Projects (general)
- Go to Java EE tab
- Right click on jpetstore project and select "Configure/Convert to Maven Project"
- Right click on jpetstore project and select "run on server"
- Select Tomcat Server and set your installation directory
- JPetStore home page should be shown!!
Now you are ready to play with it, experiment with your own changes or whatever you want.
And remember that if you find a bug or something that is missing or can be improved (for example the missing tests!), fork the repo, change it, and open a pull request. Thanks in advance!!!
NOTE JPetStore 6 should run in any Servlet 2.5 y JSP 2.1 compliant Java server. Eclipse is not needed either, you can run the sample from your favorite IDE or the command line.
第八章 示例代码(MyBatis)的更多相关文章
- 基于DotNetOpenAuth的OAuth实现示例代码: 获取access token
1. 场景 根据OAuth 2.0规范,该场景发生于下面的流程图中的(D)(E)节点,根据已经得到的authorization code获取access token. 2. 实现环境 DotNetOp ...
- 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题
调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...
- ActiveMQ笔记(1):编译、安装、示例代码
一.编译 虽然ActiveMQ提供了发布版本,但是建议同学们自己下载源代码编译,以后万一有坑,还可以尝试自己改改源码. 1.1 https://github.com/apache/activemq/r ...
- C#微信公众平台接入示例代码
http://mp.weixin.qq.com/wiki/17/2d4265491f12608cd170a95559800f2d.html 这是微信公众平台提供的接入指南.官网只提供了php的示例代码 ...
- 编译opengl编程指南第八版示例代码通过
最近在编译opengl编程指南第八版的示例代码,如下 #include <iostream> #include "vgl.h" #include "LoadS ...
- 股票数据调用示例代码php
<!--?php // +---------------------------------------------------------------------- // | JuhePHP ...
- php示例代码之类似于C#中的String.Format方法
php示例代码之类似于C#中的String.Format方法 原文来自于 http://stackoverflow.com/questions/1241177/c-string-format-equ ...
- redis 学习笔记(2)-client端示例代码
redis提供了几乎所有主流语言的client,java中主要使用二种:Jedis与Redisson 一.Jedis的使用 <dependency> <groupId>redi ...
- 正则表达式学习笔记(附:Java版示例代码)
具体学习推荐:正则表达式30分钟入门教程 . 除换行符以外的任意字符\w word,正常字符,可以当做变量名的,字母.数字.下划线.汉字\s space,空白符 ...
随机推荐
- NEXYS 3开发板练手--USB UART(三)
接着上一篇,今天我们来建立一个能用于实际工程中的DEMO. 首先,为了使我们的发送机不像上一个DEMO一样无节制的循环发送,我们需要修改代码,增加使发送机停止发送的控制部分,修改后的代码如下: `ti ...
- bazel-链接第三方动态库,静态库。
demo4示例 链接第三方动态库,静态库. 使用cc_import链接外部库.原理是通过cc_import规则导入第三方库,然后cc_binary再依赖cc_import规则的target,也即依赖第 ...
- JS转换HTML转义符,防止javascript注入攻击,亲测可用
function removeHtmlTab(tab) { return tab.replace(/<[^<>]+?>/g,'');//删除所有HTML标签 } functi ...
- Ubuntu和Busybox下用make menuconfig配置出错解决
http://blog.csdn.net/satiling/article/details/6965985 # make menuconfig In file included from script ...
- c++派生类的访问控制权限
派生类必须通过使用类派生列表, 明确指出它是从哪个(哪些)基类继承而来的.类派生列表的形式是:首先是一个冒号,后面紧跟以逗号分隔的基类列表(可以多继承,但一般不使用多继承),其中每个基类前面可以有以下 ...
- windows下PIP安装模块编码错误解决
原因是pip安装Python包会加载我的用户目录,我的用户目录恰好是中文的,ascii不能编码.解决办法是: python目录 Python27\Lib\site-packages 建一个文件site ...
- 常见Java工具——jps
简介 最常用的一个. 与Linux中的查看Java进程命令功能相同: ps -ef | grep java jps与这个命令的区别在于,jps仅仅过滤出Java本身的进程以及运行的引导类,就是引导ma ...
- vue分享插件
vshare 基于百度分享开发的支持VUE2.X的分享插件,为您带来更多的流量!提供多种风格按钮,代码加载更快,引入社会化流量,提升网页抓取速度等优点.github地址:https://github. ...
- C语言 · 最大子阵
历届试题 最大子阵 时间限制:1.0s 内存限制:256.0MB 问题描述 给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大. 其中,A的子矩阵指在A中行和 ...
- Linux 系统串口信息查看
先确认系统启动的时候串口的信息. ECM_5412@chenfl:~$ dmesg | grep tty [ 0.000000] console [tty0] enabled [ 2.511678] ...