Spring事务机制主要包括声明式事务和编程式事务,声明式事务让咱们从复杂的事务处理中获得解脱,编程式事务在实际开发中得不到普遍使用,仅供学习参考。spring
spring的事务管理提供了统一的API接口支持不一样的资源,提供声明式事务管企且方便与Spring框架集成。spring的事务管理器使用抽象的设计方式实现,如下为spring中事务管理器的逻辑实现代码 (精简了一部分,突出核心逻辑)sql
## 事务状态
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事务的隔离级别:api
接下来看一下代码方式与标签方式的事务实现:bash
## 代码方式实现
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
}
}
}
复制代码
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;
}
}
复制代码
dao 接口app
// 继成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 ")
}
}
复制代码
controller 控制层dom
@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事务的管理过程:
Session原生事务:
##注解方式注入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应用程序能够和这些命名服务和目录服务之间进行交互。