一、事务的特性
1、 原子性(Atomicity):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
2、一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。
3、隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
4、持久性(Durability):持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
二、Spring中的事务
1、 Spring对事务有两种支持
①编程式事务:通过DataSourceTransactionManager 获得TransactionStatus 手动commit、rollback。(相信很少有人用把)
②声明式事务:Spring通过aop技术对事务进行管理,我们只需要简单的配置,就可以。有xml方式和注解方式。
2、JavaConfig方式
①通过@EnableTransactionManagement开启事务管理
@Configuration
@ComponentScan(basePackages = "com.caofanqi")
@EnableTransactionManagement //启动事务管理
@Import(DataSourceConfig.class)
public class JavaConfig {
}
②配置Druid数据库连接池
@Configuration
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
@Value("${jdbc.driver}")
private String driverClassName;
@Value("${jdbc.url}")
private String jdbcUrl;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* Druid数据库连接池配置
*/
@Bean
public DruidDataSource dataSource() throws SQLException {
DruidDataSource ds = new DruidDataSource();
/**
* 基本属性
*/
ds.setDriverClassName(driverClassName);
ds.setUrl(jdbcUrl);
ds.setUsername(username);
ds.setPassword(password);
/**
* 配置初始化大小、最小、最大
* 通常来说,只需要修改initialSize、minIdle、maxActive
*/
ds.setInitialSize(2);
ds.setMinIdle(2);
ds.setMaxActive(20);
ds.setTestWhileIdle(true);
/**
* 配置获取连接等待超时的时间
*/
ds.setMaxWait(5000);
/**
* 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
*/
ds.setTimeBetweenEvictionRunsMillis(60000);
/**
* 配置一个连接在池中最小生存的时间,单位是毫秒
*/
ds.setMinEvictableIdleTimeMillis(30000);
return ds;
}
/**
* 事务管理器
*/
@Bean
public DataSourceTransactionManager dataSourceTransactionManager() throws SQLException {
return new DataSourceTransactionManager(dataSource());
}
/**
* 使用JDBC模板
*/
@Bean
public JdbcTemplate jdbcTemplate(DruidDataSource druidDataSource) throws SQLException {
return new JdbcTemplate(druidDataSource);
}
}
③现在我们只需要在Service层需要使用到事务的方法上添加@Transactional注解就好了
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Transactional
public void add(User user) {
userDao.addUser(user.getName(),user.getAge());
int i = 1/0;//故意制造一个错误,看一下事务是否生效了
}
}
④测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JavaConfig.class)
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testAdd(){
userService.add(new User("zs",23));
}
}
运行后,项目报错,但是数据并没有插入到数据库,把int i = 1 /0;去掉后,插入成功;
注意:@Transactional也可以加载类上(不推荐);可以通过readOnly属性,设置是否使只读,默认false,开启事务;可以通过propagation设置事务的传播级别,默认REQUIRED;
事物是程序运行如果没有错误,会自动提交事物,如果程序运行发生异常,则会自动回滚。 如果使用了try捕获异常时,一定要在catch里面手动回滚。
事物手动回滚代码TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
如下:
@Transactional
public void add(User user) {
try{
userDao.addUser(user.getName(),user.getAge());
int i = 1/0;//故意制造一个错误,看一下事务是否生效了
}catch (Exception e){
//如果必须try-catch,一定要手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
e.printStackTrace();
}
}
}
三、Spring中的事务传播行为
1、Propagation.REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。默认的。
2、Propagation.SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。(如果当前有事物,就用当前事物,如果当前没有事物,就以非事物进行执行)
3、Propagation.MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
4、Propagation.REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
5、Propagation.NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、Propagation.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、Propagation.NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
大家可以自己自己测试以下:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private LogService logService;
/**
* 验证事务传播行为
*/
@Transactional(propagation = Propagation.REQUIRED)
public void add(User user) {
userDao.addUser(user.getName(), user.getAge());
logService.addLog("添加了用户"+ user.getName());
throw new RuntimeException("制造错误");
}
}
@Service
public class LogServiceImpl implements LogService {
@Autowired
private LogDao logDao;
@Transactional(propagation = Propagation.REQUIRED)
public void addLog(String context) {
logDao.addLog(context);
}
}
源码:https://gitee.com/itcaofanqi/CaoFanqiStudyRepository/tree/master/stuspring