Spring Boot Learning (10) Spring Boot Open declarative transaction

Source: Internet
Author: User
Tags assert rollback xmlns

What is a transaction and what are its advantages?

To perform operations on data, enterprise applications often require concurrent access to data that is shared across multiple artifacts. These applications should maintain the integrity of the data (defined by the application's business rules) under the following conditions:


Distributed access to a single data resource, as well as access to distributed resources from a single application artifact.

In this case, a set of operations on (distributed) resources may be required to be treated as a unit of work (unit). In one unit of work, all parts of the operation succeed or fail together and recover. The problem is more complicated under the following conditions:


Implementing a unit of work through a distributed set of artifacts that access data from multiple resources, and/or partial operations are executed sequentially or in parallel threads that require coordination and/or synchronization.


In all cases, the application is required to maintain the success or failure of a unit of work. In the case of failure, all resources have to return the data state to the previous state

(for example, the state before the start of the work unit).

The concept of transactions and the maintenance of the data integrity of the transaction manager (or a transactional service) in one unit of work simplifies the construction of such an enterprise-level distributed application.


A transaction is a unit of work that has the following properties:


Atomicity (atomicity):

A transaction is to be done or undone completely without ambiguity. In the case of an error in any operation, the effect of all operations that make up the transaction must be undone and the data should be rolled back to its previous state.


Consistency (consistency):

A transaction should protect all immutable attributes (such as integrity constraints) that are defined on the data. When a successful transaction is completed, the data should be in a consistent state. In other words, a transaction should shift the system from one consistent-state to another consistent state. For example, in the case of a relational database,

A consistent transaction will protect all integrity constraints that are defined on the data.


Isolation (Isolation):

There may be multiple transactions executing concurrently in the same environment, and each transaction should be performed independently. The effect of serially executing a series of transactions should be the same as executing them concurrently. This requires two things:


During the execution of a transaction, the intermediate (possibly inconsistent) state of the data should not be exposed to all other transactions.

Two concurrent transactions should not be able to manipulate the same data. Database management systems typically use locks to implement this feature.


Persistence (Durability):

The effect of a completed transaction should be persistent.


Quick Start

In spring boot, when we use SPRING-BOOT-STARTER-JDBC or SPRING-BOOT-STARTER-DATA-JPA dependencies, The framework automatically injects Datasourcetransactionmanager or Jpatransactionmanager, respectively, by default. So we don't need any extra configuration to use @transactional annotations for transactions.

First look at the Pom.xml file

<?xml version= "1.0" encoding= "UTF-8"?> <project xmlns= "http://maven.apache.org/POM/4.0.0" xmlns:xsi= "http ://www.w3.org/2001/XMLSchema-instance "xsi:schemalocation=" http://maven.apache.org/POM/4.0.0/http Maven.apache.org/xsd/maven-4.0.0.xsd "> <modelVersion>4.0.0</modelVersion> <groupId> Com.xiaojingg</groupid> <artifactId>springbootstudy-demo10-transactional</artifactId> < Version>0.0.1-snapshot</version> <packaging>jar</packaging> <name> Springbootstudy-demo10-transactional</name> <description>demo Project for Spring boot</description > <parent> <groupId>org.springframework.boot</groupId> <artifactid>spring-boot- Starter-parent</artifactid> <version>1.5.9.RELEASE</version> <relativePath/> <!--l Ookup parent from repository-</parent> <properties> <project.buIld.sourceencoding>utf-8</project.build.sourceencoding> <project.reporting.outputencoding>utf-8 </project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> &L T;dependencies> <dependency> <groupId>org.springframework.boot</groupId> < artifactid>spring-boot-starter</artifactid> </dependency> <dependency> <grou
         Pid>org.springframework.boot</groupid> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupid>mysql&lt ;/groupid> <artifactId>mysql-connector-java</artifactId> <version>5.1.21</versio 
         n> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactid>spring-boot-starter-data-jpa</artifactid> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> &L t;artifactid>spring-boot-maven-plugin</artifactid> </plugin> </plugins> </build&

Gt </project>

The

Defines the length of the user's Name property as 5, which causes the exception to be triggered by the long name property of the user entity when it is created.

Package com.xiaojingg.domain;
Import Javax.persistence.Column;
Import javax.persistence.Entity;
Import Javax.persistence.GeneratedValue;

Import Javax.persistence.Id;

    /** * into GG * * @Entity public class User {@Id @GeneratedValue private Long Id;

    @Column (nullable = false, length = 5) private String name;

    @Column (nullable = False) private Integer age;
        Public user () {} public user (String name, Integer age) {this.name = name;
    This.age = age;
    } public Long GetId () {return id;
    } public void SetId (Long id) {this.id = ID;
    } public String GetName () {return name;
    } public void SetName (String name) {this.name = name;
    } public Integer Getage () {return age;
    public void Setage (Integer age) {this.age = age;

}} Create service implementation userrepository package com.xiaojingg.domain; Import Org.springframework.data.jpa.repository.JpaRepository;
Import Org.springframework.data.jpa.repository.Query;

Import Org.springframework.data.repository.query.Param; /** * Xiao Jin GG */public interface userrepository extends Jparepository<user, long> {User findbyname (String name

    );

    User findbynameandage (String name, Integer age);
@Query ("from User U where u.name=:name") User Finduser (@Param ("name") String name); }

To view the test class:

In the database, a record of name from AAA to GGG is created, with no records for Hhhhhhhhhh, III, JJJ. And if this is a situation where you want to guarantee integrity, the AAA to GGG Record wants to be able to be rolled back when an exception occurs, so it's very simple to use a transaction to get it back, and we just need to add the @transactional annotation to the test function.

Package Com.xiaojingg;
Import Com.xiaojingg.domain.User;
Import Com.xiaojingg.domain.UserRepository;
Import Org.junit.Assert;
Import Org.junit.Test;
Import Org.junit.runner.RunWith;
Import org.springframework.beans.factory.annotation.Autowired;
Import Org.springframework.boot.test.context.SpringBootTest;
Import Org.springframework.test.context.junit4.SpringRunner;

Import org.springframework.transaction.annotation.Transactional; @RunWith (Springrunner.class) @SpringBootTest public class Springbootstudydemo10transactionalapplicationtests {@

   autowired private Userrepository userrepository; @Test @Transactional public void Test () throws Exception {//Create 10 Records Userrepository.save (The new User ("AAA
      ", 10));
      Userrepository.save (The New User ("BBB", 20));
      Userrepository.save (The New User ("CCC", 30));
      Userrepository.save (New User ("DDD", 40));
      Userrepository.save (The New User ("EEE", 50));
      Userrepository.save (New User ("FFF", 60)); Userrepository.save (New User ("GGG", 70));
      Userrepository.save (New User ("Hhhhhhhhhh", 80));
      Userrepository.save (New User ("III", 90));

      Userrepository.save (New User ("JJJ", 100));

      Test FindAll, query all Records assert.assertequals (Ten, Userrepository.findall (). Size ());

      Test Findbyname, Query the user assert.assertequals named FFF, Userrepository.findbyname ("FFF"). Getage (). Longvalue ());

      Test Finduser, Query the user assert.assertequals named FFF, Userrepository.finduser ("FFF"). Getage (). Longvalue ()); Test Findbynameandage, Query the user assert.assertequals ("FFF", Userrepository.findbynameandage ("FFF", 60) with the name FFF and age 60.

      GetName ());

      Test Delete User Userrepository.delete (Userrepository.findbyname ("AAA") with name AAA;

   Test FindAll, query all records, verify that the above deletion succeeded Assert.assertequals (9, Userrepository.findall (). Size ());
}} View configuration file Spring.datasource.url=jdbc:mysql://localhost:3306/test1 Spring.datasource.username=root Spring.datasource.password=root SPRING.DATASOURCE.DRiver-class-name=com.mysql.jdbc.driver spring.jpa.properties.hibernate.hbm2ddl.auto=create 

Executing the test case, you can see that the console throws the following exception, the Name field is very long:

Org.springframework.dao.DataIntegrityViolationException:could not execute statement; SQL [n/a]; Nested exception is org.hibernate.exception.DataException:could not execute statement

Looking at the database, the user table will not have AAA to GGG users data, the successful implementation of automatic rollback.

The main unit test here demonstrates how to use @transactional annotations to declare a function that needs to be managed by a transaction, usually in order to ensure that the data between each test is independent, the @rollback annotations are used to make each unit test rollback at the end. When it comes to developing business logic, we typically use @transactional in the service layer interface to configure the transaction management of each business logic, such as:

Package com.xiaojingg.service;

Import Com.xiaojingg.domain.User;
Import org.springframework.transaction.annotation.Propagation;
Import org.springframework.transaction.annotation.Isolation;
Import org.springframework.transaction.annotation.Transactional;
/**
 * Created by Administrator on 2017-12-7 20:44:58
 *
/public interface UserService {

    @Transactional ( Isolation = isolation.default, propagation = propagation.required)
    User Login (string name, string password);

}

Detailed Business

In the example above we used the default transaction configuration to meet some basic transaction requirements, but when our project is more complex (for example, there are multiple data sources, etc.), we need to specify a different transaction manager when declaring a transaction. The transaction management configuration for different data sources can be found in the settings in Spring boot multi-data source configuration and use. When declaring a transaction, you only need to specify the configured transaction manager name through the Value property, for example: @Transactional (value= "Transactionmanagerprimary").

In addition to specifying a different transaction manager, you can also control the isolation level and propagation behavior of transactions, which are explained in detail below:

# # # Isolation LEVEL

Isolation level refers to the degree of isolation between several concurrent transactions, including dirty reads, repeated reads, and Phantom reads, which are primarily relevant to our development time.

We can see that the Org.springframework.transaction.annotation.Isolation enumeration class defines five values that represent the isolation level:

Public enum Isolation {

DEFAULT (-1),

read_uncommitted (1),

Read_committed (2),

Repeatable_read (4),

SERIALIZABLE (8);

} default: This is the defaults that represent the default isolation level for using the underlying database. For most databases, this is usually the value: read_committed. Read_uncommitted: This isolation level indicates that one transaction can read data that has been modified by another transaction but has not yet been committed. This level does not prevent dirty reads and non-repeatable reads, so this isolation level is rarely used. Read_committed: This isolation level indicates that a transaction can only read data that has been committed by another transaction. This level prevents dirty reads, which is the recommended value in most cases. Repeatable_read: This isolation level indicates that a transaction can repeatedly execute a query multiple times throughout the process, and that the records returned are the same each time. These new records are ignored even if there are new data that satisfies the query between multiple queries. This level protects against dirty reads and non-repeatable reads. SERIALIZABLE: All transactions are executed one after the other, so that there is absolutely no possibility of interference between transactions, that is, the level prevents dirty reads, non-repeatable reads, and Phantom reads. However, this will severely affect the performance of the program. This level is not normally used.

Specify method: By using the Isolation property setting, for example:

@Transactional (isolation = isolation.default)


propagation Behavior

The so-called transaction propagation behavior is that if a transaction context already exists before the current transaction is started, there are several options to specify the execution behavior of a transactional method.

We can see that the Org.springframework.transaction.annotation.Propagation enumeration class defines 6 enumeration values that represent propagation behavior:


Public enum Propagation {

REQUIRED (0),

SUPPORTS (1),

MANDATORY (2),

Requires_new (3),

Not_supported (4),

Never (5),

NESTED (6);

REQUIRED: If a transaction is currently present, the transaction is joined and a new transaction is created if there is no current transaction. SUPPORTS: If a transaction is currently present, the transaction is joined, and if no transaction is currently present, it will continue in a non-transactional manner. MANDATORY: If a transaction is currently present, the transaction is joined and an exception is thrown if there is no current transaction. Requires_new: Creates a new transaction and suspends the current transaction if a transaction is currently present. Not_supported: Runs in a non-transactional manner, suspending the current transaction if a transaction is currently present. Never: Runs in a non-transactional manner and throws an exception if a transaction is currently present. NESTED: If a transaction is currently present, create a transaction to run as a nested transaction for the current transaction, or the value is equivalent to required if there is no transaction currently.

Specify method: By using the Propagation property setting, for example:

@Transactional (propagation = propagation.required)



Here we are finished, more configuration please see Springboot official website configuration yo!


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.