目标:熟悉事务管理的核心接口,能够说出它的3个核心接口及内容
spring-tx-5.2.8.RELEAS依赖包的3个接口
- PlatformTransactionManager接口:可以根据属性管理事务。
- TransactionDefinition接口:用于定义事务的属性。
- TransactionStatus接口:用于界定事务的状态。
1.PlatformTransactionManager接口
PlatformTransactionManager接口主要用于管理事务,该接口中提供了三个管理事物的方法。
在实际应用中,Spring事务管理实际是由具体的持久化技术完成的,而
PlatformTransactionManager接口只提供统一的抽象方法。为了应对不同持久化技术的差异性,Spring为他们提供了具体的实现类,例如,Spring为Spring JDBC和MyBatis等依赖于DataSource的持久化技术提供了实现类DataSourceTransactionManager,如此以来,Spring JDBC或MyBatis等持久化技术的事务管理可以由DataSourceTransactionManager类实现,而且Spring 可以通过PlatformTransactionManager接口对这些实现类进行统一管理。
2.TransactionDefinition接口
TransactionDefinition接口中定义了事务描述相关的常量,其中包括了事务的隔离级别、事务的传播行为、事务的超时时间和是否为只读事务。
事务的传播行为
事务的传播行为是指处于不同事务中的方法在相互调用时,方法执行期间,事务的维护情况。例如,当一个事务的方法B调用另一个事务的方法A时,可以规定A方法继续在B方法所属的现有事务中运行,也可以规定A方法开启一个新事务,在新事务中运行,B方法所属的现有事务先挂起,等A方法的新事务执行完毕后再恢复。
TransactionDefinition接口中定义的7种事务传播行为
事务的超时时间
事务的超时时间是指事务执行的时间界限,超过这个时间界限,事务将会回滚。TransactionDefinition接口提供了TIMEOUT_DEFAULT常量定义事务的超时时间。
是否为只读事务
当事务为只读时,该事务不修改任何数据,只读事务有助于提升性能,如果在只读事务中修改数据,会引发异常。TransactionDefinition接口中除了提供事务的隔离级别、事务的传播行为、事务的超时时间和是否为只读事务的常量外,还提供了一系列方法来获取事务的属性。
TransactionDefinition接口常用方法
3.TransactionStatus接口
TransactionStatus接口主要用于界定事务的状态,通常情况下,编程式事务中使用该接口较多。TransactionStatus接口提供了一系列返回事务状态信息的方法,具体如下。
熟悉事务管理的方式,能够说出Spring事务管理的两种方式分别是什么
Spring中的事务管理分为两种方式,一种是传统的编程式事务管理,另一种是声明式事务管理。
- 编程式事务管理:通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
- 声明式事务管理:通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP技术将事务管理的“切面”代码植入到业务目标类中。
编写式事务管理有点落后,一般使用第二种,声明式事务管理
实现XML方式的声明式事务
没有配置事务具体实现代码:
这是一开始表中的数据:
现在模拟zhangsan给lisi转账100元
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>_20230319</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.6</version>
</dependency>
<!-- 事务相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
</project>
AccountDaoImpl:
package cn.hdc.dao.impl;
import cn.hdc.dao.AccountDao;
import cn.hdc.model.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Integer addAccount(Account account) {
String sql = "insert into account(username,balance) values(?,?)";
Object[] params = new Object[]{account.getUsername(), account.getBalance()};
jdbcTemplate.update(sql, params);
return jdbcTemplate.update(sql, params);
}
@Override
public Integer deleteAccount(Integer id) {
String sql = "delete from account where id = ?";
return jdbcTemplate.update(sql, id);
}
@Override
public Integer updateAccount(Account account) {
String sql = "update account set username=?,balance=? where id=?";
Object[] params = new Object[]{account.getUsername(), account.getBalance(), account.getId()};
return jdbcTemplate.update(sql, params);
}
@Override
public Account findAccountById(Integer id) {
String sql = "select * from account where id = ?";
RowMapper<Account> accountRowMapper = new BeanPropertyRowMapper<>(Account.class);
Account account = jdbcTemplate.queryForObject(sql, accountRowMapper, id);
return jdbcTemplate.queryForObject(sql, accountRowMapper, id);
}
@Override
public List<Account> findAll() {
String sql = "select * from account";
RowMapper<Account> accountRowMapper = new BeanPropertyRowMapper<>(Account.class);
List<Account> list = jdbcTemplate.query(sql, accountRowMapper);
return list;
}
//模拟转账操作
@Override
public void transfer(String outUser, String inUser, Double money) {
//收款
jdbcTemplate.update("update account set balance = balance + ? where username = ?", money, inUser);
//模拟异常
int i = 10 / 0;
//付款
jdbcTemplate.update("update account set balance = balance - ? where username = ?", money, outUser);
}
}
AccountDao接口:
package cn.hdc.dao;
import cn.hdc.model.Account;
import java.util.List;
public interface AccountDao {
public Integer addAccount(Account account);
public Integer deleteAccount(Integer id);
public Integer updateAccount(Account account);
public Account findAccountById(Integer id);
public List<Account> findAll();
public void transfer(String outUser, String inUser, Double money);
}
Account实体类:
package cn.hdc.model;
public class Account {
private Integer id;
private String username;
private Double balance;
@Override
public String toString() {
return "Account{" +
"id=" + id +
", username='" + username + '\'' +
", balance=" + balance +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
}
测试类:
import cn.hdc.dao.AccountDao;
import cn.hdc.dao.impl.AccountDaoImpl;
import cn.hdc.model.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class APP {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
AccountDao accountDao = (AccountDao) context.getBean("accountDao");
accountDao.transfer("zhangsan", "lisi", 100.0);
// List<Account> list = accountDao.findAll();
// list.forEach(account -> {
// System.out.println(account);
// });
// Account account = accountDao.findAccountById(1);
// System.out.println(account);
// Integer ret = accountDao.deleteAccount(2);
// if (ret > 0) {
// System.out.println("删除成功!");
// } else {
// System.out.println("删除失败!");
// }
// Account account1 = new Account();
// account1.setId(2);
// account1.setUsername("zhangsan");
// account1.setBalance(5000.02153);
// Integer ret = accountDao.updateAccount(account1);
// if (ret > 0) {
// System.out.println("修改成功!");
// } else {
// System.out.println("修改失败!");
// }
// Account account = new Account();
// account.setUsername("tom");
// account.setBalance(1000.011);
// Integer ret = accountDao.addAccount(account);
// if (ret > 0) {
// System.out.println("插入成功!");
// } else {
// System.out.println("插入失败!");
// }
// jdbcTemplate.execute("create table account" +
// "(" +
// " id int primary key auto_increment," +
// " username varchar(50)," +
// " balance double" +
// ");");
// System.out.println("account表创建成功!");
// UserDaoImpl userDao = (UserDaoImpl) context.getBean("userDao");
// System.out.println(userDao.getJdbcTemplate());
}
}
运行结果:
看一下表数据:
张三的钱没扣!
接下来我们使用事务
具体实现代码:
现在还原一下表
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>_20230319</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.6</version>
</dependency>
<!-- 事务相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
AccountDao接口:
package cn.hdc.dao;
import cn.hdc.model.Account;
import java.util.List;
public interface AccountDao {
public Integer addAccount(Account account);
public Integer deleteAccount(Integer id);
public Integer updateAccount(Account account);
public Account findAccountById(Integer id);
public List<Account> findAll();
public void transfer(String outUser, String inUser, Double money);
}
AccountDaoImpl
package cn.hdc.dao.impl;
import cn.hdc.dao.AccountDao;
import cn.hdc.model.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Integer addAccount(Account account) {
String sql = "insert into account(username,balance) values(?,?)";
Object[] params = new Object[]{account.getUsername(), account.getBalance()};
jdbcTemplate.update(sql, params);
return jdbcTemplate.update(sql, params);
}
@Override
public Integer deleteAccount(Integer id) {
String sql = "delete from account where id = ?";
return jdbcTemplate.update(sql, id);
}
@Override
public Integer updateAccount(Account account) {
String sql = "update account set username=?,balance=? where id=?";
Object[] params = new Object[]{account.getUsername(), account.getBalance(), account.getId()};
return jdbcTemplate.update(sql, params);
}
@Override
public Account findAccountById(Integer id) {
String sql = "select * from account where id = ?";
RowMapper<Account> accountRowMapper = new BeanPropertyRowMapper<>(Account.class);
Account account = jdbcTemplate.queryForObject(sql, accountRowMapper, id);
return jdbcTemplate.queryForObject(sql, accountRowMapper, id);
}
@Override
public List<Account> findAll() {
String sql = "select * from account";
RowMapper<Account> accountRowMapper = new BeanPropertyRowMapper<>(Account.class);
List<Account> list = jdbcTemplate.query(sql, accountRowMapper);
return list;
}
//模拟转账操作
@Override
public void transfer(String outUser, String inUser, Double money) {
//收款
jdbcTemplate.update("update account set balance = balance + ? where username = ?", money, inUser);
//模拟异常
int i = 10 / 0;
//付款
jdbcTemplate.update("update account set balance = balance - ? where username = ?", money, outUser);
}
}
Account实体类:
package cn.hdc.model;
public class Account {
private Integer id;
private String username;
private Double balance;
@Override
public String toString() {
return "Account{" +
"id=" + id +
", username='" + username + '\'' +
", balance=" + balance +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
}
applicationContext.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:content="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://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/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置一下:bean注解扫描的根路径(方面后面更简单存储对象到spring容器)-->
<!-- <content:component-scan base-package="cn.hdc"></content:component-scan>-->
<!-- 定义数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
<property name="username" value="root"></property>
<property name="password" value="666666"></property>
</bean>
<!-- 定义jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 普通类-->
<bean id="userDao" class="cn.hdc.dao.impl.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- accountDao类-->
<bean id="accountDao" class="cn.hdc.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 事务管理对象,注意:id必须叫做 transactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- xml事务配置-->
<!-- 事务通知相关的属性配置-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false" isolation="DEFAULT" timeout="-1"/>
</tx:attributes>
</tx:advice>
<!-- 事务的aop-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* cn.hdc.dao.impl.AccountDaoImpl.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
</beans>
测试类:
import cn.hdc.dao.AccountDao;
import cn.hdc.dao.impl.AccountDaoImpl;
import cn.hdc.model.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class APP {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
AccountDao accountDao = (AccountDao) context.getBean("accountDao");
accountDao.transfer("zhangsan", "lisi", 100.0);
// List<Account> list = accountDao.findAll();
// list.forEach(account -> {
// System.out.println(account);
// });
// Account account = accountDao.findAccountById(1);
// System.out.println(account);
// Integer ret = accountDao.deleteAccount(2);
// if (ret > 0) {
// System.out.println("删除成功!");
// } else {
// System.out.println("删除失败!");
// }
// Account account1 = new Account();
// account1.setId(2);
// account1.setUsername("zhangsan");
// account1.setBalance(5000.02153);
// Integer ret = accountDao.updateAccount(account1);
// if (ret > 0) {
// System.out.println("修改成功!");
// } else {
// System.out.println("修改失败!");
// }
// Account account = new Account();
// account.setUsername("tom");
// account.setBalance(1000.011);
// Integer ret = accountDao.addAccount(account);
// if (ret > 0) {
// System.out.println("插入成功!");
// } else {
// System.out.println("插入失败!");
// }
// jdbcTemplate.execute("create table account" +
// "(" +
// " id int primary key auto_increment," +
// " username varchar(50)," +
// " balance double" +
// ");");
// System.out.println("account表创建成功!");
// UserDaoImpl userDao = (UserDaoImpl) context.getBean("userDao");
// System.out.println(userDao.getJdbcTemplate());
}
}
运行结果:
表中数据没有变化,事务发生了回滚。
基于注解方式的声明式事务
@Transactional的属性
AccountDaoImpl在原来的基础上加上注解
applicationContext.xml
运行结果:
表中数据没有发生改变,事务生效