深入理解 Spring 事务管理及其配置
目录
1. Spring 事务角色
事务角色的代码示例:
2. 事务相关配置
常用事务属性:
事务配置的代码示例:
3. 事务传播行为
常见的事务传播行为:
事务传播行为的代码示例:
4. 事务配置案例:转账业务与日志记录
完整案例代码:
结论
Spring 事务管理是现代企业级应用中不可或缺的一部分。通过事务管理,开发者可以保证一系列的数据库操作要么全部成功,要么全部回滚,确保数据一致性。在本文中,我们将讨论 Spring 事务管理的角色、配置以及常见的事务传播行为。
1. Spring 事务角色
在 Spring 的事务管理中,存在两个关键角色:事务管理员 和 事务协调员(33-Spring事务角色)。
-
事务管理员:指发起事务的一方,通常是在业务层中开启事务的方法。它负责启动整个事务过程。
-
事务协调员:指参与事务的一方,通常是数据层或业务层中的方法。这些方法会加入由事务管理员开启的事务中,确保操作的原子性。
事务角色的代码示例:
public interface AccountDao {@Update("update tbl_account set money = money + #{money} where name = #{name}")void inMoney(@Param("name") String name, @Param("money") Double money);@Update("update tbl_account set money = money - #{money} where name = #{name}")void outMoney(@Param("name") String name, @Param("money") Double money);
}@Service
public class AccountService {@Autowiredprivate AccountDao accountDao;@Transactionalpublic void transfer(String out, String in, Double money) {accountDao.outMoney(out, money);accountDao.inMoney(in, money);}
}
在这个示例中,AccountService
类是事务管理员,它开启了事务,调用了数据层的两个方法 outMoney()
和 inMoney()
,而这些方法则是事务协调员,参与到同一个事务中。
2. 事务相关配置
在 Spring 事务管理中,可以通过注解或 XML 来配置事务。Spring 提供了多种属性来灵活控制事务的行为,例如设置事务是否为只读、是否回滚以及事务传播行为等(34-Spring事务相关配置) 。
常用事务属性:
-
readOnly:指示事务是否只读,若为
true
则事务仅用于读取操作,优化性能。 -
timeout:设置事务的超时时间,单位为秒。若超过指定时间事务仍未完成,则回滚。
-
rollbackFor:指定哪些异常会导致事务回滚。例如,可以设置为
rollbackFor = {NullPointerException.class}
,遇到空指针异常时回滚。 -
propagation:设置事务的传播行为,控制事务之间的关系(详细见下文)。
事务配置的代码示例:
@Service
public class AccountService {@Autowiredprivate AccountDao accountDao;@Transactional(readOnly = false, timeout = 30, rollbackFor = Exception.class)public void transfer(String out, String in, Double money) {accountDao.outMoney(out, money);accountDao.inMoney(in, money);}
}
在这个示例中,transfer()
方法配置了事务为读写事务(非只读),超时时间为 30 秒,并且遇到 Exception
异常时回滚。
3. 事务传播行为
事务传播行为 是指事务协调员对事务管理员所开启事务的处理方式。在 Spring 中,事务传播行为有多种选择,用于定义不同的方法在执行时如何与现有事务互动(34-Spring事务相关配置) 。
常见的事务传播行为:
-
REQUIRED:默认传播行为。如果存在事务,则加入当前事务;如果不存在事务,则开启一个新的事务。
-
REQUIRES_NEW:无论是否存在事务,都会开启一个新的事务,并暂停当前事务。
-
NESTED:开启一个嵌套事务,允许事务回滚到保存点(save point),而不是完全回滚整个事务。
-
SUPPORTS:如果当前存在事务,则加入事务;如果不存在事务,则以非事务方式执行。
事务传播行为的代码示例:
假设我们要为每次转账操作记录日志,但日志的保存不应依赖于转账事务的成功与否。因此,可以将日志操作配置为 REQUIRES_NEW
,确保日志记录总是独立完成。
@Service
public class LogService {@Autowiredprivate LogDao logDao;@Transactional(propagation = Propagation.REQUIRES_NEW)public void logTransaction(String out, String in, Double money) {logDao.saveLog("转账操作:由 " + out + " 到 " + in + " 金额:" + money);}
}@Service
public class AccountService {@Autowiredprivate AccountDao accountDao;@Autowiredprivate LogService logService;@Transactionalpublic void transfer(String out, String in, Double money) {try {accountDao.outMoney(out, money);accountDao.inMoney(in, money);} finally {logService.logTransaction(out, in, money);}}
}
在这个示例中,LogService
使用 REQUIRES_NEW
传播行为,确保日志记录在一个新事务中完成,即使转账操作回滚,日志也不会丢失。
4. 事务配置案例:转账业务与日志记录
一个典型的事务管理案例是银行转账业务。转账需要在 A 账户减钱,B 账户加钱的过程中保持事务的一致性。如果任何一个操作失败,整个事务将回滚。
在加入日志记录功能后,日志应该独立于转账事务进行保存,无论转账是否成功,日志都需要保留。我们可以通过 REQUIRES_NEW
来确保日志记录不受转账事务的影响。
完整案例代码:
@Service
public class AccountService {@Autowiredprivate AccountDao accountDao;@Autowiredprivate LogService logService;@Transactionalpublic void transfer(String out, String in, Double money) {try {accountDao.outMoney(out, money);accountDao.inMoney(in, money);} finally {logService.logTransaction(out, in, money);}}
}@Service
public class LogService {@Autowiredprivate LogDao logDao;@Transactional(propagation = Propagation.REQUIRES_NEW)public void logTransaction(String out, String in, Double money) {logDao.saveLog("转账操作:由 " + out + " 到 " + in + " 金额:" + money);}
}
结论
Spring 的事务管理为我们提供了灵活的配置选项,通过注解或 XML 配置,我们可以控制事务的传播行为、回滚规则和超时时间等属性。在企业级应用中,事务的正确管理可以确保数据的一致性和完整性,同时提高系统的健壮性。