Maison  >  Article  >  Java  >  Explication détaillée des méthodes de gestion des transactions dans le framework Spring de Java

Explication détaillée des méthodes de gestion des transactions dans le framework Spring de Java

高洛峰
高洛峰original
2017-01-23 11:06:091221parcourir

Une transaction de base de données est une séquence d'opérations traitée comme une seule unité de travail. Ces opérations soit toutes terminées, soit toutes ont échoué. La gestion des transactions est une partie importante du SGBDR pour les applications d'entreprise afin de garantir l'intégrité et la cohérence des données. Le concept de transaction peut être décrit par les quatre propriétés clés suivantes d'ACID :

Atomicité : une transaction doit être traitée comme une unité d'opération unique représentant toute séquence complète d'opérations qui réussit ou échoue.

Cohérence : Ceci représente l'intégrité référentielle de la base de données, la cohérence des clés primaires uniques dans les tables, etc.

Isolement : Il peut y avoir plusieurs transactions traitant le même ensemble de données en même temps, chaque transaction doit être isolée des autres pour éviter la corruption des données.

Persistance : Une fois qu'une transaction est terminée, les résultats de cette transaction doivent être rendus permanents et ne peuvent pas être supprimés de la base de données en raison d'une défaillance du système.

Un véritable système de base de données SGBDR garantira les quatre attributs pour chaque transaction. Une vue simple de l'émission d'une transaction à l'aide d'une base de données SQL est la suivante :

Utilisez la commande start transaction pour démarrer une transaction.

Effectuez diverses opérations de suppression, de mise à jour ou d'insertion à l'aide de requêtes SQL.

Si toutes les opérations réussissent, alors la validation est effectuée, sinon toutes les opérations sont annulées.

Une couche d'abstraction au-dessus des différentes API de gestion des transactions sous-jacentes fournies par le framework Spring. La prise en charge des transactions dans Spring vise à remplacer les transactions EJB en ajoutant des fonctionnalités de transaction aux POJO. Spring prend en charge la gestion des transactions programmatique et déclarative. Un serveur d'applications EJB est requis, mais la gestion des transactions Spring est implémentée sans avoir besoin d'un serveur d'applications.

Transactions locales et globales
Les transactions locales concernent une seule ressource transactionnelle comme une connexion JDBC, tandis que les transactions globales peuvent s'étendre sur des systèmes distribués comme plusieurs ressources transactionnelles.

La gestion locale des transactions peut être utile dans un environnement informatique centralisé où les composants et les ressources d'une application sont situés sur un seul site, alors que la gestion des transactions implique uniquement la gestion locale des données exécutées sur une machine distincte. Les transactions locales sont plus faciles à mettre en œuvre.

La gestion globale des transactions est requise dans un environnement informatique distribué avec toutes les ressources réparties sur plusieurs systèmes. Dans ce cas, la gestion des transactions doit être effectuée aux niveaux local et mondial. Une transaction distribuée ou globale est exécutée sur plusieurs systèmes et son exécution nécessite une coordination entre le système de gestion des transactions globales et tous les gestionnaires de données locaux de tous les systèmes associés.

Programmation et déclaration
Spring prend en charge deux types de gestion des transactions :

Gestion programmatique des transactions : Spring prend en charge deux types de gestion des transactions :

Gestion déclarative des transactions : cela signifie la gestion des transactions est séparée de votre code métier. Vous utilisez uniquement des annotations ou une configuration basée sur XML pour gérer les transactions.

Gestion programmatique des transactions
La gestion programmatique des transactions vous permet de gérer les transactions à l'aide du code source programmatique. Cela donne une grande flexibilité, mais c'est difficile à maintenir.

Avant de commencer, il y a au moins deux tables de base de données sur lesquelles nous pouvons effectuer diverses opérations CRUD à l'aide de transactions. Prenons la table Student, qui peut être créée dans une base de données MySQL pour tester avec le DDL suivant :

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

La deuxième table est Marks, nous garderons les étudiants notés en fonction de années . Ici, SID est la clé étrangère de la table.

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

Utilisons PlatformTransactionManager pour implémenter directement des méthodes de programmation pour implémenter des transactions. Pour démarrer une nouvelle transaction, il doit exister une instance de TransactionDefinition avec les attributs de transaction appropriés. Dans cet exemple, nous allons simplement créer une instance de DefaultTransactionDefinition en utilisant les propriétés de transaction par défaut.

Une fois une TransactionDefinition créée, vous pouvez démarrer une transaction en appelant la méthode getTransaction(), qui renvoie une instance de l'objet TransactionStatus. L'objet TransactionStatus permet de suivre l'état actuel de la transaction et enfin, si tout se passe bien, vous pouvez utiliser la méthode commit(PlatformTransactionManager) pour valider la transaction, sinon vous pouvez utiliser rollback() pour annuler l'opération terminée.

Maintenant, nous écrivons une application Spring JDBC qui implémentera des opérations simples sur les tables Student et Marks.
Ce qui suit est le contenu du fichier d'interface de l'objet d'accès aux données 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();
}


Ce qui suit est le contenu du Fichier 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;
  }
}

Voici le contenu du fichier 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;
  }
}


Ce qui suit est le fichier de classe d'implémentation StudentJDBCTemplate.java définit l'interface DAO 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;
  }
}


Déplaçons maintenant le fichier d'application principal MainApp.java, qui se présente comme suit :

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());
   }
  }
}

Voici le fichier de configuration 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"
  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>

Créez le code source et une fois le fichier de configuration du bean terminé, exécutons l'application. Si tout se passe bien, cela affichera ce qui suit :

------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

Gestion déclarative des transactions
L'approche de gestion déclarative des transactions vous aide à gérer la configuration plutôt que dans le code source des transactions codées en dur. Cela signifie que la gestion des transactions peut être effectuée séparément du code métier. Gérez les transactions en utilisant uniquement des annotations ou une configuration basée sur XML. La configuration du bean précisera que la méthode est transactionnelle. Voici les étapes déclaratives et liées aux transactions :

我们使用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中文网!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn