小Alan接着上一篇Spring事務管理入門與進階做一些補充,如果對Spring事務管理還不了解的可以看看上一篇文章。
實例
在我們開始之前,至少有兩個數據庫表是至關重要的,在事務的幫助下,我們可以實現各種 CRUD 操作。以 Student 表為例,該表是使用下述 DDL 在 MySQL TEST 數據庫中創建的。
1 CREATE TABLE Student( 2 ID INT NOT NULL AUTO_INCREMENT, 3 NAME VARCHAR(20) NOT NULL, 4 AGE INT NOT NULL, 5 PRIMARY KEY (ID) 6 );
第二個表是 Marks,我們用來存儲基於年份的學生標記。在這里,SID 是 Student 表的外鍵。
1 CREATE TABLE Marks( 2 SID INT NOT NULL, 3 MARKS INT NOT NULL, 4 YEAR INT NOT NULL 5 );
現在讓我們編寫 Spring JDBC 應用程序來在 Student 和 Marks 表中實現簡單的操作。讓我們適當的使用 Eclipse IDE,並按照如下所示的步驟來創建一個 Spring 應用程序:
步驟一:創建一個名為 SpringExample 的項目,並在創建的項目中的 src 文件夾下創建包 com.tutorialspoint。
步驟二:使用 Add JARs 選項添加必需的 Spring 庫。
步驟三:在項目中添加其它必需的庫 mysql-connector-java.jar,org.springframework.jdbc.jar 和 org.springframework.transaction.jar。如果你還沒有這些庫,你可以下載它們。
步驟四:創建 DAO 接口 StudentDAO 並列出所有需要的方法。盡管它不是必需的並且你可以直接編寫 StudentJDBCTemplate 類,但是作為一個好的實踐,我們還是做吧。
步驟五:在 com.tutorialspoint 包下創建其他必需的 Java 類 StudentMarks,StudentMarksMapper,StudentJDBCTemplate 和 MainApp。如果需要的話,你可以創建其他的 POJO 類。
步驟六:確保你已經在 TEST 數據庫中創建了 Student 和 Marks 表。還要確保你的 MySQL 服務器運行正常並且你使用給出的用戶名和密碼可以讀/寫訪問數據庫。
步驟七:在 src 文件夾下創建 Beans 配置文件 Beans.xml 。
最后一步:最后一步是創建所有 Java 文件和 Bean 配置文件的內容並按照如下所示的方法運行應用程序。
下面是數據訪問對象接口文件 StudentDAO.java 的內容:
1 package com.tutorialspoint; 2 import java.util.List; 3 import javax.sql.DataSource; 4 public interface StudentDAO { 5 /** 6 * This is the method to be used to initialize 7 * database resources ie. connection. 8 */ 9 public void setDataSource(DataSource ds); 10 /** 11 * This is the method to be used to create 12 * a record in the Student and Marks tables. 13 */ 14 public void create(String name, Integer age, Integer marks, Integer year); 15 /** 16 * This is the method to be used to list down 17 * all the records from the Student and Marks tables. 18 */ 19 public List<StudentMarks> listStudents(); 20 }
以下是 StudentMarks.java 文件的內容:
1 package com.tutorialspoint; 2 public class StudentMarks { 3 private Integer age; 4 private String name; 5 private Integer id; 6 private Integer marks; 7 private Integer year; 8 private Integer sid; 9 public void setAge(Integer age) { 10 this.age = age; 11 } 12 public Integer getAge() { 13 return age; 14 } 15 public void setName(String name) { 16 this.name = name; 17 } 18 public String getName() { 19 return name; 20 } 21 public void setId(Integer id) { 22 this.id = id; 23 } 24 public Integer getId() { 25 return id; 26 } 27 public void setMarks(Integer marks) { 28 this.marks = marks; 29 } 30 public Integer getMarks() { 31 return marks; 32 } 33 public void setYear(Integer year) { 34 this.year = year; 35 } 36 public Integer getYear() { 37 return year; 38 } 39 public void setSid(Integer sid) { 40 this.sid = sid; 41 } 42 public Integer getSid() { 43 return sid; 44 } 45 }
下面是 StudentMarksMapper.java 文件的內容:
1 package com.tutorialspoint; 2 import java.sql.ResultSet; 3 import java.sql.SQLException; 4 import org.springframework.jdbc.core.RowMapper; 5 public class StudentMarksMapper implements RowMapper<StudentMarks> { 6 public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException { 7 StudentMarks studentMarks = new StudentMarks(); 8 studentMarks.setId(rs.getInt("id")); 9 studentMarks.setName(rs.getString("name")); 10 studentMarks.setAge(rs.getInt("age")); 11 studentMarks.setSid(rs.getInt("sid")); 12 studentMarks.setMarks(rs.getInt("marks")); 13 studentMarks.setYear(rs.getInt("year")); 14 return studentMarks; 15 } 16 }
下面是定義的 DAO 接口 StudentDAO 實現類文件 StudentJDBCTemplate.java:
1 package com.tutorialspoint; 2 import java.util.List; 3 import javax.sql.DataSource; 4 import org.springframework.dao.DataAccessException; 5 import org.springframework.jdbc.core.JdbcTemplate; 6 public class StudentJDBCTemplate implements StudentDAO{ 7 private JdbcTemplate jdbcTemplateObject; 8 public void setDataSource(DataSource dataSource) { 9 this.jdbcTemplateObject = new JdbcTemplate(dataSource); 10 } 11 public void create(String name, Integer age, Integer marks, Integer year){ 12 try { 13 String SQL1 = "insert into Student (name, age) values (?, ?)"; 14 jdbcTemplateObject.update( SQL1, name, age); 15 // Get the latest student id to be used in Marks table 16 String SQL2 = "select max(id) from Student"; 17 int sid = jdbcTemplateObject.queryForInt( SQL2 ); 18 String SQL3 = "insert into Marks(sid, marks, year) " + 19 "values (?, ?, ?)"; 20 jdbcTemplateObject.update( SQL3, sid, marks, year); 21 System.out.println("Created Name = " + name + ", Age = " + age); 22 // to simulate the exception. 23 throw new RuntimeException("simulate Error condition") ; 24 } catch (DataAccessException e) { 25 System.out.println("Error in creating record, rolling back"); 26 throw e; 27 } 28 } 29 public List<StudentMarks> listStudents() { 30 String SQL = "select * from Student, Marks where Student.id=Marks.sid"; 31 List <StudentMarks> studentMarks=jdbcTemplateObject.query(SQL, 32 new StudentMarksMapper()); 33 return studentMarks; 34 } 35 }
現在讓我們改變主應用程序文件 MainApp.java,如下所示:
1 package com.tutorialspoint; 2 import java.util.List; 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 public class MainApp { 6 public static void main(String[] args) { 7 ApplicationContext context = 8 new ClassPathXmlApplicationContext("Beans.xml"); 9 StudentDAO studentJDBCTemplate = 10 (StudentDAO)context.getBean("studentJDBCTemplate"); 11 System.out.println("------Records creation--------" ); 12 studentJDBCTemplate.create("Zara", 11, 99, 2010); 13 studentJDBCTemplate.create("Nuha", 20, 97, 2010); 14 studentJDBCTemplate.create("Ayan", 25, 100, 2011); 15 System.out.println("------Listing all the records--------" ); 16 List<StudentMarks> studentMarks = studentJDBCTemplate.listStudents(); 17 for (StudentMarks record : studentMarks) { 18 System.out.print("ID : " + record.getId() ); 19 System.out.print(", Name : " + record.getName() ); 20 System.out.print(", Marks : " + record.getMarks()); 21 System.out.print(", Year : " + record.getYear()); 22 System.out.println(", Age : " + record.getAge()); 23 } 24 } 25 }
以下是配置文件 Beans.xml 的內容:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context-3.0.xsd 11 http://www.springframework.org/schema/tx 12 http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 13 http://www.springframework.org/schema/aop 14 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 15 16 <!-- 開啟注解 --> 17 <context:annotation-config /> 18 19 <!-- 注解掃描包路徑 --> 20 <context:component-scan base-package="com.tutorialspoint.*" /> 21 22 <!-- 通過spring讀取配置文件 --> 23 <bean 24 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 25 <property name="fileEncoding" value="UTF-8"></property> 26 <property name="locations"> 27 <list> 28 <value>classpath:conf_db.properties</value> 29 </list> 30 </property> 31 </bean> 32 33 <!-- Initialization for data source --> 34 <bean id="dataSource" 35 class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 36 <property name="driverClassName" value="${jdbc.url.driver}" /> 37 <property name="url" value="${jdbc.url.master}" /> 38 <property name="username" value="${jdbc.username.master}" /> 39 <property name="password" value="${jdbc.password.master}" /> 40 </bean> 41 42 <!-- Initialization for TransactionManager --> 43 <bean id="transactionManager" 44 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 45 <property name="dataSource" ref="dataSource" /> 46 </bean> 47 48 <!-- 支持事務注解 --> 49 <tx:annotation-driven transaction-manager="transactionManager"/> 50 51 <!-- Definition for studentJDBCTemplate bean --> 52 <bean id="studentJDBCTemplate" class="com.tutorialspoint.StudentJDBCTemplate"> 53 <property name="dataSource" ref="dataSource" /> 54 </bean> 55 56 </beans>
以下是數據庫配置文件conf_db.properties的內容:
jdbc.url.driver=com.mysql.jdbc.Driver jdbc.url.master=jdbc:mysql://localhost:3306/TEST jdbc.username.master=root jdbc.password.master=123456
最后讓我們在StudentJDBCTemplate類上面加上事務注解,此處不建議在Dao層使用事務注解,在平時工作中一般會使用在Service層,小Alan為了演示效果節約時間沒有敲Service層代碼,請注意,如圖:
當你完成了創建源和 bean 配置文件后,讓我們運行應用程序。如果你的應用程序運行順利的話,那么會輸出如下所示的異常。在這種情況下,事務會回滾並且在數據庫表中不會創建任何記錄。
------Records creation-------- Created Name = Zara, Age = 11 Exception in thread "main" java.lang.RuntimeException: simulate Error condition
在刪除StudentJDBCTemplate類中create方法拋出異常的代碼后,你可以嘗試上述示例,在這種情況下,會提交事務並且你可以在數據庫中看見記錄。
注意:項目jar包如圖所示,其中也包含了Spring其它方面的一些jar包,小Alan偷一下懶就沒去剔除了,這一塊大家可以嘗試只留下必要的jar包,以便熟悉Spring所包含的每個jar包在項目中所能夠起到的作用,記得把jar包引入項目中才能夠運行上述的示例。
@Transactional注解
@Transactional 可以作用於接口、接口方法、類以及類方法上。當作用於類上時,該類的所有 public 方法將都具有該類型的事務屬性,同時,我們也可以在方法級別使用該標注來覆蓋類級別的定義。
雖然 @Transactional 注解可以作用於接口、接口方法、類以及類方法上,但是 Spring 建議不要在接口或者接口方法上使用該注解,因為這只有在使用基於接口的代理時它才會生效。另外, @Transactional 注解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。如果你在 protected、private 或者默認可見性的方法上使用 @Transactional 注解,這將被忽略,也不會拋出任何異常。
默認情況下,只有來自外部的方法調用才會被AOP代理捕獲,也就是,類內部方法調用本類內部的其他方法並不會引起事務行為,即使被調用方法使用@Transactional注解進行修飾。
@Transactional參數
參數名稱 |
功能描述 |
readOnly |
該屬性用於設置當前事務是否為只讀事務,設置為true表示只讀,false則表示可讀寫,默認值為false。例如:@Transactional(readOnly=true) |
rollbackFor |
該屬性用於設置需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。例如: 指定單一異常類:@Transactional(rollbackFor=RuntimeException.class) 指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
rollbackForClassName |
該屬性用於設置需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,則進行事務回滾。例如: 指定單一異常類名稱:@Transactional(rollbackForClassName="RuntimeException") 指定多個異常類名稱:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
noRollbackFor |
該屬性用於設置不需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,不進行事務回滾。例如: 指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class) 指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName |
該屬性用於設置不需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,不進行事務回滾。例如: 指定單一異常類名稱:@Transactional(noRollbackForClassName="RuntimeException") 指定多個異常類名稱: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}) |
propagation |
該屬性用於設置事務的傳播行為。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
isolation |
該屬性用於設置底層數據庫的事務隔離級別,事務隔離級別用於處理多事務並發的情況,通常使用數據庫的默認隔離級別即可,基本不需要進行設置 |
timeout |
該屬性用於設置事務的超時秒數,默認值為-1表示永不超時 |
結束語:無論正在經歷什么,都請不要輕言放棄,因為從來沒有一種堅持會被辜負。誰的人生不是荊棘前行,生活從來不會一蹴而就,也不會永遠安穩,只要努力,就能做獨一無二平凡可貴的自己。
可愛博主:AlanLee
博客地址:http://www.cnblogs.com/AlanLee
本文出自博客園,歡迎大家加入博客園。