Spring事务机制主要包括声明式事务和编程式事务,声明式事务让我们从复杂的事务处理中得到解脱,编程式事务在实际开发中得不到广泛使用,仅供学习参考。
spring的事务管理提供了统一的API接口支持不同的资源, 提供声明式事务 管企且方便与Spring框架集成。spring的事务管理器使用抽象的设计方式实现,以下为spring中事务管理器的逻辑实现代码 (精简了一部分,突出核心逻辑)
## 事务状态 public interface TransactionStatus extends SavepointManager{ boolean isNewTransaction(); boolean hasSavepoint(); void setRollbackOnly(); boolean isRollbackOnly(); boolean isCompleted(); } ## 事务定义 public interface TransactionDefinition{ int getPropagationBehavior(); int getIsolationLevel(); String getName(); int getTimeout(); boolean isReadOnly(); } ## 事务管理器 public interface PlatformTransactionManager{ TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; } 复制代码
Spring在TransactionDefinition接口中规定了 7 种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:
事务传播行为类型:
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
当使用PROPAGATION_NESTED时,底层的数据源必须基于JDBC 3.0,并且实现者需要支持保存点事务机制。
spring如果没有指定事务隔离级别的话,则spring的事务隔离级别跟数据库的隔离级别走,数据库是什么隔离级别,spring就是什么隔离级别。
Spring事务的隔离级别:
接下来看一下代码方式与标签方式的事务实现:
## 代码方式实现 public OrderService{ @Autowire PlatformTransactionManager txManager; void buyTicket(BuyTicketDTO dto){ DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try{ ## 执行业务代码 txManager.commit(status); }catch(Exception e){ txManager.rollback(status); } } } ## Transaction标签实现方式 public OrderService{ @Transactonal void buyTick(BuyTicketDTO dto){ // get and begin transaction manager from context try{ /**业务代码*/ // commit transaction }catch(Exception e){ // rollback transaction } } } 复制代码
策略接口 PlatformTransactionManager 事务管理器的常见实现有:
DataSourceTransactionManager、JpTransactionManager、JmsTransactionManager、JtaTransactionManager
JPA是Java的一个规范(Java持久性API)。 它用于在Java对象和关系数据库之间保存数据。 JPA充当面向对象的领域模型和关系数据库系统之间的桥梁。 由于JPA只是一个规范,它本身不执行任何操作。 它需要一个实现。 因此,像Hibernate,TopLink和iBatis这样的ORM工具实现了JPA数据持久性规范。
关于JPA事务实例的代码:
domian实体对象
@Entity(name = "customer") public class Customer { ## id 自增长 @Id @GeneratedValue private Long id; ## 唯一索引 @Column(name = "user_name", unique = true) private String username; private String password; private String role; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } } 复制代码
// 继成JpaRepository中的方法,其中已经包含基本的CRUD public interface CustomerRepository extends JpaRepository<Customer, Long> { Customer findOneByUsername(String username); } 复制代码
service 业务操作,以下2种事务实现的效果是一样的,意在告诉大家如何使用代码的方式实现与注解声明事务相同的效果。
@Service public class CustomerServiceTxInAnnotation { private static final Logger LOG = LoggerFactory.getLogger(CustomerServiceTxInAnnotation.class); @Autowired private CustomerRepository customerRepository; ## 使用注解的方式声明事务存在 @Transactional public Customer create(Customer customer) { LOG.info("CustomerService In Annotation create customer:{}", customer.getUsername()); if (customer.getId() != null) { throw new RuntimeException("用户已经存在"); } customer.setUsername("Annotation:" + customer.getUsername()); return customerRepository.save(customer); } } 复制代码
@Service public class CustomerServiceTxInCode { private static final Logger LOG = LoggerFactory.getLogger(CustomerServiceTxInCode.class); @Autowired private CustomerRepository customerRepository; @Autowired private PlatformTransactionManager transactionManager; public Customer create(Customer customer) { LOG.info("CustomerService In Code create customer:{}", customer.getUsername()); if (customer.getId() != null) { throw new RuntimeException("用户已经存在"); } ## 使用代码的方式来声明事务存在 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); ## 当使用ISOLATION_SERIALIZABLE级别时,如果外部没事务存在,则本身创建事务,,所以submitError方法抛出异常可以回滚 //def.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); ## 当使用PROPAGATION_REQUIRED级别时,如果外部没事务存在,则本身也不存在事务,,所以submitError方法抛出异常依然可以保存数据成功 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); def.setTimeout(15); TransactionStatus status = transactionManager.getTransaction(def); try { customer.setUsername("Code:" + customer.getUsername()); customerRepository.save(customer); submitError(); transactionManager.commit(status); return customer; } catch (Exception e) { transactionManager.rollback(status); throw e; } } private void submitError(){ throw new RuntimeException("some Data error ") } } 复制代码
@RestController @RequestMapping("/api/customer") public class CustomerResource { private static final Logger LOG = LoggerFactory.getLogger(CustomerResource.class); @Autowired private CustomerServiceTxInAnnotation customerService; @Autowired private CustomerServiceTxInCode customerServiceInCode; @Autowired private CustomerRepository customerRepository; @PostMapping("/annotation") public Customer createInAnnotation(@RequestBody Customer customer) { LOG.info("CustomerResource create in annotation create customer:{}", customer.getUsername()); return customerService.create(customer); } @PostMapping("/code") public Customer createInCode(@RequestBody Customer customer) { LOG.info("CustomerResource create in code create customer:{}", customer.getUsername()); return customerServiceInCode.create(customer); } @GetMapping("") public List<Customer> getAll() { return customerRepository.findAll(); } } 复制代码
接下来看一下程序的执行结果及JPA事务的管理过程:
在整个事务管理过程中使用的是Spring事务控制,并且由相关ORM框架实现JPA规范
Session原生事务:
JmsTransactionManager事务:
##注解方式注入Spring Bean @EnableJms @Configuration public class JmsConfig { private static final Logger LOG = LoggerFactory.getLogger(CustomerService.class); @Bean public JmsTemplate initJmsTemplate(ConnectionFactory connectionFactory) { LOG.debug("init jms template with converter."); JmsTemplate template = new JmsTemplate(); ## JmsTemplate使用的connectionFactory跟JmsTransactionManager使用的必须是同一个,不能在这里封装成caching之类的。 template.setConnectionFactory(connectionFactory); return template; } ## 这个用于设置 @JmsListener使用的containerFactory @Bean public JmsListenerContainerFactory<?> msgFactory(ConnectionFactory connectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer, PlatformTransactionManager transactionManager) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setTransactionManager(transactionManager); factory.setCacheLevelName("CACHE_CONNECTION"); factory.setReceiveTimeout(10000L); configurer.configure(factory, connectionFactory); return factory; } @Bean public PlatformTransactionManager transactionManager(ConnectionFactory connectionFactory) { return new JmsTransactionManager(connectionFactory); } } 复制代码
@Service public class CustomerService { private static final Logger LOG = LoggerFactory.getLogger(CustomerService.class); @Autowired JmsTemplate jmsTemplate; @Autowired private PlatformTransactionManager transactionManager; @PostConstruct public void init() { jmsTemplate.setReceiveTimeout(3000); } ## 原生事务 @JmsListener(destination = "customer:msg:new", containerFactory = "msgFactory") public void handle(String msg) { LOG.debug("Get JMS message to from customer:{}", msg); String reply = "Replied - " + msg; jmsTemplate.convertAndSend("customer:msg:reply", reply); if (msg.contains("error")) { simulateError(); } } ## JmsTransactionManager事务 @JmsListener(destination = "customer:msg2:new", containerFactory = "msgFactory") public void handle2(String msg) { LOG.debug("Get JMS message2 to from customer:{}", msg); DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setTimeout(15); TransactionStatus status = transactionManager.getTransaction(def); try { String reply = "Replied-2 - " + msg; jmsTemplate.convertAndSend("customer:msg:reply", reply); if (!msg.contains("error")) { transactionManager.commit(status); } else { transactionManager.rollback(status); } } catch (Exception e) { transactionManager.rollback(status); throw e; } } private void simulateError() { throw new RuntimeException("some Data error."); } } 复制代码
紧密依赖于底层资源管理器(例如数据库连接 ),事务处理局限在当前事务资源内。此种事务处理方式不存在对应用服务器的依赖,因而部署灵活却无法支持多数据源的分布式事务。
在数据库连接中使用本地事务示例如下:
public void transferAccount() { Connection conn = null; Statement stmt = null; try{ conn = getDataSource().getConnection(); ## 将自动提交设置为 false, ## 若设置为 true 则数据库将会把每一次数据更新认定为一个事务并自动提交 conn.setAutoCommit(false); stmt = conn.createStatement(); ## 将 A 账户中的金额减少 500 stmt.execute("/ update t_account set amount = amount - 500 where account_id = 'A'"); ## 将 B 账户中的金额增加 500 stmt.execute("/ update t_account set amount = amount + 500 where account_id = 'B'"); ## 提交事务 conn.commit(); ## 事务提交:转账的两步操作同时成功 } catch(SQLException sqle){ try{ ## 发生异常,回滚在本事务中的操做 conn.rollback(); ## 事务回滚:转账的两步操作完全撤销 stmt.close(); conn.close(); }catch(Exception ignore){ } sqle.printStackTrace(); } } 复制代码
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。