简介
今天面试被问到Spring
事务的几种配置方法。主流的就是AOP
和注解
,非主流的忽略。
虽然学过AOP,可我一直都是用注解。哑口无言状态。。。233333
大部分时间我接触的事务处理都是用注解
,简单方便。但注解必须添加在目标方法上,如果手上没有源码就呵呵了。
通过配置切面的方式开启事务,可以为特定的包、特定前缀的方法,批量添加事务控制。
为了方便测试,我集成了MybatisPlus
依赖
/项目/pom.xml
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
<!-- 如果添加了 spring-aspects 此项可省 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
<scope>compile</scope>
</dependency>
事务配置
注意
- tx:advice 配置的事务,默认是触发
RuntimeException
时回滚。 - 如果想手动指定触发回滚的异常,看下面
add*
的配置。(似曾相识吧)
/项目/src/main/resources/applicationContext.xml
...
<!-- =========================== 事务 =========================== -->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务的传播特性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 写操作: 增、删、改 -->
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="upd*" propagation="REQUIRED" />
<!-- 只读事务:查 -->
<tx:method name="get*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置需要事务的方法 (切点) -->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.jerry.springmvc.service..*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
服务层(事务就在这干活)
/项目/src/main/java/com/jerry/springmvc/service/impl/PoemService.java
注意这里抛异常用的是RuntimeException
package com.jerry.springmvc.service.impl;
import java.io.Serializable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.jerry.springmvc.entity.Poem;
import com.jerry.springmvc.mapper.PoemMapper;
import com.jerry.springmvc.service.IPoemService;
/**
* 服务实现类
* @author jerryjin
* @since 2019-06-19
*/
@Service
public class PoemService extends ServiceImpl<PoemMapper, Poem> implements IPoemService {
@Override
public String updTransactionTest(Serializable id) throws Exception {
// 首次查询
Poem poem1 = selectById(id);
System.out.println("修改前:" + poem1);
// 修改作者名为 666 后更新数据库
poem1.setAuthor("666");
updateById(poem1);
Poem poem2 = selectById(id);
System.out.println("修改后:" + poem2);
// 最后抛出异常,触发回滚
throw new RuntimeException("改作者名称是盗版行为");
}
}
控制器
/项目/src/main/java/com/jerry/springmvc/controller/indexController.java
package com.jerry.springmvc.controller;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.jerry.springmvc.entity.Poem;
import com.jerry.springmvc.service.IPoemService;
@Controller
@Scope("prototype")
public class indexController extends BaseController {
@Resource
private IPoemService poemService;
@RequestMapping(value="/xmltx/{id}", produces={"application/json; charset=UTF-8"})
@ResponseBody
public Map<String, Object> updTransactionTest(@PathVariable Long id) {
Map<String , Object> map = new HashMap<String ,Object>();
try {
poemService.updTransactionTest(id);
} catch (Exception e) {
map.put("exception", e.getMessage());
}
System.out.println("回滚后:" + poemService.selectById(id));
map.put("pome", poemService.selectById(id));
return map;
}
}
访问 http://127.0.0.1/xmltx/1
控制台输出如下,回滚成功。
修改前:{"author":"笑虾","content":"<p>多情黯叹痴情癫,痴情苦笑多情难。</p><p>相思自古无良药,从来独步赴黄泉。</p","id":1,"title":"痴情癫"}
修改后:{"author":"666","content":"<p>多情黯叹痴情癫,痴情苦笑多情难。</p><p>相思自古无良药,从来独步赴黄泉。</p","id":1,"title":"痴情癫"}
回滚后:{"author":"笑虾","content":"<p>多情黯叹痴情癫,痴情苦笑多情难。</p><p>相思自古无良药,从来独步赴黄泉。</p","id":1,"title":"痴情癫"}
如果我不抛出异常则,作者被改成 666
修改前:{"author":"笑虾","content":"<p>多情黯叹痴情癫,痴情苦笑多情难。</p><p>相思自古无良药,从来独步赴黄泉。</p","id":1,"title":"痴情癫"}
修改后:{"author":"666","content":"<p>多情黯叹痴情癫,痴情苦笑多情难。</p><p>相思自古无良药,从来独步赴黄泉。</p","id":1,"title":"痴情癫"}
回滚后:{"author":"666","content":"<p>多情黯叹痴情癫,痴情苦笑多情难。</p><p>相思自古无良药,从来独步赴黄泉。</p","id":1,"title":"痴情癫"}
参考资料
Spring 事务学习笔记
Spring 学习笔记《注解方式配置事务》
Spring 事务传播行为 Transaction Propagation
Spring官网 - 定义切点的例子