Spring-Ibatis中文帮助文档

简介

What is MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。 使用这个类库中的类, Spring 将会加载必要的 MyBatis 工厂类和 session 类。 这个类库也提供一个简单的方式来注入 MyBatis 数据映射器和 SqlSession 到业务层的 bean 中。 而且它也会处理事务, 翻译 MyBatis 的异常到 Spring DataAccessException 异常(数据访问异常,译者注)中。最终,它并 不会依赖于 MyBatis,Spring MyBatis-Spring 来构建应用程序代码。

Motivation

正如第二版那样,Spring 3.0 也仅支持 iBatis2。那么,我们就想将 MyBatis3 的支持添加 Spring3.0(参考 Spring Jira 中的问题)中。而不幸的是,Spring 3.0 的开发在 MyBatis 3.0 官方发布前就结束了。 因为 Spring 开发团队不想发布一个基于非发布版的 MyBatis 的整合支 ,那么 Spring 官方的支持就不得不继续等待了。要在 Spring 中支持 MyBatis,MyBatis 区认为现在应该是自己团结贡献者和有兴趣的人一起来开始将 Spring 的整合作为 MyBatis 区的子项目的时候了。

Requirements

在开始使用 MyBatis-Spring 的整合之前,很重要的一点是,你要熟悉 Spring MyBatis 这两个框架还有和它们有关的术语,本手册中不会提供二者的基本内容,安装和配置教程。

MyBatis-Spring requires Java 5 or higher and the following MyBatis and Spring versions:

MyBatis-Spring

MyBatis

Spring

1.0.0 and 1.0.1

3.0.1 to 3.0.5

3.0.0 or higher

1.0.2

3.0.6

3.0.0 or higher

1.1.0

3.1.0 or higher

3.0.0 or higher

Acknowledgements

特别感谢那些使得本项目成为现实的人们(按字母顺序排序) Eduardo Macarron, Hunter PresnallPutthibong Boonbong的编码, 测试和文档修改工作; Andrius Juozapaitis, Giovanni Cuccu,Raj NagappanTomas Pinos的贡献;Simone Tripodi发现了这些人并 把他们带入项目之中。没有他们的努力,这个项目是不可能存在的。

第二章 入门

本章将会以简略的步骤告诉你如何安装和创建 MyBatis-Spring,并构建一个简单的数据 访问事务性的应用程序。

Installation

要使用 MyBatis-Spring 模块,你只需要包含 mybatis-spring-x.x.x.jar  件就可以了,并在类路径中加入相关的依赖。

如果你使用 Maven,那么在 pom.xml 中加入下面的代码即可:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>x.x.x</version>
</dependency>

Quick Setup

要和 Spring 一起使用 MyBatis,你需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。

MyBatis-Spring ,SqlSessionFactoryBean 是用于创建 SqlSessionFactory 的。要 配置这个工厂 bean,放置下面的代码在 Spring XML 配置文件中:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

要注意 SqlSessionFactory 需要一个 DataSource(数据源,译者注) 。这可以是任意 DataSource,配置它就和配置其它 Spring 数据库连接一样。

Assume you have a data mapper class defined like the following:

public interface UserMapper {
  @Select("SELECT * FROM users WHERE id = #{userId}")
  User getUser(@Param("userId") String userId);
} 

那么可以使用 MapperFactoryBean,像下面这样来把接口加入到 Spring :

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

要注意,所指定的映射器类必须是一个接口,而不是具体的实现类。在这个示例中, 解被用来指定 SQL 语句,但是 MyBatis 的映射器 XML 文件也可以用。

一旦配置好,你可以用注入其它任意 Spring bean 相同的方式直接注入映射器到你的 business/service 对象中。MapperFactoryBean 处理 SqlSession 的创建和关闭它。如果使用 Spring 的事务,那么当事务完成时,session 将会提交或回滚。最终,任何异常都会被翻 译成 Spring DataAccessException 异常。

调用 MyBatis 数据方法现在只需一行代码:

public class FooServiceImpl implements FooService {
 
private UserMapper userMapper;
 
public void setUserMapper(UserMapper userMapper) {
  this.userMapper = userMapper;
}
 
public User doSomeBusinessStuff(String userId) {
  return this.userMapper.getUser(userId);
}

SqlSessionFactoryBean

在基本的 MyBatis ,session 工厂可以使用 SqlSessionFactoryBuilder 来创建。而在 MyBatis-Spring ,则使用 SqlSessionFactoryBean 来替代。

Setup

要创建工厂 bean,放置下面的代码在 Spring XML 配置文件中:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

要注意 SqlSessionFactoryBean 实现了 Spring FactoryBean 接口(请参考 Spring 档的 3.8 章节)这就说明了由 Spring 最终创建的 bean 不是 SqlSessionFactoryBean 本身, 而是工厂类的 getObject()返回的方法的结果。这种情况下,Spring 将会在应用启动时为你 创建 SqlSessionFactory 对象,然后将它以 SqlSessionFactory 为名来存储。在 Java , 相同的代码是:

SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
SqlSessionFactory sessionFactory = factoryBean.getObject();

在一般的 MyBatis-Spring 用法中, 你不需要直接使用 SqlSessionFactoryBean 或和其对 应的 SqlSessionFactory。相反,session 工厂将会被注入到 MapperFactoryBean 或其它扩 展了 SqlSessionDaoSupport DAO(Data Access Object,数据访问对象,译者注)中。

属性

SqlSessionFactory 有一个单独的必须属性,就是 JDBC DataSource。这可以是任意 DataSource,其配置应该和其它 Spring 数据库连接是一样的。

一个通用的属性是 configLocation,它是用来指定 MyBatis XML 配置文件路径的。 如果基本的 MyBatis 配置需要改变, 那么这就是一个需要它的地方。 通常这会是<settings><typeAliases>的部分。

要注意这个配置文件不需要是一个完整的 MyBatis 配置。确切地说,任意环境,数据源 MyBatis 的事务管理器都会被忽略。SqlSessionFactoryBean 会创建它自己的,使用这些 值定制 MyBatis Environment 时是需要的。

如果 MyBatis 映射器 XML 文件在和映射器类相同的路径下不存在,那么另外一个需要 配置文件的原因就是它了。使用这个配置,有两种选择。第一是手动在 MyBatis XML 置文件中使用<mappers>部分来指定类路径。第二是使用工厂 bean mapperLocations 性。

mapperLocations 属性使用一个资源位置的 list 这个属性可以用来指定 MyBatis XML 映射器文件的位置。 它的值可以包含 Ant 样式来加载一个目录中所有文件, 或者从基路径下 递归搜索所有路径。比如:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml" />
</bean>

这会从类路径下加载在 sample.config.mappers 包和它的子包中所有的 MyBatis 映射器 XML 文件。

在容器环境管理事务中,一个可能需要的属性是 transactionFactoryClass。请参考 第四章(4.2 )中来查看有关部分。

第四章 事务

一个使用 MyBatis-Spring 的主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而 不是给 MyBatis 创建一个新的特定的事务管理器,MyBatis-Spring 利用了存在于 Spring 中的 DataSourceTransactionManager

一旦 Spring PlatformTransactionManager 配置好了,你可以在 Spring 中以你通常的做 法来配置事务。@Transactional 注解和 AOP(Aspect-Oriented Program,面向切面编程, 者注)样式的配置都是支持的。在事务处理期间,一个单独的 SqlSession 对象将会被创建 和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。

一旦事务创建之后,MyBatis-Spring 将会透明的管理事务。在你的 DAO 类中就不需要额 外的代码了。

标准配置

Spring , Spring XML DataSourceTransactionManager 对象:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>

指定的 DataSource 一般可以是你使用 Spring 的任意 JDBC DataSource。这包含了连接 池和通过 JNDI 查找获得的 DataSource

要注意, 为事务管理器指定的 DataSource 必须和用来创建 SqlSessionFactoryBean 是同一个数据源,否则事务管理器就无法工作了。

容器管理事务

如果你正使用一个 JEE 容器而且想让 Spring 参与到容器管理事务(Container managed transactions,CMT,译者注),那么 Spring 应该使用 JtaTransactionManager 或它的容 器指定的子类来配置。做这件事情的最方便的方式是用 Spring 的事务命名空间:

<tx:jta-transaction-manager />

在这种配置中,MyBatis 将会和其它由 CMT 配置的 Spring 事务资源一样。Spring 会自动 使用任意存在的容器事务,在上面附加一个 SqlSession。如果没有开始事务,或者需要基 于事务配置,Spring 会开启一个新的容器管理事务。

, 使 CMT , 使 Spring , SqlSessionFactoryBean 来使用基本的 MyBatis ManagedTransactionFactory 而不是其 它任意的 Spring 事务管理器:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="transactionFactory">
    <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
  </property>  
</bean>

编程式事务管理

MyBatis SqlSession 提供指定的方法来处理编程式的事务。 但是当使用 MyBatis-Spring , bean 将会使用 Spring 管理的 SqlSession 或映射器来注入。 那就是说 Spring 通常是处理 事务的。

Spring SqlSession SqlSession.commit() , SqlSession.rollback() SqlSession.close() , UnsupportedOperationException 异常。注意在使用注入的映射器时不能访问那些方法。

无论 JDBC 连接是否设置为自动提交, SqlSession 数据方法的执行或在 Spring 事务之外 任意调用映射器方法都将会自动被提交。

如果你想编程式地控制事务,请参考 Spring 手册的 10.6 节。这段代码展示了如何手动 使用在 10.6.2 章节描述的 PlatformTransactionManager 来处理事务。

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
 
TransactionStatus status = txManager.getTransaction(def);
try {
  userMapper.insertUser(user);
}
catch (MyException ex) {
  txManager.rollback(status);
  throw ex;
}
txManager.commit(status);

注意这段代码展示了一个映射器,但它也能和 SqlSession 一起使用。

第五章 使用 SqlSession

MyBatis ,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射语句,提交或回滚连接,最后,当不再需要它的时 , 你可以关闭 session 使用 MyBatis-Spring 之后, 你不再需要直接使用 SqlSessionFactory ,因为你的 bean 可以通过一个线程安全的 SqlSession 来注入,基于 Spring 的事务配置 来自动提交,回滚,关闭 session

注意通常不必直接使用 SqlSession 在大多数情况下 MapperFactoryBean, 将会在 bean 中注入所需要的映射器。下一章节中的 MapperFactoryBean(6.1 )会解释这个细节。

SqlSessionTemplate

SqlSessionTemplate MyBatis-Spring 的核心。 这个类负责管理 MyBatis SqlSession, 调用 MyBatis SQL 方法, 翻译异常。 SqlSessionTemplate 是线程安全的, 可以被多个 DAO 所共享使用。

当调用 SQL 方法时, 包含从映射器 getMapper()方法返回的方法, SqlSessionTemplate 将会保证使用的 SqlSession 是和当前 Spring 的事务相关的。此外,它管理 session 的生命 周期,包含必要的关闭,提交或回滚操作。

SqlSessionTemplate 实现了 SqlSession 接口,这就是说,在代码中无需对 MyBatis SqlSession 进行替换。 SqlSessionTemplate 通常是被用来替代默认的 MyBatis 实现的 DefaultSqlSession , 因为模板可以参与到 Spring 的事务中并且被多个注入的映射器类所使 用时也是线程安全的。相同应用程序中两个类之间的转换可能会引起数据一致性的问题。

SqlSessionTemplate 对象可以使用 SqlSessionFactory 作为构造方法的参数来创建。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

这个 bean 现在可以直接注入到 DAO bean 中。你需要在 bean 中添加一个 SqlSession 属性,就像下面的代码:

public class UserDaoImpl implements UserDao {
 
  private SqlSession sqlSession;
 
  public void setSqlSession(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }
 
  public User getUser(String userId) {
    return (User) sqlSession.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

如下注入 SqlSessionTemplate:

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
  <property name="sqlSession" ref="sqlSession" />
</bean>

SqlSessionTemplate 有一个使用 ExecutorType 作为参数的构造方法。这允许你用来 创建对象,比如,一个批量 SqlSession,但是使用了下列 Spring 配置的 XML 文件:

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
  <constructor-arg index="1" value="BATCH" />
</bean>

现在你所有的语句可以批量操作了,下面的语句就可以在 DAO 中使用了。

public void insertUsers(User[] users) {
   for (User user : users) {
     sqlSession.insert("org.mybatis.spring.sample.mapper.UserMapper.insertUser", user);
   }
 }

注意,如果所需的执行方法和默认的 SqlSessionFactory 设置不同,这种配置风格才 能使用。

对这种形式需要说明的是当这个方法被调用时,不能有一个存在使用不同 ExecutorType 运行的事务。也要保证在不同的事务中,使用不同执行器来调用 SqlSessionTemplate , (比如 PROPAGATION_REQUIRES_NEW)或完全在一个事务外面。

SqlSessionDaoSupport

SqlSessionDaoSupport 抽象 的支 , 用来 为你 提供 SqlSession getSqlSession()方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法, 就像下面这样:

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
  public User getUser(String userId) {
    return (User) getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

通常 MapperFactoryBean 是这个类的首选,因为它不需要额外的代码。但是,如果你 需要在 DAO 中做其它非 MyBatis 的工作或需要具体的类,那么这个类就很有用了。

SqlSessionDaoSupport 需要一个 sqlSessionFactory sqlSessionTemplate 属性来 Spring , SqlSessionFactory 是被忽略的。

假设类 UserMapperImpl SqlSessionDaoSupport 的子类,它可以在 Spring 中进行如 下的配置:

<bean id="userMapper" class="org.mybatis.spring.sample.mapper.UserDaoImpl">
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

第六章 注入映射器

为了代替手工使用 SqlSessionDaoSupport SqlSessionTemplate 编写数据访问对象 (DAO)的代码,MyBatis-Spring 提供了一个动态代理的实现:MapperFactoryBean。这个类 可以让你直接注入数据映射器接口到你的 service bean 中。当使用映射器时,你仅仅如调 用你的 DAO 一样调用它们就可以了,但是你不需要编写任何 DAO 实现的代码,因为 MyBatis-Spring 将会为你创建代理。

使用注入的映射器代码, MyBatis,Spring MyBatis-Spring 上面不会有直接的依赖。 MapperFactoryBean 创建的代理控制开放和关闭 session,翻译任意的异常到 Spring DataAccessException 异常中。此外,如果需要或参与到一个已经存在活动事务中,代理将 会开启一个新的 Spring 事务。

MapperFactoryBean

数据映射器接口可以按照如下做法加入到 Spring :

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

MapperFactoryBean 创建的代理类实现了 UserMapper 接口,并且注入到应用程序中。 因为代理创建在运行时环境中(Runtime,译者注) ,那么指定的映射器必须是一个接口, 不是一个具体的实现类。

如果 UserMapper 有一个对应的 MyBatis XML 映射器文件, 如果 XML 文件在类路径的 位置和映射器类相同时, 它会被 MapperFactoryBean 自动解析。 没有必要在 MyBatis 配置文 , XML SqlSessionFactoryBean configLocation 属性(第三章)来获取更多信息。

注意, MapperFactoryBean 需要 SqlSessionFactory SqlSessionTemplate 时。 这些可以通过各自的 SqlSessionFactory SqlSessionTemplate 属性来设置, 或者可以由 Spring 来自动装配。如果两个属性都设置了,那么 SqlSessionFactory 就会被忽略,因为 SqlSessionTemplate 是需要有一个 session 工厂的设置; 那个工厂会由 MapperFactoryBean. 来使用。

你可以直接在 business/service 对象中以和注入任意 Spring bean 的相同方式直接注入映 射器:

<bean id="fooService" class="org.mybatis.spring.sample.mapper.FooServiceImpl">
  <property name="userMapper" ref="userMapper" />
</bean>

这个 bean 可以直接在应用程序逻辑中使用:

public class FooServiceImpl implements FooService {
 
  private UserMapper userMapper;
 
  public void setUserMapper(UserMapper userMapper) {
    this.userMapper = userMapper;
  }
 
  public User doSomeBusinessStuff(String userId) {
    return this.userMapper.getUser(userId);
  }
}

注意在这段代码中没有 SqlSession MyBatis 的引用。也没有任何需要创建,打开或 关闭 session 的代码,MyBatis-Spring 会来关心它的。

MapperScannerConfigurer

没有必要在 Spring XML 配置文件中注册所有的映射器。相反,你可以使用一个 MapperScannerConfigurer , MapperFactoryBean

要创建 MapperScannerConfigurer,可以在 Spring 的配置中添加如下代码:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="org.mybatis.spring.sample.mapper" />
</bean>

basePackage 属性是让你为映射器接口文件设置基本的包路径。 你可以使用分号或逗号 作为分隔符设置多于一个的包路径。每个映射器将会在指定的包路径中递归地被搜索到。

MapperScannerConfigurer 属性不支持使用了 PropertyPlaceholderConfigurer 的属 性替换,因为会在 Spring 其中之前来它加载。但是,你可以使用 PropertiesFactoryBean SpEL 表达式来作为替代。

, SqlSessionFactory SqlSessionTemplate , MapperScannerConfigurer 将会创建 MapperFactoryBean,之后自动装配。但是,如果你使 用了一个 以上的 DataSource , 么自动 装配可 能会失效 。这种 情况下 ,你可 以使用 sqlSessionFactoryBeanName sqlSessionTemplateBeanName 属性来设置正确的 bean 称来使用。这就是它如何来配置的,注意 bean 的名称是必须的,而不是 bean 的引用, ,value 属性在这里替代通常的 ref:

<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />

MapperScannerConfigurer annotationClass 属性指定了要寻找的注解名称。 markerInterface 属性指定了要寻找的父 接口。如果两者都被指定了,加入到接口中的映射器会匹配两种标准。默认情况下,这两个 属性都是 null,所以在基包中给定的所有接口可以作为映射器加载。

被发现的映射器将会使用 Spring 对自动侦测组件(参考 Spring 手册的 3.14.4)默认的命 名策略来命名。也就是说,如果没有发现注解,它就会使用映射器的非大写的非完全限定类 名。但是如果发现了@Component JSR-330 @Named 注解,它会获取名称。注意你可以 org.springframework.stereotype.Component , javax.inject.Named(如果你使用 JSE 6 的话)或你自己的注解(肯定是自我注解), 样注解将会用作生成器和名称提供器。

第七章 使用 MyBatis API

使用 MyBatis-Spring,你可以继续直接使用 MyBatis API。仅仅在代码中使用 Spring SqlSessionFactoryBean 来创建一个 SqlSessionFactory

public class UserMapperSqlSessionImpl implements UserMapper {
  // SqlSessionFactory would normally be set by SqlSessionDaoSupport
  private SqlSessionFactory sqlSessionFactory;
 
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
  }
 
  public User getUser(String userId) {
    // note standard MyBatis API usage - opening and closing the session manually
    SqlSession session = sqlSessionFactory.openSession();
 
    try {
      return (User) session.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
    } finally {
      session.close();
    }
  }
}

小心使用此选项, 因为错误的使用会产生运行时错误, 或者更糟糕的数据一致性的问题。 这些是告诫:

<!--[if !supportLists]-->·                       <!--[endif]-->它不会参与到 Spring 的事务之中。

<!--[if !supportLists]-->·                       <!--[endif]-->如果 SqlSession 使用 DataSource,它也会被 Spring 事务管理器使用,而且当前 有事务在进行时,这段代码会抛出异常。

<!--[if !supportLists]-->·                       <!--[endif]-->MyBatis DefaultSqlSession 是线程不安全的。如果在 bean 中注入了它,就会 发生错误。

<!--[if !supportLists]-->·                       <!--[endif]-->使用 DefaultSqlSession 创建的映射器也不是线程安全的。如果你将它们注入到 bean ,是会发生错误的。

<!--[if !supportLists]-->·                       <!--[endif]-->你必须保证在 finally 块中来关闭 SqlSession

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 for its predecessors: keep it simple. The main purpose behind JPetStore 6 is to demonstrate that a full web application can be built with just a few classes, and what is more important, with 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 a bit later, you will not find JDBC code, object creation, binding code or transaction handling code. What is more impressive is that you will not even 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 with no dependency on MyBatis.

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 need aMapperScannerConfigurer for that, as we will see 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 BeanActions so you can just use them without caring about its creation or lookup. Have a look atCatalogActionBean:

@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 insertLineItemmapper 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 7 with NetBeans 7:

<!--[if !supportLists]-->·                         <!--[endif]-->Download and install NetBeans 7.x JEE version with Tomcat 7

<!--[if !supportLists]-->·                         <!--[endif]-->In NetBeans select Team/Subversion/Checkout

<!--[if !supportLists]-->·                         <!--[endif]-->Enter "http://mybatis.googlecode.com/svn" as repository URL

<!--[if !supportLists]-->·                         <!--[endif]-->Enter "sub-projects/jpetstore-6/trunk" as remote repository folder

<!--[if !supportLists]-->·                         <!--[endif]-->Enter your projects folder name, in my case "/home/eduardo/NetBeansProjects/jpetstore6"

<!--[if !supportLists]-->·                         <!--[endif]-->NetBeans will prompt "A new project was found do you want to open it". Press ok

<!--[if !supportLists]-->·                         <!--[endif]-->A new project named "JPetStore Demo 6" will be shown in NetBeans projects tab

<!--[if !supportLists]-->·                         <!--[endif]-->Right click on jpetstore project and select "Run"

<!--[if !supportLists]-->·                         <!--[endif]-->Select Tomcat 7 Server

<!--[if !supportLists]-->·                         <!--[endif]-->JPetStore home page should be shown!!

These are the steps to run it in Eclipse. The process is a bit longer because Eclipse does not provide built-in SVN and maven support and does not provide an option to install Tomcat.

<!--[if !supportLists]-->·                         <!--[endif]-->Download and install a JDK 6 or later

<!--[if !supportLists]-->·                         <!--[endif]-->Download and upzip Eclipse

<!--[if !supportLists]-->·                         <!--[endif]-->Download and unzip Tomcat 7

<!--[if !supportLists]-->·                         <!--[endif]-->Run Eclipse

<!--[if !supportLists]-->·                         <!--[endif]-->In eclipse, go to Help/Eclipse Marketplace

<!--[if !supportLists]-->·                         <!--[endif]-->Install m2e plugin (maven)

<!--[if !supportLists]-->·                         <!--[endif]-->Install m2e-wtp plugin (maven for wtp)

<!--[if !supportLists]-->·                         <!--[endif]-->Install subclipse plugin (subversion)

<!--[if !supportLists]-->·                         <!--[endif]-->Go to SVN tab

<!--[if !supportLists]-->·                         <!--[endif]-->In SVN add a new repository "http://mybatis.googlecode.com/svn"

<!--[if !supportLists]-->·                         <!--[endif]-->In SVN go to "sub-projects/jpetstore-6/trunk"

<!--[if !supportLists]-->·                         <!--[endif]-->Mouse right click and select "Check out", name the project as "jpetstore"

<!--[if !supportLists]-->·                         <!--[endif]-->Go to Java EE tab

<!--[if !supportLists]-->·                         <!--[endif]-->Right click on jpetstore project and select "Configure/Convert to Maven Project"

<!--[if !supportLists]-->·                         <!--[endif]-->Right click on jpetstore project and select "run on server"

<!--[if !supportLists]-->·                         <!--[endif]-->Select Tomcat 7 Server and set your installation directory

<!--[if !supportLists]-->·                         <!--[endif]-->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 improvable (for example the missing tests!), change it, create a diff patch file and fill an issue with it in the tracker. Thanks in advance!!!

 

你可能感兴趣的:(spring)