天天看點

MyBatis與Spring內建示例續——MyBatis學習筆記之六

      限于篇幅,MyBatis與Spring內建的一些細節在上篇博文中并未提及,今天繼續。

一、引子

      前面的博文介紹了如何查詢一個具有has-a關系的實體,今天就來看看如何向資料庫中插入這樣的一個實體。仍以學生為例,現在的問題是:學生實體把教師對象作為自己的指導教師屬性,然而在學生表中,卻僅有指導教師的ID一列,這如何映射呢?

      解決的方法其實很簡單(這個方法是筆者猜出來的,哈哈,得瑟中…),關鍵在于在StudentMapper.xml中如下編寫insert語句:

<insert id="add" parameterType="Student"
useGeneratedKeys="true" keyProperty="id">
insert into student(name,gender,major,grade,supervisor_id)
values(#{name},#{gender},#{major},#{grade},#{supervisor.id})
</insert>      

       這裡的關鍵是使用了supervisor.id這樣的寫法,這表明是把學生實體的supervisor屬性的id屬性值,作為插入的記錄的supervisor_id字段的值。其他地方與之前的插入示例一緻(MyBatis增删改示例)。

      當然在StudentMapper.java中插入實體的方法聲明是少不了的,如下:

public void add(Student student);      

      執行程式如下:

package com.demo;
import org.springframework.context.ApplicationContext;
import com.abc.mapper.StudentMapper;
import com.abc.domain.Student;
import com.abc.domain.Teacher;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyBatisSpringDemo
{
private static ApplicationContext ctx;
static
{
//在類路徑下尋找resources/beans.xml檔案
ctx = new ClassPathXmlApplicationContext("resources/beans.xml");
}
public static void main(String[] args)
{
StudentMapper mapper =
(StudentMapper)ctx.getBean("studentMapper");
Student student = new Student();
student.setName("李林");
student.setGender("男");
student.setMajor("計算機科學與技術");
student.setGrade("2011");
Teacher supervisor = new Teacher();
supervisor.setId(1);
student.setSupervisor(supervisor);
mapper.add(student);
}
}      

      執行後,登入MySQL查詢。如下圖所示:

MyBatis與Spring內建示例續——MyBatis學習筆記之六

 (注:第一個紅框裡的指令“set names gbk”是為了能夠顯示中文。)

      從第二個和第三個紅框之間的對比可以看出,資料已被寫入到資料庫(點此進入無事務處理的×××頁面)。

二、事務管理

      在上面的示例中,并沒有為程式配置事務管理器,而在程式中也不像以前的示例那樣調用了送出事務的方法,但資料卻已實實在在地被寫入到了資料庫中。這是怎麼回事呢?mybatis官方文檔告訴我們,凡是在Spring事務之外執行的映射器方法,都會被自動送出(英文可參見http://www.mybatis.org/spring/transactions.html中的“Programmatic Transaction Management”部分,中文可參見http://www.mybatis.org/spring/zh/transactions.html中的“程式設計式事務管理”部分)。這樣一來,就沒有用上Spring強大的事務管理功能。Spring事務管理提供聲明式(declarative)和程式設計式(programmatic)兩種方式。今天就先給大家介紹一下程式設計式的基本用法(雖然聲明式是最常用的方式,但是發現想把它說清楚不是那麼容易滴,留待以後有機會再詳述吧)。

      首先在Spring的配置檔案(本例中為beans.xml)中配置MyBatis-Spring要用到的事務管理器,即org.springframework.jdbc.datasource.DataSourceTransactionManager。配置内容如下: 

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>      

      在這裡,事務管理器引用了在前面配置的資料源dataSource。顯而易見,這個資料源必須是事務管理器所管理的事務将要操作的那個資料源。也就是說,應該與SqlSessionFactoryBean引用同一個資料源。因為我們正是由SqlSessionFactoryBean來獲得映射器,進而調用映射器的方法來對資料庫進行操作。

      需要注意的是,Spring對事務管理做了抽象,提供了統一的程式設計接口。例如上述的DataSourceTransactionManager事務管理器,實際上是實作了接口org.springframework.transaction.PlatformTransactionManager。針對不同的環境,Spring提供了不同的實作。例如,對于Hibernate,可使用事務管理器org.springframework.orm.hibernate3.HibernateTransactionManager。與此相關的接口還有org.springframework.transaction.TransactionDefinition和org.springframework.transaction.TransactionStatus,分别代表事務的定義和事務的狀态。提供統一接口的好處是我們隻需要針對這個接口程式設計,而無需考慮不同環境下事務處理的不同細節。

      程式設計式事務管理代碼如下:

package com.demo;
import org.springframework.context.ApplicationContext;
import com.abc.mapper.StudentMapper;
import com.abc.domain.Student;
import com.abc.domain.Teacher;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
public class MyBatisSpringDemo
{
private static ApplicationContext ctx;
static
{
//在類路徑下尋找resources/beans.xml檔案
ctx = new ClassPathXmlApplicationContext("resources/beans.xml");
}
public static void main(String[] args)
{
//從Spring容器中請求映射器
StudentMapper mapper =
(StudentMapper)ctx.getBean("studentMapper");
//從Spring容器中請求事務管理器,用PlatformTransactionManager
//類型的引用指向它
PlatformTransactionManager tm =
(PlatformTransactionManager)ctx.getBean("transactionManager");
Student student = new Student();
student.setName("王芳");
student.setGender("女");
student.setMajor("計算機科學與技術");
student.setGrade("2011");
Teacher supervisor = new Teacher();
supervisor.setId(1);
student.setSupervisor(supervisor);
//TransactionDefinition對象代表着事務的定義,即事務的傳播行為,
//隔離級别和是否可讀等屬性。DefaultTransactionDefinition是此
//接口的預設實作,給上述屬性指定了預設值。如傳播行為是PROPAGATION_REQUIRED,
//隻讀為false等(可參見Spring api文檔)
TransactionDefinition def = new DefaultTransactionDefinition();
//TransactionStatus對象代表着事務的狀态。以下代碼根據傳入的事務定義
//對象傳回事務并啟動事務
TransactionStatus status = (TransactionStatus)tm.getTransaction(def);
try {
mapper.add(student);
//若執行下述語句,則事務復原。
//讀者可自行驗證
//int a = 1/0;
}
catch (Exception e) {
//復原事務
tm.rollback(status);
e.printStackTrace();
}
//送出事務
tm.commit(status);
}
}      

      執行結果如下:

MyBatis與Spring內建示例續——MyBatis學習筆記之六

       查詢資料庫如下圖所示:

MyBatis與Spring內建示例續——MyBatis學習筆記之六

       顯然,資料已被寫入。

三、使用bean繼承配置映射器

      當我們需要在beans.xml中配置多個映射器時,它們的class和sqlSessionFactory屬性都是一樣的(也許還有其它一樣的屬性)。顯然,我們需要消除這種備援資訊。借助于bean繼承機制,我們可以達到這個目的。如下所示:

<bean id="parentMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"
abstract="true">
<!--sqlSessionFactory屬性指定要用到的SqlSessionFactory執行個體-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="studentMapper" parent="parentMapper">
<!--mapperInterface屬性指定映射器接口,用于實作此接口并生成映射器對象-->
<property name="mapperInterface" value="com.abc.mapper.StudentMapper"/>
</bean>      

      這裡的關鍵是把父bean parentMapper的abstract屬性指定為true。這樣,Spring就不會建立這個bean執行個體。它存在的意義是配置好class和sqlSessionFactory這兩個屬性,供其他子bean繼承。而子bean通過把parent屬性設定為parentMapper,即可繼承這兩個屬性(點此進入有程式設計式事務處理和bean繼承的源碼)。

       猛戳這裡全面系統地學習MyBatis 3

       MyBatis技術交流群:188972810,或掃描二維碼:

MyBatis與Spring內建示例續——MyBatis學習筆記之六

【MyBatis學習筆記】系列之預備篇一:ant的下載下傳與安裝

【MyBatis學習筆記】系列之預備篇二:ant入門示例

【MyBatis學習筆記】系列之一:MyBatis入門示例

【MyBatis學習筆記】系列之二:MyBatis增删改示例

【MyBatis學習筆記】系列之三:MyBatis的association示例

【MyBatis學習筆記】系列之四:MyBatis association的兩種形式

【MyBatis學習筆記】系列之五:MyBatis與Spring內建示例

【MyBatis學習筆記】系列之六:MyBatis與Spring內建示例續

【MyBatis學習筆記】系列之七:MyBatis一對多雙向關聯

【MyBatis學習筆記】系列之八:MyBatis MapperScannerConfigurer配置

【MyBatis學習筆記】系列之九:MyBatis collection的兩種形式

【MyBatis學習筆記】系列之十:MyBatis日志之Log4j示例

【MyBatis學習筆記】系列之十一:MyBatis多參數傳遞之注解方式示例

【MyBatis學習筆記】系列之十二:MyBatis多參數傳遞之預設命名方式示例

【MyBatis學習筆記】系列之十三:MyBatis多參數傳遞之Map方式示例

【MyBatis學習筆記】系列之十四:MyBatis中的N+1問題

【MyBatis學習筆記】系列之十五:MyBatis多參數傳遞之混合方式

【MyBatis學習筆記】系列之十六:Spring聲明式事務管理示例

【MyBatis學習筆記】系列之十七:MyBatis多對多儲存示例

【MyBatis學習筆記】系列之十八:MyBatis多對多關聯查詢示例