hibernate4 spring3.2 事务不提交分析

  最近在做微信项目,我搭建了一个基于servlet,spring3.2,hibernate4.1的框架。因为基于消息的servlet和基于业务层是分开做的,也就是先把业务层做了,再去将所有的请求转到业务层处理。所以一开始开发就用junit做测试,模拟的消息保存数据库也都能正常进行。下面列出某一个junit
的 testcase,在这个测试的例子中,我为junit配置了事务,事务也能正常提交。所以,后面的业务层写法就一直这么开发下去,也没什么问题

  


package com.cpic.inf.tools.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONArray;
import com.cpic.inf.model.json.JaoFeiJson;
import com.cpic.inf.service.SendModelMsgProxy;
import com.cpic.inf.tools.exception.SendException;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring_*.xml"})
@Transactional
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)//true:始终回滚 false:数据提交
public class TestCase {

@Autowired
SendModelMsgProxy proxy;

@Test
public void testModelMsg() throws SendException{
JaoFeiJson jaofei = new JaoFeiJson();
jaofei.setAccount("62260****4765");
jaofei.setAmount("200元");
jaofei.setContno("张三");
jaofei.setFinish("2014年1月30日");
jaofei.setPaycode("交通银行");
jaofei.setPeriod("第3期");
jaofei.setPolicyno("201400000009");
jaofei.setProduct("鸿发年年及其附加险");
jaofei.setRemark("如有疑问,请在”更多--小薇在线“里留言~");
jaofei.setTemplate_id("4g3_jAn3qPqhX6DR2TbU1r9-rDN2N4KazEIPLhQ3FKQ");
jaofei.setTitle("您好,您的保单续期交费成功啦~");
//jaofei.setTopcolor("#04B404");
jaofei.setTouser("ox6yJjtSe02C6b3I_Fues2WPYszk");
jaofei.setUrl("http://www.baidu.com");
String str = JSONArray.toJSON(jaofei).toString();
proxy.sendMsg(str);
}
}

后面当业务层开发完后,将工程放到web容器里跑,发送一个请求,调用到业务层方法,前面一直正常,后面发现一个问题,事务始终不提交,先看我的棕spring部分配置


<!-- 开启AOP监听 只对当前配置文件有效 -->
<aop:aspectj-autoproxy expose-proxy="true"/>

<!-- 开启注解事务 只对当前配置文件有效 -->
<tx:annotation-driven transaction-manager="txManager"/>

<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="merge*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="put*" propagation="REQUIRED" />
<tx:method name="use*" propagation="REQUIRED"/>
<!--hibernate4必须配置为开启事务 否则 getCurrentSession()获取不到-->
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="count*" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" propagation="REQUIRED" read-only="true" />
<tx:method name="list*" propagation="REQUIRED" read-only="true" />
<tx:method name="send*" propagation="REQUIRED" read-only="true"/>
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config expose-proxy="true">
<!-- 只对业务逻辑层实施事务 -->
<aop:pointcut id="txPointcut" expression="execution(* com.cpic..service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
<!-- 自定义前置通知 -->
<!-- <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> -->
</aop:config>

<bean id="methodAdvice" class="com.cpic.inf.service.impl.MethodBefore"></bean>

事务配置是比较清楚的,配置了aspectj的aop做为事务,我想要在所有的impl层的类里加上配置的事务,其中我现在测试的类是com.cpic.inf.service.impl.JaoFeiImpl

来看一下这个类的实现


package com.cpic.inf.service.impl;

import java.util.Map;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSONArray;
import com.cpic.inf.model.BaseValue;
import com.cpic.inf.model.RequestModel;
import com.cpic.inf.model.in.JaoFeiModel;
import com.cpic.inf.model.mapper.JiaoFeiModelDB;
import com.cpic.inf.service.SendModelMsgABS;
import com.cpic.inf.tools.Token;
import com.cpic.inf.tools.exception.SendException;

/**
* 交费成功通知
* @author wuxw
*
*/
public class JaoFeiImpl extends SendModelMsgABS {

private static Logger logger = Logger.getLogger(Token.class);

/** 模版顶部边框颜色*/
public static final String TOP_COLOR = "#04B404";

/** 模版字体颜色*/
public static final String COLOR = "#000000";

public String sendMsg(Map<String,String> map) throws SendException {
String token;
try {
token = Token.getToken();
} catch (Exception e1) {
throw new SendException("无法连接Token服务器");
}
RequestModel<JaoFeiModel> reqModel = null;
try{
...

try{
this.save(jiaofei);
} catch(Exception e){
throw new SendException("数据库错误:" + e.getMessage());
}
} catch (Exception e) {
throw new SendException("模板参数错误!请填写正确的模板对应参数");
}
String json = JSONArray.toJSONString(reqModel);
try {
logger.info("jiaofei end");
return this.SendModelMsg(json, token);
} catch (Exception e) {
throw new SendException("网络异常,无法连接微信服务器");
}
}
}

按照我的逻辑,如果我调用这个sendMsg方法,就能够发送模板消息(注:这是一个发送微信模板消息的功能),并将这个模板消息存到数据库里。另外,用junit测试过,这个功能不存在逻辑错误,能够在junit下,发送消息成功后能正常保存到数据库里。但后面在web工程里,面前都能成功,直到this.save(jiaofei);的时候,没有看到hibernate打印出sql(show_sql=true);也就是事务没提交,为什么会这样了?

我第一反映是

<aop:pointcut id="txPointcut" expression="execution(* com.cpic..service.*.*(..))" />

这块是不是配置错了

在网上查了一下,这个表达式怎么写,发现没有写错,我如果改成其他目录的时候

this.save(jiaofei);在执行这句的时候,会报得不到session,也就是表明aop配置是正确的。

因为我的方法是sendMsg方法,事务里没有配置这个,所以我又在配置里加上了这句

<tx:method name="send*" propagation="REQUIRED" read-only="true"/>

运行的时候还是跟以前的一样,逻辑上我是没问题,但一直找不到问题出在哪里,后面在网上找了个投机的办法

在session.save();的后面加一句session.flush();

这下子,事务能正确提交了,问题暂时是解决了,因为当时数据库操作只用到了save()方法。接着赶进度。

但做为一个严谨的程序,我知道这是不对的,因为我需要的是aop来自动管理事务,而不是现在这样,操作一次就提交一次,要是遇到事务传播,这种方法就不奏效了,所以在空闲的时候,我把这个问题仔细查找一下原因。

后面我配置了一个前置消息,配置在aop中,配置是这样的


  <aop:config expose-proxy="true">
<!-- 只对业务逻辑层实施事务 -->
<aop:pointcut id="txPointcut" expression="execution(* com.cpic..service.*.*(..))" />
<!-- 自定义前置通知 -->
<aop:advisor advice-ref="methodAdvice" pointcut-ref="txPointcut"/>
</aop:config>

<bean id="methodAdvice" class="com.cpic.inf.service.impl.MethodBefore"></bean>

当我执行到sendMsg()方法之前,是先进入我的前置消息的,也就是证明,不是aop的原因,事务不提交肯定是配置出了问题,我后面在这其他业务类里加个@Transactional注解,事务也提交了,所以我猜,肯定是配置出了问题,我仔细检查,发现问题出现在

<tx:method name="send*" propagation="REQUIRED" read-only="true"/>

我仔细查了查这里面每个属性的意思,因为我的理解也都只在单词字面意思上

propagation="REQUIRED":指事务传翻

read-only="true":指只读情况下,不会有事务

所以,我把read-only="true"去掉后,事务就正常了

总结:我以前搭建过很多类似的框架,也就是把几个框架整合在一起,很多配置也都是从已有的项目中照搬过来,大部分理解也都处理字面意思。所以,今天出现了这样一个问题,一时半会也解决不了,最终在不服输的态度下,还是解决了。所以,我的感受是,就像做数学题一样,还是得多练习,才能在解决问题的速度上上一个层次。希望跟我遇到同样困难的同学共勉,共同学习!

hibernate4 spring3.2 事务不提交分析,布布扣,bubuko.com

时间: 2024-10-05 15:49:52

hibernate4 spring3.2 事务不提交分析的相关文章

Spring 事务源码分析——Hibernate篇

在Spring与Hibernate整合的时候,可以利用Spring的事务管理机制,为我们管理事务的开启.提交.回滚等操作.这样的方式极大的减少了我们的代码量,让我们只专注于业务代码的编写.在使用Hibernate的时候,每一个操作都要经历事务开启与提交这样的操作,他们在业务代码的周围,这样来看是不是就想到了AOP编程,把这部分代码抽取出来.没错,Spring正是这样做的,Spring的事务管理就是基于AOP的. 1 Spring的事务隔离与传播 Srping的事务定义了五个隔离等级(isolat

解决 spring mvc 3.0 结合 hibernate3.2 使用&lt;tx:annotation-driven&gt;声明式事务无法提交的问题(转载)

1.问题复现 spring 3.0 + hibernate 3.2 spring mvc使用注解方式:service使用@service注解 事务使用@Transactional 事务配置使用 Java代码   <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> [java] view plaincopy <tx:an

MySQL可重复读采坑记录-对事务B进行更新时,事务A提交的更新会不会影响到事务B

之前线上出现数据重复插入的问题,通过对问题进行排查发现该问题和MySQL的默认隔离级别-Repeatable Read(可重读)有关系,可重复读确保同一事务的多个实例在并发读取数据时,会看到同样的数据行.现在通过实验,对问题进行下分析. 1.在终端A开启事务A,查询一下. START TRANSACTION; select spt.id,spt.audit_status,spt.is_deleted from stat_point_task spt limit 5; 结果如下: 2.在终端B开启

Spring事务管理全面分析

Spring 事务属性分析什么是事物  事务管理对于企业应用而言至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.就像银行的自助取款机,通常都能正常为客户服务,但是也难免遇到操作过程中机器突然出故障的情况,此时,事务就必须确保出故障前对账户的操作不生效,就像用户刚才完全没有使用过取款机一样,以保证用户和银行的利益都不受损失.   简单来说:事物指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败.在 Spring 中,事务是通过 Tran

[MySQL]对于事务并发处理带来的问题,脏读、不可重复读、幻读的理解与数据库事务隔离级别 - 分析脏读 &amp; 不可重复读 &amp; 幻读

刚开始写博客.. 写的太low. 1.数据库的两种读,每种读读的数据版本不一样,所以也称为MVCC,即多版本并发控制 a) 快照读 select * from where xxx  这种形式的都是快照读. b) 当前读 update , insert ,delete ,select xx from xx for update ,  in share mode 都是当前读 当前读会等待,不会返回数据的历史版本 2.mvcc 的实现原理 mvcc是基于read view.活跃事务列表 做的,以后的文

Spring3 (事务管理)

简介: 1.事务管理.2.整合Junit.3.整和Web 1       事务管理 1.1   回顾事务 l  事务:一组业务操作ABCD,要么全部成功,要么全部不成功. l  特性:ACID 原子性:整体 一致性:完成 隔离性:并发 持久性:结果 l  隔离问题: 脏读:一个事务读到另一个事务没有提交的数据 不可重复读:一个事务读到另一个事务已提交的数据(update) 虚读(幻读):一个事务读到另一个事务已提交的数据(insert) l  隔离级别: read uncommitted:读未提

解决 spring mvc 3.+ 结合 hibernate3.+ 使用&lt;tx:annotation-driven&gt;声明式事务无法提交的问题

spring mvc使用注解方式:service使用@service注解 事务使用@Transactional 事务配置使用 Java代码 <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> 在插入或更新数据时,无报错,但数据库中无结果,而查询正常.疑为事务未提交. 方式用来扫描该包以及其子包下的@Controller注解的

数据库事务隔离级别分析----转载

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted.Read committed.Repeatable read.Serializable,这四个级别可以逐个解决脏读.不可重复读.幻读这几类问题. √: 可能出现    ×: 不会出现   脏读 不可重复读 幻读 Read uncommitted √ √ √ Read committed × √ √ Repeatable read × × √ Serializable × × × 注意:我们讨论隔离级别的场景,主要是在多个

Spring3.x事务失效的原因以及解决办法

项目中如果使用spring来管理事务,可能会出现事务失效的情况,我认为主要的原因是cglib无法获取到代代理的实例.. 如果带上事务,那么用annotation方式的事务注解和bean配置,事务会失效,要将service的bean配置到xml文件中才行,这样springmvc就不会扫描到@Service的类了 这个问题有另一种解决办法: 首先在主容器中(applicationContext.xml),将Controller的注解排除掉 <context:component-scan base-p