版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
项目环境的搭建
beans.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" xmlns:p="http://www.springframework.org/schema/p"
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">
<!-- 开启注解 -->
<context:component-scan base-package="com.itmayiedu"></context:component-scan>
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ibis"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 配置jdbc数据源 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- <!– 配置事物 dataSource –>-->
<!-- <bean id="dataSourceTransactionManager"-->
<!-- class="org.springframework.jdbc.datasource.DataSourceTransactionManager">-->
<!-- <property name="dataSource" ref="dataSource"></property>-->
<!-- </bean>-->
<!-- <!– 开启事物注解 –>-->
<!-- <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>-->
</beans>
logdao
package com.itmayiedu.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class LogDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void addLog() {
String sql = "insert into log values(1,?);";
jdbcTemplate.update(sql, System.currentTimeMillis());
}
}
userdao
在这里插入代码片
l;ogservice
package com.itmayiedu.service;
import com.itmayiedu.dao.LogDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LogService {
@Autowired
private LogDao logDao;
@Transactional(propagation=Propagation.NEVER)
public void addLog() {
logDao.addLog();
System.out.println("我日志添加成啦");
}
}
userSevice
package com.itmayiedu.service;
import com.itmayiedu.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private LogService logService;
public void add() {
logService.addLog();
userDao.addUser("lisi-" + System.currentTimeMillis(), 18);
// int i = 1 / 0;
}
}
app
package com.itmayiedu;
import com.itmayiedu.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.add();
System.out.println(userService);
}
}
引出事务的产生的原因
此时当我UserSerice调用日志服务的时候,加入此时我userService服务报错,此时数据log的数据能否添加上?
答案都可以的。因为 他们已经做了操作,此时无法回滚啦,这就是
如果是这样呢?
此时只会添加log而不会添加用户。 这就是分布式事务的问题的演示
事务的解决方案
编程事务
在xml添加事务管理器
<!-- 配置事物 -->
<bean id="DataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
添加事务管理的配置类
package com.itmayiedu.transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
@Component
public class TransactionManager {
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
public TransactionStatus begin() {
return dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
}
public void commit(TransactionStatus transactionStatus) {
dataSourceTransactionManager.commit(transactionStatus);
}
public void rollback(TransactionStatus transactionStatus) {
dataSourceTransactionManager.rollback(transactionStatus);
}
}
修改service
package com.itmayiedu.service;
import com.itmayiedu.dao.UserDao;
import com.itmayiedu.transaction.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private LogService logService;
@Autowired
private TransactionManager transactionManager;
public void add() {
TransactionStatus begin = null;
try {
begin = transactionManager.begin();
logService.addLog();
int i = 1 / 0;
userDao.addUser("lisi-" + System.currentTimeMillis(), 18);
transactionManager.commit(begin);
} catch (RuntimeException r) {
transactionManager.rollback(begin);
new RuntimeException(r.getMessage());
}
}
}
此时运行会发现一方报错,双方都不会修改数据
增强事务
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="*" read-only="false" />
</tx:attributes>
</tx:advice>
<!-- Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
<aop:config>
<aop:pointcut expression="execution(* com.itmayiedu.service.*.*(..))"
id="pt" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
</aop:config>
public void add() {
TransactionStatus begin = null;
try {
logService.addLog();
int i = 1 / 0;
userDao.addUser("lisi-" + System.currentTimeMillis(), 18);
} catch (Exception e){
e.printStackTrace();
}
}
}
此时在进行添加的时候,我们发现log还是添加了数据这是为什么呢?
事物是程序运行如果没有错误,会自动提交事物,如果程序运行发生异常,则会自动回滚。
如果使用了try捕获异常时.一定要在catch里面手动回滚。
事物手动回滚代码
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
注解事务
<?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:p="http://www.springframework.org/schema/p"
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">
<!-- 开启注解 -->
<context:component-scan base-package="com.itmayiedu"></context:component-scan>
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ibis"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 配置jdbc数据源 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事物 dataSource -->
<!-- 配置事物 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">-->
<!-- <tx:attributes>-->
<!-- <tx:method name="get*" read-only="true" />-->
<!-- <tx:method name="find*" read-only="true" />-->
<!-- <tx:method name="*" read-only="false" />-->
<!-- </tx:attributes>-->
<!-- </tx:advice>-->
<!-- <!– Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 –>-->
<!-- <aop:config>-->
<!-- <aop:pointcut expression="execution(* com.itmayiedu.service.*.*(..))"-->
<!-- id="pt" />-->
<!-- <aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />-->
<!-- </aop:config>-->
<!-- 开启注解事物 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
</beans>
@Transactional
public void add() {
TransactionStatus begin = null;
logService.addLog();
int i = 1/0;
userDao.addUser("lisi-" + System.currentTimeMillis(), 18);
}
}
Spring的事务传播行为
需求的出现:根据我们刚刚的列子我们可以知道,如果log服务出错的话,相关的user的add服务也会回滚,但是在正常的服务的当中,日志影响正常服务的是是非的严重的,那么如何避免这个问题,引出今天的话题;
Spring中事务的定义:
Propagation(key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。)有以下选项可供使用:
• PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
• PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
• PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
• PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
• PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
• PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
详情参考这位大佬写的博客,本来写好啦,但是感觉别人写的比我好…
https://blog.csdn.net/weixin_39625809/article/details/80707695