SSM Chapter 07 MyBatis与Spring的整合

SSM Chapter 07 MyBatis 与 Spring 的整合 笔记

本章目标:

  • 掌握Spring与MyBatis的集成
  • 掌握使用SqlSessionTemplate实现整合
  • 掌握使用MapperFactoryBean实现整合
  • 掌握Spring的事务切面实现声明式事务处理
  • 掌握使用注解实现声明式事务处理

技术内容:

我们已经学习了在MyBatis的基础知识,能够使用MyBatis 进行数据库操作.而Spring框架通过IoC.AOP等机制,能够对项目中的组件进行解耦合管理,建立一个低耦合的应用架构.这大大增强了系统开发和维护的灵活性,便于功能扩展.

这里我们将利用 Spring 对 MyBatis 进行整合,在对组件实现解耦的同时,还能使MyBatis框架的使用变得更加方便和简单. 还能使MyBatis框架的使用变得更加方便和简单.

此外,通过Spring提供的声明式事务等服务,能够进一步简化代码,减少开发工作量,提高开发效率

1 . Spring 对 MyBatis 的整合思路

作为Beans容器,Spring框架提供IoC机制,可以接管所有组件的创建工作并进行依赖管理. 因而整合的主要工作是把MyBatis框架中所涉及的核心组件配置到Spring容器中,交给Spring来创建和管理.

具体来说,业务逻辑对象依赖基于MyBatis技术实现的DAO对象,核心是获取SqlSession实例.要获得SqlSession实例,则需要依赖SqlSessionFactory实例. 而SqlSessionFactory是SqlSessionFactoryBuilder 依据MyBatis配置文件中的数据源,SQL映射文件等信息来构建的.

针对上述依赖关系,以往我们需要自行编码通过SqlSessionFactoryBuilder读取配置文件,构建SqlSessionFactory,进而获得SqlSession实例,满足业务对象对于数据访问的需要.随着Spring框架的引入,以上流程将全部移交给Spring,发挥Spring 框架 Bean容器的作用,接管组件的创建工作,管理组件的生命周期,并对组件之间的依赖关系进行解耦合管理.

2 . Spring 整合 MyBatis 的准备工作

我们以超市订单系统的功能为例 来完成 Spring 与 MyBatis 的整合

1. 在项目中加入Spring MyBatis 及整合相关的 jar 文件

在使用 Spring 整合MyBatis 之前,首先要下载与整合相关的jar文件.由于Spring 3 的开发在 MyBatis 3 官方发布前就结束了,Spring 开发团队不想发布一个基于非发布版本的MyBatis的整合支持,因此Spring 3 没有提供了对MyBatis 3 的支持.

为了使Spring 3 支持 MyBatis 3, MyBatis 团队按照Spring 集成ORM框架的统一风格开发了相关的组件,令开发者可以在Spring中集成使用MyBatis

整合时,项目中需要mybatis-spring的jar文件可以在官网上下载,官网地址:http://www.mybatis.org/spring/zh/index.html 官网文档也参考此地址

由于在整合中还有用到Spring的数据源支持 以及 事务支持,因此在项目中还需要添加 spring-jdbc 和 spring-tx两个jar文件,项目所需的jar文件如下:

  • junit:junit:4.12
  • log4j:log4j:1.2.17
  • mysql:mysql-connector-java:5.1.38
  • org.springframework:spring-context:5.1.5.RELEASE
  • org.springframework:spring-aop:5.1.5.RELEASE
  • org.springframework:spring-beans:5.1.5.RELEASE
  • org.springframework:spring-core:5.1.5.RELEASE
  • org.springframework:spring-expression:5.1.5.RELEASE
  • org.aspectj:aspectjweaver:1.9.2
  • commons-logging:commons-logging:1.2
  • org.springframework:spring-test:5.1.5.RELEASE
  • org.springframework:spring-jdbc:5.1.5.RELEASE
  • org.springframework:spring-tx:5.1.5.RELEASE
  • commons-dbcp:commons-dbcp:1.4
  • commons-pool:commons-pool:1.6
  • org.mybatis:mybatis:3.5.1
  • org.mybatis:mybatis-spring:2.0.3

使用maven创建web项目,在pom.xml文件中加入如下的依赖:

<properties>
    <spring.version>5.1.5.RELEASE</spring.version>
</properties>
<dependencyManagement>
    <!--统一声明所有关于spring依赖的jar-->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>${spring.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <!--mybatis-spring-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.3</version>
    </dependency>

    <!--spring-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
    </dependency>

    <!--spring-jdbc-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
    </dependency>
    
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>

    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.38</version>
    </dependency>

    <!--dbcp-->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
        <version>2.6.0</version>
    </dependency>
</dependencies>

**分析 : **

解决问题的步骤如下 :

  • (1) 获取Spring 开发包 并为 工程添加Spring支持
  • (2) 为业务层 和 数据访问层 设计接口,声明所需方法
  • (3) 编写数据访问层UserDao的实现类,完成具体的持久化操作
  • (4) 在业务实现类中声明 UserDao 接口类型的属性 , 并添加适当的构造方法为属性赋值
  • (5) 在Spring的配置文件中,将DAO对象以构造注入的方式,赋值给业务实例中的UserDao类型的属性
  • (6) 在代码中获取Spring配置文件中装配好的业务类对象,实现程序功能

2. 建立目录结构,创建实体类

在cn.smbms.pojo包下创建实体类User.

public class User implements Serializable {
    private Integer id;			//用户id
    private String userCode;	//用户编码
    private String userName;	//用户名称
    private String userPassword;//用户密码
    private Integer gender;		//性别
    private Date  birthday;		//生日
    private String phone;		//联系方式
    private String address;		//地址
    private Integer userRole;	//用户角色id
    private Integer createdBy;	//创建者
    private Date creationDate;	//创建时间
    private Integer modifyBy;	//更新者
    private Date modifyDate;	//更新日期
    //省略getter和setter方法 以及toString方法
  }

3. 创建数据访问接口

在cn.smbms.dao.user 包中创建 实体类 User 对应的 DAO接口 UserMapper.这里先添加一个查询所有用户信息的方法,其他方法在需要时添加.

public interface UserMapper {
    /**
     * 查询所有的用户列表
     * @return
     */
    List<User> getUserList();
}

4. 配置SQL映射文件

同样在cn.smbms.dao.user包中,为UserMapper配置SQL语句映射文件UserMapper.xml,实现指定的查询映射:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.smbms.dao.user.UserMapper">
    <!--查询用户列表-->
    <select id="getUserList" resultType="user">
        select * from smbms_user
    </select>
</mapper>

5. 配置MyBatis 配置文件

编写MyBatis配置文件 mybatis-config.xml,设置所需参数

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!-- 这个配置使全局的映射器启用或禁用缓存 -->
        <setting name="cacheEnabled" value="true"/>
        <!-- :设置驱动程序等待数据库响应的秒数 -->
        <setting name="defaultStatementTimeout" value="5"/>
        <!-- 
         启用从经典数据库列名A_COLUMN到驼峰式经典Java属性名aColumn的自动映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--允许JDBC 生成主键。需要驱动器支持。如果设为了true,
这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 -->
        <setting name="useGeneratedKeys" value="true"/>
	</settings>
    <!--为实体类指定别名-->
    <typeAliases>
        <package name="cn.smbms.pojo"/>
    </typeAliases>
</configuration>

注意 :

在上述代码中的MyBatis 配置文件内容 与之前的相比简单了许多,这是因为Spring 可以接管MyBatis配置信息的维护工作.这里我们选择把数据源配置和SQL映射信息转移至 Spring 配置文件中进行管理,以了解如何在Spring中配置MyBatis.

3 . 实现Spring 对 MyBatis 的整合

如前文所述,Spring需要依次完成加载MyBatis配置信息,构建SqlSessionFactory 和 SqlSession实例,完成对业务逻辑对象的依赖注入等工作.需要注意的是,这些工作大多以配置文件的方式实现,无须编写相关类,大大简化了开发且更容易维护.Spring配置文件中的主要配置如下:

3.1 配置数据源

对于任何持久化解决方案,数据库连接都是首先要解决的问题. 在Spring 中,数据源作为一个重要的组件,可以进行单独配置和维护.接下来我们将MyBatis配置中有关数据源的配置,转移到Spring 配置文件中进行维护.

配置DBCP

在Spring中配置数据源,首先要选择一种具体的数据源实现技术. 目前流行的数据源有 dbcp,c3p0,druid…等,它们都实现了连接池的功能.

这里以配置dbcp数据源为例进行讲解,其他的数据源配置方法与此类似,大家可以自行查阅官方网站以及相关资料进行学习. dbcp数据源隶属于 Apache Commons 项目,因此使用dbcp数据源就需要下载 相关的jar文件(这里包括两个,分别是: commons-dbcp-1.4.jar 和 commons-pool-1.4.jar).

当然如果是maven项目,则需要在pom.xml文件中加入相关的依赖,前文中我们已经加入了. 依赖代码如下:

<!--commons - dbcp-->
<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.4</version>
</dependency>
<!--commons-pool-->
<dependency>
    <groupId>commons-pool</groupId>
    <artifactId>commons-pool</artifactId>
    <version>1.6</version>
</dependency>

也可使用dbcp2 包含了以上两个依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.6.0</version>
</dependency>

建立Spring 配置文件 applicationContext-mybatis.xml,配置数据源的关键代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置数据源  BasicDataSource类实现了DataSource接口,可以用于DBCP连接池的简单使用-->
    <bean id="dataSource"
          class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://127.0.0.1:3306/smbms?useUnicode=true&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
</beans>    

解释 BasicDataSource 配置如下:

参数描述
username传递给JDBC驱动的用于建立连接的用户名
password传递给JDBC驱动的用于建立连接的密码
url传递给JDBC驱动的用于建立连接的URL
driverClassName使用的JDBC驱动的完整有效的java 类名

注意:因为url属性的值包含特殊符号 " & ",所以赋值是使用了实体引用 “&amp;”,当然也可以使用<![CDATA[]]>标记

其他配置可参考官网 : http://commons.apache.org/proper/commons-dbcp/configuration.html

配置DriverManagerDataSource

使用Spring内置的连接池DriverManagerDataSource ,配置数据源, 代码如下:

<!--注入spring内置的数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url"
              value="jdbc:mysql://127.0.0.1:3306/smbms?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</bean> 

参考官网如下:

在这里插入图片描述

3.2 配置SqlSessionFactoryBean

配置完数据源之后,就可以在此基础上集合 SQL映射文件信息 以及 MyBatis配置文件中的其他信息,创建SqlSessionFactory 实例

在MyBatis中,SqlSessionFactory 的实例需要使用SqlSessionFactoryBuilder 创建,而在集成环境中,则可以使用MyBatis-Spring 整合包中的 SqlSessionFactoryBean来代替.SqlSessionFactoryBean封装了使用SqlSessionFactoryBuilder 创建 SqlSessionFactory 的过程,我们可以在Spring中以配置文件的形式,通过SqlSessionFactoryBean 获得 SqlSessionFactory 实例.

代码如下:

<!--配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory"  class="org.mybatis.spring.SqlSessionFactoryBean">
	<!--引用数据源组件-->
    <property name="dataSource" ref="dataSource"/>
    <!--引用MyBatis配置文件中的配置-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!--配置SQL映射文件信息-->
<!-- <property name="mapperLocations"> -->
    	<!--此处注意classpath 与 classpath* 的区别:
    	classpath:只会到你的class路径中查找找文件。
    	classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找。
		注意: 用classpath*:需要遍历所有的classpath,所以加载速度是很慢的;
		因此,在规划的时候,应该尽可能规划好资源文件所在的路径,尽量避免使用classpath*。-->
		
	 <!-- classpath*的使用:当项目中有多个classpath路径,并同时加载多个classpath路径下
	 (此种情况多数不会遇到)的文件,*就发挥了作用,如果不加*,则表示仅仅加载第一个classpath路径-->
       <!--  <list>
        	<value>classpath*:cn/smbms/dao/**/*.xml</value>
        </list>
</property>	-->
	<property name="mapperLocations"
                  value="classpath*:cn/smbms/mapper/*.xml" />
</bean>

上述代码中 通过 配置 id 为 sqlSessionFactory 的 Bean 即可获得 SqlSessionFactory实例

注意

逐个列出所有的SQL 映射文件比较烦琐, 在SqlSessionFactoryBean的配置中可以是使用mapperLocations属性扫描式加载SQL映射文件. 其中,"classpath*:cn/smbms/dao/**/*.xml"表示扫描 cn.smbms.dao 包及其任意层级子包中,任意名称的xml 类型文件.

此处注意classpathclasspath* 的区别:

classpath:只会到你的class路径中查找文件。
classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找。

注意: 用classpath*:需要遍历所有的classpath,所以加载速度是很慢的;

因此,在规划的时候,应该尽可能规划好资源文件所在的路径,尽量避免使用classpath*

classpath*的使用:当项目中有多个classpath路径,并同时加载多个classpath路径下(此种情况多数不会遇到)的文件,*就发挥了作用,如果不加*,则表示仅仅加载第一个classpath路径

除了数据源和SQL映射信息,其他的MyBatis 配置信息也可以转移至Spring 配置文件中进行维护,只需要通过SqlSessionFactoryBean 的 对应属性进行赋值 即可. 具体可查阅官网 以及相关资料进行学习.

3.3 使用SqlSessionTemplate 实现数据库的操作

SqlSessionTemplate 类实现了MyBatis 的SqlSession接口,可以替换MyBatis中原有的SqlSession实现类提供数据库访问操作.

使用SqlSessionTemplate可以更好的与Spring 服务融合并简化部分流程化的工作,可以保证和当前Spring事务相关联,自动管理会话的生命周期,包括必要的关闭,提交和回滚操作.

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSessionSqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。

当调用 SQL 方法时(包括由 getMapper() 方法返回的映射器中的方法),SqlSessionTemplate 将会保证使用的 SqlSession 与当前 Spring 的事务相关。此外,它管理 session 的生命周期,包含必要的关闭、提交或回滚操作。另外,它也负责将 MyBatis 的异常翻译成 Spring 中的 DataAccessExceptions

由于模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。避免在同一应用程序中的不同类之间混杂使用时 , 可能会引起数据一致性的问题。

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

配置SqlSessionTemplate 并在 UserMapper实现类中使用SqlSessionTemplate类,具体代码如下:

/**
 * 定义 DAO 接口的实现类,实现UserMapper接口
 */
public class UserMapperImpl implements UserMapper {
    private SqlSession sqlSession;

    public void setSqlSession(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }
    @Override
    public List<User> getUserList() {
        return sqlSession.selectList("cn.smbms.dao.user.UserMapper.getUserList");
    }

}

Spring 配置文件中的关键代码如下:

<!--定义sqlSessionTemplate实例时,需要通过构造方法注入 sqlSessionFactory实例,
    这里引用的是id 为 sqlSessionFactory的bean-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!-- 配置DAO组件 并 注入 SqlSessionTemplate 实例 -->
<bean id="userMapper" class="cn.smbms.dao.user.impl.UserMapperImpl">
    <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
注意:
  1. 创建SqlSessionTemplate 实例时,需要通过其构造方法注入SqlSessionFactory实例.这里引用的是前文配置过的id为sqlSessionFactory的Bean
  2. 与MyBatis 中默认的SqlSession 实现不同,SqlSessionTemplate是线程安全的,可以以单例模式配置并被多个DAO对象共用,而且不必为每个DAO单独配置一个SqlSessionTemplate.

3.4 编写业务逻辑代码并测试

完成DAO组件的装配,接下来就可以在cn.smbms.service.user 包中开发业务组件,并通过Spring装配.代码如下:

业务接口的关键代码:

public interface UserService {
    /**
     * 查询所用的用户信息
     * @return
     */
    List<User> findUserList();
}

业务接口实现类的关键代码:

public class UserServiceImpl implements UserService {
    private UserMapper userMapper;
    @Override
    public List<User> findUserList() {
        return userMapper.getUserList();
    }

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
}

Spring 配置文件中的关键代码:

<!--配置业务Bean 并注入 DAO 实例-->
<bean id="userService"
      class="cn.smbms.service.user.impl.UserServiceImpl">
    <property name="userMapper" ref="userMapper"/>
</bean>

完成所有组件的开发和装配之后,接下来就可以测试整合的效果了,测试方法中的关键代码如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-mybatis.xml")
public class TestUserService {
    private final Logger logger = Logger.getLogger(this.getClass());
    @Autowired
    private UserService userService;
    @Test
    public void testGetUserList(){
        List<User> userList = userService.findUserList();
        userList.forEach(logger :: info);
    }
}

Eclipse 运行测试代码,结果正确输出.但是,请注意 若是使用 IDEA 运行测试代码时,发现程序报错,报错信息如下:

###Caused by: org.springframework.core.NestedIOException: Failed to parse mapping resource: 'class path resource [cn/smbms/dao/user/UserMapper.xml]'; nested exception is java.io.FileNotFoundException: class path resource [cn/smbms/dao/UserMapper.xml] cannot be opened because it does not exist

这两句报错信息,很明显说的是 mapper.xml里的namespace报错 或者是方法名报错,但是仔细排查发现并未出现错误.错误原因究竟在哪儿呢?

这是因为 IDEA 在使用maven编译的时候,如果配置文件不是放在resources文件夹下就不会被执行编译,所以导致运行时找不到对应的xml映射文件。

解决方法:

  • (1) 在main/resources目录下创建 mapper目录, 将对应的xml映射文件拖到mapper目录下,然后修改applicationContext-mybatis.xml文件中的 SqlSessionFactory的属性 mapperLocations,改为如下代码:

    <!--配置SQL映射文件信息-->
    <!--<property name="mapperLocations">
        <list>
            <value>classpath*:mapper/*Mapper.xml</value>
        </list>
    </property>-->
    <property name="mapperLocations"
                    value="classpath*:mapper/*.xml" />
    

第一种方式的弊端是:破坏了项目的包结构.若项目代码已经很多了,而且很多人都在用,改变了整体结构就会变得很麻烦,所以建议采用第二种方式:

  • (2) 在项目pom.xml文件中找到<build></build>标签,在其中添加以下几行,用于扫描xml文件

    <resources>
        <resource>
        	<directory>src/main/java</directory>
        	<includes>
        		<include>**/*.xml</include>
        	</includes>
        </resource>
    </resources>
    

小结:

利用Spring框架和MyBatis-Spring整合资源提供的组件,能够以配置的方式得到数据源、SqlSessionFactoryBean、SqlSessionTemplate 等组件,并在此基础上完成DAO模块和业务模块的开发和装配,简化了开发过程且便于维护.

知识扩展:

针对SqlSessionTemplate 的使用,MyBatis-Spring 还提供了 SqlSessionDaoSupport 类 来简化 SqlSessionTemplate 的配置和获取. SqlSessionDaoSupport 用法如下:

修改 UserMapper的实现类,注视掉SqlSessionTemplate属性以及对应的setter方法,然后继承SqlSessionDaoSupport:

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> getUserList() {
        return this.getSqlSession().selectList("cn.smbms.dao.user.UserMapper.getUserList");
    }
}    

Spring 配置文件中的关键代码如下:

<!--将之前id 为 sqlSessionFactory的bean注视掉,重新配置dao-->
<bean id="userMapper" class="cn.smbms.dao.user.impl.UserMapperImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

SqlSessionDaoSupport类提供了setSqlSessionFactory()方法用来注入 SqlSessionFactory实例并创建SqlSessionTemplate实例,同时提供了getSqlSession()方法来返回创建好的SqlSessionTemplate实例.

**这样,DAO实现类 只需要 继承自SqlSessionDaoSupport类,就可以通过调用getSqlSession()方法获得创建好的SqlSessionTemplate实例,无须额外定义 SqlSession 属性 和 setter方法. **

而Spring的配置文件中 也无须再配置SqlSessionTemplate,只需要通过DAO对象的setSqlSessionFactory()方法为其注入SqlSessionFactory 即可,在一定程度上简化了DAO组件的开发.

如图:
在这里插入图片描述

4 . 注入映射器的实现

为了不需要一个个地注册所有的映射器。你可以让 MyBatis-Spring 对类路径进行扫描来发现它们。

有几种办法来发现映射器:

  • 使用 <mybatis:scan/> 元素
  • 使用 @MapperScan 注解
  • 在经典 Spring XML 配置文件中注册一个 MapperScannerConfigurer

<mybatis:scan/>@MapperScan 都在 MyBatis-Spring 1.2.0 中被引入。@MapperScan 需要你使用 Spring 3.1+。

4.1 使用MapperFacotoryBean注入映射器

与其在数据访问对象(DAO)中手工编写使用 SqlSessionDaoSupport 或 SqlSessionTemplate 的代码,还不如让 Mybatis-Spring 为你创建一个线程安全的映射器,这样你就可以直接注入到其它的 bean 中了

注册映射器的方法根据你的配置方法,即经典的 XML 配置或新的 3.0 以上版本的 Java 配置(也就是常说的 @Configuration),而有所不同。

**注意 : **

SQL 映射文件中须遵循以下命名规则:

  • 映射文件中的命名空间 和 映射接口 的名称相同;

  • 映射元素的id 和 映射器 接口的方法相同;

在之前案例的基础上,删除UserMapper的实现类UserMapperImpl,仅保留 UserMapper 接口 和 相关的SQL映射文件,在Spring 配置文件中,重新配置DAO组件,关键代码如下:

<!--配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--引用数据源组件-->
    <property name="dataSource" ref="dataSource"/>
    <!--引用MyBatis配置文件中的配置-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!--配置SQL映射文件的信息-->
    <!-- <property name="mapperLocations">
            <list>
            <value>classpath*:cn/smbms/dao/**/*.xml</value>
            </list>
     </property>
	<property name="mapperLocations"
                  value="classpath*:cn/smbms/mapper/*.xml" />-->
</bean>
<!--配置DAO-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<!--也可以使用构造注入的方式 注入接口的完全限定名-->
<!-- <constructor-arg value="cn.smbms.dao.UserMapper"/>-->
    <property name="mapperInterface" value="cn.smbms.dao.user.UserMapper"/>
</bean>
<!--配置业务Bean 并注入 DAO 实例-->
<bean id="userService" class="cn.smbms.service.user.impl.UserServiceImpl">
	<property name="userMapper" ref="userMapper"/>
</bean>

业务组件的定义 和 配置以及测试代码与之前的相同.无须手工编码定义UserMapper的实现类. 通过配置MapperFactoryBean即可自动生成,减少了DAO模块的编码工作量.

注意:
  • (1) 配置DAO组件 userMapper时,class属性不是某个实现类,而是MapperFactoryBean.

  • (2) 通过mapperInterface 属性 指定映射器, 而且只能是接口类型,不能是某个实现类.

  • (3) MapperFactoryBean 是 SqlSessionDaoSupport 的子类,需要通过 setSqlSessionFactory()方法注入SqlSessionFactory实例 以创建 SqlSessionTemplate 实例.

  • (4) **如果映射器对应的SQL文件与映射器的类路径相同,该映射文件可以自动被MapperFactoryBean解析.在此情况下,配置SqlSessionFactoryBean 时 可以不必指定 SQL 映射文件的位置. **

如上述代码 将 SqlSessionFactoryBean中元素的属性 mapperLocations 注释掉之后,程序仍能正常运行;反之,若映射器与映射文件的类路径不同,则仍需在配置SqlSessionFactoryBean时 明确指定 映射文件的位置.

4.2 使用MapperScannerConfigurer注入映射器:

**在Spring配置文件中 使用 MapperFactoryBean 对映射器做配置,简化了DAO模块的编码,不过如果映射器很多,相应的配置项应该也会很多. **

为了简化配置工作量,MyBatis-Spring 中提供了MapperScannerConfigurer,它可以扫描指定包中的接口,并将它们直接注册为MapperFactoryBean.

MapperScannerConfigurer 的配置方法如下:

<!--配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--引用数据源组件-->
    <property name="dataSource" ref="dataSource"/>
    <!--引用MyBatis配置文件中的配置-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!--配置SQL映射文件的信息-->
    <!-- <property name="mapperLocations">
            <list>
            <value>classpath*:cn/smbms/dao/**/*.xml</value>
            </list>
     </property>-->
</bean>
<!--配置DAO 注意:这里的sqlSessionFatory是按照类型自动注入的 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<!--注意:这里的sqlSessionFatory是按照类型自动注入的  -->
    <!--basePackage 属性 指定了扫描的基准包 MapperScannerConfigurer将递归扫描基准包下所有的接口
        并将它们动态注册为MapperFactoryBean,如此即可批量生产映射器实现类-->
    <property name="basePackage" value="cn.smbms.dao"/>
</bean>

basePackage 属性 指定了扫描的基准包 MapperScannerConfigurer将递归扫描基准包(包括各层级子包)下所有的接口. 如果他们在SQL映射文件中定义过,则将它们动态注册为MapperFactoryBean,如此即可批量生产映射器实现类

注意 :
  • (1) basePackage 属性中可以包含多个包名,多个包名之间可以使用逗号或分号隔开

  • (2) MapperScannerConfigurer 会为所有由它创建的映射器开启自动装配。

    也就是说:MapperScannerConfigurer创建的所有映射器实现都会(按照类型)被自动注入SqlSessionFactory 实例。因此在上面代码中 配置DAO组件时,无须显式注入SqlSessionFactory实例。

  • (3) 若环境中处于不同目的配置了多个SqlSessionFactory实例,自动装配将无法进行,此时应显式指定所依赖的SqlSessionFactory实例。

配置方式如下:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!--basePackage 属性 指定了扫描的基准包 MapperScannerConfigurer将递归扫描基准包下所有的接口
        并将它们动态注册为MapperFactoryBean,如此即可批量生产映射器实现类-->
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    <property name="basePackage" value="cn.smbms.dao"/>
</bean>

**注意 **

  • 此处使用的是sqlSessionFactoryBeanName属性而不是sqlSessionFactory属性,正如该属性名所表达的,这个属性关注的是Bean的名称,所以为其赋值使用的是value属性 而不是 ref属性.

通过配置MapperScannerConfigurer将映射器注册到Spring容器时,Spring会根据其接口名称为其命名,默认规则是首字母小写的非完全限定类名。

例如:UserMapper类型的组件会被默认命名为userMapper。按此命名规则,依然可以在Spring配置文件中按如下方式按业务组件注入映射器。

此处使用的是sqlSessionFactoryBeanName属性而不是sqlSessionFactory属性,正如该属性名所表达的,这个属性关注的是Bean的名称,所以为其赋值使用的是value属性 而不是 ref属性.

通过配置MapperScannerConfigurer将映射器注册到Spring容器时,Spring会根据其接口名称为其命名,默认规则是首字母小写的非完全限定类名.例如:UserMapper类型的组件会被默认命名为userMapper.按此命名规则,依然可以在Spring配置文件中按如下方式按业务组件注入映射器.

代码如下:

<!--配置业务Bean 并注入 DAO 实例-->
<bean id="userService" class="cn.smbms.service.user.impl.UserServiceImpl">
	<property name="userMapper" ref="userMapper"/>
</bean>

当然,更普遍的做法是,在MapperScannerConfigurer 自动完成映射器注册之后,使用@Autowired 或者 @Resource 注解实现对业务组件的依赖注入,以简化业务组件的配置. 使用注解对业务组件的定义和配置如下:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public List<User> findUserList() {
        return userMapper.getUserList();
    }

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
}

Spring 配置文件中的关键代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
		   http://www.springframework.org/schema/context
		   http://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注解扫描-->
    <context:component-scan base-package="cn.smbms.service"/>
    <!--省略配置数据源-->
    <!--省略 配置sqlSessionFactoryBean-->
    <!--省略 配置DAO -->
</beans>    

注意:Spring 配置文件中需要引入 context 命名空间

测试代码与之前相同

4.3 <mybatis:scan/>

<mybatis:scan/> 元素会发现映射器,它发现映射器的方法与 Spring 内建的 <context:component-scan/> 发现 bean 的方法非常类似。

示例如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://mybatis.org/schema/mybatis-spring
       http://mybatis.org/schema/mybatis-spring.xsd">

	<!-- 省略其他配置
		从基包开始递归搜索接口,并将其注册为MapperFactoryBeans。
        请注意,将只注册至少具有一个方法的接口;忽略具体类-->
    <mybatis:scan base-package="cn.smbms.mapper"/>

</beans>

base-package 属性允许你设置映射器接口文件的基础包。通过使用逗号或分号分隔,你可以设置多个包。并且会在你所指定的包中递归搜索映射器。

注意:
  • 不需要为 <mybatis:scan/> 指定 SqlSessionFactorySqlSessionTemplate,这是因为它将使用能够被自动注入的 MapperFactoryBean。但如果你正在使用多个数据源(DataSource),自动注入可能不适合你。在这种情况下,你可以使用 factory-reftemplate-ref 属性指定你想使用的 bean 名称。

  • <mybatis:scan/> 支持基于标记接口或注解的过滤操作。在 annotation 属性中,可以指定映射器应该具有的特定注解。而在 marker-interface 属性中,可以指定映射器应该继承的父接口。当这两个属性都被设置的时候,被发现的映射器会满足这两个条件。默认情况下,这两个属性为空,因此在基础包中的所有接口都会被作为映射器被发现。

提示 <context:component-scan/> 无法发现并注册映射器。映射器的本质是接口,为了将它们注册到 Spring 中,发现映射器必须知道如何为找到的每个接口创建一个 MapperFactoryBean

4.4 知识扩展:

使用注解注入映射器:

1. 创建普通的Java类,类名为AppConfig,包名为cn.smbms.config,代码如下:
@Configuration
@ComponentScan("cn.smbms.service")
@MapperScan("cn.smbms.dao")
public class AppConfig {
    @Bean
    public DataSource dataSource(){
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/smbms?useUnicode=true&characterEncoding=utf-8");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
    
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception{
        SqlSessionFactoryBean sqlSessionFactoryBean =
                new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource());
        //为实体类设置别名
        sqlSessionFactoryBean.setTypeAliasesPackage("cn.smbms.pojo");
        return sqlSessionFactoryBean.getObject();
    }
//    @Bean
//    public MapperScannerConfigurer mapperScannerConfigurer(){
//        MapperScannerConfigurer mapperScannerConfigurer =
//                new MapperScannerConfigurer();
//        mapperScannerConfigurer.setBasePackage("cn.smbms.dao");
//        return mapperScannerConfigurer;
//    }
}    

@MapperScan解释:这个注解具有与之前见过的 <mybatis:scan/> 元素一样的工作方式。它也可以通过 markerInterfaceannotationClass 属性设置标记接口或注解类

2. 创建测试类代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= AppConfig.class)
public class TestAppConfigUser {
    private final Logger logger = Logger.getLogger(this.getClass());
    @Autowired
    private UserService userService;
    @Test
    public void testGetUserList(){
        List<User> userList = userService.findUserList();
        userList.forEach(logger :: info);
    }
}

其他的不做改动,运行测试代码,控制台正常输出.

小结:

MapperScannerConfigurer 与 @Autowired 注解 或 @Resource 注解配置使用,自动创建映射器并注入给业务组件,最大限度的减少DAO组件与业务组件的编码和配置工作.而在实际开发中也是这种方式使用的更多,而基于全注解的配置用的很少,因此了解即可.

5 . 为业务层添加声明式事务

业务层的的职能不仅仅是调用DAO这么简单,事务处理是任何企业级应用开发中不能回避的一个重要问题. 以往我们通过在业务方法中硬编码的方式进行事务控制,这样做的弊端显而易见:事务代码分散在业务方法中难以重用,需要调整时工作量也比较大;复杂事务的编码难度较高,增加了开发难度等.

Spring 提供了声明式事务处理机制,它基于AOP实现,无须编写任何事务管理代码,所有的工作全在配置文件中完成.这意味着与业务代码完全分离,配置即可用,降低了开发和维护的难度.

5.1 配置声明式事务:

这里以添加用户的功能为例,介绍如何实现声明式事务处理.

  • 首先在UserMapper 和 UserMapper.xml中添加相关的方法和SQL映射,代码如下:

  • UserMapper.java 中 添加的关键代码:

    int add(User user);
    
  • UserMapper.xml 中 添加的关键代码:

    <!--新增用户信息-->
    <insert id="add" parameterType="user">
         insert into smbms_user
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="userCode !=null and userCode != ''">
                    userCode,
                </if>
                <if test="userName !=null and userName != ''">
                    userName,
              </if>
                <if test="userPassword !=null and userPassword != ''">
                    userPassword,
                </if>
                <if test="gender !=null">
                    gender,
                </if>
                <if test="birthday !=null">
                    birthday,
                </if>
                <if test="phone !=null and phone!='' ">
                    phone,
                </if>
                <if test="address !=null and address!=''">
                    address,
                </if>
                <if test="userRole !=null">
                    userRole,
                </if>
                <if test="createdBy !=null">
                    createdBy,
                </if>
                <if test="creationDate !=null">
                    creationDate
                </if>
            </trim>
            <trim prefix="values (" suffix=")" suffixOverrides=",">
                <if test="userCode !=null and userCode != ''">
                    #{userCode},
                </if>
                <if test="userName !=null and userName != ''">
                    #{userName},
                </if>
                <if test="userPassword !=null and userPassword != ''">
                    #{userPassword},
                </if>
                <if test="gender !=null">
                    #{gender},
                </if>
                <if test="birthday !=null">
                    #{birthday},
                </if>
                <if test="phone !=null and phone!='' ">
                    #{phone},
                </if>
                <if test="address !=null and address!=''">
                    #{address},
                </if>
                <if test="userRole !=null">
                    #{userRole},
                </if>
                <if test="createdBy !=null">
                    #{createdBy},
                </if>
                <if test="creationDate !=null">
                    #{creationDate}
                </if>
            </trim>
    </insert>
    

由于使用了MapperScannerConfigurer 动态创建映射器实现,没有自定义的接口实现类,也就没有相关代码需要修改.

  • 在业务组件中添加的相应的业务方法:

  • 业务接口中添加相关的业务方法,代码如下:

    boolean addNewUser(User user);
    
  • 业务实现类中的方法实现代码:

    @Override
    public boolean addNewUser(User user) {
        boolean result = userMapper.add(user)>0;
        //int i = 3/0;//模拟异常发生时 事务是否回滚
        return result;
    }
    

注意:

业务类中没有事务控制的相关代码,所以将 int i = 3/0 去掉其注释时,运行测试类发现事务并没有回滚.

接下来为业务方法配置事务切面,这里需要用到 tx 和 aop 两个命名空间下的标签,所以首先在Spring配置文件中导入这两个命名空间.代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">
       <!-- 省略具体配置 -->
</beans>       

接下来需要配置一个事务管理组件,它提供了对事务处理的全面支持和统一管理,在切面中相当于增强处理的角色.这里使用Spring 提供的事务管理器类 DataSourceTransactionManager , 其配置方式如下:

<!--省略数据源 SqlSessionFactoryBean DAO 以及 业务Bean的配置-->
<!--定义事务管理器-->
<bean id="txManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- <property name="dataSource" ref="dataSource"/>-->
	<constructor-arg ref="dataSource"/>
</bean>

**注意: **

配置DataSourceTransactionManage 时, 要为其注入事先定义好的数据源组件,可参考Spring的官网:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-first-example,如图所示:
在这里插入图片描述

与之前接触过的增强处理不同,事务管理器还可以进一步配置,以便更好的适应不同的业务方法对于事务的不同要求.

可以通过<tx:advice>标签配置事务增强,设定事务属性,为不同的业务方法指定具体的事务规则.其代码片段如下:

<!--通过<tx:advice>标签为指定的事务管理器 设置事务属性
  默认使用的事务管理器的bean的名字是:transactionManager,
  若定义的事务管理器的Bean的id是:transactionManager,则可以不用指定
 transaction-manager属性-->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <!--定义属性,声明事务规则-->
    <tx:attributes>
        <tx:method name="find*" propagation="SUPPORTS"/>
        <tx:method name="add*" propagation="REQUIRED"/>
        <tx:method name="del*" propagation="REQUIRED"/>
        <tx:method name="update*" propagation="REQUIRED"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<tx:advice>标签内可以设置id属性 和 transaction-manager属性.其中 transaction-manager 属性 引用一个事务管理器Bean.

详细配置可参考官网:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-first-example 。 如图所示:
在这里插入图片描述

**注意: **

transaction-manager 属性的默认值是 transactionManager,也就是说,如果定义的事务管理器Bean的名称是transactionManager,则可以不指定该属性值

除了这两个属性外,还可以通过<tx:attributes>子标签定制事务属性.事务属性通过<tx:method> 标签进行设置.Spring支持对不同的方法设置事务属性,所以可以为一个<tx:attributes>设置多个<tx:method> .

<tx:method>标签中中的name属性是必需的,用于指定匹配的方法.这里需要对方法名进行约定,可以使用通配符(*).其他属性均为可选,用于指定具体的事务规则,这些属性解释如下:

propagation : 事务传播机制.该属性可选的值 有 如下几种:

  • REQUIRED:默认值:表示如果存在事务,则支持当前事务,如果当前没有事务,则开启一个新的事务
  • PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  • REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
  • MANDATORY(强制的):支持当前事务,如果当前没有事务,就抛出异常
  • NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • NEVER:以非事务方式执行,如果当前存在事务,则抛出异常

**经验: **

  • REQUIRED : 能过满足大多数的事务需求,可以作为首选的事务传播行为

isolation : 事务隔离级别.即当前事务和其他事务的隔离程度,在并发事务处理的情况下,需要考虑它的设置.该属性可选的值有如下几种:

  • DEFAULT(一般情况下使用这种配置即可):这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别
  • READ_COMMITTED:读已提交的数据(会出现不可重复读,幻读)大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统
  • READ_UNCOMMITTED:保证了读取过程中不会读取到非法数据。
  • REPEATABLE_READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失
  • SERIALIZABLE:最严格的级别,事务串行执行,资源消耗最大;

timeout:事务超时时间,允许事务运行的最长时间,以秒为单位。默认值为-1,表示不超时

read-only:事务是否为只读,默认值为false

rollback-for:设定能够触发回滚的异常类型,Spring默认只在抛出RuntimeException时才标识事务回滚,可以通过全限定类名指定需要回滚事务的异常,多个类名用逗号隔开

no-rollback-for:设定不触发回滚的异常类型,Spring默认 checked Exception 不会触发事务回滚,可以通过全限定类名指定不需回滚事务的异常,多个类名用英文逗号隔开

关于事务规则更加详细的说明,请参考官网https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-txadvice-settings

设置完事务规则,最后还要定义切面,将事务规则应用到指定方法上.代码如下:

<!--定义切面-->
<aop:config>
    <!--定义切入点-->
    <aop:pointcut id="pointcut" expression="execution(* cn.smbms.service.user.*.*(..))"/>
    <!--将事务增强 与 切入点结合-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>

注意:

aop:advisor 和 advice-ref 属性引用的是通过<tx:advice> 标签设定了事务属性的组件.

至此 Spring的声明式事务就配置完成了,最后再总结一下配置的步骤:
  • **(1) 导入 tx 和 aop 命名空间 **
  • (2) 定义事务管理器Bean,并为其注入数据源Bean
  • (3) 通过 <tx:advice>配置事务增强,绑定事务管理器并针对不同方法定义事务规则
  • (4) 配置切面,将事务增强 与 方法切入点 组合

测试方法的关键代码如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-mybatis.xml")
public class TestUserService {
    private final Logger logger = Logger.getLogger(this.getClass());
    @Autowired
    private UserService userService;
    @Test
    public void testAddNewUser() throws Exception{
        User user = new User();
        user.setUserCode("tom");
        user.setUserName("Tom");
        user.setUserPassword("123");
        user.setAddress("New York");
        user.setGender(1);
        user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1990-09-09"));
        user.setPhone("13800000000");
        user.setCreatedBy(1);
        user.setCreationDate(new Date());
        boolean flag = userService.addNewUser(user);
        logger.info("是否添加成功==>"+flag);
    }
}

5.2 使用注解实现声明式事务处理

**Spring 还支持使用注解配置声明式事务,所使用的注解是 @Transactional **

首先 仍然需要在Spring配置文件中配置事务管理类,并添加对注解配置的事务的支持,代码如下:

<!--定义事务管理器-->
<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <!-- <property name="dataSource" ref="dataSource"/>-->
	<constructor-arg ref="dataSource"/>
</bean>
<!--启用事务注解 属性 transaction-manager的默认值是 transactionManager 
	若指定事务管理器的Bean的id 为 transactionManager,此属性可以不用指定 -->
<tx:annotation-driven transaction-manager="transactionManager" />

经过如上配置,程序边支持使用@Transactional 注解来配置事务了,代码如下:

@Transactional
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<User> findUserList() {
        return userMapper.getUserList();
    }
    @Override
    public boolean addNewUser(User user) {
        boolean result = userMapper.add(user)>0;
        int i = 3/0;//测试程序出现RuntimeException时,事务是否回滚
        return  result;
    }
}

在业务实现类上添加@Transactional 注解即为该类的所有业务方法统一添加事务处理,如果某一业务方法需要采用不同的事务规则,可以在该业务方法上添加@Transactional注解单独进行设置.

@Transactional 注解也可以设置事务属性的值,默认的@Transactional 设置如下:

  • 事务传播设置是 PROPAGATION_REQUIRED
  • 事务隔离级别是 ISOLATION_DEFAULT
  • 事务是读/写
  • 事务超时默认是依赖于事务系统的,或者事务超时没有被支持
  • 任何 RuntimeException 将触发事务回滚,但是任何checked Exception 将不触发事务回滚.

这些默认的设置当然也是可以改变的.@Transactional注解的各属性如表:

属性类型说明
propagation枚举型:Propagation可选的传播性设置。使用举例:@Transactional(propagation=Propagation.REQUIRES_NEW)
isolation枚举型:Isolation可选的隔离性级别。使用举例:@Transactional(isolation=Isolation.READ_COMMITTED)
readOnly布尔型是否为只读型事务。使用举例:@Transactional(readOnly=true)
timeoutint型(以秒为单位)事务超时。使用举例:Transactional(timeout=10)
rollbackFor一组Class类的实例,必须是Throwable的子类一组异常类,遇到时必须 回滚。使用举例:@Transactional(rollbackFor={SQLException.class}),多个异常用逗号隔开
rollbackForClassName一组Class类的名字,必须是Throwable的子类一组异常类名,遇到时必须回滚。使用举例:@Transactional( rollbackForClassName={“SQLException”}),多个异常用逗号隔开
noRollbackFor一组Class类的实例,必须是Throwable的子类一组异常类,遇到时必须不回滚
noRollbackForClassName一组Class类的名字,必须是Throwable的子类一组异常类名,遇到时必须不回滚

5.3 知识扩展:

全部使用注解实现编程式事务管理:

修改配置类的代码:

@Configuration
@ComponentScan(basePackages = "cn.smbms.service")
@MapperScan(basePackages = "cn.smbms.mapper")
@EnableTransactionManagement//启用事务管理注解
public class AppConfig {
    @Bean
    public DataSource dataSource(){
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf-8");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean =
                new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource());
        sqlSessionFactoryBean.setTypeAliasesPackage("cn.smbms.pojo");
        return sqlSessionFactoryBean.getObject();
    }
    @Bean
    public TransactionManager transactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }
}

业务类的注解与声明式事务中的注解一致

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值