1 场景
spring中整合mybatis。
mybatis官网:https://mybatis.org/mybatis-3/zh/index.html
spring-mybatis官网:http://mybatis.org/spring/zh/index.html
1.1 版本说明
jdk版本:1.8
spring版本:5.2.2.RELEASE
mysql版本:5.6.27
innoDb数据库引擎
1.2 代码地址
https://github.com/yjhcpdd/mymvc
1.3 项目结构
--java
--resources
--conf
-mybatis-config.xml*[1]
-spring.xml*********[4]
-spring-mvc.xml
-sping-mybatis.xml**[2]
--properties
-system.properties
-jdbc.properties****[3]
java外层包路径为:com.demo.cs
相关包路径如下:
com.demo.cs.template.bean
com.demo.cs.template.controller
com.demo.cs.template.mapper.auto.model
com.demo.cs.template.mapper.auto.xml
com.demo.cs.template.mapper.ext
com.demo.cs.template.mapper.ext.model
com.demo.cs.template.mapper.ext.xml
com.demo.cs.template.service
com.demo.cs.template.service.impl
1.4 建表语句
CREATE TABLE `temp_db_user` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT ,
`user_name` varchar(50) NULL DEFAULT '' ,
`age` int NULL ,
PRIMARY KEY (`id`)
);
2 整合步骤
2.1 配置maven依赖
pom.xml中配置相关依赖包(完整配置,可参见源码):
<properties>
......
<mysql.connector.java>5.1.30</mysql.connector.java>
</properties>
<dependencies>
....
<!-- ==========【mybatis相关】========== -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.java}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
</dependencies>
<build>
<resources>
<!-- 将src/main/java目录下的xml文件,也进行打包 -->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<!-- 默认情况下使用false,不进行过滤替占位符。为true的情况下,将会使得编译后目录下的doc,excel等二进制文件受影响无法打开 -->
<filtering>false</filtering>
</resource>
</resources>
</build>
2.2 配置数据库配置文件
创建数据库配置文件jdbc.properties
[3],增加如下配置:
#mysql数据库连接配置
mysql.jdbc.driver=com.mysql.jdbc.Driver
mysql.jdbc.url=jdbc:mysql://localhost:3306/db_test
mysql.jdbc.username=root
mysql.jdbc.password=root
2.3 创建spring全局配置
创建文件mybatis-config.xml[1],配置spring全局参数:
<?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="cacheEnabled" value="true"/>
<!-- 指定 MyBatis 所用日志的具体实现,未指定时将自动查找 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
2.4 spring整合mybatis
创建文件sping-mybatis.xml[2],在spring中整合mybatis。
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- =====数据源===== -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${mysql.jdbc.driver}"/>
<property name="url" value="${mysql.jdbc.url}"/>
<property name="username" value="${mysql.jdbc.username}"/>
<property name="password" value="${mysql.jdbc.password}"/>
</bean>
<!-- =====session工厂===== -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- mybatis全局配置文件 -->
<property name="configLocation" value="classpath:conf/mybatis-config.xml"/>
<!-- 加载mapper配置文件
(默认“src/main/java”路径下,只有.class文件会放到编译后的目录进行打包,其他xml等文件不会放到编译后目录,需在pom文件的build环节配置)
(需用"classpath*:",而不是"classpath:",否则单元测试时,无法加载到Mapper.xml)
-->
<property name="mapperLocations" value="classpath*:com/demo/cs/**/*.xml"/>
</bean>
<!-- =====mapper包扫描器,将扫描到的接口注册为dao层的bean===== -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 包扫描路径 -->
<property name="basePackage" value="com.demo.cs.template.mapper.auto,com.demo.cs.template.mapper.ext"/>
</bean>
<!-- =====事务管理器===== -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- =====事务支持一:支持式注解事务(@Transactional);注解式事务order为0,“注解事务”优先“声明式事务”===== -->
<tx:annotation-driven transaction-manager="transactionManager" order="0" />
<!-- =====事务支持二:支持声明式事务(方法名匹配);声明式事务order为1,“注解事务”优先“声明式事务”===== -->
<!-- 事务传播特性(事务通知) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务 -->
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<!-- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行 -->
<tx:method name="find" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置切面,使事务生效 -->
<aop:config >
<!-- 切点(配置增强业务点) -->
<aop:pointcut id="txPointcut" expression="execution(* com.demo.cs..*Service.*(..))"/>
<!-- 通知(增强) -->
<aop:advisor id="txAdvisor" advice-ref="txAdvice" pointcut-ref="txPointcut" order="1" />
</aop:config>
</beans>
同时,在web.xml
文件中加载spring配置文件时,增加对sping-mybatis.xml文件的加载:
<!-- ============【spring配置】============ -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:conf/spring.xml,
classpath:conf/spring-mybatis.xml
</param-value>
</context-param>
3 代码示例
在包com.demo.cs.template.mapper.ext中创建mapper的接口文件,在包com.demo.cs.template.mapper.ext.xml中创建mapper的xml配置文件。
数据库表temp_db_user对应实体类如下:
@Data
public class ExtTempDbUser {
private Integer id;
private String userName;
private Integer age;
}
3.1 xml配置
接口:
public interface ExtTempDbUserMapper {
List<ExtTempDbUser> getRecordList(ExtTempDbUser model);
void insertRecord(ExtTempDbUser model);
}
xml配置文件:
<mapper namespace="com.demo.cs.template.mapper.ext.ExtTempDbUserMapper">
<select id="getRecordList" parameterType="com.demo.cs.template.mapper.ext.model.ExtTempDbUser" resultType="com.demo.cs.template.mapper.ext.model.ExtTempDbUser">
select id,user_name,age from temp_db_user
<where>
<if test="id!=null">
and id = #{id}
</if>
<if test="userName!=null and userName!=''">
and user_name =#{userName}
</if>
<if test="age!=null">
and age = #{age}
</if>
</where>
</select>
<insert id="insertRecord" parameterType="com.demo.cs.template.mapper.ext.model.ExtTempDbUser">
insert into temp_db_user(id,user_name,age)value(
#{id},#{userName},#{age}
)
</insert>
</mapper>
3.2 注解配置
接口:
public interface ExtTempDbUserMapper {
@Delete("delete from temp_db_user where id=#{id}")
void deleteRecordById(int id);
}
3.3 spring调用
@Autowired
private ExtTempDbUserMapper extTempDbUserMapper;
4 事务校验
通过spring-mybatis.xml文件中的事务切面通知配置,可以知道,aop通过对service中的方法名进行匹配后,进行了事务增强。
<!-- 事务传播特性(事务通知) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务 -->
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<!-- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行 -->
<tx:method name="find" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
如果以bean的形式调用service方法时
(非内部的this调用),如果方法前缀满足要求
(insert开头、update开头、delete开头、find开头、find开头),则会进行相应的事务控制
及事务传播属性
的控制。如果service方法不满足前缀要求,此service方法,则不会受spring的事务管理
。
此章节主要通过mybatis执行日志的形式,查看:当事务管理器管理的方法调用非事务管理器管理的方法的影响。
4.1 带事务批量操作
调用:
tempDbUserService.insertBatch();
service代码:
@Override
public void insertBatch() {
ExtTempDbUser model1=new ExtTempDbUser();
model1.setUserName("测试1");
extTempDbUserMapper.insertRecord(model1);
ExtTempDbUser model2=new ExtTempDbUser();
model2.setUserName("测试2");
extTempDbUserMapper.insertRecord(model2);
}
日志:
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@315f43d5] will be managed by Spring
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试1(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0] from current transaction
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试2(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
结论:
开启事务,service内的每个数据库操作,共用一个JDBCConnection
315f43d5、共用一个session连接
23d1e5d0(session的id都一致),一起提交。
4.2 无事务批量操作
调用:
tempDbUserService.noTransInsertBatch();
service代码:
public void noTransInsertBatch() {
ExtTempDbUser model1=new ExtTempDbUser();
model1.setUserName("测试1");
extTempDbUserMapper.insertRecord(model1);
ExtTempDbUser model2=new ExtTempDbUser();
model2.setUserName("测试2");
extTempDbUserMapper.insertRecord(model2);
}
日志:
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2cc3ad05] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.jdbc.JDBC4Connection@3569fc08] will not be managed by Spring
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试1(String), null
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2cc3ad05]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7ac2e39b] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.jdbc.JDBC4Connection@3569fc08] will not be managed by Spring
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试2(String), null
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7ac2e39b]
结论:
service内的每个方法,共用一个connection
都会使用新的session连接
(非事务控制的session,session的id都不一致2cc3ad05/7ac2e39b,但是共用一个数据库连接
3569fc08),自己单独提交。
4.3 多个带事务的方法
调用:
System.out.println("----------第一个事务调用----------");
tempDbUserService.insertBatch();
System.out.println("----------第二个事务调用----------");
tempDbUserService.insertBatch();
代码:
@Override
public void insertBatch() {
ExtTempDbUser model1=new ExtTempDbUser();
model1.setUserName("测试1");
extTempDbUserMapper.insertRecord(model1);
ExtTempDbUser model2=new ExtTempDbUser();
model2.setUserName("测试2");
extTempDbUserMapper.insertRecord(model2);
}
日志:
----------第一个事务调用----------
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@315f43d5] will be managed by Spring
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试1(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0] from current transaction
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试2(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23d1e5d0]
----------第二个事务调用----------
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@435fb7b5]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@315f43d5] will be managed by Spring
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试1(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@435fb7b5]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@435fb7b5] from current transaction
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试2(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@435fb7b5]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@435fb7b5]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@435fb7b5]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@435fb7b5]
结论:
调用两个事务管理的批量操作,每个事务均会单独提交。
共用一个connection
315f43d5,每个事务使用独立的被spring管理的sqlSession(带事务)
23d1e5d0/435fb7b5。
4.4 事务方法内调用非事务方法
调用:
tempDbUserService.testNestTrasnAndNoTrans();
代码:
@Override
public void insertNestTrasnAndNoTrans() {
ExtTempDbUser model1=new ExtTempDbUser();
model1.setUserName("测试1 for trans");
extTempDbUserMapper.insertRecord(model1);
ExtTempDbUser model2=new ExtTempDbUser();
model2.setUserName("测试2 for trans");
extTempDbUserMapper.insertRecord(model2);
System.out.println("--------调用非事务管理方法--------");
tempDbUserService.noTransInsertBatch();
}
@Override
public void noTransInsertBatch() {
ExtTempDbUser model1=new ExtTempDbUser();
model1.setUserName("测试1-not trasn");
extTempDbUserMapper.insertRecord(model1);
ExtTempDbUser model2=new ExtTempDbUser();
model2.setUserName("测试2-not trasn");
extTempDbUserMapper.insertRecord(model2);
}
日志:
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591]
JDBC Connection [com.mysql.jdbc.JDBC4Connection@68fa0ba8] will be managed by Spring
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试1 for trans(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591] from current transaction
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试2 for trans(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591]
--------调用非事务管理方法--------
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591] from current transaction
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试1-not trasn(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591] from current transaction
==> Preparing: insert into temp_db_user(id,user_name,age)value( ?,?,? )
==> Parameters: null, 测试2-not trasn(String), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@704f1591]
结论:
此结论很重要,当前受事务管理的service方法,调用非事务管理的service方法时,非事务管理的service方法,也会受事务管理。
网友评论