Springboot-声明式事务

Springboot-声明式事务,第1张

目录

1 Spring的事务机制

2 声名式事务

3 注解事务行为

4 类级别使用@Transactional

5 Spring Data JPA的事务支持

6 Spring Boot的事务支持

7.代码示例

1 Spring的事务机制


所有的数据访问技术都有事务处理机制,这些技术提供了API用来开启事务、提交事务来完成数据 *** 作,或者在发生错误的时候回滚数据。
而Spring的事务机制是用统一的机制来处理不同数据访问技术的事务处理。Spring的事务机制提供了一个PlatformTransactionManager接口,不同的数据访问技术的事务使用不同的接口实现

2 声名式事务


Spring支持声名式事务,即使用注解来选择需要使用事务的方法,它使用@Transactional注解在方法上表明该方法需要事务支持。这是一个基于AOP的实现 *** 作,使用注解式的拦截方式来理解Spring的声名式事务。被注解的方法在被调用时,Spring开启一个新的事务,当方法无异常运行结束后,Spring会提交这个事务。

在此处特别注意的是,此@Transactional注解来自org.springframework.transaction.annotation包,而不是javax.transaction。
Spring提供了一个@EnableTransactionManagement注解在配置类上来开启声名式事务的支持。使用了@EnableTransactionManagement后,Spring容器会自动扫描注解@Transactional的方法和类。

3 注解事务行为


@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=备注哈哈哈

 

 查询数据库,没有新增数据。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/786004.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-04
下一篇 2022-05-04

发表评论

登录后才能评论

评论列表(0条)

保存