ホームページ  >  記事  >  Java  >  JavaのSpringフレームワークにおけるトランザクション管理方法の詳細な説明

JavaのSpringフレームワークにおけるトランザクション管理方法の詳細な説明

高洛峰
高洛峰オリジナル
2017-01-23 11:06:091174ブラウズ

データベース トランザクションは、単一の作業単位として扱われる一連の操作です。これらの操作はすべて完了するか、すべて失敗します。トランザクション管理は、エンタープライズ アプリケーションにとってデータの整合性と一貫性を確保するための RDBMS の重要な部分です。トランザクションの概念は、ACID の次の 4 つの主要なプロパティによって説明できます。

原子性: トランザクションは、成功または失敗した一連の操作全体を表す単一の操作単位として扱われる必要があります。

一貫性: これは、データベースの参照整合性、テーブル内の固有の主キーの一貫性などを表します。

分離: 同じデータセットを同時に処理する多くのトランザクションが存在する可能性があり、各トランザクションは他のトランザクションから分離される必要があります。データの破損を防ぎます。

永続性: トランザクションが完了すると、このトランザクションの結果は永続化される必要があり、システム障害のためにデータベースから削除することはできません。

真の RDBMS データベース システムは、すべてのトランザクションに対して 4 つのプロパティすべてを保証します。 SQL データベースを使用してトランザクションを発行する簡単な図は次のとおりです:

トランザクションを開始するには、begintransaction コマンドを使用します。

SQL クエリを使用して、さまざまな削除、更新、または挿入操作を実行します。

すべての操作が成功した場合はコミットし、そうでない場合はすべての操作をロールバックします。

Spring フレームワークによって提供されるさまざまな基礎となるトランザクション管理 API の上にある抽象化レイヤー。 Spring のトランザクション サポートは、POJO にトランザクション機能を追加することで、EJB トランザクションの代替を提供することを目的としています。 Spring は、プログラムによるトランザクション管理と宣言的なトランザクション管理の両方をサポートしています。 EJB アプリケーション サーバーが必要ですが、Spring トランザクション管理はアプリケーション サーバーを必要とせずに実装されます。

ローカル トランザクションとグローバル トランザクション
ローカル トランザクションは JDBC 接続などの単一のトランザクション リソース用ですが、グローバル トランザクションは複数のトランザクション リソースなどの分散システムにまたがることができます。

ローカル トランザクション管理は、アプリケーションのコンポーネントとリソースが 1 つのサイトに配置されている集中コンピューティング環境で役立ちますが、トランザクション管理には別のマシンで実行されるローカル データ管理のみが含まれます。ローカルトランザクションは実装が簡単です。

すべてのリソースが複数のシステムに分散されている分散コンピューティング環境では、グローバル トランザクション管理が必要です。この場合、トランザクション管理はローカル レベルとグローバル レベルの両方で行う必要があります。分散トランザクションまたはグローバル トランザクションは複数のシステムで実行され、その実行にはグローバル トランザクション管理システムとすべての関連システムのすべてのローカル データ マネージャー間の調整が必要です。

プログラミングと宣言型
Spring は 2 種類のトランザクション管理をサポートします:

プログラムによるトランザクション管理: Spring は 2 種類のトランザクション管理をサポートします:

宣言型トランザクション管理: これは、ビジネス コードがトランザクション管理を分離することを意味します。トランザクションを管理するには、注釈または XML ベースの構成のみを使用します。

プログラムによるトランザクション管理
プログラムによるトランザクション管理を使用すると、プログラムによるソース コードを使用してトランザクションを管理できます。これにより優れた柔軟性が得られますが、維持するのが困難です。

始める前に、トランザクションを使用してさまざまな CRUD 操作を実行できるデータベース テーブルが少なくとも 2 つあることを確認します。 Student テーブルを考えてみましょう。これは、次の DDL でテストするために MySQL データベースに作成できます:

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

2 番目のテーブルは Marks で、ここに年に基づいて学生の成績を保持します。ここで、SID はテーブルの外部キーです。

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

PlatformTransactionManager を使用して、トランザクションを実装するためのプログラミング メソッドを直接実装しましょう。新しいトランザクションを開始するには、適切なトランザクション属性を持つ TransactionDefinition のインスタンスが必要です。この例では、デフォルトのトランザクション プロパティを使用して DefaultTransactionDefinition のインスタンスを作成します。

TransactionDefinition が作成されたら、getTransaction() メソッドを呼び出してトランザクションを開始できます。このメソッドは TransactionStatus オブジェクトのインスタンスを返します。 TransactionStatus オブジェクトは、トランザクションの現在の状態を追跡するのに役立ちます。最後に、すべてがうまくいけば、 commit(PlatformTransactionManager) メソッドを使用してトランザクションをコミットできます。そうでない場合は、 rollback() を使用して、完了した操作をロールバックできます。

ここで、Student テーブルと Marks テーブルに簡単な操作を実装する Spring JDBC アプリケーションを作成します。
以下はデータ アクセス オブジェクト インターフェイス ファイル 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;
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;
  }
}


次に、メイン アプリケーション ファイル MainApp.java を移動しましょう。これは次のとおりです:

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

以下は構成ファイル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>

ソースコードとBean構成ファイルを作成した後、アプリケーションを実行しましょう。すべてがうまくいけば、次のメッセージが出力されます:

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

宣言型トランザクション管理
宣言型トランザクション管理アプローチは、ソース コードでトランザクションをハードコーディングする代わりに、設定を管理するのに役立ちます。これは、トランザクション管理をビジネス コードとは別に実行できることを意味します。注釈または XML ベースの構成のみを使用してトランザクションを管理します。 Bean の設定では、メソッドがトランザクションであることが指定されます。宣言的なトランザクション関連の手順は次のとおりです:

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

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。