从头学习Spring之二(数据操作之抽象)
一、事务抽象
1.一致的事务模型
JDBC/Hibernate/myBatis
DataSource/JTA
2.核心接口
PlatformTransactionManager(事务操作):DataSourceTransactionManager、HibernateTransactionManager、JtatransactionManager
方法:commit、rollback、getTransaction
TransactionDefinition(事务定义):Propagation(传播特性)、Isolation(隔离性)、Timeout(超时)、Read-only status(只读)
3.事务的传播特性
传播性 | 值 | 描述 |
PROPAGATION_REQUIRED | 0 | 当前有事务就用当前的,没有就用新的(默认) |
PROPAGATION_SUPPORTS | 1 | 事务可有可无,不是必须的 |
PROPAGATION_MANDATORY | 2 | 当前一定要有事务,不然就抛异常 |
PROPAGATION_REQUIRES_NEW | 3 | 无论是否有事务,都起个新的事务(原事务挂起) |
PROPAGATION_NOT_SUPPORTED | 4 | 不支持事务,按非事务方式运行 |
PROPAGATION_NEVER | 5 | 不支持事务,如果有事务则抛异常 |
PROPAGATION_NESTED | 6 |
当前有事务就在当前事务里再起一个事务 (内部事务拥有自己的属性,内部事务回滚不会影响外部事务) |
4.隔离特性
隔离性 | 值 | 脏读 | 不可重复读 | 幻读 |
ISOLATON_READ_UNCOMMITTED | 1 | √ | √ | √ |
ISOLATON_READ_COMMITTED | 2 | × | √ | √ |
ISOLATON_REPEATABLE_READ | 3 | × | × | √ |
ISOLATON_SERIALIZABLE | 4 | × | × | × |
5.编程式事务和声明式事务
编程式事务:
使用TransactionTemplate:transactionCallback、transactionCallbackWithoutResult
1 /** 2 * TransactionTemplate事务展示 3 */ 4 private void showTransaction(){ 5 // 事务执行前,查看记录数 6 log.info("COUNT BEFORE TRANSACTION:{}",getCount()); 7 transactionTemplate.execute(new TransactionCallbackWithoutResult() { 8 @Override 9 protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { 10 jdbcTemplate.execute("INSERT INTO FOO (ID, BAR) VALUES (1, 'aaa')"); 11 // 事务执行时查看记录数 12 log.info("COUNT IN TRANSACTION:{}",getCount()); 13 // 设置为只能回滚 14 transactionStatus.setRollbackOnly(); 15 } 16 }); 17 // 事务执行后查看记录数 18 log.info("COUNT AFTER TRANSACTION:{}",getCount()); 19 }
使用PlatformTransactionManager:可以传入TransactionDefinition进行定义
声明式事务:
基于注解的配置方式:
开启事务注解支持的方式:添加@EnableTransactionManagement:配置属性:proxyTargetClass(是基于接口还是基于类)、mode、order(事务拦截顺序,默认最低级)
开启事务:在需要开启事务的方法或者类上添加@Transactional注解:配置项:transactionManager、propagation、isolation、timeout、readOnly、怎么判断回滚(可以设置当碰到特定的异常类才回滚)
1 @Service 2 public class UserService implements IUserService{ 3 @Autowired 4 private JdbcTemplate jdbcTemplate; 5 6 /** 7 * 默认配置的事务 8 */ 9 @Override 10 @Transactional 11 public void insertRecord(){ 12 jdbcTemplate.execute("INSERT INTO FOO (BAR VALUES ('AAA')"); 13 } 14 15 /** 16 * 只有遇到SQLTransactionRollbackException及其子孙异常时才会回滚 17 * @throws SQLTransactionRollbackException 用于演示而随意使用的异常 18 */ 19 @Override 20 @Transactional(rollbackFor = SQLTransactionRollbackException.class) 21 public void insertThenRollback() throws SQLTransactionRollbackException { 22 jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')"); 23 throw new SQLTransactionRollbackException(); 24 } 25 26 /** 27 * 本身没有事务支持的方法(所在类也没有事务支持),在调用有事务支持的方法时,发生错误也不会回滚 28 * 没有真正的开启一个事务 29 * @throws SQLTransactionRollbackException 用于演示而随意使用的异常 30 */ 31 @Override 32 public void invokeInsertThenRollback() throws SQLTransactionRollbackException{ 33 insertThenRollback(); 34 } 35 }
关于调用未加事务的方法内部调用添加事务的方法不回滚的问题的解法:访问增强后的代理类的方法,而非直接访问滋生的方法(AOP的本质是为类做了一个代理类,调用时看似调用自己写的类,实际用的是增前后的代理类)
1 @Autowired 2 private IUserService userService; 3 4 /** 5 * 调用代理类中的方法,而不是本类中的方法 6 * @throws SQLTransactionRollbackException 用于演示而随意使用的异常 7 */ 8 public void invokeInsertThenRollback() throws SQLTransactionRollbackException{ 9 userService.insertThenRollback(); 10 }
二、异常抽象
1)处理方式:
①:Spring 会将数据操作的异常转换为 DataAccessException ,无论使用何种数据访问方式,都能使用一样的异常
②:Spring 通过SQLErrorCodeSQLExceptionTranslator 解析错错误码
2)自定义数据操作抽象异常:
在Classpsth下定义一个 sql-error-codes.xml 文件,在文件中定义异常和异常码,格式参照 org/springframework/jdbc/support/sql-error-codes.xml 文件中的定义格式