詳解Java的Spring框架中的事務(wù)管理方式
數(shù)據(jù)庫(kù)事務(wù)是被當(dāng)作單個(gè)工作單元的操作序列。這些操作要么全部完成或全部不成功。事務(wù)管理是面向企業(yè)應(yīng)用程序,以確保數(shù)據(jù)的完整性和一致性RDBMS中的重要組成部分。事務(wù)的概念可以用下面的描述為ACID四個(gè)關(guān)鍵屬性來(lái)描述:
原子性: 一個(gè)事務(wù)應(yīng)該被視為單個(gè)操作單元表示的操作的任一整個(gè)序列是成功的或不成功的。
一致性: 這代表了數(shù)據(jù)庫(kù)的參照完整性,在桌等唯一主鍵的一致性
隔離性: 可能有很多事務(wù)處理相同的數(shù)據(jù)集的同時(shí),每個(gè)事務(wù)都應(yīng)由他人隔離,以防止數(shù)據(jù)損壞。
持久性: 一旦事務(wù)完成,本次事務(wù)的結(jié)果必須作出永久性的,不能從數(shù)據(jù)庫(kù)中刪除因系統(tǒng)故障。
一個(gè)真正的RDBMS數(shù)據(jù)庫(kù)系統(tǒng)將保證所有的四個(gè)屬性為每個(gè)事務(wù)。頒發(fā)給使用SQL數(shù)據(jù)庫(kù)的事務(wù)的簡(jiǎn)單觀點(diǎn)如下:
使用begin transaction命令開(kāi)始事務(wù)。
使用SQL查詢執(zhí)行各種刪除,更新或插入操作。
如果所有的操作都成功,那么執(zhí)行提交,否則回滾所有操作。
Spring框架提供的不同的底層事務(wù)管理API之上的抽象層。在Spring的事務(wù)支持,旨在通過(guò)增加事務(wù)功能,的POJO提供EJB的替代品事務(wù)。 Spring支持兩種編程式和聲明式事務(wù)管理。需要的EJB應(yīng)用程序服務(wù)器,但Spring事務(wù)管理,而不需要一個(gè)應(yīng)用服務(wù)器來(lái)實(shí)現(xiàn)。
局部與全局事務(wù)
局部事務(wù)是針對(duì)像一個(gè)JDBC連接一個(gè)單一的事務(wù)性資源,而全局事務(wù)可以跨越像事務(wù)多個(gè)事務(wù)資源的分布式系統(tǒng)。
局部事務(wù)管理可以在一個(gè)集中式計(jì)算環(huán)境下的應(yīng)用程序的組件和資源都位于一個(gè)單一的網(wǎng)站是有用的,而事務(wù)管理只涉及一個(gè)單獨(dú)的機(jī)器上運(yùn)行的本地?cái)?shù)據(jù)管理。局部事務(wù)更容易實(shí)現(xiàn)。
全局事務(wù)管理,需要在分布在多個(gè)系統(tǒng)中的所有資源的分布式計(jì)算環(huán)境。在這種情況下,事務(wù)管理既需要在地方和全局層面的工作要做。一個(gè)分布式或全局事務(wù)在多個(gè)系統(tǒng)上執(zhí)行,其執(zhí)行需要全局事務(wù)管理系統(tǒng)和所有相關(guān)系統(tǒng)的所有局部數(shù)據(jù)管理人員之間的協(xié)調(diào)。
編程與聲明
Spring支持兩種類型的事務(wù)管理:
- 編程式事務(wù)管理: Spring支持兩種類型的事務(wù)管理:
- 聲明式事務(wù)管理: 這意味著你的業(yè)務(wù)代碼分開(kāi)事務(wù)管理。你只用注釋或基于XML 配置來(lái)管理事務(wù)。
編程式事務(wù)管理
編程式事務(wù)管理辦法允許您管理與編程的源代碼的幫助下事務(wù)。這就給了極大的靈活性,但它難以維護(hù)。
在我們開(kāi)始之前,它至少有兩個(gè)數(shù)據(jù)庫(kù)表上,我們可以在事務(wù)的幫助下執(zhí)行各種CRUD操作。讓我們以Student表,它可以在MySQL數(shù)據(jù)庫(kù)中測(cè)試用下面的DDL創(chuàng)建:
CREATE TABLE Student( ID INT NOT NULL AUTO_INCREMENT, NAME VARCHAR(20) NOT NULL, AGE INT NOT NULL, PRIMARY KEY (ID) );
第二個(gè)表是Marks,我們將保持標(biāo)記為基于多年的學(xué)生。這里SID是表表的外鍵。
CREATE TABLE Marks( SID INT NOT NULL, MARKS INT NOT NULL, YEAR INT NOT NULL );
讓我們使用PlatformTransactionManager直接實(shí)現(xiàn)編程的方法來(lái)實(shí)現(xiàn)事務(wù)。要開(kāi)始一個(gè)新的事務(wù),需要有TransactionDefinition 適當(dāng)?shù)氖聞?wù)屬性的一個(gè)實(shí)例。在這個(gè)例子中,我們將簡(jiǎn)單地創(chuàng)建DefaultTransactionDefinition的實(shí)例使用默認(rèn)的事務(wù)屬性。
一旦TransactionDefinition被創(chuàng)建,你可以通過(guò)調(diào)用getTransaction()方法,它返回的TransactionStatus對(duì)象的一個(gè)實(shí)例開(kāi)始事務(wù)。TransactionStatus對(duì)象有助于跟蹤事務(wù)的當(dāng)前狀態(tài),最后,如果一切順利,可以使用提交(的PlatformTransactionManager的)方法來(lái)提交事務(wù),否則可以使用rollback() 回滾完成操作。
現(xiàn)在我們編寫(xiě)Spring JDBC應(yīng)用程序,將實(shí)現(xiàn)Student和Marks表簡(jiǎn)單的操作。
以下是數(shù)據(jù)訪問(wèn)對(duì)象接口文件StudentDAO.java的內(nèi)容:
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文件的內(nèi)容:
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文件的內(nèi)容:
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; } }
下面是實(shí)現(xiàn)類文件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; } }
現(xiàn)在讓我們移動(dòng)主應(yīng)用程序文件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>
創(chuàng)建源代碼和bean配置文件完成后,讓我們運(yùn)行應(yīng)用程序。如果一切順利,這將打印以下信息:
------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
聲明式事務(wù)管理
聲明式事務(wù)管理的方法可幫助您管理配置,而不是在源代碼中硬編碼的事務(wù)。這意味著,可以單獨(dú)從業(yè)務(wù)代碼事務(wù)管理。只用注釋或基于XML配置來(lái)管理事務(wù)。bean的配置將指定的方法是事務(wù)性。以下是聲明性與事務(wù)相關(guān)的步驟:
我們使用<tx:advice/>標(biāo)簽,這將創(chuàng)建我們定義了一個(gè)切入點(diǎn)匹配所有我們想做成事務(wù),并引用其中的事務(wù)通知方法的事務(wù)并同時(shí)處理建議。
如果一個(gè)方法的名字已被列入事務(wù)配置,然后創(chuàng)建意見(jiàn),將調(diào)用該方法之前開(kāi)始交易。
目標(biāo)方法將在一個(gè)try/ catch塊被執(zhí)行。
如果方法正常完成,AOP的建議提交事務(wù)成功,否則執(zhí)行回滾。
讓我們來(lái)看看為何上述步驟的工作,但在我們開(kāi)始之前,它至少有兩個(gè)數(shù)據(jù)庫(kù)表上,我們可以用交易的幫助下執(zhí)行各種CRUD操作是很重要的。讓我們以Student表,它可以在MySQL數(shù)據(jù)庫(kù)中測(cè)試用下面的DDL創(chuàng)建:
CREATE TABLE Student( ID INT NOT NULL AUTO_INCREMENT, NAME VARCHAR(20) NOT NULL, AGE INT NOT NULL, PRIMARY KEY (ID) );
第二個(gè)表是Marks ,我們將保持標(biāo)記為基于多年的學(xué)生。這里SID是表Student的外鍵。
CREATE TABLE Marks( SID INT NOT NULL, MARKS INT NOT NULL, YEAR INT NOT NULL );
同樣來(lái)看一下相照應(yīng)的例子。
以下是數(shù)據(jù)訪問(wèn)對(duì)象接口文件StudentDAO.java的內(nèi)容:
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文件的內(nèi)容:
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文件的內(nèi)容:
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; } }
下面是實(shí)現(xiàn)類文件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; } }
現(xiàn)在我們移動(dòng)主應(yīng)用程序文件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>
創(chuàng)建源代碼和bean配置文件來(lái)完成,讓我們運(yùn)行應(yīng)用程序。如果一切順利,這將打印以下,將引發(fā)異常。在這種情況下,事務(wù)將回滾,并沒(méi)有記錄將在數(shù)據(jù)庫(kù)表中創(chuàng)建。
------Records creation-------- Created Name = Zara, Age = 11 Exception in thread "main" java.lang.RuntimeException: simulate Error condition
你可以試試上面的例子中去除異常后,在這種情況下,應(yīng)該提交事務(wù),應(yīng)該看到在數(shù)據(jù)庫(kù)中的記錄。
相關(guān)文章
servlet的常見(jiàn)注冊(cè)方式總結(jié)
servlet大家都不陌生,當(dāng)開(kāi)發(fā)?Web?應(yīng)用程序時(shí),注冊(cè)?Servlet?是一個(gè)常見(jiàn)的任務(wù),本文將介紹一些常見(jiàn)的?Servlet?注冊(cè)方法,希望對(duì)大家有所幫助2023-10-10Hibernate傳入Java對(duì)象創(chuàng)建動(dòng)態(tài)表并錄入數(shù)據(jù)
這篇文章主要介紹了Hibernate傳入Java對(duì)象創(chuàng)建動(dòng)態(tài)表并錄入數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10從Myeclipse 導(dǎo)入到eclipse中無(wú)法識(shí)別為 web項(xiàng)目 問(wèn)題的解決步驟
這篇文章主要介紹了從Myeclipse 導(dǎo)入到eclipse中無(wú)法識(shí)別為 web項(xiàng)目 問(wèn)題的解決步驟,需要的朋友可以參考下2018-05-05關(guān)于SpringBoot中Ajax跨域以及Cookie無(wú)法獲取丟失問(wèn)題
這篇文章主要介紹了關(guān)于SpringBoot中Ajax跨域以及Cookie無(wú)法獲取丟失問(wèn)題,本文具有參考意義,遇到相同或者類似問(wèn)題的小伙伴希望可以從中找到靈感2023-03-03ImportBeanDefinitionRegistrar手動(dòng)控制BeanDefinition創(chuàng)建注冊(cè)詳解
這篇文章主要為大家介紹了ImportBeanDefinitionRegistrar手動(dòng)控制BeanDefinition創(chuàng)建注冊(cè)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Java圖像處理教程之正片疊底效果的實(shí)現(xiàn)
正片疊底效果是我們平時(shí)在Photoshop中會(huì)見(jiàn)到的一種效果,下面這篇文章主要給大家介紹了關(guān)于利用Java如何實(shí)現(xiàn)正片疊底的效果,分享出來(lái)供大家參考學(xué)習(xí),文中給出了詳細(xì)的示例代碼供大家參考學(xué)習(xí),需要的朋友可以參考借鑒,下面來(lái)一起看看詳細(xì)的介紹吧。2017-09-09