在 Spring 框架中事务管理有两种方式:一种是传统的编程式事务管理,即通过编写代码实现的事务管理;另一种是基于 AOP 技术实现的声明式事务管理。由于在 Spring 框架中,编程式事务管理很少使用,所以我们只对 Spring 的声明式事务管理进行详细讲解。
Spring 的声明式事务管理在底层采用了 AOP 技术,其最大的优点在于无须通过编程的方式管理事务,只需要在配置文件中进行相关的规则声明,就可以将事务规则应用到业务逻辑中。
Spring 实现声明式事务管理主要有两种方式:
在 Spring 框架中提供了多种事务管理器来进行事务管理。Spring 的事务管理器是基于AOP 实现的。在 Spring 的事务管理器中包含了配置事务传播行为、隔离级别、只读和超时属性,这些属性提供了事务应用的方法和描述策略。
在 Java EE 项目开发经常会使用分层模式,Spring 的事务处理位于业务逻辑层,它提供了针对事务的解决方案。
在 Spring 的事务模块(spring-tx-5.2.7.RELEASE.jar)中包括事务管理的三个核心接口。
PlatformTransactionManager 接口是 Spring 提供的事务管理器接口,用于管理事务。Spring 将事务的配置详细信息封装到 TransactionDefinition 对象中,然后通过事务管理器的getTransaction() 方法获得事务的状态(TransactionStatus),并对事务进行下一步的操作。
该接口中提供了三个事务操作方法,具体如下:
TransactionDefinition 接口是事务定义(描述)的对象,它提供了事务相关信息获取的方法,其中包括五个操作,具体如下:
TransactionStatus 接口是事务的状态,它描述了某一时间点上事务的状态信息。其中包括六个操作,具体如下:
事务传播行为是指:多个含有事务的方法相互调用时,事务如何在这些方法间传播。
Spring 声明式事务管理实现方式:
Spring 核心容器模块:
spring-beans-5.2.7.RELEASE.jar
spring-context-5.2.7.RELEASE.jar
spring-core-5.2.7.RELEASE.jar
spring-expression-5.2.7.RELEASE.jar
Spring JDBC 模块:
spring-jdbc-5.2.7.RELEASE.jar
Spring 事务模块:
spring-tx-5.2.7.RELEASE.jar
Spring AOP 模块:
spring-aop-5.2.7.RELEASE.jar
AspectJ 框架:
aspectjweaver-1.9.5.jar
Spring 整合 AspectJ 框架模块:
spring-aspects-5.2.7.RELEASE.jar
Commons-Loggin 日志:
commons-logging-1.2.jar
数据库驱动:
mysql-connector-java-5.1.48.jar
public class Users {
private int userid;
private String username;
private String usersex;
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsersex() {
return usersex;
}
public void setUsersex(String usersex) {
this.usersex = usersex;
}
@Override
public String toString() {
return "Users{" +
"userid=" + userid +
", username='" + username + '\'' +
", usersex='" + usersex + '\'' +
'}';
}
}
public class Orders {
private int orderid;
private double orderprice;
public int getOrderid() {
return orderid;
}
public void setOrderid(int orderid) {
this.orderid = orderid;
}
public double getOrderprice() {
return orderprice;
}
public void setOrderprice(double orderprice) {
this.orderprice = orderprice;
}
@Override
public String toString() {
return "Orders{" +
"orderid=" + orderid +
", orderprice=" + orderprice +
'}';
}
}
public interface UsersDao {
}
public class UsersDaoImpl implements UsersDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
public interface UsersService {
}
public class UsersServiceImpl implements UsersService {
private UsersDao usersDao;
public UsersDao getUsersDao() {
return usersDao;
}
public void setUsersDao(UsersDao usersDao) {
this.usersDao = usersDao;
}
}
<?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">
<!-- 配置解析Properties文件的工具类-->
<context:property-placeholder location="db.properties"/>
<!-- 配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置持久层-->
<bean id="usersDao" class="com.bjsxt.dao.impl.UsersDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!-- 配置业务层-->
<bean id="usersService" class="com.bjsxt.service.impl.UsersServiceImpl">
<property name="usersDao" ref="usersDao"/>
</bean>
</beans>
业务案例:添加用户的同时添加一个订单。
public interface UsersDao {
void insertUsers(Users users);
void insertOrders(Orders orders);
}
public class UsersDaoImpl implements UsersDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//添加用户
@Override
public void insertUsers(Users users) {
String sql = "insert into users values(default,?,?)";
Object[] args = new Object[]{users.getUsername(),users.getUsersex()};
this.jdbcTemplate.update(sql,args);
}
//添加订单
@Override
public void insertOrders(Orders orders) {
String sql = "insert into orders values(default,?,null)";
Object[] args = new Object[]{orders.getOrderprice()};
this.jdbcTemplate.update(sql,args);
}
}
public interface UsersService {
void addUsersAndOrders(Users users, Orders orders);
}
public class UsersServiceImpl implements UsersService {
private UsersDao usersDao;
public UsersDao getUsersDao() {
return usersDao;
}
public void setUsersDao(UsersDao usersDao) {
this.usersDao = usersDao;
}
@Override
public void addUsersAndOrders(Users users, Orders orders) {
this.usersDao.insertUsers(users);
this.usersDao.insertOrders(orders);
}
}
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-service.xml");
UsersService usersService = (UsersService)applicationContext.getBean("usersService");
Users users = new Users();
users.setUsername("HUAWEI");
users.setUsersex("male");
Orders orders = new Orders();
orders.setOrderprice(800);
usersService.addUsersAndOrders(users,orders);
}
}
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
<?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: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/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">
<import resource="applicationContext-service.xml"/>
<!-- 配置事务管理器切面对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 需要注入数据源对象-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器属性-->
<tx:advice id="txAdvice">
<tx:attributes>
<!-- name:执行受事务控制的方法。配置方法:1.给定完全方法名。2.通过*统配符指定方法名-->
<!-- propagation:配置事务的传播行为-->
<!-- isolation:配置事务的隔离级别-->
<tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- 配置切面-->
<aop:config>
<!-- 配置切点-->
<aop:pointcut id="txPointcut" expression="execution(* com.bjsxt.service.*.*(..))"/>
<!-- 配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"applicationContext-service.xml","applicationContext-tx.xml"});
UsersService usersService = (UsersService)applicationContext.getBean("usersService");
Users users = new Users();
users.setUsername("HUAWEI11");
users.setUsersex("male");
Orders orders = new Orders();
orders.setOrderprice(1800);
usersService.addUsersAndOrders(users,orders);
}
}
name=”方法名称|方法名称*” 指定受事务控制的方法,支持*通配符。
propagation=”传播行为名称” 配置事务的传播行为。
Isolation=”隔离级别名称” 配置事物的隔离级别。
readonly=”true|false” 是否为只读事务。
true:开启只读事务。由于只读事务不存在数据的修改,因此数据库将会为只读事务提供一些优化手段,会对性能有一定提升。建议在查询中开启只读事务。
false:不开启只读事务,默认为 false。
timeout=”秒”
timeout 是设置超时属性。以秒为单位。当在限定的时间内不能完成所有操作,就会抛异常。
rollback-for=”异常名称” Spring 默认情况下会对 RunTimeException 类型异常进行事务回滚。如果是 Exception 类型的异常则不回滚。
注意:如果异常被 try{}catch{}了,事务就不回滚了,如果想让事务回滚则必须再抛出异常。
no-rollback-for=”异常名称” 当出现什么异常时不滚回事务。
@Transactional 注解 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该注解来覆盖类级别的定义。
虽然@Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
<?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: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/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">
<import resource="applicationContext-service.xml"/>
<!-- 配置事务管理器的切面-->
<bean id="txTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 注册事务管理驱动-->
<tx:annotation-driven transaction-manager="txTransactionManager"/>
</beans>
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public class UsersServiceImpl implements UsersService {
private UsersDao usersDao;
public UsersDao getUsersDao() {
return usersDao;
}
public void setUsersDao(UsersDao usersDao) {
this.usersDao = usersDao;
}
@Override
public void addUsersAndOrders(Users users, Orders orders) {
this.usersDao.insertUsers(users);
this.usersDao.insertOrders(orders);
}
}