데이터베이스 트랜잭션은 단일 작업 단위로 처리되는 일련의 작업입니다. 이러한 작업은 모두 완료되거나 모두 실패합니다. 트랜잭션 관리는 데이터 무결성과 일관성을 보장하기 위한 엔터프라이즈 애플리케이션용 RDBMS의 중요한 부분입니다. 트랜잭션의 개념은 ACID의 다음 네 가지 주요 속성으로 설명할 수 있습니다.
원자성: 트랜잭션은 성공 또는 실패하는 전체 작업 시퀀스를 나타내는 단일 작업 단위로 처리되어야 합니다.
일관성: 이는 데이터베이스의 참조 무결성, 테이블의 고유 기본 키 등의 일관성을 나타냅니다.
격리: 동일한 데이터 세트를 동시에 처리하는 많은 트랜잭션이 있을 수 있습니다. 데이터 손상을 방지하려면 각 트랜잭션을 다른 트랜잭션과 격리해야 합니다.
지속성: 트랜잭션이 완료되면 이 트랜잭션의 결과는 영구적으로 유지되어야 하며 시스템 오류로 인해 데이터베이스에서 삭제할 수 없습니다.
진정한 RDBMS 데이터베이스 시스템은 모든 거래에 대해 네 가지 속성을 모두 보장합니다. SQL 데이터베이스를 사용하여 트랜잭션을 발행하는 간단한 보기는 다음과 같습니다.
트랜잭션을 시작하려면 트랜잭션 시작 명령을 사용하십시오.
SQL 쿼리를 사용하여 다양한 삭제, 업데이트, 삽입 작업을 수행합니다.
모든 작업이 성공하면 커밋하고, 그렇지 않으면 모든 작업을 롤백합니다.
Spring 프레임워크에서 제공하는 다양한 기본 트랜잭션 관리 API 위에 있는 추상화 계층입니다. Spring의 트랜잭션 지원은 POJO에 트랜잭션 기능을 추가하여 EJB 트랜잭션을 대체하는 것을 목표로 합니다. Spring은 프로그래밍 방식과 선언적 트랜잭션 관리를 모두 지원합니다. EJB 애플리케이션 서버가 필요하지만 Spring 트랜잭션 관리는 애플리케이션 서버 없이도 구현됩니다.
로컬 및 글로벌 트랜잭션
로컬 트랜잭션은 JDBC 연결과 같은 단일 트랜잭션 리소스를 위한 반면, 글로벌 트랜잭션은 여러 트랜잭션 리소스처럼 분산 시스템에 걸쳐 있을 수 있습니다.
로컬 트랜잭션 관리는 애플리케이션의 구성 요소와 리소스가 단일 사이트에 있는 중앙 집중식 컴퓨팅 환경에서 유용할 수 있는 반면, 트랜잭션 관리에는 별도의 시스템에서 실행되는 로컬 데이터 관리만 포함됩니다. 로컬 트랜잭션을 구현하는 것이 더 쉽습니다.
모든 리소스가 여러 시스템에 분산되어 있는 분산 컴퓨팅 환경에서는 글로벌 트랜잭션 관리가 필요합니다. 이 경우 트랜잭션 관리는 로컬 및 글로벌 수준에서 모두 수행되어야 합니다. 분산 또는 글로벌 트랜잭션은 여러 시스템에서 실행되며 이를 실행하려면 글로벌 트랜잭션 관리 시스템과 모든 관련 시스템의 모든 로컬 데이터 관리자 간의 조정이 필요합니다.
프로그래밍 및 선언
Spring은 두 가지 유형의 트랜잭션 관리를 지원합니다.
프로그램적 트랜잭션 관리: Spring은 두 가지 유형의 트랜잭션 관리를 지원합니다.
선언적 트랜잭션 관리: 이는 거래 관리는 비즈니스 코드와 분리되어 있습니다. 트랜잭션을 관리하려면 주석이나 XML 기반 구성만 사용하세요.
프로그래밍 방식 트랜잭션 관리
프로그래밍 방식 트랜잭션 관리를 사용하면 프로그래밍 방식 소스 코드의 도움으로 트랜잭션을 관리할 수 있습니다. 이는 뛰어난 유연성을 제공하지만 유지 관리가 어렵습니다.
시작하기 전에 트랜잭션을 통해 다양한 CRUD 작업을 수행할 수 있는 데이터베이스 테이블이 두 개 이상 있습니다. 다음 DDL로 테스트하기 위해 MySQL 데이터베이스에서 생성할 수 있는 Student 테이블을 살펴보겠습니다.
CREATE TABLE Student( ID INT NOT NULL AUTO_INCREMENT, NAME VARCHAR(20) NOT NULL, AGE INT NOT NULL, PRIMARY KEY (ID) );
두 번째 테이블은 Marks입니다. 여기서 연도를 기준으로 학생의 점수를 유지합니다. . 여기서 SID는 테이블의 외래 키입니다.
CREATE TABLE Marks( SID INT NOT NULL, MARKS INT NOT NULL, YEAR INT NOT NULL );
PlatformTransactionManager를 사용하여 트랜잭션을 구현하는 프로그래밍 방식을 직접 구현해 보겠습니다. 새 트랜잭션을 시작하려면 적절한 트랜잭션 속성을 가진 TransactionDefinition 인스턴스가 있어야 합니다. 이 예에서는 기본 트랜잭션 속성을 사용하여 DefaultTransactionDefinition 인스턴스를 생성합니다.
TransactionDefinition이 생성되면 TransactionStatus 객체의 인스턴스를 반환하는 getTransaction() 메서드를 호출하여 트랜잭션을 시작할 수 있습니다. 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中文网!