咕嚕大大,專注編程和單身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整合下的編程式事務管理,歡迎大家對文章提出意見和指出錯誤的地方。