1. 程式人生 > >Java學習筆記(12)Spring JDBC框架和事務管理

Java學習筆記(12)Spring JDBC框架和事務管理

Spring JDBC框架

JDBC框架概述:

在使用普通的 JDBC 資料庫時,就會很麻煩的寫不必要的程式碼來處理異常,開啟和關閉資料庫連線等。但 Spring JDBC 框架負責所有的低層細節,從開始開啟連線,準備和執行 SQL 語句,處理異常,處理事務,到最後關閉連線。

所以當從資料庫中獲取資料時,你所做的是定義連線引數,指定要執行的 SQL 語句,每次迭代完成所需的工作。

Spring JDBC 提供幾種方法和資料庫中相應的不同的類與介面。我將給出使用 JdbcTemplate 類框架的經典和最受歡迎的方法。這是管理所有資料庫通訊和異常處理的中央框架類。

JdbcTemplate型別:

JdbcTemplate 類執行 SQL 查詢、更新語句和儲存過程呼叫,執行迭代結果集和提取返回引數值。它也捕獲 JDBC 異常並轉換它們到 org.springframework.dao 包中定義的通用類、更多的資訊、異常層次結構。

JdbcTemplate 類的例項是執行緒安全配置的。所以你可以配置 JdbcTemplate 的單個例項,然後將這個共享的引用安全地注入到多個 DAOs 中。

使用 JdbcTemplate 類時常見的做法是在你的 Spring 配置檔案中配置資料來源,然後共享資料來源 bean 依賴注入到 DAO 類中,並在資料來源的設值函式中建立了 JdbcTemplate。

<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/jsp_db"/>
   <property name="username" value="root"/>
   <property
name="password" value="123456"/>
</bean>

資料訪問物件(DAO):

DAO 代表常用的資料庫互動的資料訪問物件。DAOs 提供一種方法來讀取資料並將資料寫入到資料庫中,它們應該通過一個介面顯示此功能,應用程式的其餘部分將訪問它們。

在 Spring 中,資料訪問物件(DAO)支援很容易用統一的方法使用資料訪問技術,如 JDBC、Hibernate、JPA 或者 JDO。

Spring JDBC 示例:

目標:通過Spring JDBC 框架,來實現建立一個數據表。

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

StudentDAO.java

package jdbc;

import java.util.List;
import javax.sql.DataSource;
public interface StudentDAO {

   //用於初始化資料庫連線的方法
   public void setDataSource(DataSource dateSource);

   //這是用來在學生表中建立一條記錄的方法。
   public void create(String name, Integer age);

   //這是用於列出學生表中與通過的學生ID相對應的的記錄的方法。
   public Student getStudent(Integer id);

   //這是用來列出學生表中的所有記錄的方法。
   public List<Student> listStudents();

   //這是用於刪除學生表中與通過的學生id相對應的記錄的方法。
   public void delete(Integer id);

   //這是用來將一條記錄更新到學生表中的方法。
   public void update(Integer id, Integer age);
}

Student.java

package jdbc;

public class Student {
    private Integer age;
    private String name;
    private Integer id;
    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;
    }
}

StudentMapper.java

package jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class StudentMapper implements RowMapper<Student> {
   public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
      Student student = new Student();
      student.setId(rs.getInt("id"));
      student.setName(rs.getString("name"));
      student.setAge(rs.getInt("age"));
      return student;
   }
}

下面是為定義的 DAO 介面 StudentDAO 的實現類檔案 StudentJDBCTemplate.java

package jdbc;

import java.util.List;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

public class StudentJDBCTemplate implements StudentDAO{

    private DataSource dataSource;
    private JdbcTemplate jdbcTemplateObject;

    @Override
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        this.jdbcTemplateObject = new JdbcTemplate(dataSource);
    }

    @Override
    public void create(String name, Integer age) {
        String SQL = "insert into Student (name, age) values (?, ?)";
        jdbcTemplateObject.update(SQL, name, age);
        System.out.println("建立記錄名:" + name + "年齡:" + age);
        return;
    }

    @Override
    public Student getStudent(Integer id) {
        String SQL = "select * from Student where id = ?";
        Student student = jdbcTemplateObject.queryForObject(SQL, 
                new Object[]{id}, new StudentMapper());
        return student;
    }

    @Override
    public List<Student> listStudents() {
        String SQL = "select * from Student";
        List <Student> students = jdbcTemplateObject.query(SQL, 
                new StudentMapper());
        return students;
    }

    @Override
    public void delete(Integer id) {
        String SQL = "delete from Student where id = ?";
        jdbcTemplateObject.update(SQL, id);
        System.out.println("刪除ID為 : " + id + " 的記錄!" );
        return;
    }

    @Override
    public void update(Integer id, Integer age) {
        String SQL = "update Student set age = ? where id = ?";
        jdbcTemplateObject.update(SQL, age, id);
        System.out.println("更新記錄的學生ID為 = " + id );
        return;
    }

}

MainApp.java

package jdbc;

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("JDBC.xml");
        StudentJDBCTemplate studentJDBCTemplate = 
                (StudentJDBCTemplate)context.getBean("studentJDBCTemplate");    
        System.out.println("------建立記錄--------" );
        studentJDBCTemplate.create("xiaoming", 11);
        studentJDBCTemplate.create("xiaoli", 2);
        studentJDBCTemplate.create("xiaozhang", 15);
        System.out.println();
        System.out.println("------列出所有記錄--------" );
        List<Student> students = studentJDBCTemplate.listStudents();
        for (Student record : students) {
            System.out.print("ID : " + record.getId() );
            System.out.print(", Name : " + record.getName() );
            System.out.println(", Age : " + record.getAge());
        }
        System.out.println("----更新ID=2的記錄 -----" );
        studentJDBCTemplate.update(2, 20);
        System.out.println("----列出ID=2的記錄 -----" );
        Student student = studentJDBCTemplate.getStudent(2);
        System.out.print("ID : " + student.getId() );
        System.out.print(", Name : " + student.getName() );
        System.out.println(", Age : " + student.getAge());      
    }
}

配置檔案 JDBC.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/jsp_db?useSSL=false"/>
      <property name="username" value="root"/>
      <property name="password" value="123456"/>
   </bean>

   <!-- Definition for studentJDBCTemplate bean -->
   <bean id="studentJDBCTemplate" 
      class="jdbc.StudentJDBCTemplate">
      <property name="dataSource"  ref="dataSource" />    
   </bean>

</beans>

執行結果:

------建立記錄--------
建立記錄名:xiaoming年齡:11
建立記錄名:xiaoli年齡:2
建立記錄名:xiaozhang年齡:15

------列出所有記錄--------
ID : 1, Name : xiaoming, Age : 11
ID : 2, Name : xiaoli, Age : 20
ID : 3, Name : xiaozhang, Age : 15

----更新ID=2的記錄 -----
更新記錄的學生ID為 = 2

----列出ID=2的記錄 -----
ID : 2, Name : xiaoli, Age : 20

Spring事務管理

事物管理:

一個數據庫事務是一個被視為單一的工作單元的操作序列。這些操作應該要麼完整地執行,要麼完全不執行。事務管理是一個重要組成部分,RDBMS 面向企業應用程式,以確保資料完整性和一致性。事務的概念可以描述為具有以下四個關鍵屬性說成是 ACID

  • 原子性:事務應該當作一個單獨單元的操作,這意味著整個序列操作要麼是成功,要麼是失敗的。
  • 一致性:這表示資料庫的引用完整性的一致性,表中唯一的主鍵等。
  • 隔離性:可能同時處理很多有相同的資料集的事務,每個事務應該與其他事務隔離,以防止資料損壞。
  • 永續性:一個事務一旦完成全部操作後,這個事務的結果必須是永久性的,不能因系統故障而從資料庫中刪除。

一個真正的 RDBMS 資料庫系統將為每個事務保證所有的四個屬性。使用 SQL 釋出到資料庫中的事務的簡單檢視如下:

  • 使用 begin transaction 命令開始事務。
  • 使用 SQL 查詢語句執行各種刪除、更新或插入操作。
  • 如果所有的操作都成功,則執行提交操作,否則回滾所有操作。

Spring 框架在不同的底層事務管理 APIs 的頂部提供了一個抽象層。Spring 的事務支援旨在通過新增事務能力到 POJOs 來提供給 EJB 事務一個選擇方案。Spring 支援程式設計式和宣告式事務管理。EJBs 需要一個應用程式伺服器,但 Spring 事務管理可以在不需要應用程式伺服器的情況下實現。

區域性事物和全域性事物:

區域性事務是特定於一個單一的事務資源,如一個 JDBC 連線,而全域性事務可以跨多個事務資源事務,如在一個分散式系統中的事務。

區域性事務管理在一個集中的計算環境中是有用的,該計算環境中應用程式元件和資源位於一個單位點,而事務管理只涉及到一個執行在一個單一機器中的本地資料管理器。區域性事務更容易實現。

全域性事務管理需要在分散式計算環境中,所有的資源都分佈在多個系統中。在這種情況下事務管理需要同時在區域性和全域性範圍內進行。分散式或全域性事務跨多個系統執行,它的執行需要全域性事務管理系統和所有相關係統的區域性資料管理人員之間的協調。

Spring支援兩種型別的事物管理:

  • 程式設計式事務管理 :這意味著你在程式設計的幫助下有管理事務。這給了你極大的靈活性,但卻很難維護。
  • 宣告式事務管理 :這意味著你從業務程式碼中分離事務管理。你僅僅使用註釋或 XML 配置來管理事務。

宣告式事務管理比程式設計式事務管理更可取,儘管它不如程式設計式事務管理靈活,但它允許你通過程式碼控制事務。但作為一種橫切關注點,宣告式事務管理可以使用 AOP 方法進行模組化。Spring 支援使用 Spring AOP 框架的宣告式事務管理。

Spring事物抽象:

Spring 事務抽象的關鍵是由 org.springframework.transaction.PlatformTransactionManager 介面定義,如下所示:

public interface PlatformTransactionManager {
   TransactionStatus getTransaction(TransactionDefinition definition);
   throws TransactionException;
   void commit(TransactionStatus status) throws TransactionException;
   void rollback(TransactionStatus status) throws TransactionException;
}
序號 方法 & 描述
1 TransactionStatus getTransaction(TransactionDefinition definition)根據指定的傳播行為,該方法返回當前活動事務或建立一個新的事務。
2 void commit(TransactionStatus status)該方法提交給定的事務和關於它的狀態。
3 void rollback(TransactionStatus status)該方法執行一個給定事務的回滾。

TransactionDefinition 是在 Spring 中事務支援的核心介面,它的定義如下:

public interface TransactionDefinition {
   int getPropagationBehavior();
   int getIsolationLevel();
   String getName();
   int getTimeout();
   boolean isReadOnly();
}
序號 方法 & 描述
1 int getPropagationBehavior()該方法返回傳播行為。Spring 提供了與 EJB CMT 類似的所有的事務傳播選項。
2 int getIsolationLevel()該方法返回該事務獨立於其他事務的工作的程度。
3 String getName()該方法返回該事務的名稱。
4 int getTimeout()該方法返回以秒為單位的時間間隔,事務必須在該時間間隔內完成。
5 boolean isReadOnly()該方法返回該事務是否是隻讀的。

下面是隔離級別的可能值:

序號 隔離 & 描述
1 TransactionDefinition.ISOLATION_DEFAULT這是預設的隔離級別。
2 TransactionDefinition.ISOLATION_READ_COMMITTED表明能夠阻止誤讀;可以發生不可重複讀和虛讀。
3 TransactionDefinition.ISOLATION_READ_UNCOMMITTED表明可以發生誤讀、不可重複讀和虛讀。
4 TransactionDefinition.ISOLATION_REPEATABLE_READ表明能夠阻止誤讀和不可重複讀;可以發生虛讀。
5 TransactionDefinition.ISOLATION_SERIALIZABLE表明能夠阻止誤讀、不可重複讀和虛讀。

下面是傳播型別的可能值:

序號 傳播 & 描述
1 TransactionDefinition.PROPAGATION_MANDATORY支援當前事務;如果不存在當前事務,則丟擲一個異常。
2 TransactionDefinition.PROPAGATION_NESTED如果存在當前事務,則在一個巢狀的事務中執行。
3 TransactionDefinition.PROPAGATION_NEVER不支援當前事務;如果存在當前事務,則丟擲一個異常。
4 TransactionDefinition.PROPAGATION_NOT_SUPPORTED不支援當前事務;而總是執行非事務性。
5 TransactionDefinition.PROPAGATION_REQUIRED支援當前事務;如果不存在事務,則建立一個新的事務。
6 TransactionDefinition.PROPAGATION_REQUIRES_NEW建立一個新事務,如果存在一個事務,則把當前事務掛起。
7 TransactionDefinition.PROPAGATION_SUPPORTS支援當前事務;如果不存在,則執行非事務性。
8 TransactionDefinition.TIMEOUT_DEFAULT使用預設超時的底層事務系統,或者如果不支援超時則沒有。

TransactionStatus 介面為事務程式碼提供了一個簡單的方法來控制事務的執行和查詢事務狀態。

public interface TransactionStatus extends SavepointManager {
   boolean isNewTransaction();
   boolean hasSavepoint();
   void setRollbackOnly();
   boolean isRollbackOnly();
   boolean isCompleted();
}
序號 方法 & 描述
1 boolean hasSavepoint()該方法返回該事務內部是否有一個儲存點,也就是說,基於一個儲存點已經建立了巢狀事務。
2 boolean isCompleted()該方法返回該事務是否完成,也就是說,它是否已經提交或回滾。
3 boolean isNewTransaction()在當前事務時新的情況下,該方法返回 true。
4 boolean isRollbackOnly()該方法返回該事務是否已標記為 rollback-only。
5 void setRollbackOnly()該方法設定該事務為 rollback-only 標記。

Spring程式設計式事物管理:

程式設計式事務管理方法允許你在對你的原始碼程式設計的幫助下管理事務。這給了你極大地靈活性,但是它很難維護。

首先得建立兩張表:

Student表:

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

直接使用 PlatformTransactionManager 來實現程式設計式方法從而實現事務。要開始一個新事務,你需要有一個帶有適當的 transaction 屬性的 TransactionDefinition 的例項。這個例子中,我們使用預設的 transaction 屬性簡單的建立了 DefaultTransactionDefinition 的一個例項。

當 TransactionDefinition 建立後,你可以通過呼叫 getTransaction() 方法來開始你的事務,該方法會返回 TransactionStatus 的一個例項。 TransactionStatus 物件幫助追蹤當前的事務狀態,並且最終,如果一切執行順利,你可以使用 PlatformTransactionManagercommit() 方法來提交這個事務,否則的話,你可以使用 rollback() 方法來回滾整個操作。

下面是資料訪問物件介面檔案 StudentDAO.java 的內容:

package transaction;

import java.util.List;

import javax.sql.DataSource;

public interface StudentDAO {
    //這是用於初始化資料庫資源(即連線)的方法。
    public void setDataSource(DataSource ds);

    //這是用來在學生和標記表中建立一條記錄的方法。
    public void create(String name, Integer age, Integer marks, Integer year);

    //這是用來列出學生和標記表中的所有記錄的方法
    public List<StudentMarks> listStudents();
}

 StudentMarks.java

package transaction;

public class StudentMarks {
    private Integer age;
    private String name;
    private Integer id;
    private Integer marks;
    private Integer year;
    private Integer sid;
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getMarks() {
        return marks;
    }
    public void setMarks(Integer marks) {
        this.marks = marks;
    }
    public Integer getYear() {
        return year;
    }
    public void setYear(Integer year) {
        this.year = year;
    }
    public Integer getSid() {
        return sid;
    }
    public void setSid(Integer sid) {
        this.sid = sid;
    }

}

 StudentMarksMapper.java

package transaction;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class StudentMarksMapper implements RowMapper<StudentMarks> {

    @Override
    public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException {
        StudentMarks studentMarks = new StudentMarks();
        studentMarks.setId(rs.getInt("id"));
        studentMarks.setAge(rs.getInt("age"));
        studentMarks.setName(rs.getString("name"));
        studentMarks.setMarks(rs.getInt("marks"));
        studentMarks.setYear(rs.getInt("year"));
        studentMarks.setSid(rs.getInt("sid"));
        return studentMarks;
    }
}

下面是定義的 DAO 介面 StudentDAO 實現類檔案 StudentJDBCTemplate.java

package transaction;

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;
    @Override
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        this.jdbcTemplateObject = new JdbcTemplate(dataSource);
    }

    public void setTransactionManager(
            PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
    @Override
    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);
            // 獲取要在標記表中使用的最新學生ID.
            String SQL2 = "select max(id) from Student";
            int sid = jdbcTemplateObject.queryForObject(SQL2, Integer.class);
            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;
    }
    @Override
    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 transaction;

import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import transaction.StudentJDBCTemplate;
public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("transaction.xml");
      StudentJDBCTemplate studentJDBCTemplate = 
      (StudentJDBCTemplate)context.getBean("studentJDBCTemplate");     
      System.out.println("------建立記錄--------" );
      studentJDBCTemplate.create("Zara", 11, 99, 2010);
      studentJDBCTemplate.create("Nuha", 20, 97, 2010);
      studentJDBCTemplate.create("Ayan", 25, 100, 2011);
      System.out.println("------列出所有記錄--------" );
      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());
      }
   }
}

配置檔案:transacation.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/jsp_db?useSSL=false"/>
      <property name="username" value="root"/>
      <property name="password" value="123456"/>
   </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="transaction.StudentJDBCTemplate">
      <property name="dataSource"  ref="dataSource" />
      <property name="transactionManager"  ref="transactionManager" />    
   </bean>

</beans>

程式執行結果:

------建立記錄--------
Created Name = Zara, Age = 11
Created Name = Nuha, Age = 20
Created Name = Ayan, Age = 25
------列出所有記錄--------
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

Spring宣告式事務管理:

宣告式事務管理方法允許你在配置的幫助下而不是原始碼硬程式設計來管理事務。這意味著你可以將事務管理從事務程式碼中隔離出來。你可以只使用註釋或基於配置的 XML 來管理事務。 bean 配置會指定事務型方法。下面是與宣告式事務相關的步驟:

  • 我們使用標籤,它建立一個事務處理的建議,同時,我們定義一個匹配所有方法的切入點,我們希望這些方法是事務型的並且會引用事務型的建議。
  • 如果在事務型配置中包含了一個方法的名稱,那麼建立的建議在呼叫方法之前就會在事務中開始進行。
  • 目標方法會在 try / catch 塊中執行。
  • 如果方法正常結束,AOP 建議會成功的提交事務,否則它執行回滾操作。

StudentDAO.java

package transaction;

import java.util.List;

import javax.sql.DataSource;

public interface StudentDAO {
    //這是用於初始化資料庫資源(即連線)的方法。
    public void setDataSource(DataSource ds);

    //這是用來在學生和標記表中建立一條記錄的方法。
    public void create(String name, Integer age, Integer marks, Integer year);

    //這是用來列出學生和標記表中的所有記錄的方法
    public List<StudentMarks> listStudents();
}

StudentMarks.java

package transaction;

public class StudentMarks {
    private Integer age;
    private String name;
    private Integer id;
    private Integer marks;
    private Integer year;
    private Integer sid;
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getMarks() {
        return marks;
    }
    public void setMarks(Integer marks) {
        this.marks = marks;
    }
    public Integer getYear() {
        return year;
    }
    public void setYear(Integer year) {
        this.year = year;
    }
    public Integer getSid() {
        return sid;
    }
    public void setSid(Integer sid) {
        this.sid = sid;
    }
}

StudentMarksMapper.java

package transaction;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class StudentMarksMapper implements RowMapper<StudentMarks> {

    @Override
    public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException {
        StudentMarks studentMarks = new StudentMarks();
        studentMarks.setId(rs.getInt("id"));
        studentMarks.setAge(rs.getInt("age"));
        studentMarks.setName(rs.getString("name"));
        studentMarks.setMarks(rs.getInt("marks"));
        studentMarks.setYear(rs.getInt("year"));
        studentMarks.setSid(rs.getInt("sid"));
        return studentMarks;
    }
}

StudentJDBCTemplate.java

package transaction;

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;
    @Override
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        this.jdbcTemplateObject = new JdbcTemplate(dataSource);
    }

    public void setTransactionManager(
            PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
    @Override
    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);
             //獲取要在標記表中使用的最新學生ID。
             String SQL2 = "select max(id) from Student";
             int sid = jdbcTemplateObject.queryForObject(SQL2, Integer.class );
             String SQL3 = "insert into Marks(sid, marks, year) " + 
                           "values (?, ?, ?)";
             jdbcTemplateObject.update( SQL3, sid, marks, year);
             System.out.println("Created Name = " + name + ", Age = " + age);
             // 來模擬異常
             throw new RuntimeException("simulate Error condition") ;
          } catch (DataAccessException e) {
             System.out.println("建立記錄時的錯誤, 回滾");
             throw e;
        }
    }
    @Override
    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 transaction;

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("transaction2.xml");
      StudentDAO studentJDBCTemplate = 
              (StudentDAO)context.getBean("studentJDBCTemplate");   
      System.out.println("------建立記錄--------" );
      studentJDBCTemplate.create("Zara", 11, 99, 2010);
      studentJDBCTemplate.create("Nuha", 20, 97, 2010);
      studentJDBCTemplate.create("Ayan", 25, 100, 2011);
      System.out.println("------列出所有記錄--------" );
      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());
      }
   }
}

配置檔案transcation2.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"/>
      <pr