不管哪种框架,都是实现Spring中提供的PlatformTransactionManager接口。让你的数据库操作交给Spring事务管理
Spring中事务控制的API介绍
PlatformTransactionManager接口
- 获取事务状态信息 TransactionStatus getTransaction(TransactionDifinition difinition)
- 提交事务 void commit(TransactionStatus status)
- 回滚事务 void rollback(TransactionDifinition difinition)
TransactionDifinition
事务定义的信息对象
- 获取事务对象的名称 String getName()
- 获取事务的隔离级别 int getIsolationLevel()
- 获取事务的传播行为 int getPropagationBehavior();
- 获取事务的超时时间 int getTimeout()
- 获取事务是否只读 boolean idReadOnly()
TransactionStatus
事务的传播行为:
主要解决的问题是在业务方法进行调试(嵌套)的时候,事务应该如何进行处理
- 保证在同一事务中
- PROPAGATION_REQUIRED 支持当前事务, 如果不存在 就新建一个(默认)事务
- PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务。
- PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常 - 保证不在同一事务中
- PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
- PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
- PROPAGATION_NEVER 以非事务方式运 行,如果有事务存在,抛出异常
- PROPAGATION_NESTED 如果当前事务存 在,则嵌套事务执行
超时时间
如果没有设定超时时间,默认是没有超时时间限制。默认值为-1。 如果设置了,以秒为单位的。
是否是只读事务
对于增删改设定为false,对于查询业务设定为true。默认值为false。
基于JDBC原生的事务控制演示
代码如下:
/*dao层接口代码如下:*/
//dao接口
public interface UserDao {
//CRUD
//添加用户
void addUser(User user);
}
/*dao层接口实现类代码如下*/
```java
/**
* UserDao接口实现类
*/
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Autowired//前提是Spring容器中含有该对象
private JdbcTemplate jdbcTemplate;
//Connection
@Autowired/*引入数据源*/
private DataSource dataSource;
@Override//对于数据库来说 增删改 都是更新
public void addUser(User user) {
Connection connection = null;
//开启事务
try {
connection = dataSource.getConnection();
//开启事务
connection.setAutoCommit(false);
PreparedStatement ps = connection.prepareStatement("insert into user values(null,?,?,?)");
ps.setString(1, user.getU_name());
ps.setInt(2, user.getU_age());
ps.setDouble(3, user.getU_salary());
int num = ps.executeUpdate();
//添加用户
//int num = jdbcTemplate.update("insert into user values(null,?,?,?)", user.getU_name(), user.getU_age(), user.getU_salary());
//进行事务的提交
//int i = 1/0;
connection.commit();
if (num > 0) {
System.out.println("数据添加成功!");
} else {
System.out.println("数据添加失败!");
}
} catch (Exception throwables) {
//有异常信息发生
System.out.println("此时添加过程中有异常信息。。。数据回滚。。。");
try {
//进行事务的回滚
connection.rollback();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
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"
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">
<!--组件扫描-->
<context:component-scan base-package="com.zhiyou100"/>
<!--context:property-->
<!--引入db.properties文件到spring容器中-->
<context:property-placeholder location="db.properties"/>
<!--配置数据源-->
<!--采用数据库连接池 依赖于数据源DataSource ComboPooledDataSource-->
<!--把ComboPooledDataSource该对象注入到Spring到容器中-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置JdbcTemplate对象-->
<!--该对象也依赖于数据源-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--属性注入 采用set方式-->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
db.properties数据库连接配置信息代码如下:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/db_spring?CharacterEncoding=UTF-8
jdbc.user=root
jdbc.password=admin
log4j配置文件如下:
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
基于XML配置的声明式事务控制[xml配置]
开发步骤:
- 添加Spring事务需要的jar包依赖
- 拷贝SpringTx_jdbc工程代码
- 书写service层的业务接口和实现类
- 配置事务管理器
- 配置事务通知 关联事务管理器
- 配置事务的通知属性
- 配置AOP切入点和事务通知,进行绑定
备注:把可能出现异常的代码放进try catch语句块中。当事务中出现异常,使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),进行手动回滚
当用try-catch包括dao层接口,如果有异常数据无法完成回滚因为我们把异常捕获了,所以spring事务就不回滚
代码示例如下:
实体类的代码如下:
@Component("user")
public class User implements Serializable {
@Value("1")
private Integer u_id;
@Value("小孙")
private String u_name;
@Value("20")
private Integer u_age;
@Value("10000")
private Double u_salary;
public Integer getU_id() {
return u_id;
}
public void setU_id(Integer u_id) {
this.u_id = u_id;
}
public String getU_name() {
return u_name;
}
public void setU_name(String u_name) {
this.u_name = u_name;
}
public Integer getU_age() {
return u_age;
}
public void setU_age(Integer u_age) {
this.u_age = u_age;
}
public Double getU_salary() {
return u_salary;
}
public void setU_salary(Double u_salary) {
this.u_salary = u_salary;
}
public User(Integer u_id, String u_name, Integer u_age, Double u_salary) {
this.u_id = u_id;
this.u_name = u_name;
this.u_age = u_age;
this.u_salary = u_salary;
}
public User() {
}
@Override
public String toString() {
return "User{" +
"u_id=" + u_id +
", u_name='" + u_name + '\'' +
", u_age=" + u_age +
", u_salary=" + u_salary +
'}';
}
}
/*dao层*/
public interface UserDao {
// 根据用户名称查询用户信息
User findUserByUsername(String username);
}
/*dao 层接口实现类代码如下:*/
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Autowired //前提是Spring容器含有该对象
private JdbcTemplate jdbcTemplate;
// Connection 引入数据源
@Autowired
private DataSource dataSource;
@Override
public User findUserByUsername(String username) {
// 根据用户名值查询一条记录 queryForObject方法
User user = null;
try {
user = jdbcTemplate.queryForObject("select * from user where u_name = ?", new BeanPropertyRowMapper<>(User.class), username);
}catch (Exception e) {
e.printStackTrace();
}
return user;
}
}
service层接口代码如下:
// 业务层接口
public interface UserService {
// 工资变更 把一个人工资的降一降 把另一个工资升一升
boolean changeSalary(String descUsername ,String ascUsername,double money);// 修改 写的动作 readOnly--->false
}
/*service层接口实现类代码如下*/
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public boolean setSalary(String ascSalary, String descSalary, double money) {
User asc = userDao.queryByUserName(ascSalary);
User desc = userDao.queryByUserName(descSalary);
asc.setU_salary(asc.getU_salary()+money);
desc.setU_salary(desc.getU_salary()-money);
boolean flan = true;
try {
userDao.updateUser(asc);
int i = 1 / 0;//此处为人为手动异常 为验证遇到异常是否自动回滚
userDao.updateUser(desc);
} catch (Exception e) {
flan = false;
System.out.println("出现异常。。。。事务回滚");
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return flan;
}
}
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:ts="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.zhiyou100"/>
<!--content:property-->
<!--引入db.properties文件到Spring容器-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置数据源-->
<!--采用数据库连接池 C3p0 依赖于数据源DataSource ComboPooledDataSource -->
<!--把ComboPooledDataSource该对象注入到Spring的容器中-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置JdbcTemplate对象-->
<!--该对象也依赖于数据源DataSource-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--属性注入 采用set方式-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--依赖于数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知 关联事务管理器-->
<ts:advice id="txAdvice" transaction-manager="transactionManager">
<ts:attributes>
<!--* 此时代表所有的方法-->
<ts:method name="*" propagation="REQUIRED" isolation="DEFAULT"/>
<ts:method name="findUserById" propagation="REQUIRED" isolation="DEFAULT"/>
<ts:method name="changeSalary" propagation="REQUIRED" isolation="DEFAULT"/>
</ts:attributes>
</ts:advice>
<!--配置AOP切入点-->
<aop:config >
<aop:pointcut id="pct" expression="execution(* com.zhiyou100.service..*.*(..))"/>
<!--配置aop通知 可以关联到事务通知和切入点-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pct"/>
</aop:config>
</beans>
db.properties的配置信息如下
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/db_spring?CharacterEncoding=UTF-8
jdbc.user=root
jdbc.password=admin
测试类代码如下
@RunWith(SpringJUnit4ClassRunner.class)
// @Configuration 配置Spring 一个主配置类
// @ContextConfiguration 读取applicationContext.xml
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateTest {
@Autowired
private UserService userService;
/**
*
* 测试 工资变更的事务
*/
@Test
public void testChangSalary(){
boolean flag = userService.changeSalary("李四", "张三", 1000);
System.out.println(flag);
}
}
验证后代码无异议,测试正常,这里就不展示测试结果了。。。。。。。
基于XML配置的声明式事务控制[全问注解配置]
此处实体类代码与dao层代码同上
service代码如下:
// 业务层接口
public interface UserService {
// 工资变更 把一个人工资的降一降 把另一个工资升一升
boolean changeSalary(String descUsername ,String ascUsername,double money);// 修改 写的动作 readOnly--->false
}
/*service层接口实现类代码如下:*/
@Service("userService")// 把UserServiceImpl类对象交给Spring管理
@Transactional(propagation = Propagation.REQUIRED, readOnly = true, isolation = Isolation.DEFAULT)//
public class UserServiceImpl implements UserService {
// 属性注入
@Autowired
private UserDao userDao;
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
@Override
public boolean changeSalary(String descUsername, String ascUsername, double money) {
// descUsername 等待降工资的人
// ascUsername 等待升工资的人
// 把降工资的人信息查询出来
User descUser = userDao.findUserByUsername(descUsername);
// 把升工资的人信息查询出来
User ascUser = userDao.findUserByUsername(ascUsername);
// 在当前工资的基础上减
descUser.setU_salary(descUser.getU_salary() - money);
// 在当前工资的基础上加
ascUser.setU_salary(ascUser.getU_salary() + money);
// 定义一个开关
boolean flag = false;
// 更新两个人的信息
try {
userDao.updateUserByUsername(descUser);
//int i = 1 / 0; // 算术异常
userDao.updateUserByUsername(ascUser);
} catch (Exception e) {
flag = true;
//当出现异常时进行手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return flag;
}
}
数据库工具类配置如下:
/**
* 配置数据源
*/
public class DBUtil {
//引入数据源的四大组件
@Value("${jdbc.driverClass}")
private String driverClass;
@Value("${jdbc.jdbcUrl}")
private String jdbcUrl;
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.password}")
private String password;
//构建数据源对象
@Bean(name = "dataSource")
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driverClass);
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
//构建Jdbc模版类,JdbcTemplate对象
@Bean(name = "jdbcTemplate")
public JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
}
Spring核心住配置类代码如下:
@Configuration//标记当前类为Spring的主配置类
@ComponentScan("com.zhiyou100")//把com.zhiyou100当前包的类以及子包中的类扫描到Spring容器中
@PropertySource("classpath:db.properties")//数据的四大组件
@Import({DBUtil.class,TransactionConfig.class})//把其他的配置类引入到当前主配置类中
@EnableTransactionManagement//标记当前的类具有事务管理职能
public class SpringConfiguration {
}
配置事务管理器的类的代码如下
/**
* 配置事务管理器
*/
public class TransactionConfig {
@Bean(name = "transactionManager")
public PlatformTransactionManager createTransactionManager(@Autowired DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
数据库连接配置信息如下(db.properties):
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/db_spring?CharacterEncoding=UTF-8
jdbc.user=root
jdbc.password=admin
@RunWith(SpringJUnit4ClassRunner.class)
// @Configuration 配置Spring 一个主配置类
// @ContextConfiguration 读取applicationContext.xml
@ContextConfiguration(classes = SpringConfiguration.class)
public class JdbcTemplateTest {
@Autowired
private UserService userService;
/**
*
* 测试 工资变更的事务
*/
@Test
public void testChangSalary(){
boolean flag = userService.changeSalary("李四", "张三", 1000);
System.out.println(flag);
}
}