背景
数据库操作过程中,对于增删改等操作,因为涉及到数据库状态的变更,为保证数据安全,需要进行事务管理;Spring事务管理有两种方式,即声明式事务管理和编程式事务管理;
配置
连接池配置:
# jdbc configuration
jdbc.url=jdbc\:mysql\://127.0.0.1\:3306/ssm?useUnicode\=true&characterEncoding\=utf8&characterSetResults\=utf8
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=123456
# 以下为 优化配置
jdbc.filters=stat
jdbc.maxActive=20
jdbc.initialSize=1
jdbc.maxWait=60000
jdbc.minIdle=10
jdbc.maxIdle=15
jdbc.timeBetweenEvictionRunsMillis=60000
jdbc.minEvictableIdleTimeMillis=300000
jdbc.validationQuery=SELECT 'x'
jdbc.testWhileIdle=true
jdbc.testOnBorrow=false
jdbc.testOnReturn=false
jdbc.maxOpenPreparedStatements=20
jdbc.removeAbandoned=true
jdbc.removeAbandonedTimeout=1800
jdbc.logAbandoned=true
<!-- 阿里 Druid 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 数据库基本信息配置 -->
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="filters" value="${jdbc.filters}" />
<!-- 最大并发连接数 -->
<property name="maxActive" value="${jdbc.maxActive}" />
<!-- 初始化连接数量 -->
<property name="initialSize" value="${jdbc.initialSize}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${jdbc.maxWait}" />
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="${jdbc.maxIdle}" />
<!-- 最小空闲连接数 -->
<property name="minIdle" value="${jdbc.minIdle}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${jdbc.validationQuery}" />
<property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
<property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
<property name="testOnReturn" value="${jdbc.testOnReturn}" />
<property name="maxOpenPreparedStatements" value="${jdbc.maxOpenPreparedStatements}" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="${jdbc.removeAbandoned}" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="${jdbc.logAbandoned}" />
</bean>
<!--事务管理器-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
编程式事务管理
代码演示:
package com.dongzz.ssm.modules.system.service;
import com.dongzz.ssm.SpringBaseJunit;
import com.dongzz.ssm.modules.system.entity.SysUser;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import javax.sql.DataSource;
/**
* 编程式事务管理
*/
public class UserServiceTest extends SpringBaseJunit {
@Autowired
PlatformTransactionManager transactionManager;
@Autowired
DataSource dataSource;
JdbcTemplate jdbcTemplate;
@Test
public void transactionTest() {
String sql1 = "update book set `name` = '算法导论(原书篇3版)' where id = 1";
String sql2 = "update book set `name` = 'Java编程思想(篇4版)' where id = 2";
// 设置事务隔离级别 传播特性
DefaultTransactionDefinition dtd = new DefaultTransactionDefinition();
dtd.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
dtd.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 获取事务状态,spring根据传播行为来开启事务
TransactionStatus status = transactionManager.getTransaction(dtd);
jdbcTemplate = new JdbcTemplate(dataSource);
try {
jdbcTemplate.update(sql1);
jdbcTemplate.update(sql2);
transactionManager.commit(status); // 提交事务
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback(status); // 回滚事务
}
}
}
借助 spring 的 TransactionTemplate
工具类简化事务管理编码;
@Test
public void transactionTemplateTest() {
String sql1 = "update book set `name` = '算法导论(原书篇3版)' where id = 1";
String sql2 = "update book set `name` = 'Java编程思想(篇4版)' where id = 2";
jdbcTemplate = new JdbcTemplate(dataSource);
TransactionTemplate template = new TransactionTemplate(transactionManager);
template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
template.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
jdbcTemplate.update(sql1);
jdbcTemplate.update(sql2);
}
});
}
声明式事务管理
注解式
spring xml 添加如下配置:
<!-- 开启 aop -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--事务管理器-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 注解式事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
使用演示:
在需要开启事务管理的类或方法上添加 @Transactional
注解即可;
package com.dongzz.ssm.modules.system.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import cn.hutool.core.util.StrUtil;
import com.dongzz.ssm.common.base.impl.BaseMybatisServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dongzz.ssm.common.plugin.datatables.PageTableHandler;
import com.dongzz.ssm.common.plugin.datatables.PageTableHandler.CountHandler;
import com.dongzz.ssm.common.plugin.datatables.PageTableHandler.ListHandler;
import com.dongzz.ssm.common.plugin.datatables.PageTableHandler.OrderHandler;
import com.dongzz.ssm.common.exception.ServiceException;
import com.dongzz.ssm.common.plugin.datatables.PageTableRequest;
import com.dongzz.ssm.common.plugin.datatables.PageTableResponse;
import com.dongzz.ssm.modules.system.dao.SysUserMapper;
import com.dongzz.ssm.modules.system.entity.SysUser;
import com.dongzz.ssm.modules.system.service.UserService;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServiceImpl extends BaseMybatisServiceImpl<SysUser> implements UserService {
@Autowired
private SysUserMapper userMapper;
@Override
@Transactional
public void addUser(SysUser user) throws Exception {
SysUser u = userMapper.selectUserByUname(user.getUsername());
if (null != u) {
throw new ServiceException("用户名已存在");
}
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
user.setStatus("1");
user.setIsDel("0");
userMapper.insertSelective(user);
}
}
AOP方式
spring xml 添加如下配置:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="add*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="save*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="delete*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.dongzz.*.service..*(..))" />
<aop:advisor pointcut-ref="pc" advice-ref="txAdvice" />
</aop:config>
上述配置将 txAdvice
切面中的通知方法动态的切入到名称为 pc
的切入点表达式对应的位置,所有通配符匹配的方法均纳入事务管理;