Spring boot Learning Series -- declarative transaction

Keywords: Programming Spring JDBC Database Attribute

1, Spring's transaction mechanism

All data access technologies have transaction processing mechanisms, which provide API s to enable transactions, commit transactions to complete data operations, or roll back data in case of errors.
Spring's transaction mechanism is to use a unified mechanism to handle transactions of different data access technologies. Spring's transaction mechanism provides a PlatformTransactionManager interface. Different data access technologies use different interfaces to implement transactions, as shown in the following table:
Data access technology realization
JDBC DataSourceTransactionManager
JPA JpaTransactionManager
Hibernate HibernateTransactionManager
JDO JdoTransactionManager
Distributed transaction JtaTransactionManager
Define the transaction manager code in the program as follows:
@Bean
public PlatformTransactionManager transactionManager(){
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setDataSource(getDataSource());
    return transactionManager;
}

@Bean
public DataSource getDataSource() {
    DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
    dataSourceBuilder.driverClassName("com.mysql.jdbc.Driver");
    dataSourceBuilder.url("jdbc:mysql://localhost:3306/demo?serverTimezone=UTC");
    dataSourceBuilder.username("root");
    dataSourceBuilder.password("root");
    return dataSourceBuilder.build();
}

II. Declarative affairs

Spring supports declarative transactions, that is, using annotations to select the method that needs to use transactions. It uses @ Transactional annotation to indicate that the method needs transaction support. This is an AOP based implementation. When the annotated method is called, spring starts a new transaction. When the method runs without exception, spring will commit the transaction.
@Transactional
public void saveSomething(Long id,String name){
    //Database operations
}
This @ Transactional annotation comes from the org.springframework.transaction.annotation package, not javax.transaction.
Spring provides a @ EnableTransactionManagement annotation to enable declarative transactions on configuration classes. use @After the EnableTransactionManagement annotation, the Spring container automatically scans the methods and classes of annotation @ Transactional. @EnableTransactionManagement is used as follows:
@Configuration
@EnableTransactionManagement
public class AppConfiguration {
}

3, Annotate transaction behavior

Transactional has the following properties to customize transaction behavior.

3.1 propagation

Propagation defines the life cycle of a transaction, mainly including the following options:
  • REQUIRED (default)
    If there is no transaction when method A calls, A new transaction will be created. When method A calls another method B, method B uses the same transaction
    If data rollback is required due to method B exception, the whole transaction data will be rolled back.
  • REQUIRES_NEW
    For methods A and B, A new transaction is opened whether there is A transaction or not when the method is called;
    If there is an exception in method B, the data of method A will not be rolled back.
  • NESTED
    Similar to requirements? New, but JDBC is supported, JPA or Hibernate is not supported.
  • SUPPORTS
    When a method is called, a transaction is used when there is a transaction, and a transaction is not used when there is no transaction.
  • NOT_SUPPORTED
    The mandatory method is not executed in the transaction, and if there is a transaction, the transaction will be suspended from the method call to the end.
  • NEVER
    The enforcement method is not executed in a transaction, and an exception is thrown if there is a transaction.
  • MANDATORY
    Forces the method to execute in a transaction and throws an exception if there is no transaction.

3.2 isolation

Isolation determines the integrity of transactions. The processing mechanism for processing multiple transactions with the same data mainly includes the following isolation levels (actually, it depends on the current database)
  • READ_COMMITTED
    Only after A record is modified in transaction A and the transaction is submitted, can transaction B read the submitted record;
    Block dirty reading, but may result in non repeatable and unreal reading.
  • REPEATABLE_READ
    It can not only realize the read committed function, but also prevent A transaction from reading A record, and B transaction will not be allowed to modify this record;
    Prevent dirty reading and non repeatable reading, but unreal reading can occur.
  • SERIALIZABLE
    The transactions at this level are executed in sequence, which can avoid the defects at the above level, but the overhead is large.
  • DEFAULT (DEFAULT)
    Use the default isolation level of the current database, for example, Oracle, SQL Server is read committed, MySQL is repeatable read.

3.3 timeout

Timeout specifies the transaction expiration time, which defaults to the transaction expiration time of the current database. The default value is: timeout? Default.

3.4 readOnly

Specifies whether the current transaction is read-only. The default value is: false.

3.5 rollbackFor

Specifies which or which exceptions can cause the transaction to roll back. The default value is: a subclass of Throwable.

3.6 noRollbackFor

Specifies which or which exceptions cannot cause the transaction to roll back. The default value is: Subclass of Throwable.

4, Class level uses @ Transactional

Transactional can be annotated not only on methods, but also on classes. When the annotation is on a class, it means that all public methods of this class are transaction enabled. If @ transactional annotation is used at both the class level and method level, the annotation at the class level will overload the annotation at the method level.

5, Transaction support of Spring Data JPA

Spring Data JPA has transaction support enabled for all default methods, and the query class transaction has the readOnly=true attribute enabled by default.
Let's take a look at the source code of the reduced simplejpaepository:
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
    
    @Transactional
    public void deleteById(ID id) {}

    @Transactional
    public void delete(T entity) {}

    @Transactional
    public void deleteAll(Iterable<? extends T> entities) {}

    @Transactional
    public void deleteInBatch(Iterable<T> entities) {}

    @Transactional
    public void deleteAll() {}

    @Transactional
    public void deleteAllInBatch() {}

    public Optional<T> findById(ID id) {}

    protected QueryHints getQueryHints() {}

    public T getOne(ID id) {}

    public boolean existsById(ID id) {}

    public List<T> findAll() {}

    public List<T> findAllById(Iterable<ID> ids) {}

    public List<T> findAll(Sort sort) {}

    public Page<T> findAll(Pageable pageable) {}

    public Optional<T> findOne(@Nullable Specification<T> spec) {}

    public List<T> findAll(@Nullable Specification<T> spec) {}

    public Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable) {}

    public List<T> findAll(@Nullable Specification<T> spec, Sort sort) {}

    public <S extends T> Optional<S> findOne(Example<S> example) {}

    public <S extends T> long count(Example<S> example) {}

    public <S extends T> boolean exists(Example<S> example) {}

    public <S extends T> List<S> findAll(Example<S> example) {}

    public <S extends T> List<S> findAll(Example<S> example, Sort sort) {}

    public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {}

    public long count() {}

    public long count(@Nullable Specification<T> spec) {}

    @Transactional
    public <S extends T> S save(S entity) {}

    @Transactional
    public <S extends T> S saveAndFlush(S entity) {}

    @Transactional
    public <S extends T> List<S> saveAll(Iterable<S> entities) {}

    @Transactional
    public void flush() {}
}
Simplejpaepository defines @ Transactional(readOnly=true) at the class level, while the @ Transactional attribute is overridden in the operations related to save and delete. At this time, the readOnly attribute is false, and the rest of the query operations readOnly are still the default value of false.

6, Transaction support of SpringBoot

6.1 automatically configured transaction manager

If JDBC is used as data access technology, SpringBoot defines the implementation of PlatformTransactionManager: the Bean of DataSourceTransactionManager. See org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration for configuration
Definition in class:
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
DataSourceTransactionManager transactionManager(DataSource dataSource,
                                                ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
    transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
    return transactionManager;
}

If JPA is used as data access technology, SpringBoot defines a PlatformTransactionManager implementation for us: Bean of jpartransactionmanager; see the definition in org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration.class class for configuration:

@Bean
@ConditionalOnMissingBean
public PlatformTransactionManager transactionManager(
    ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
    return transactionManager;
}

6.2 support for automatically opening annotation transactions

The classes that SpringBoot is specifically used to configure transactions are:
    org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,
This configuration class depends on JpaBaseConfiguration and DataSourceTransactionManagerAutoConfiguration.
TransactionAutoConfiguration is a static internal class EnableTransactionManagementConfiguration. In the configuration, support for declarative transactions is enabled. The code is as follows:
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(TransactionManager.class)
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {

    @Configuration(proxyBeanMethods = false)
    @EnableTransactionManagement(proxyTargetClass = false)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
                           matchIfMissing = false)
    public static class JdkDynamicAutoProxyConfiguration {
    }

    @Configuration(proxyBeanMethods = false)
    @EnableTransactionManagement(proxyTargetClass = true)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
                           matchIfMissing = true)
    public static class CglibAutoProxyConfiguration {
    }
}

Therefore, in spring boot, you do not need to explicitly open the @ EnableTransactionManagement annotation.

7, Actual combat

7.1 define business interface and its implementation

public interface PersonService {
    void savePersonWithRollback(Person person);
}
@Service
public class IPersonService implements PersonService {
    @Autowired
    private PersonRepository personRepository;

    //@The Transactional annotation also has the noRollbackFor attribute, which is consistent with the usage of rollbackFor and has the opposite effect
    @Transactional(rollbackFor = {IllegalArgumentException.class})
    @Override
    public void savePersonWithRollback(Person person) {
        try{
            personRepository.save(person);
        }catch(Throwable e){
            throw new IllegalArgumentException("Parameter error, data rollback!");
        }
    }
}
Note: database design, name and age cannot be empty.

7.2 define controller method

@RequestMapping(value = "/saveWithNameCheck")
public void saveWithNameCheck(String name,Integer age,String address){
    personService.savePersonWithRollback(new Person(null,name,age,address));
}

7.3 verification

The browser control bar enters the following address request:
After returning, the result is as follows:

 

Posted by fredi_bieging on Mon, 11 May 2020 00:00:46 -0700