Mybatis3和Spring3整合下的程式設計式事務管理
咕嚕大大,專注程式設計和單身30年!
尊重原創,請大家轉載時註明該文章來自:http://blog.csdn.net/ymh198816/article/details/43187747
大家都知道在對資料庫操作時,最重要的就是要保證業務的原子性和一致性,這樣才能基本保證資料庫中資料的正確性。就比如說,某個系統中有兩張表,一個是使用者表users,另一個是使用者的基本資訊表users_info,用來儲存使用者真實姓名,地址,聯絡電話等,這兩張表是1對1的關係,並且當新使用者註冊進users表中,相應地users_info表中也一定要生成對應的資訊;這就需要兩步操作,第一步是在users表中生成新使用者的使用者名稱,郵箱,密碼等資訊,第二步是在users_info表中生成新的記錄並關聯剛剛在users表中生成的使用者記錄;如果在第一步操作完成時伺服器出現異常了,導致第二步中的使用者基本資訊的記錄沒有生成,那麼日後去查詢這個使用者的基本資訊時就會查詢不到,程式報錯。所以,這裡就要用到事務管理的機制,將第一步和第二步一起看成是一個事務,如果在事務執行的過程中出現異常,那麼就進行事務的回滾,資料返回到執行前的狀態,只有當第一步和第二步都完成的時候,才提交事務,資料庫中兩張表的記錄才會進行更新。
在mybatis中,我們使用SqlSession中的回滾,提交方法去程式設計式地管理事務;但是,當我們將Mybatis3和Spring3整合,使Mybatis中的Mappers或SqlSession能被Spring管理起來並注入到其它的bean中,卻發現SqlSession的程式設計式事務管理方式無法正常執行並報UnsupportedOperationException的錯誤,這是因為使用了MyBatis-Spring外掛後,總是由Spring來管理你的事務,所以Sqlsession被Spring管理了起來後,使得原先的SqlSession.rollback(),SqlSession.commit(),SqlSession.close()都失去了作用。同時在Spring事務之外用SqlSession的資料方法或呼叫Mapper方法提交的資料都會自動被commit。
所以當MyBatis3和Spring3整合後,我們無法再像以前一樣用SqlSession來程式設計式地管理事務,取而代之的是使用實現了Spring3的統一事務介面PlatformTransactionManager的事務管理器DataSourceTransactionManager去程式設計式地處理事務。
讓我們來舉個栗子:
在使用者表中註冊新使用者以及在使用者資訊表中建立該新使用者所對應的基本資訊記錄,並將兩條記錄關聯起來,同時用Spring的事務管理這整個過程。
首先在資料庫中建立兩張表:Users和User_Info, 以及這兩張表所對應的JavaBean:
usersBean.java
public class usersBean {
private int Users_Id;
private String Users_Password;
private String Users_Email;
private int Users_Status;
private String Users_Register_Time;
private String Users_Nickname;
set and get method……
}
userInfoBean.java
public class userInfoBean {
private int User_Info_Id;
private String User_Info_Rname;
private int User_Info_Gender;
private String User_Info_Avatar;
private String User_Info_Birthday;
private int User_Info_Users_Id;
set and get method……
}
接著需要使用xml檔案去mapper這兩個javabean
users.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tutie.dao.loginDao">
<resultMap type="com.tutie.domain.usersBean" id="usersmap">
<id column="Users_Id" property="Users_Id"/>
<result column="Users_Password" property="Users_Password"/>
<result column="Users_Email" property="Users_Email"/>
<result column="Users_Status" property="Users_Status"/>
<result column="Users_Register_Time" property="Users_Register_Time"/>
<result column="Users_Nickname" property="Users_Nickname"/>
</resultMap>
<insert id="register" useGeneratedKeys="true"
keyProperty="Users_Id" parameterType="com.tutie.domain.usersBean">
insert into Users (Users_Password,Users_Email,Users_Status,Users_Register_Time,Users_Nickname)
values (#{Users_Password},#{Users_Email},#{Users_Status},#{Users_Register_Time},#{Users_Nickname})
</insert>
</mapper>
注意,這裡的“KeyProperty”的值是對應的usersBean中主鍵的名稱(事實上是對應javabean類中一個或多個屬性名稱),表示將插入使用者記錄時自動生成的主鍵值賦值給usersBean的主鍵。
userInfo.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tutie.dao.loginDao">
<resultMap type="com.tutie.domain.userInfoBean" id="userinfomap">
<id column="User_Info_Id" property="User_Info_Id"/>
<result column="User_Info_Rname" property="User_Info_Rname"/>
<result column="User_Info_Gender" property="User_Info_Gender"/>
<result column="User_Info_Avatar" property="User_Info_Avatar"/>
<result column="User_Info_Birthday" property="User_Info_Birthday"/>
<result column="User_Info_Users_Id" property="User_Info_Users_Id"/>
</resultMap>
<insert id="register_info" useGeneratedKeys="true"
keyProperty="User_Info_Id" parameterType="com.tutie.domain.userInfoBean">
insert into User_Info (User_Info_Rname,User_Info_Gender,User_Info_Avatar,User_Info_Birthday,User_Info_Users_Id)
values (#{User_Info_Rname},#{User_Info_Gender},#{User_Info_Avatar},#{User_Info_Birthday},#{User_Info_Users_Id})
</insert>
</mapper>
配置mapper介面
usersDao.java
public interface usersDao {
public List<testUsersBean> getUsers();
}
配置spring的注入檔案
applicationContext-beans.xml
先配置事務管理器,並保證這裡的datasource和sqlSessionFactory的bean中的datasource一致(關於sqlSessionFactory的bean如何配置可以去檢視mybatis-spring整合的介紹手冊,這裡就不做說明了)
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
接下來配置MapperFactoryBean,並將MapperFactoryBean中生成的mapper例項以及Spring的事務管理器DataSourceTransactionManager注入到業務bean中.
<bean id="loginDaoInjection" class="org.mybatis.spring.mapper.MapperFactoryBean"
scope="prototype">
<property name="mapperInterface" value="com.tutie.dao.loginDao" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="loginService" class="com.tutie.serviceImp.loginImp"
scope="prototype">
<property name="logindao" ref="loginDaoInjection" />
<property name="ptm" ref="transactionManager" />
</bean>
這裡值得注意的地方是:當Mybatis和Spring整合後,我們不必寫mapper介面的實現類,因為Spring自動幫你把SqlSession管理起來了,它會直接幫你生成mapper例項並注入到業務bean中,這也是為什麼無法直接使用mybatis的SqlSession去管理事務的原因之一。在Spring的管理下,從某種意義上講SqlSession對使用者不可見。
下面在業務bean的實現類中使用Spring的事務管理器去管理註冊新使用者的行為:
loginImp.java
package com.tutie.serviceImp;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import com.tutie.dao.loginDao;
import com.tutie.domain.userInfoBean;
import com.tutie.domain.usersBean;
import com.tutie.service.loginInterface;
public class loginImp implements loginInterface {
private loginDao logindao;
private PlatformTransactionManager ptm;
public void register(usersBean usersbean){
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = ptm.getTransaction(def);
try{
logindao.register(usersbean);
//create user base information
userInfoBean userinfobean = new userInfoBean();
userinfobean.setUser_Info_Users_Id(usersbean.getUsers_Id());
logindao.register_info(userinfobean);
} catch (Exception e) {
ptm.rollback(status);
e.printStackTrace();
}
ptm.commit(status);
}
public void setLogindao(loginDao logindao) {
this.logindao = logindao;
}
public void setPtm(PlatformTransactionManager ptm) {
this.ptm = ptm;
}
}
如果註冊新使用者logindao.register(usersbean)和註冊新使用者的基本資訊logindao.register_info(userinfobean)這兩個動作在執行過程中出現異常,那麼就使用rollback進行回滾,否則就commit提交這兩個修改,這樣就保證了每一個註冊的新使用者都能生成其對應的基本資訊記錄。
以上就是Mybatis和Spring整合下的程式設計式事務管理,歡迎大家對文章提出意見和指出錯誤的地方。