- 整合mybatis的目的
- 为了让spring去实现对mybatis的事务管理。
- 业务层对数据访问层的注入
- 自动创建SQLSessionFactory对象等
一、环境搭建
pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
applicationContext.xml
<!-- 开启注解支持 -->
<context:annotation-config />
<!-- 指定注解所在的包 让spring去扫描他们 -->
<context:component-scan base-package="com.ttc" />
<!--加载外部属性文件 -->
<context:property-placeholder location="db.properties" />
<!--配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxTotal" value="10" />
<property name="maxIdle" value="5" />
</bean>
<!--配置mybatis的SqlSessionFactory,使用mybatis-spring整合包中的 SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接池 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!--mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ttc.dao" />
</bean>
model
public class JdpcPojo {
private int start;
private int end;
// setter getter
}
public class StudentPojo {
private int id;
private String name;
private String sex;
private int age;
private String password;
private int cid;
//setter getter
}
mapper
@Repository
public interface StudentMapper {
public StudentPojo selectById(int id);
public List<StudentPojo> selectAll(JdpcPojo jdpcPojo);
}
<?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="com.ttc.dao.StudentMapper">
<resultMap type="com.ttc.pojo.StudentPojo" id="studentBO">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="age" column="age" />
<result property="sex" column="sex" />
<result property="password" column="password" />
<result property="cid" column="cid" />
</resultMap>
<sql id="BaseColumn">
id, name, age, sex, password, cid
</sql>
<select id="selectById" parameterType="int" resultMap="studentBO">
select <include refid="BaseColumn"/>
from student where id = #{id}
</select>
<select id="selectAll" parameterType="com.ttc.pojo.JdpcPojo" resultMap="studentBO">
select <include refid="BaseColumn" />
from student
limit #{start }, #{end}
</select>
</mapper>
test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTab {
@Autowired
private StudentMapper studentMapper;
@Test
public void testMybatis() {
JdpcPojo jdpcPojo = new JdpcPojo();
jdpcPojo.setStart(0);
jdpcPojo.setEnd(3);
List<StudentPojo> list = studentMapper.selectAll(jdpcPojo);
System.out.println(list);
StudentPojo studentPojo = studentMapper.selectById(1);
System.out.println(studentPojo);
}
}
二、事务管理
Spring对事务处理进行了抽象定义,不仅仅是简单的执行commit,rollback。
Spring事务管理主要包括3个接口PlatformTransactionManager、TransactionDefinition、TransactionStatus
- TransactionDefinition:事务详细信息的封装
隔离级别、传播、超时、只读。。 - TransactionStatus:事务具体运行状态
是否提交、是否完成、是否是新的事务。。 - PlatformTransactionManager:事务管理器
2.1 事务管理简介
- PlatformTransactionManager:继承了上面两个接口
是 Spring 提供的平台事务管理器,用于管理事务。该接口中提供了三个事务操作方法,具体如下。
- getTransaction:获取一个事务;
- commit:提交
- rollback:回滚
在项目中,Spring 将 xml 中配置的事务详细信息封装到对象TransactionDefinition 中,然后通过事务管理器的 getTransaction() 方法获得事务的状态(TransactionStatus),并对事务进行下一步的操作。
Spring为不同的持久化框架提供了不同事务管理器的接口实现:
事务 | 说明 |
---|---|
DataSourceTransactionManager | 适用于基本的jdbc及MyBatis |
HibernateTransactionManager | 用于整合Hibernate ORM框架 |
JpaTransactionManager | JavaEE里定的Jpa的持久化ORM框架 |
JtaTransactionManager | 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用 |
- TransactionDefinition:
TransactionDefinition 接口是事务定义(描述)的对象,它提供了事务相关信息获取的方法,其中包括五个操作,具体如下。
- String getName():获取事务对象名称。
- int getIsolationLevel():获取事务的隔离级别。
- int getPropagationBehavior():获取事务的传播行为。
- int getTimeout():获取事务的超时时间。
- boolean isReadOnly():获取事务是否只读。
事务的传播行为:
是指在同一个方法中,不同操作的前后所使用的事务。
当一个用于操作数据库的方法(dao层方法),执行过程中调用了另一个同样用于操作数据库的方法时,这两个方法之间的事务怎么处理,就是通过传播行为。
传播行为的种类:
属性名称 | 值 | 描述 |
---|---|---|
PROPAGATION_REQUIRED | required | 支持当前事务。如果 A 方法已经在事务中,则 B 事务将直接使用。否则将创建新事务 |
PROPAGATION_SUPPORTS | supports | 支持当前事务。如果 A 方法已经在事务中,则 B 事务将直接使用。否则将以非事务状态执行 |
PROPAGATION_MANDATORY | mandatory | 支持当前事务。如果 A 方法没有事务,则抛出异常 |
PROPAGATION_REQUIRES_NEW | requires_new | 将创建新的事务,如果 A 方法已经在事务中,则将 A 事务挂起 |
PROPAGATION_NOT_SUPPORTE | not_supported | 不支持当前事务,总是以非事务状态执行。如果 A 方法已经在事务中,则将其挂起 |
PROPAGATION_NEVER | never | 不支持当前事务,如果 A 方法在事务中,则抛出异常 |
PROPAGATION.NESTED | nested | 嵌套事务,底层将使用 Savepoint 形成嵌套事务 |
在事务管理过程中,传播行为可以控制是否需要创建事务以及如何创建事务。通常情况下,数据的查询不会改变原数据,所以不需要进行事务管理,而对于数据的增加、修改和删除等操作,必须进行事务管理。如果没有指定事务的传播行为,则 Spring默认的传播行为是 required。
- TransactionStatus:
TransactionStatus 接口是事务的状态,它描述了某一时间点上事务的状态信息。其中包含六个操作:
名称 | 说明 |
---|---|
void flush() | 刷新事务 |
boolean hasSavepoint() | 获取是否存在保存点 |
boolean isCompleted() | 获取事务是否完成 |
boolean isNewTransaction() | 获取是否是新事务 |
boolean isRollbackOnly() | 获取是否回滚 |
void setRollbackOnly() | 设置事务回滚 |
上述涉及的挂起,意思是开启一个独立的事务,已存在的事务暂停执行,等待新事物之星完毕后继续执行,两个事物不会相互影响。
2.2 手动实现事务
添加jar包:
<!-- 事务管理 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
配置bean
<import resource="classpath:mybatisConfig.xml" />
<!-- 事务管理器 -->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务的属性定义 -->
<bean class="org.springframework.transaction.support.DefaultTransactionDefinition">
<!-- 不止包含只读(不止包含查询语句)默认 -->
<property name="readOnly" value="false" />
<!-- 隔离级别默认repeat-able -->
<!-- 事务传递行为 -->
<property name="propagationBehavior" value="0"/>
<!-- <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/> -->
</bean>
结构:
- service - dao - mapper
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTab {
@Autowired
private StudentService studentService;
@Autowired
private DataSourceTransactionManager manager;
@Autowired
private DefaultTransactionDefinition definition;
@Test
public void testMybatis() {
TransactionStatus status = manager.getTransaction(definition);
try {
StudentPojo studentPojo2 = new StudentPojo();
studentPojo2.setName("tom");
studentPojo2.setSex("男");
studentPojo2.setPassword("password01");
studentPojo2.setAge(23);
studentPojo2.setCid(1);
studentService.insertStudent(studentPojo2);
int i = 1/0;
manager.commit(status);
} catch (Exception e) {
manager.rollback(status);
}
}
}
2.3 事务模板
配置bean
<import resource="classpath:mybatisConfig.xml" />
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务模板 -->
<bean class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTxAuto {
@Autowired
private StudentService studentService;
@Autowired
private TransactionTemplate template;
@Test
public void test() {
template.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus transactionStatus) {
StudentPojo studentPojo = studentService.selectStudentById(11);
System.out.println(studentPojo);
studentPojo.setName("java");
studentService.insertStudent(studentPojo);
int i = 1 / 0;
return null;
}
});
}
}
2.4 tx名称空间
<!-- 引入 xmlns:tx="http://www.springframework.org/schema/tx" -->
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- tx名称空间 专门处理事务 -->
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.ttc.service..*(..))" id="transactionPoint"/>
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="transactionPoint"/>
</aop:config>
service方法模拟异常
public void updateStudent(StudentPojo studentPojo) throws Exception {
studentMapper.updateStudent(studentPojo);
int i = 1/0;
}
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTxName {
@Autowired
private StudentService studentService;
@Test
public void test() {
StudentPojo studentPojo = studentService.selectStudentById(11);
System.out.println(studentPojo);
studentPojo.setName("java");
studentService.insertStudent(studentPojo);
}
}
2.5 注解方式配置
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 启动一个用于扫描事务注解的Bean即可 -->
<!-- <tx:annotation-driven transaction-manager="transactionManager"/> -->
<tx:annotation-driven /><!-- 默认会找到名字叫transactionManager的bean -->
service
@Transactional(propagation = Propagation.REQUIRED)
public void updateStudent(StudentPojo studentPojo) {
studentMapper.updateStudent(studentPojo);
int i = 1/0;
}
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransactional {
@Autowired
private StudentService studentService;
@Test
public void test(){
StudentPojo studentPojo = studentService.selectStudentById(11);
System.out.println(studentPojo);
studentPojo.setName("Java);
studentService.updateStudent(studentPojo);
}
}