目录
1 Spring的事务机制
2 声名式事务
3 注解事务行为
4 类级别使用@Transactional
5 Spring Data JPA的事务支持
6 Spring Boot的事务支持
7.代码示例
1 Spring的事务机制
所有的数据访问技术都有事务处理机制,这些技术提供了API用来开启事务、提交事务来完成数据 *** 作,或者在发生错误的时候回滚数据。
而Spring的事务机制是用统一的机制来处理不同数据访问技术的事务处理。Spring的事务机制提供了一个PlatformTransactionManager接口,不同的数据访问技术的事务使用不同的接口实现
Spring支持声名式事务,即使用注解来选择需要使用事务的方法,它使用@Transactional注解在方法上表明该方法需要事务支持。这是一个基于AOP的实现 *** 作,使用注解式的拦截方式来理解Spring的声名式事务。被注解的方法在被调用时,Spring开启一个新的事务,当方法无异常运行结束后,Spring会提交这个事务。
在此处特别注意的是,此@Transactional注解来自org.springframework.transaction.annotation包,而不是javax.transaction。
Spring提供了一个@EnableTransactionManagement注解在配置类上来开启声名式事务的支持。使用了@EnableTransactionManagement后,Spring容器会自动扫描注解@Transactional的方法和类。
@Transactional注解的属性来定制事务行为。
属性 | 含义 | 默认值 |
propagation | propagation定义了事务的生命周期,主要有以下选项: | REQUIRED |
REQUIRED:方法A调用时没有事务新建一个事务,当在方法A调用另一个方法B的时候,方法B将使用相同的事务;如果方法B发生异常需要数据回滚的时候,整个事务数据回滚。 | 默认 | |
REQUIRES_NEW:对于方法A和B,在方法调用的时候无论是否有事务都开启一个新的事务;这样如果方法B有异常不会导致方法A的数据问题 | ||
NESTED:和REQUIRES_NEW类似,但支持JDBC,不支持JPA或Hibernate | ||
SUPPORTS:方法调用时有事务就用事务,没事务就不用事务 | ||
NOT_SUPPORTED:强制方法不在事务中执行,若有事务,在方法调用到结束阶段事务都将会被挂起 | ||
NEVER:强制方法不在事务中执行,若有事务则抛出异常 | ||
MANDATORY:强制方法在事务中执行,若无事务则抛出异常 | ||
isolation | isolation(隔离)决定了事务的完整性,处理在多事务对相同数据下的处理机制,主要包含下面的隔离级别(当然我们也不可以随意设置,这要看当前数据库是否支持) | DEFAULT |
READ_UNCOMMITTED:对于在A事务里修改了一条记录但没有提交事务,在B事务可以读取到修改后的记录。可导致脏读、不可重复读以及幻读 | ||
READ_COMMITTED:只有当在A事务里修改了一条记录且提交事务之后,B事务才可以读取到提交后的记录;阻止脏读,但可能导致不可重复读和幻读 | ||
REPEATABLE_READ:不仅能实现READ_COMMITTED的功能,而且还能阻止当A事务读取了一条记录,B事务将不允许修改这条记录;阻止脏读和不可重复读,但可能出现幻读 | ||
SERIALIZABLE:次级别下事务是顺序执行的,可以避免上述级别的缺陷,但开销较大 | ||
DEFAULT:使用当前数据库的默认隔离级别,如Oracle、SQLServer是READ_COMMITTED;Mysql是REPEATABLE_READ | 默认 | |
timeout | timeout指定事务过期时间,默认为当前数据库的事务过期时间 | -1,没有过期时间 |
readOnly | 指定当前事务是否是只读事务 | false |
rollbackFor | 指定哪个或者哪些异常可以引起事务回滚 | Throwable的子类 |
noRollbackFor | 指定哪个或者哪些异常不可以引起事务回滚 | Throwable的子类 |
4 类级别使用@Transactional
@Transactional不仅可以注解在方法上,也可以注解在类上。当注解在类上的时候意味着此类的所有public方法都是开启事务的。如果类级别和方法级别同时使用了@Transactional注解,则使用在类级别的注解会重载方法级别的注解。
5 Spring Data JPA的事务支持
Spring Data JPA对所有的默认方法都开启了事务支持,且查询类事务默认启用readOnly=true属性,这些在SimpleJpaRepository的源码中可以看到,自行去看SimpleJpaRepository的源码。
从源码我们可以看出,SimpleJpaRepository在类级别定义了@Transactional(readOnly=true),而在和save、delete相关的 *** 作重写了@Transactional属性,此时readOnly属性是false,其余查询 *** 作readOnly仍然为false。
6 Spring Boot的事务支持
1.自动配置的事务管理器
在使用JDBC作为数据访问技术的时候,Spring Boot为我们定义了PlatformTransactionManager的实现DataSourceTransactionManager的Bean;配置见org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration类中的定义。
在使用JPA作为数据访问技术的时候,Spring Boot为我们了定义一个PlatformTransactionManager的实现JpaTransactionManager的Bean;配置见org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration.class类中的定义:
@Bean
@ConditionalOnMissingBean({TransactionManager.class})
public PlatformTransactionManager transactionManager(ObjectProvider transactionManagerCustomizers) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManagerCustomizers.ifAvailable((customizers) -> {
customizers.customize(transactionManager);
});
return transactionManager;
}
2.自动开启注解事务的支持
Spring Boot专门用于配置事务的类为:org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,此配置类依赖于JpaBaseConfiguration和DataSourceTransactionManagerAutoConfiguration。
而在DataSourceTransactionManagerAutoConfiguration配置里还开启了对声名式事务的支持,代码如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(DataSource.class)
static class JdbcTransactionManagerConfiguration {
@Bean
@ConditionalOnMissingBean(TransactionManager.class)
DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource,
ObjectProvider transactionManagerCustomizers) {
DataSourceTransactionManager transactionManager = createTransactionManager(environment, dataSource);
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
private DataSourceTransactionManager createTransactionManager(Environment environment, DataSource dataSource) {
return environment.getProperty("spring.dao.exceptiontranslation.enabled", Boolean.class, Boolean.TRUE)
? new JdbcTransactionManager(dataSource) : new DataSourceTransactionManager(dataSource);
}
}
所以在Spring Boot中,无须显示开启使用@EnableTransactionManagement注解。
7.代码示例1.引入jar包
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
runtime
2.entity层
package com.example.demo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* SearchRecordEntity
* @Entity注解指明这是一个和数据库表映射的实体类
* @Description
*/
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class SearchRecordEntity {
/**
* 主键ID
* @Id注解指明这个属性映射为数据库的主键
* @GeneratedValue注解默认使用主键生成方式为自增,hibernate会为我们自动生成一个名为HIBERNATE_SEQUENCE的序列
*/
@Id
@GeneratedValue
private Integer id;
/**
* 名称
*/
private String name;
/**
* 备注
*/
private String remark;
}
3.dao层
package com.example.demo.dao;
import com.example.demo.entity.SearchRecordEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* SearchRecordRepository
* 定义数据访问 *** 作的方法
* @Description
*/
@Repository
public interface SearchRecordRepository extends JpaRepository {
}
4.service层
package com.example.demo.service;
import com.example.demo.entity.SearchRecordEntity;
/**
* SearchRecordService
*
* @Description
*/
public interface SearchRecordService {
/**
*
* @param searchRecordEntity
* @return
*/
SearchRecordEntity saveSearchRecordWithRollback(SearchRecordEntity searchRecordEntity);
/**
*
* @param searchRecordEntity
* @return
*/
SearchRecordEntity saveSearchRecordWithoutRollback(SearchRecordEntity searchRecordEntity);
}
package com.example.demo.service;
import com.example.demo.dao.SearchRecordRepository;
import com.example.demo.entity.SearchRecordEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* SearchRecordServiceImpl
*
* @Description
*/
@Service
public class SearchRecordServiceImpl implements SearchRecordService {
/**
* 可以直接注入SearchRecordRepository的Bean
*/
@Autowired
private SearchRecordRepository searchRecordRepository;
/**
* 使用@Transactional注解的rollbackFor属性,指定特定异常时,数据回滚
* @param searchRecordEntity
* @return
*/
@Transactional(rollbackFor = {IllegalArgumentException.class})
@Override
public SearchRecordEntity saveSearchRecordWithRollback(SearchRecordEntity searchRecordEntity) {
SearchRecordEntity save = searchRecordRepository.save(searchRecordEntity);
if(save.getName().equalsIgnoreCase("全省影视")){
//手动触发异常
throw new IllegalArgumentException("全省影视已存在,数据将回滚");
}
return save;
}
/**
* 使用@Transactional注解的noRollbackFor属性,指定特定异常时,数据回滚
* @param searchRecordEntity
* @return
*/
@Transactional(noRollbackFor ={IllegalArgumentException.class} )
@Override
public SearchRecordEntity saveSearchRecordWithoutRollback(SearchRecordEntity searchRecordEntity) {
SearchRecordEntity save = searchRecordRepository.save(searchRecordEntity);
if(save.getName().equalsIgnoreCase("全省影视")){
throw new IllegalArgumentException("全省影视已存在,数据将回滚");
}
return save;
}
}
5.controller层
package com.example.demo.controller;
import com.example.demo.entity.SearchRecordEntity;
import com.example.demo.service.SearchRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* SearchRecordController1
*
* @Description
*/
@RestController
@RequestMapping("/api/search")
public class SearchRecordController {
@Autowired
private SearchRecordService searchRecordService;
@RequestMapping("/rollback")
public SearchRecordEntity saveSearchRecordWithRollback(SearchRecordEntity searchRecordEntity) {
SearchRecordEntity save = searchRecordService.saveSearchRecordWithRollback(searchRecordEntity);
return save;
}
@RequestMapping("/noRollback")
public SearchRecordEntity saveSearchRecordWithoutRollback(SearchRecordEntity searchRecordEntity) {
SearchRecordEntity save = searchRecordService.saveSearchRecordWithoutRollback(searchRecordEntity);
return save;
}
}
6.启动类开启事务管理
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableJpaRepositories(value = "com.example.demo.dao")
@EnableTransactionManagement
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
7.测试
现有数据
(1)测试不回滚
访问:http://localhost:8080/api/search/noRollback?name=全省影视&remark=备注呵呵哈哈哈
查询现有数据,发现已保存。
(2)测试回滚
访问:http://localhost:8080/api/search/rollback?name=全省影视&remark=备注哈哈哈
查询数据库,没有新增数据。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)