Spring 与 mybatis整合---事务管理
MyBatis与Spring整合前后事务管理有所区别
整合前:通过
session = sessionFactory.openSession(true); //或者是false
设置事务是否自动提交;
整合后,在这样写就不起作用了,无论设置为true还是false 都会自动提交事务;
如果想设置事务非自动提交有以下几种方案:
① 创建session之后,手动拿到connection,设置事务非自动提交
session.getConnection().setAutoCommit(false);
② 通过spring管理事务
@Test /** * 测试插入数据,同时测试手动提交事务 */ public void test_insert() { System.out.println("==============插入数据=================="); //开启事务 DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); DataSourceTransactionManager transactionManager = (DataSourceTransactionManager) DaoApplicationContext .getInstance().getBean("txManager"); TransactionStatus transactionStatus = (TransactionStatus) transactionManager .getTransaction(definition); Fh_fullnote order = new Fh_fullnote(); order.setHf_serialid("HF100120160829155555012507"); order.setHf_orderid("OD100120160829155555012507"); order.setCharge_phone("18201304217"); order.setCharge_money(50D); int insertRet = fh_fullnoteDao.insert(order); //提交事务 transactionManager.commit(transactionStatus); System.out.println("插入数据返回值:" + insertRet); }
③ 注解式事务
首先对Dao层做一个详细的解析
Dao层应用上下文
package ctp.demo.dao.fh.dao; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class DaoApplicationContext { private static ApplicationContext ctx=null; public static ApplicationContext getCtx() { return ctx; } public static void setCtx(ApplicationContext ctx) { DaoApplicationContext.ctx = ctx; } public static ApplicationContext getInstance(){ if(ctx == null){ ctx=new ClassPathXmlApplicationContext("applicationContext-Dao.xml"); } return ctx; } }
Dao数据模型
package ctp.demo.dao.fh.vo; import java.util.Date; public class Fh_fullnote { String hf_serialid ; //流水号 String hf_orderid ; //订单号 Integer isp_id ; //运营商ID Integer province_id; //省份ID Integer city_code; //城市编码 Integer service_type; //业务类型 String charge_phone; //充值手机号 Double charge_money; //充值金额 Double finish_money; //完成金额 Double charge_flux; //充值流量 Double finish_flux; //完成充值的流量 Double charge_status; //充值状态 Integer error_code ; //错误码 String error_info ; //错误描述 Date begin_time ; //开始时间 Date end_time ; //结束时间 String charge_serialid ;//对端流水 Integer query_count ; //查询次数 Integer query_status ; //查询状态 Integer notify_flag ; //通知标识 Integer notify_count ; //通知次数 Integer card_source ; //卡来源 String card_num ; //卡号 Date charge_datetime ; //入库时间 Date query_begintime ; //查询开始时间 Integer channel_id ; //渠道ID String card_pwd ; //卡密 public String getHf_serialid() { return hf_serialid; } public void setHf_serialid(String hf_serialid) { this.hf_serialid = hf_serialid; } public String getHf_orderid() { return hf_orderid; } public void setHf_orderid(String hf_orderid) { this.hf_orderid = hf_orderid; } public Integer getIsp_id() { return isp_id; } public void setIsp_id(Integer isp_id) { this.isp_id = isp_id; } public Integer getProvince_id() { return province_id; } public void setProvince_id(Integer province_id) { this.province_id = province_id; } public Integer getCity_code() { return city_code; } public void setCity_code(Integer city_code) { this.city_code = city_code; } public Integer getService_type() { return service_type; } public void setService_type(Integer service_type) { this.service_type = service_type; } public String getCharge_phone() { return charge_phone; } public void setCharge_phone(String charge_phone) { this.charge_phone = charge_phone; } public Double getCharge_money() { return charge_money; } public void setCharge_money(Double charge_money) { this.charge_money = charge_money; } public Double getFinish_money() { return finish_money; } public void setFinish_money(Double finish_money) { this.finish_money = finish_money; } public Double getCharge_flux() { return charge_flux; } public void setCharge_flux(Double charge_flux) { this.charge_flux = charge_flux; } public Double getFinish_flux() { return finish_flux; } public void setFinish_flux(Double finish_flux) { this.finish_flux = finish_flux; } public Double getCharge_status() { return charge_status; } public void setCharge_status(Double charge_status) { this.charge_status = charge_status; } public Integer getError_code() { return error_code; } public void setError_code(Integer error_code) { this.error_code = error_code; } public String getError_info() { return error_info; } public void setError_info(String error_info) { this.error_info = error_info; } public Date getBegin_time() { return begin_time; } public void setBegin_time(Date begin_time) { this.begin_time = begin_time; } public Date getEnd_time() { return end_time; } public void setEnd_time(Date end_time) { this.end_time = end_time; } public String getCharge_serialid() { return charge_serialid; } public void setCharge_serialid(String charge_serialid) { this.charge_serialid = charge_serialid; } public Integer getQuery_count() { return query_count; } public void setQuery_count(Integer query_count) { this.query_count = query_count; } public Integer getQuery_status() { return query_status; } public void setQuery_status(Integer query_status) { this.query_status = query_status; } public Integer getNotify_flag() { return notify_flag; } public void setNotify_flag(Integer notify_flag) { this.notify_flag = notify_flag; } public Integer getNotify_count() { return notify_count; } public void setNotify_count(Integer notify_count) { this.notify_count = notify_count; } public Integer getCard_source() { return card_source; } public void setCard_source(Integer card_source) { this.card_source = card_source; } public String getCard_num() { return card_num; } public void setCard_num(String card_num) { this.card_num = card_num; } public Date getCharge_datetime() { return charge_datetime; } public void setCharge_datetime(Date charge_datetime) { this.charge_datetime = charge_datetime; } public Date getQuery_begintime() { return query_begintime; } public void setQuery_begintime(Date query_begintime) { this.query_begintime = query_begintime; } public Integer getChannel_id() { return channel_id; } public void setChannel_id(Integer channel_id) { this.channel_id = channel_id; } public String getCard_pwd() { return card_pwd; } public void setCard_pwd(String card_pwd) { this.card_pwd = card_pwd; } }
Dao操作类
package ctp.demo.dao.fh.dao; import java.sql.SQLException; import java.util.Map; import java.util.TreeMap; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import ctp.demo.dao.fh.vo.Fh_fullnote; public class Fh_fullnote_dao { SqlSession session = null; String dao_namespace = "fh.dao.fh_fullnote."; /** * 订单插入 * @param order 订单类模型 * @return 0:插入正常 -1:插入数据异常 */ public int insert(Fh_fullnote order){ try{ checkSession(); session.insert(dao_namespace + "addOrder", order); return 0; }catch(Exception e){ e.printStackTrace(); return -1; } } /** * 根据订单号更新订单状态 * @param hf_id 订单号 * @param orderStatus 订单状态 * @return 0:正常 -1:异常 */ public int updateChargeStatusByHF(String hf_id,String orderStatus){ try{ checkSession(); Map<String, String> map = new TreeMap<String, String>(); map.put("hf_id", hf_id); map.put("orderStatus", orderStatus); session.update(dao_namespace + "updateChargeStatusByHF", map); return 0; }catch(Exception e){ e.printStackTrace(); return -1; } } /** * 根据流水号 获取订单 * @param hf_id 流水号 * @return 订单 or null */ public Fh_fullnote getOrderByHF_ID(String hf_id){ try { checkSession(); Fh_fullnote order = (Fh_fullnote)session.selectOne(dao_namespace+"getOrderByHF_ID", hf_id); return order; } catch (Exception e) { // TODO: handle exception e.printStackTrace(); return null; } } /** * 检查当前是否有session,没有则打开一个session */ public void checkSession(){ if(session == null){ SqlSessionFactory sessionFactory = (SqlSessionFactory)DaoApplicationContext.getInstance().getBean("sqlSessionFactory"); session = sessionFactory.openSession(); try { System.out.println("是否自动提交:" + session.getConnection().getAutoCommit()); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
映射文件
<?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="fh.dao.fh_fullnote"> <!-- 插入数据 --> <insert id="addOrder" parameterType="ctp.demo.dao.fh.vo.Fh_fullnote"> insert into fh_fullnote ( HF_SERIALID, HF_ORDERID, ISP_ID, PROVINCE_ID, CITY_CODE, SERVICE_TYPE, CHARGE_PHONE, CHARGE_MONEY, FINISH_MONEY, CHARGE_FLUX, FINISH_FLUX, CHARGE_STATUS, ERROR_CODE, ERROR_INFO, BEGIN_TIME, END_TIME, CHARGE_SERIALID, QUERY_COUNT, QUERY_STATUS, NOTIFY_FLAG, NOTIFY_COUNT, CARD_SOURCE, CARD_NUM, CHARGE_DATETIME, QUERY_BEGINTIME, CHANNEL_ID, CARD_PWD) values(#{hf_serialid},#{hf_orderid},#{isp_id},#{province_id},#{city_code},#{service_type},#{charge_phone},#{charge_money},#{finish_money},#{charge_flux},#{finish_flux},#{charge_status},#{error_code},#{error_info},#{begin_time},#{end_time},#{charge_serialid},#{query_count},#{query_status},#{notify_flag},#{notify_count},#{card_source},#{card_num},#{charge_datetime},#{query_begintime},#{channel_id},#{card_pwd}); </insert> <!-- 根据流水号修改充值状态 --> <update id="updateChargeStatusByHF" parameterType="java.util.Map"> update fh_fullnote set CHARGE_STATUS = #{orderStatus} where HF_SERIALID = #{hf_id} </update> <!-- 根据流水号获取订单 --> <select id="getOrderByHF_ID" parameterType="String" resultType = "ctp.demo.dao.fh.vo.Fh_fullnote"> select * from fh_fullnote where HF_SERIALID = #{hf_id} </select> </mapper>
Spring配置文件
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd" > <!--开启注解配置 --> <context:annotation-config /> <!-- 扫描注解 --> <context:component-scan base-package="ctp" /> <!-- 配置数据源 --> <bean id = "dataSource" name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://192.168.61.28:3306/ctp" /> <property name="username" value="encysys48" /> <property name="password" value="encysys48" /> </bean> <!-- 配置事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 事务注解驱动 --> <tx:annotation-driven transaction-manager="txManager" /> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入mybatis的配置文件 --> <property name="configLocation"> <!-- 不加classpath:会出现错误 --> <value>classpath:mybatis-config.xml</value> </property> <property name="dataSource"> <!-- 此处填写ID --> <ref local="dataSource"/> </property> <property name="mapperLocations" value="classpath:ctp/**/*mapper.xml"> </property> </bean> <bean id="Fh_fullnote_dao" class="ctp.demo.dao.fh.dao.Fh_fullnote_dao"> </bean> <!-- 自动扫描映射器 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="ctp" /> </bean> </beans>
下面看Service层:
package ctp.demo.service.fh; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import ctp.demo.dao.fh.dao.Fh_fullnote_dao; import ctp.demo.dao.fh.vo.Fh_fullnote; @Component("fh_fullnoteService") @Transactional public class Fh_fullnoteService { @Autowired Fh_fullnote_dao fh_fullnoteDao; @Transactional(rollbackFor={Exception.class, RuntimeException.class}) public int addOrder(Fh_fullnote order){ try { int insertRet = fh_fullnoteDao.insert(order); System.out.println("下单结果:" + insertRet); return 0; } catch (Exception e) { // TODO: handle exception return -1; } } public Fh_fullnote_dao getFh_fullnoteDao() { return fh_fullnoteDao; } public void setFh_fullnoteDao(Fh_fullnote_dao fh_fullnoteDao) { this.fh_fullnoteDao = fh_fullnoteDao; } }
测试代码:
package ctp.demo.service.fh; import static org.junit.Assert.*; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.DefaultTransactionDefinition; import ctp.demo.dao.fh.dao.DaoApplicationContext; import ctp.demo.dao.fh.vo.Fh_fullnote; public class Test_Fh_fullnoteService { @Test public void test() { System.out.println("===============下单开始================"); Fh_fullnote order = new Fh_fullnote(); order.setHf_serialid("HF100120160829155555012510"); order.setHf_orderid("OD100120160829155555012510"); order.setCharge_phone("18201304217"); order.setCharge_money(50D); //关于Dao层的配置文件,需要在service层读取,然后将上下文注入到Dao里面 //否则的话 @Transactional注解是不生效的 String[] configeRations = new String[]{"applicationContext-Service.xml","applicationContext-Dao.xml"}; ApplicationContext ctx=new ClassPathXmlApplicationContext(configeRations); DaoApplicationContext.setCtx(ctx); Fh_fullnoteService fullnote_service = (Fh_fullnoteService)ctx.getBean("fh_fullnoteService"); fullnote_service.addOrder(order); System.out.println(); } }
通过断点调试,发现跳出函数之前,是不会提交的,说明注解事务生效。
需要注意的是,Dao层应用的上下文应该与Service应用的上下文一致,那么Dao层的配置文件就应该在Service层读取,然后将上下文注入到Dao层中。
附配置文件:
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" > <!-- 配置数据源 --> <bean id = "dataSource" name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://192.168.61.28:3306/ctp" /> <property name="username" value="encysys48" /> <property name="password" value="encysys48" /> </bean> <!-- 配置事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 事务注解驱动 --> <tx:annotation-driven transaction-manager="txManager" /> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入mybatis的配置文件 --> <property name="configLocation"> <!-- 不加classpath:会出现错误 --> <value>classpath:mybatis-config.xml</value> </property> <property name="dataSource"> <!-- 此处填写ID --> <ref local="dataSource"/> </property> <property name="mapperLocations" value="classpath:ctp/**/*mapper.xml"> </property> </bean> <bean id="Fh_fullnote_dao" class="ctp.demo.dao.fh.dao.Fh_fullnote_dao"> </bean> </beans>
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd" > <!--开启注解配置 --> <context:annotation-config /> <!-- 扫描注解 --> <context:component-scan base-package="ctp" /> <!-- 注解自动生效 --> <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" /> <bean id="fh_fullnoteDao" class="ctp.demo.dao.fh.dao.Fh_fullnote_dao" /> </beans>