关于Spring4+Hibernate4架构升级-整合的细节处理-由nested transactions not supported错误解决引申到事务嵌套问题处理

时间:2021-04-19 07:35:16

关于Spring4+Hibernate4架构升级-整合的细节处理-由nested transactions not supported错误解决引申到事务嵌套问题处理

本着系统稳定的原则,选择了 Spring5 和 Hibernate5版本的第一级版本Spring4+Hibernate4做开发

由于此处错误处理,毁三观,耽误了比较长的时间,给自己了一个深刻的教训。颠覆了自己解决问题的期望和方式,很有效果,借以成文,以瞻后效。

架构升级之后,问题接二连三而来,犹如打怪升级。

当百度第一页的关联问题无法解决的时候,对着堆栈看程序的错误执行流程是最有效的。

DEBUG模式下,有的看第一个错误,有的看最后一个错误。我喜欢从下往上看,从下往上翻,翻到上面才是最后一个最终的错误。而错误的引发,往往在于全部的流程,在于之前的第一个报错,而最后一个错误提示,只不过是引起了程序的中断,于你真正想要解决的错误而言,真真正正的南辕北辙,驴唇不对马嘴。

Hibernate本身不支持DBCP了,而DBCP升级的二代效果,
我感觉还不错,所以就是她了,比较轻,参数设置也给力多了啊。

下述错误细节重点问题

1、关于数据库处理

我只能说S4H4 比起 S3H3严谨的多,之前随意配置,无所谓。
即使二者的数据库连接配置有冗余,也没有影响到系统的运行。
但是,S4H4必须消除相关配置信息的冗余。

否则报错 - java.lang.UnsupportedOperationException: Not supported by BasicDataSource

交给Spring管理是最灵活和明智的选择。

而且,Hibernate并不支持DBCP,其提供的数据源定制接口的实现过于复杂和无聊,纯粹浪费感情。

果断,砍掉Hibernate的相关内容配置。

除了下述几个配置之外,可以保留,但是保留了也没有什么意思,还是别让他管了,费劲

<!-- 
<property name="hibernate.connection.isolation">50</property>
<property name="hibernate.connection.autocommit">50</property>
<property name="hibernate.connection.release_mode">50</property>

<property name="hibernate.connection.datasource">50</property> - JNDI配置

-->
<property name="hibernate.connection.pool_size">50</property>

下面是我的Spring 数据库设计,我喜欢这么玩,感觉代码还算OK

<!-- Spring与数据库设计 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<!-- 引入prperties配置文件 - DBCP -->
<value>classpath:dbcp.properties</value>
</property>
</bean>

<!-- 数据源的相关实际参数的配置和获取 -->

<!-- DBCP数据库连接池参数配置 -->
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp2.BasicDataSource">

<property name="driverClassName" value="${jdbc.dbcp.driverClassName}"/>
<property name="url" value="${jdbc.dbcp.url}"/>
<property name="username" value="${jdbc.dbcp.username}"/>
<property name="password" value="${jdbc.dbcp.password}"/>
<property name="initialSize" value="${jdbc.dbcp.initialSize}"/>
<property name="maxTotal" value="${jdbc.dbcp.maxTotal}"/>
<property name="maxIdle" value="${jdbc.dbcp.maxIdle}"/>
<property name="minIdle" value="${jdbc.dbcp.minIdle}"/>
<property name="defaultQueryTimeout" value="${jdbc.dbcp.defaultQueryTimeout}"/>
<property name="maxWaitMillis" value="${jdbc.dbcp.maxWaitMillis}"/>
<property name="connectionProperties" value="${jdbc.dbcp.connectionProperties}"/>
<property name="defaultAutoCommit" value="${jdbc.dbcp.defaultAutoCommit}"/>
<property name="defaultReadOnly" value="${jdbc.dbcp.defaultReadOnly}"/>
<property name="defaultTransactionIsolation" value="${jdbc.dbcp.defaultTransactionIsolation}"/>
<property name="lifo" value="${jdbc.dbcp.lifo}"/>
<property name="poolPreparedStatements" value="${jdbc.dbcp.poolPreparedStatements}"/>
<property name="maxOpenPreparedStatements" value="${jdbc.dbcp.maxOpenPreparedStatements}"/>
</bean>

OK,事务管理器不用多说,非她莫属

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

<property name="configLocations">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>


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


<!-- 事务管理器的具体的事务管理策略 - 示例 -->
<tx:advice id="txServiceAdvice" transaction-manager="txManager">

<tx:attributes>
<tx:method name="get*" read-only="true" />
<!-- other methods use the default transaction settings (see below) -->

<!--
异常的冲突处理,遵循最匹配的规则。 - 就近原则
-->

<tx:method name="*" />
</tx:attributes>
</tx:advice>

<tx:advice id="txDaoAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" rollback-for="java.lang.Throwable" />
</tx:attributes>
</tx:advice>

这上边,一个负责Service层的声明式事务处理

一个负责DAO层的事务处理,以简化操作

2、关于Hibernate的getCurrentSession()方法

关于H4,网上看了一大堆垃圾信息,竟然有朵奇葩说,H4建议openSession、不建议getCurrentSession云云。真是不可理喻,误人误己。

都去open了,还要Spring简化处理个毛啊。

此处在整合中,坑比较多,

但是,最重要的只有一点 -

    <property name="current_session_context_class">
org.springframework.orm.hibernate4.SpringSessionContext
</property>

在H3中是 - org.springframework.orm.hibernate3.SpringSessionContext

如果没有配置此项,在与Spring整合的时候,必定报错 -
可能的错误是 - org.hibernate.HibernateException: save is not valid without active transaction

有了此项配置,才有了与Spring整合的可能性,

其它的说明。比如,必须开启和提交事务才能操作、jta云云,不多说了。

交给Spring去管理就OK了。

3、关于使用getCurrentSession()的时候,在Spring的声明式事务处理中,提示 - org.hibernate.TransactionException: nested transactions not supported

这个错误最为坑人。

又回到上面的提示 - 这个错误是最终的错误,早已南辕北辙了。

首先需要说明的是,并非是Spring的事务处理的问题,而是S4比起S3代码严谨的多。

在S3中很少会提示这个错误的,

我的项目未升级前,全部正常运行,升级之后,报的这个错误非常的奇怪,因为配置本身都是类似的。

网上的答案也是五花八门。

如果涉及到底层框架的执行的话,参考意义几乎为0。

因为事务不是我自己做的,我根本不知道到底能不能在某个时刻真正的执行,想了好久,不知所云。果断打断点,看代码。

我注意到了两点,执行目标方法前,执行了一个aspect,执行了一个advisor

代码中,事务开启之后,再次开启了,就报了这个错误。
问题涉及两点,
一是事务的传播属性为默认,
二是执行方法只有一个,但是涉及到的切面很多,再带上声明式事务的advisor

为什么会开启两次?

去排查aspect方法,果然发现了!

有切面类手动处理了事务,去操作数据库
- 由于方法单一,并未考虑Spring声明式
- 考虑的话,事务嵌套的问题可能会更加严重。

由此,总结出一点,
在Spring的执行流程中,
默认传播属性的执行中,
- session一直存在,那么,这个session获取的事务,只要有一处完成了开启,那么后期处理的时候,全部都是开启的。无需再次开启。否则就会引起事务嵌套问题而报错。
- 问题的关键在于分清楚,到底谁是目标方法,其它的切面以及advisor的执行时机。

当然,这只是当前情境下的一种解决方案,仅供参考,如有转载,请注明出处来源,谢谢。

如需要转载
请注明文章出处
http://blog.csdn.net/MUXINGYE/article/details/54359884
谢谢~