Home >Java >javaTutorial >Detailed explanation of transaction management methods in Java's Spring framework

Detailed explanation of transaction management methods in Java's Spring framework

高洛峰
高洛峰Original
2017-01-23 11:06:091245browse

A database transaction is a sequence of operations that is treated as a single unit of work. These operations either all complete or all are unsuccessful. Transaction management is an important part of RDBMS for enterprise applications to ensure data integrity and consistency. The concept of a transaction can be described by the following four key properties for ACID:

Atomicity: A transaction should be treated as a single unit of operation representing whether the entire sequence of operations is successful or unsuccessful. .

Consistency: This represents the referential integrity of the database, the consistency of the unique primary key in the table etc.

Isolation: There may be many transactions processing the same data set at the same time, each transaction should be isolated from others to prevent data corruption.

Persistence: Once a transaction is completed, the results of this transaction must be made permanent and cannot be deleted from the database due to system failure.

A true RDBMS database system will guarantee all four attributes for each transaction. A simple view of issuing a transaction using a SQL database is as follows:

Use the begin transaction command to start a transaction.

Use SQL queries to perform various delete, update or insert operations.

If all operations are successful, then commit is performed, otherwise all operations are rolled back.

An abstraction layer on top of the different underlying transaction management APIs provided by the Spring framework. The transaction support in Spring aims to provide a replacement for EJB transactions by adding transaction functionality to POJOs. Spring supports both programmatic and declarative transaction management. An EJB application server is required, but Spring transaction management is implemented without the need for an application server.

Local and global transactions
Local transactions are for a single transactional resource like a JDBC connection, while global transactions can span distributed systems like multiple transactional resources.

Local transaction management can be useful in a centralized computing environment where an application's components and resources are located on a single site, whereas transaction management involves only local data management running on a separate machine. Local transactions are easier to implement.

Global transaction management is required in a distributed computing environment with all resources distributed across multiple systems. In this case, transaction management needs to be done at both local and global levels. A distributed or global transaction is executed on multiple systems, and its execution requires coordination between the global transaction management system and all local data managers of all related systems.

Programming and Declaration
Spring supports two types of transaction management:

Programmatic transaction management: Spring supports two types of transaction management:

Declarative transaction Management: This means transaction management is separated from your business code. You only use annotations or XML-based configuration to manage transactions.

Programmatic Transaction Management
Programmatic Transaction Management allows you to manage transactions with the help of programmatic source code. This gives great flexibility, but it is difficult to maintain.

Before we start, it is at least two database tables on which we can perform various CRUD operations with the help of transactions. Let's take the Student table, which can be created in a MySQL database for testing with the following DDL:

CREATE TABLE Student(
  ID  INT NOT NULL AUTO_INCREMENT,
  NAME VARCHAR(20) NOT NULL,
  AGE INT NOT NULL,
  PRIMARY KEY (ID)
);

The second table is Marks, where we will hold students marked based on years. Here SID is the foreign key of the table.

CREATE TABLE Marks(
  SID INT NOT NULL,
  MARKS INT NOT NULL,
  YEAR  INT NOT NULL
);

Let us use PlatformTransactionManager to directly implement the programming method to implement transactions. To start a new transaction, there needs to be an instance of TransactionDefinition with the appropriate transaction attributes. In this example, we will simply create an instance of DefaultTransactionDefinition using the default transaction properties.

Once a TransactionDefinition is created, you can start a transaction by calling the getTransaction() method, which returns an instance of the TransactionStatus object. The TransactionStatus object helps track the current state of the transaction, and finally, if all goes well, you can use the commit(PlatformTransactionManager) method to commit the transaction, otherwise you can use rollback() to rollback the completed operation.

Now we write a Spring JDBC application that will implement simple operations on the Student and Marks tables.
The following is the content of the data access object interface file StudentDAO.java:

package com.yiibai;
 
import java.util.List;
import javax.sql.DataSource;
 
public interface StudentDAO {
  /**
  * This is the method to be used to initialize
  * database resources ie. connection.
  */
  public void setDataSource(DataSource ds);
  /**
  * This is the method to be used to create
  * a record in the Student and Marks tables.
  */
  public void create(String name, Integer age, Integer marks, Integer year);
  /**
  * This is the method to be used to list down
  * all the records from the Student and Marks tables.
  */
  public List<StudentMarks> listStudents();
}


The following is the content of the StudentMarks.java file:

package com.yiibai;
 
public class StudentMarks {
  private Integer age;
  private String name;
  private Integer id;
  private Integer marks;
  private Integer year;
  private Integer sid;
 
  public void setAge(Integer age) {
   this.age = age;
  }
  public Integer getAge() {
   return age;
  }
 
  public void setName(String name) {
   this.name = name;
  }
  public String getName() {
   return name;
  }
 
  public void setId(Integer id) {
   this.id = id;
  }
  public Integer getId() {
   return id;
  }
  public void setMarks(Integer marks) {
   this.marks = marks;
  }
  public Integer getMarks() {
   return marks;
  }
 
  public void setYear(Integer year) {
   this.year = year;
  }
  public Integer getYear() {
   return year;
  }
 
  public void setSid(Integer sid) {
   this.sid = sid;
  }
  public Integer getSid() {
   return sid;
  }
}

The following is the content of the StudentMarksMapper.java file:

package com.yiibai;
 
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
 
public class StudentMarksMapper implements RowMapper<StudentMarks> {
  public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException {
 
   StudentMarks studentMarks = new StudentMarks();
 
   studentMarks.setId(rs.getInt("id"));
   studentMarks.setName(rs.getString("name"));
   studentMarks.setAge(rs.getInt("age"));
   studentMarks.setSid(rs.getInt("sid"));
   studentMarks.setMarks(rs.getInt("marks"));
   studentMarks.setYear(rs.getInt("year"));
 
   return studentMarks;
  }
}


##The following is the implementation class file StudentJDBCTemplate.java Define DAO interface StudentDAO:

package com.yiibai;
 
import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
 
public class StudentJDBCTemplate implements StudentDAO {
  private DataSource dataSource;
  private JdbcTemplate jdbcTemplateObject;
  private PlatformTransactionManager transactionManager;
 
  public void setDataSource(DataSource dataSource) {
   this.dataSource = dataSource;
   this.jdbcTemplateObject = new JdbcTemplate(dataSource);
  }
 
  public void setTransactionManager(
   PlatformTransactionManager transactionManager) {
   this.transactionManager = transactionManager;
  }
 
  public void create(String name, Integer age, Integer marks, Integer year){
 
   TransactionDefinition def = new DefaultTransactionDefinition();
   TransactionStatus status = transactionManager.getTransaction(def);
 
   try {
     String SQL1 = "insert into Student (name, age) values (?, ?)";
     jdbcTemplateObject.update( SQL1, name, age);
 
     // Get the latest student id to be used in Marks table
     String SQL2 = "select max(id) from Student";
     int sid = jdbcTemplateObject.queryForInt( SQL2 );
 
     String SQL3 = "insert into Marks(sid, marks, year) " +
            "values (?, ?, ?)";
     jdbcTemplateObject.update( SQL3, sid, marks, year);
 
     System.out.println("Created Name = " + name + ", Age = " + age);
     transactionManager.commit(status);
   } catch (DataAccessException e) {
     System.out.println("Error in creating record, rolling back");
     transactionManager.rollback(status);
     throw e;
   }
   return;
  }
 
  public List<StudentMarks> listStudents() {
   String SQL = "select * from Student, Marks where Student.id=Marks.sid";
 
   List <StudentMarks> studentMarks = jdbcTemplateObject.query(SQL,
                     new StudentMarksMapper());
   return studentMarks;
  }
}


Now let us move the main application file MainApp.java, which is as follows:

package com.yiibai;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.yiibai.StudentJDBCTemplate;
 
public class MainApp {
  public static void main(String[] args) {
   ApplicationContext context =
       new ClassPathXmlApplicationContext("Beans.xml");
 
   StudentJDBCTemplate studentJDBCTemplate =
   (StudentJDBCTemplate)context.getBean("studentJDBCTemplate");
    
   System.out.println("------Records creation--------" );
   studentJDBCTemplate.create("Zara", 11, 99, 2010);
   studentJDBCTemplate.create("Nuha", 20, 97, 2010);
   studentJDBCTemplate.create("Ayan", 25, 100, 2011);
 
   System.out.println("------Listing all the records--------" );
   List<StudentMarks> studentMarks = studentJDBCTemplate.listStudents();
   for (StudentMarks record : studentMarks) {
     System.out.print("ID : " + record.getId() );
     System.out.print(", Name : " + record.getName() );
     System.out.print(", Marks : " + record.getMarks());
     System.out.print(", Year : " + record.getYear());
     System.out.println(", Age : " + record.getAge());
   }
  }
}

The following is the configuration file beans.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">
 
  <!-- Initialization for data source -->
  <bean id="dataSource"
   class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
   <property name="username" value="root"/>
   <property name="password" value="password"/>
  </bean>
 
  <!-- Initialization for TransactionManager -->
  <bean id="transactionManager"
   class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource" /> 
  </bean>
 
  <!-- Definition for studentJDBCTemplate bean -->
  <bean id="studentJDBCTemplate"
   class="com.yiibai.StudentJDBCTemplate">
   <property name="dataSource" ref="dataSource" />
   <property name="transactionManager" ref="transactionManager" /> 
  </bean>
    
</beans>

After creating the source code and bean configuration file, let us run the application. If all goes well, this will print the following:

------Records creation--------
Created Name = Zara, Age = 11
Created Name = Nuha, Age = 20
Created Name = Ayan, Age = 25
------Listing all the records--------
ID : 1, Name : Zara, Marks : 99, Year : 2010, Age : 11
ID : 2, Name : Nuha, Marks : 97, Year : 2010, Age : 20
ID : 3, Name : Ayan, Marks : 100, Year : 2011, Age : 25

Declarative Transaction Management

The declarative transaction management approach helps you manage configuration rather than hardcoding it in your source code affairs. This means that transaction management can be done separately from the business code. Use only annotations or XML-based configuration to manage transactions. The bean's configuration will specify that the method is transactional. The following are declarative transaction-related steps:

我们使用ded7ea56a902e316700ac5ee62e194d4标签,这将创建我们定义了一个切入点匹配所有我们想做成事务,并引用其中的事务通知方法的事务并同时处理建议。

如果一个方法的名字已被列入事务配置,然后创建意见,将调用该方法之前开始交易。

目标方法将在一个try/ catch块被执行。

如果方法正常完成,AOP的建议提交事务成功,否则执行回滚。

让我们来看看为何上述步骤的工作,但在我们开始之前,它至少有两个数据库表上,我们可以用交易的帮助下执行各种CRUD操作是很重要的。让我们以Student表,它可以在MySQL数据库中测试用下面的DDL创建:

CREATE TABLE Student(
  ID  INT NOT NULL AUTO_INCREMENT,
  NAME VARCHAR(20) NOT NULL,
  AGE INT NOT NULL,
  PRIMARY KEY (ID)
);

第二个表是Marks ,我们将保持标记为基于多年的学生。这里SID是表Student的外键。

CREATE TABLE Marks(
  SID INT NOT NULL,
  MARKS INT NOT NULL,
  YEAR  INT NOT NULL
);

同样来看一下相照应的例子。
以下是数据访问对象接口文件StudentDAO.java的内容:

package com.yiibai;
 
import java.util.List;
import javax.sql.DataSource;
 
public interface StudentDAO {
  /**
  * This is the method to be used to initialize
  * database resources ie. connection.
  */
  public void setDataSource(DataSource ds);
  /**
  * This is the method to be used to create
  * a record in the Student and Marks tables.
  */
  public void create(String name, Integer age, Integer marks, Integer year);
  /**
  * This is the method to be used to list down
  * all the records from the Student and Marks tables.
  */
  public List<StudentMarks> listStudents();
}

以下是StudentMarks.java文件的内容:

package com.yiibai;
 
public class StudentMarks {
  private Integer age;
  private String name;
  private Integer id;
  private Integer marks;
  private Integer year;
  private Integer sid;
 
  public void setAge(Integer age) {
   this.age = age;
  }
  public Integer getAge() {
   return age;
  }
 
  public void setName(String name) {
   this.name = name;
  }
  public String getName() {
   return name;
  }
 
  public void setId(Integer id) {
   this.id = id;
  }
  public Integer getId() {
   return id;
  }
  public void setMarks(Integer marks) {
   this.marks = marks;
  }
  public Integer getMarks() {
   return marks;
  }
 
  public void setYear(Integer year) {
   this.year = year;
  }
  public Integer getYear() {
   return year;
  }
 
  public void setSid(Integer sid) {
   this.sid = sid;
  }
  public Integer getSid() {
   return sid;
  }
}

 

以下是StudentMarksMapper.java文件的内容:

package com.yiibai;
 
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
 
public class StudentMarksMapper implements RowMapper<StudentMarks> {
  public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException {
 
   StudentMarks studentMarks = new StudentMarks();
 
   studentMarks.setId(rs.getInt("id"));
   studentMarks.setName(rs.getString("name"));
   studentMarks.setAge(rs.getInt("age"));
   studentMarks.setSid(rs.getInt("sid"));
   studentMarks.setMarks(rs.getInt("marks"));
   studentMarks.setYear(rs.getInt("year"));
 
   return studentMarks;
  }
}

 

下面是实现类文件StudentJDBCTemplate.java 的定义DAO接口StudentDAO:

package com.yiibai;
 
import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
 
public class StudentJDBCTemplate implements StudentDAO{
  private JdbcTemplate jdbcTemplateObject;
 
  public void setDataSource(DataSource dataSource) {
   this.jdbcTemplateObject = new JdbcTemplate(dataSource);
  }
 
  public void create(String name, Integer age, Integer marks, Integer year){
 
   try {
     String SQL1 = "insert into Student (name, age) values (?, ?)";
     jdbcTemplateObject.update( SQL1, name, age);
 
     // Get the latest student id to be used in Marks table
     String SQL2 = "select max(id) from Student";
     int sid = jdbcTemplateObject.queryForInt( SQL2 );
 
     String SQL3 = "insert into Marks(sid, marks, year) " +
            "values (?, ?, ?)";
     jdbcTemplateObject.update( SQL3, sid, marks, year);
 
     System.out.println("Created Name = " + name + ", Age = " + age);
     // to simulate the exception.
     throw new RuntimeException("simulate Error condition") ;
   } catch (DataAccessException e) {
     System.out.println("Error in creating record, rolling back");
     throw e;
   }
  }
 
  public List<StudentMarks> listStudents() {
   String SQL = "select * from Student, Marks where Student.id=Marks.sid";
 
   List <StudentMarks> studentMarks=jdbcTemplateObject.query(SQL,
   new StudentMarksMapper());
   return studentMarks;
  }
}

  

现在我们移动主应用程序文件MainApp.java,如下:

package com.yiibai;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class MainApp {
  public static void main(String[] args) {
   ApplicationContext context =
       new ClassPathXmlApplicationContext("Beans.xml");
 
   StudentDAO studentJDBCTemplate =
   (StudentDAO)context.getBean("studentJDBCTemplate");
    
   System.out.println("------Records creation--------" );
   studentJDBCTemplate.create("Zara", 11, 99, 2010);
   studentJDBCTemplate.create("Nuha", 20, 97, 2010);
   studentJDBCTemplate.create("Ayan", 25, 100, 2011);
 
   System.out.println("------Listing all the records--------" );
   List<StudentMarks> studentMarks = studentJDBCTemplate.listStudents();
   for (StudentMarks record : studentMarks) {
     System.out.print("ID : " + record.getId() );
     System.out.print(", Name : " + record.getName() );
     System.out.print(", Marks : " + record.getMarks());
     System.out.print(", Year : " + record.getYear());
     System.out.println(", Age : " + record.getAge());
   }
  }
}

  

以下是配置文件beans.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
 
  <!-- Initialization for data source -->
  <bean id="dataSource"
   class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
   <property name="username" value="root"/>
   <property name="password" value="cohondob"/>
  </bean>
  
  <tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
   <tx:method name="create"/>
   </tx:attributes>
  </tx:advice>
  
  <aop:config>
   <aop:pointcut id="createOperation"
   expression="execution(* com.yiibai.StudentJDBCTemplate.create(..))"/>
   <aop:advisor advice-ref="txAdvice" pointcut-ref="createOperation"/>
  </aop:config>
  
  <!-- Initialization for TransactionManager -->
  <bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource" /> 
  </bean>
 
  <!-- Definition for studentJDBCTemplate bean -->
  <bean id="studentJDBCTemplate"
  class="com.yiibai.StudentJDBCTemplate">
   <property name="dataSource" ref="dataSource" />
  </bean>
 
</beans>

创建源代码和bean配置文件来完成,让我们运行应用程序。如果一切顺利,这将打印以下,将引发异常。在这种情况下,事务将回滚,并没有记录将在数据库表中创建。

------Records creation--------
Created Name = Zara, Age = 11
Exception in thread "main" java.lang.RuntimeException: simulate Error condition

你可以试试上面的例子中去除异常后,在这种情况下,应该提交事务,应该看到在数据库中的记录。

更多详解Java的Spring框架中的事务管理方式相关文章请关注PHP中文网!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn