Spring中的事务管理
没有进行事务的管理,我们想让它们都成功才成功,有一个失败,就都失败,我们就应该需要事务!
以前我们都需要自己手动管理事务,十分麻烦!
但是Spring给我们提供了事务管理,我们只需要配置即可
Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。
Spring支持编程式事务管理和声明式的事务管理。
- 编程式事务管理
将事务管理代码嵌到业务方法中来控制事务的提交和回滚
缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码
- 声明式事务管理
-
一般情况下比编程式事务更优。
-
将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
-
将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。
使用Spring管理事务,引入头文件约束tx
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
设置事物管理器对象
无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法
要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象:
<!-- 配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
配置好事务管理器后我们需要去配置事务的通知
<!-- 结合AOP实现事务的织入
配置事务的通知
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 为哪些方法配置事务-->
<tx:attributes>
<!-- 配置事务的传播特性
为哪些方法进行事务传播行为
-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<!-- 查询则设置为只读-->
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
propagation属性配置事务的传播性
spring支持7种事务传播行为:
- REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
- REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
Spring中propagation属性默认是REQUIRED
这些配置将影响数据存储,必须根据情况选择。
isolation属性配置事务的隔离级别
spring支持五种事务隔离级别:默认隔离级别为DEFAULT
配置事务后切入AOP
<!-- 配置事务切入AOP-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.chenhui.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
测试示例:
mapper接口类:
public interface BookMapper {
//查询所有数据
List<Book> queryList();
//添加一条数据
int add(Book book);
//删除一条数据
int delete(int id);
}
Mapper.xml进行SQL语句配置:
<?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接口-->
<mapper namespace="com.chenhui.mapper.BookMapper">
<select id="queryList" resultType="book">
select * from book
</select>
<insert id="add" parameterType="book">
insert into book (id,name,price) values (#{id},#{name},#{price})
</insert>
<delete id="delete" parameterType="_int">
delete from book where id=#{id}
</delete>
</mapper>
mapper接口实现类:
public class BookMapperImpl extends SqlSessionDaoSupport implements BookMapper {
//执行查询数据的方法
public List<Book> queryList() {
//直接通过父类SqlSessionDaoSupport的getSqlSession获取sqlSessionTemplate对象
BookMapper mapper = super.getSqlSession().getMapper(BookMapper.class);
Book book = new Book(8, "xie", 4000.0);
//添加一条数据
mapper.add(book);
//自定义产生异常
int i=1/0;
//删除一条数据
mapper.delete(7);
//最后执行查询操作
return mapper.queryList();
}
public int add(Book book) {
BookMapper mapper = super.getSqlSession().getMapper(BookMapper.class);
return mapper.add(book);
}
public int delete(int id) {
BookMapper mapper = super.getSqlSession().getMapper(BookMapper.class);
return mapper.delete(id);
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置spring内置数据源Spring-JDBC-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_zr?serverTimezone=PRC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引用数据源-->
<property name="dataSource" ref="dataSource"/>
<!-- 绑定mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis.xml"/>
<!-- 配置别名-->
<property name="typeAliases" value="com.chenhui.pojo.Book"/>
<!--配置SQL语句mapper.xml文件-->
<property name="mapperLocations" value="classpath:com/chenhui/mapper/*.xml"/>
</bean>
<!-- 注册接口的实现类,交给spring来管理-->
<bean id="bookMapper" class="com.chenhui.mapper.BookMapperImpl">
<!--由于SqlSessionDaoSupport创建sqlSessionTemplate需要一个sqlSessionFactory对象,因此通过set方法注入-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!-- 配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<!-- 结合AOP实现事务的织入
配置事务的通知
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 为哪些方法配置事务-->
<tx:attributes>
<!-- 配置事务的传播特性
为哪些方法进行事务传播行为
-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<!-- 查询则设置为只读-->
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入AOP-->
<aop:config>
<!-- mapper包下的所有类、所有方法都配置切入点、-->
<aop:pointcut id="txPointcut" expression="execution(* com.chenhui.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
测试类:
public static void main(String[] args) {
//加载spring配置文件获取bean容器
ApplicationContext context=new ClassPathXmlApplicationContext("spring-dao.xml");
//获取bean对象
BookMapper bookMapper = context.getBean("bookMapper", BookMapper.class);
//执行方法
List<Book> books = bookMapper.queryList();
System.out.println(books);
}
如果没有配置事务管理:
如果配置事务管理:
执行后结果:整个方法未执行成功,数据库中不会添加一条数据