<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>janh</title>
    <description></description>
    <link>http://janh.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
          <item>
        <title>惊喜，实现hibernate中一对一关联的从方的延迟加载的一种方法</title>
        <author>janh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://janh.javaeye.com">janh</a>&nbsp;
                    链接：<a href="http://janh.javaeye.com/blog/93378" style="color:red;">http://janh.javaeye.com/blog/93378</a>&nbsp;
          发表时间: 2007年06月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          关于hibernate中一对一关联时，从方不能实现延迟加载好对帖子里都讲到了，关键是 外键列是在从方表中的，由从方指向主方，加载主方时必须要查询从方表才能知道对应的从方是否存在。<br /><br /><div class="quote_title">引用</div><div class="quote_div">关于one-to-one关联的从方不能lazy loading是由one-to-one的机制造成的，比如user和contact是一对一，user是主，contact是从，约束依赖于user，那么在数据库中，contact表中应该有一个外键字段指向user表的主键。<br /><br />当加载contact对象时，从contact表的记录中就可以得到user记录的id，这和多对一是一样的，就可以知道user对象是否存在，不存在则user为null，存在就生成代理对象，所以可以实现主方的延迟加载。<br /><br />当加载user对象时，从user表记录中没有办法知道这个contact从方是否存在，它就不能确定是用null还是生成代理对象来代替contact对象，因为代理对象一定 != null，所以必须要查询contact表，查询这个从方是否存在。<br /><br />基于上面的情况，所以我现在一般不用one-to-one关联，倒不是一定要延迟加载，还有n+1次问题等等，处理起来比较麻烦，宁可用user到contact的多对一关联，contact为一方，虽然意义上好像反了，但是从user对象可以很方便的得到contact对象，实际上也都是从user来获取contact信息的，延迟加载也没问题。</div><br />上面一段描述时实际是将外键列放在主方user表中来指向contact表，来解决contact不能延迟加载的问题的。<br />那么按照正常的contact表中外键列指向user表，到底能不能实现加载user时contact的延迟加载呢？<br /><br /><br />能不能在加载主表user表时同时查询出对应的contact表的Id呢？<br />一个偶然的机会，我测试了类似以下的配置：<br />User类的映射文件中：<br /><pre name="code" class="java">&lt;many-to-one name="contact" class="com.xxx.Contact" >
            &lt;formula>(select c.CONTACT_ID from CONTACT c where c.USER_ID = USER_ID)&lt;/formula>
        &lt;/many-to-one></pre><br />在User类中有一个contact属性对象，但是在user表中并没有一个外键指向contact表，相反是contact表中的外键列USER_ID指向user表的主键，在user类的映射文件中用一句子查询来代替contact对象，返回的是与user表对应的contact的id值。<br />测试通过，可以实现user.contact的延迟加载！！<br />加载user时hibernate生成的sql语句类似如下：<br /><pre name="code" class="java">select user0_.USER_ID as USER1_0_1_, user0_.ACCOUNT as ACCOUNT0_1_, user0_.NAME as NAME0_1_, ...... , (select c.CONTACT_ID from CONTACT c where c.USER_ID = user0_.USER_ID) as formula0_1_  from USER user0_ where ......</pre><br />当那个子查询返回null时，user.contact为null；当那个子查询返回一个值时user.contact为Contact类的代理对象，代理对象的id是该子查询的返回值；当子查询返回值多于一个时报错。再获取user.contact的其他属性时就像普通延迟加载的多对一关联一样没有任何问题。<br /><br />这种配置方式与普通的多对一配置相比，最大的不同点在于“多方”表中并没有一个外键列指向“一方”的主键，而是用一个子查询的返回值来代替这个外键列值。<br /><br />后面继续测试了多对一的lazy和fetch的各种配置，可喜的是完全与普通的多对一关联方式一样，可以实现延迟加载、非延迟加载、select方式抓取以及连接方式抓取，并且在get、hql查询、Criteria查询中都没有问题，看来hibernate完全将这个子查询代替了本来的外键列值。<br /><br />以上测试在hibernate版本3.2.2中通过。<br /><br />这种方法的最大好处在于可以实现两个方向的多对一对象关联（两个方向都是多对一），而数据库中只需要单向的指向约束，没有相互指向约束引起的问题。相当于双向一对一。也给非主外键关联提供了另一种方法。
          <br/><br/>
          <span style="color:red;">
            <a href="http://janh.javaeye.com/blog/93378#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 23 Jun 2007 09:38:58 +0800</pubDate>
        <link>http://janh.javaeye.com/blog/93378</link>
        <guid>http://janh.javaeye.com/blog/93378</guid>
      </item>
          <item>
        <title>子类与代理延迟加载，难道是鱼与熊掌不可兼得？</title>
        <author>janh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://janh.javaeye.com">janh</a>&nbsp;
                    链接：<a href="http://janh.javaeye.com/blog/74992" style="color:red;">http://janh.javaeye.com/blog/74992</a>&nbsp;
          发表时间: 2007年04月26日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最近程序中用到子类继承，一个超类Task，有几个子类如 EntryTask和GroupTask，对应同一张数据库表，用一个discriminator字段来区分是哪一个子类。<br /><br />在需要判断hibernate返回的对象是哪一个子类时遇到麻烦，如果返回的不是代理对象，使用if(task instanceof EntryTask)这样的语句就可以，但是如果是代理对象就有问题了，因为其他地方有对Task的多对一关联，并且关联处指定的父类肯定是Task超类，不会去指定具体哪个子类，这样一旦延迟加载生成的是Task类的代理对象。<br /><br />该对象只instanceof Task类，而不是它的任何子类，即使在加载了实体类数据后，该代理对象的类属性也不会改变，仍旧只instanceof Task超类，而且在其他地方查询时，对于相同的id，hibernate返回的还是以前返回过的代理对象（hibernate会保持返回的一致性）。<br /><br />当然要判断该代理对象到底是哪个具体子类还是有办法的，可以在Task超类中加一个只读的discriminator属性，通过discriminator判断该代理对象是哪个子类，但是象EntryTask entryTask ＝ (EntryTask)task这样的转换还是不行的，java只知道它是个Task，除非再通过id来load或get，如：<pre name="code" class="java">EntryTask entryTask ＝ (EntryTask)session.get(EntryTask.class,task.getId())</pre><br />还有一种直接的方法，就是直接获得被代理的实体对象，hibernate生成的代理对象都实现了HibernateProxy接口，该接口提供了获取被代理对象的方法，可以这样来获取：<br /><pre name="code" class="java">Object target = ((HibernateProxy)task).getHibernateLazyInitializer().getImplementation()</pre><br />这样就可以进行类的比较转换了，这种方法虽然不复杂，但还是觉得罗唆。<br /><br />现在的解决办法是不使用延迟加载，设置Task类映射lazy="false"，并且在查询时使用eager-fetch，这样它就不会返回代理对象了，对我们这个应用来说性能影响也不大。<br /><br />难道真的是鱼与熊掌不可兼得吗？
          <br/><br/>
          <span style="color:red;">
            <a href="http://janh.javaeye.com/blog/74992#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 26 Apr 2007 13:49:00 +0800</pubDate>
        <link>http://janh.javaeye.com/blog/74992</link>
        <guid>http://janh.javaeye.com/blog/74992</guid>
      </item>
          <item>
        <title>spring拦截器中的事务管理</title>
        <author>janh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://janh.javaeye.com">janh</a>&nbsp;
                    链接：<a href="http://janh.javaeye.com/blog/71030" style="color:red;">http://janh.javaeye.com/blog/71030</a>&nbsp;
          发表时间: 2007年04月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          探讨一下spring拦截器中的数据库操作和事务管理。<br />大家知道spring中的事务管理是通过AOP代理来实现的，对被代理对象的每个方法进行拦截，在方法执行前启动事务，方法执行完后根据是否有异常和异常的种类进行提交或回滚。<br /><br />如果要在方法执行前或后或抛出异常后加上一个自己的拦截器，或者一个环绕拦截器，在拦截器中执行一些操作，比如执行一些数据库操作，记录一些信息，这些操作通过调用一个服务类的方法来执行，这个方法也在spring事务管理拦截器的管理之下，那么这个记录方法需要在另一个事务中进行，而不是与被拦截方法在同一个事务中，不然如果被拦截方法抛出异常需要回滚时，所作的记录也会被回滚，当然有时候确实需要同时回滚，那就要放在同一个事务中。<br /><br />这和自己的拦截器和事务管理的拦截器的执行顺序有一定关系，spring事务管理拦截器是一个环绕通知，在被拦截方法执行前启动事务，执行后完成事务，如果自己的拦截器被spring事务管理拦截器包围在里面，那么在自己的拦截器运行时，spring已经启动了一个事务，如果你的记录信息方法需要与被拦截方法同在一个事务中，将你的记录信息方法的事务传播属性设为默认的REQUIRED就可以了；<br />如果你记录信息的方法需要单独的一个事务环境，那就要把事务传播属性设为REQUIRES_NEW了，这样spring事务管理器会新建一个事务，并且新建一个session连接，因为一个数据库连接不可能同时有两个事务，记录信息完了提交事务并且把新建的session连接关闭，自己的拦截器退出后继续执行被拦截的方法或它的事务处理。<br /><br />相反如果自己的拦截器在spring事务管理拦截器的外面，那么记录信息的方法会在一个单独的事务中执行，并提交，不管它的事务传播属性是REQUIRES_NEW还是REQUIRED，因为与被拦截方法的事务处理没有交叉，并且可以使用同一个session连接如果是OpenSessionInViewFilter。<br /><br />所以如果记录信息和被拦截方法要在不同事务中执行，分别提交，那么最好将自己的拦截器设在spring事务管理器拦截器的外面；如果需要将记录信息和被拦截方法在同一个事务中处理，必须将自己的拦截器被包围在spring事务管理拦截器中，并且记录信息方法的事务传播属性为默认的REQUIRED。<br /><br />设置拦截器的执行顺序可以让拦截器处理类实现org.springframework.core.Ordered接口，在spring配置文件的AOP设置中设定自己的拦截器和spring事务管理拦截器的执行顺序，将自己的拦截的序号排在spring事务管理的前面，就可以将该拦截器放到事务管理拦截器的外面执行了，对于before通知方式会先于事务管理拦截器执行，对于after returning和after和after throwing通知方式会后于事务管理拦截器的执行，对于arount通知方式会包围事务管理拦截器执行。<br /><br />下面是一个异常拦截器的例子。<br />有位朋友提到在spring异常拦截器中更新数据不能够提交，做了一下测试，测试环境基本是这样：一个用户登录的功能，spring对service中的每个方法进行事务管理，在用户检测的service方法上同时加上一个异常拦截器，当用户不存在或密码不正确时用户检测方法会抛出异常，异常拦截器捕获到该异常，同时记录一些日志。<br />spring配置文件相关：<br /><pre name="code" class="java">	&lt;!-- 事务管理 -->
	&lt;bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
		&lt;property name="sessionFactory" ref="sessionFactory">&lt;/property>
	&lt;/bean>
	
	&lt;!-- 事务通知 -->
	&lt;tx:advice id="txAdvice" transaction-manager="transactionManager">
		&lt;tx:attributes>
			&lt;tx:method name="get*" read-only="true"/>
			&lt;tx:method name="*" propagation="REQUIRES_NEW" rollback-for="Exception"/>
		&lt;/tx:attributes>
	&lt;/tx:advice>
	
	&lt;!-- aop代理设置 -->
	&lt;aop:config proxy-target-class="true">
		&lt;aop:pointcut id="txPointcut" expression="execution(* com.hbs..*Service.*(..))"/>
		&lt;aop:pointcut id="logPointcut" expression="execution(* com.hbs.customer..*Service.*(..))"/>
		&lt;aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" order="1"/>
		&lt;aop:aspect id="logAspect" ref="logInterceptor" order="2" >
			&lt;aop:after-throwing
				pointcut-ref="logPointcut" 
				method="serviceIntercept" />
		&lt;/aop:aspect>
	&lt;/aop:config>
	
	&lt;!-- log拦截器类 -->
	&lt;bean id="logInterceptor" class="com.hbs.eventlog.EventLogInterceptor">
		&lt;property name="service" ref="logService">&lt;/property>
	&lt;/bean></pre><br /><br />拦截器类：<br /><pre name="code" class="java">public class EventLogInterceptor implements Ordered {

	private int order = 1;
	
	private EventLogService service;
	
	public Object serviceIntercept(ProceedingJoinPoint point) throws Throwable{
		if(point instanceof MethodInvocationProceedingJoinPoint){
			MethodInvocationProceedingJoinPoint mpoint = (MethodInvocationProceedingJoinPoint)point;
			//
		}
		try { 
			System.out.println("记录日志开始");
        	service.eventLog();
        	System.out.println("记录日志结束");
        }catch(Exception ex) {
            ex.printStackTrace();
        }
        return null;
	}
	
	public void setOrder(int order){
		this.order = order;
	}
	public int getOrder() {
		return order;
	}
	public EventLogService getService() {
		return service;
	}
	public void setService(EventLogService service) {
		this.service = service;
	}
}</pre><br /><br />service方法中的事务传播属性都设为要求新建事务，spring事务管理切面拦截器的order设为1，而log拦截器的order设为2，这意味着这两个要同时执行时，先执行事务拦截器，后执行log拦截器，由于事务管理是一个环绕通知（around），实际上是log拦截器被包围在事务管理拦截器中。<br />一个不正确的用户登录时，打印的日志：<br />03:35:16,562 DEBUG OpenSessionInViewFilter:253 - Using SessionFactory 'sessionFactory' for OpenSessionInViewFilter<br />03:35:16,562 DEBUG OpenSessionInViewFilter:196 - Opening single Hibernate Session in OpenSessionInViewFilter<br />03:35:16,562 DEBUG SessionFactoryUtils:333 - Opening Hibernate Session<br />03:35:16,562 DEBUG TransactionSynchronizationManager:166 - Bound value [org.springframework.orm.hibernate3.SessionHolder@1fee2db] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] to thread [http-8088-Processor25]<br />03:35:16,562 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@1fee2db] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] bound to thread [http-8088-Processor25]<br />03:35:16,562 DEBUG HibernateTransactionManager:390 - Found thread-bound Session [org.hibernate.impl.SessionImpl@dfbabd] for Hibernate transaction<br />03:35:16,578 DEBUG HibernateTransactionManager:292 - Using transaction object [org.springframework.orm.hibernate3.HibernateTransactionManager$HibernateTransactionObject@5cd7f9]<br />03:35:16,578 DEBUG HibernateTransactionManager:320 - Creating new transaction with name [com.hbs.customer.CustomerService.customerLogin]<br />03:35:16,578 DEBUG HibernateTransactionManager:440 - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@dfbabd]<br />03:35:16,578 DEBUG HibernateTransactionManager:510 - Exposing Hibernate transaction as JDBC transaction [org.apache.commons.dbcp.PoolableConnection@1501026]<br />03:35:16,578 DEBUG TransactionSynchronizationManager:166 - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@1672c01] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] to thread [http-8088-Processor25]<br />03:35:16,578 DEBUG TransactionSynchronizationManager:219 - Initializing transaction synchronization<br />03:35:16,578 DEBUG TransactionInterceptor:275 - Getting transaction for [com.hbs.customer.CustomerService.customerLogin]<br />用户登录<br />03:35:16,578 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@1fee2db] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] bound to thread [http-8088-Processor25]<br />03:35:16,578 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@1fee2db] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] bound to thread [http-8088-Processor25]<br />03:35:16,578 DEBUG HibernateTemplate:354 - Found thread-bound Session for HibernateTemplate<br />03:35:16,578 DEBUG SQL:393 - select this_.CUSTOMER_ID as CUSTOMER1_5_0_, this_.CUSTOMER_GROUP_ID as CUSTOMER2_5_0_, this_.CUSTOMER_PASSWORD as CUSTOMER3_5_0_, this_.CUSTOMER_NAME as CUSTOMER4_5_0_, this_.CUSTOMER_DESCRIPTION as CUSTOMER5_5_0_, this_.CUSTOMER_KIND as CUSTOMER6_5_0_, this_.CUSTOMER_SEX as CUSTOMER7_5_0_, this_.PHONE as PHONE5_0_, this_.MOBILE as MOBILE5_0_, this_.ADDRESS as ADDRESS5_0_, this_.EMAIL as EMAIL5_0_, this_.CONFIRM_TYPE as CONFIRM12_5_0_, this_.CREATE_TIME as CREATE13_5_0_, this_.GROUP_TIME as GROUP14_5_0_, this_.FIRST_TIME as FIRST15_5_0_, this_.LAST_TIME as LAST16_5_0_, this_.LOGIN_COUNT as LOGIN17_5_0_, this_.CREDIT_VALUE as CREDIT18_5_0_, this_.CUMULATE_VALUE as CUMULATE19_5_0_, this_.STATUS as STATUS5_0_, this_.NOTES as NOTES5_0_ from hbs.hbs_customer this_ where this_.CUSTOMER_ID=? and this_.CUSTOMER_PASSWORD=?<br />03:35:16,593 DEBUG HibernateTemplate:378 - Not closing pre-bound Hibernate Session after HibernateTemplate<br /><span style="color: red">记录日志开始</span><br />03:35:16,593 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@1fee2db] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] bound to thread [http-8088-Processor25]<br />03:35:16,593 DEBUG HibernateTransactionManager:390 - Found thread-bound Session [org.hibernate.impl.SessionImpl@dfbabd] for Hibernate transaction<br />03:35:16,593 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@1672c01] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] bound to thread [http-8088-Processor25]<br />03:35:16,593 DEBUG HibernateTransactionManager:292 - Using transaction object [org.springframework.orm.hibernate3.HibernateTransactionManager$HibernateTransactionObject@5ec940]<br />03:35:16,593 DEBUG HibernateTransactionManager:358 - <span style="color: red">Suspending current transaction, creating new transaction with name [com.hbs.eventlog.EventLogService.eventLog]</span><br />03:35:16,593 DEBUG TransactionSynchronizationManager:272 - Clearing transaction synchronization<br />03:35:16,593 DEBUG TransactionSynchronizationManager:190 - Removed value [org.springframework.orm.hibernate3.SessionHolder@1fee2db] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] from thread [http-8088-Processor25]<br />03:35:16,609 DEBUG TransactionSynchronizationManager:190 - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@1672c01] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] from thread [http-8088-Processor25]<br />03:35:16,609 DEBUG HibernateTransactionManager:428 - <span style="color: red">Opened new Session [org.hibernate.impl.SessionImpl@eeb406] for Hibernate transaction</span><br />03:35:16,609 DEBUG HibernateTransactionManager:440 - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@eeb406]<br />03:35:16,609 DEBUG HibernateTransactionManager:510 - Exposing Hibernate transaction as JDBC transaction [org.apache.commons.dbcp.PoolableConnection@8543aa]<br />03:35:16,609 DEBUG TransactionSynchronizationManager:166 - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@16d03ba] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] to thread [http-8088-Processor25]<br />03:35:16,609 DEBUG TransactionSynchronizationManager:166 - Bound value [org.springframework.orm.hibernate3.SessionHolder@fbfa2] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] to thread [http-8088-Processor25]<br />03:35:16,609 DEBUG TransactionSynchronizationManager:219 - Initializing transaction synchronization<br />03:35:16,609 DEBUG TransactionInterceptor:275 - Getting transaction for [com.hbs.eventlog.EventLogService.eventLog]<br />03:35:16,625 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@fbfa2] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] bound to thread [http-8088-Processor25]<br />03:35:16,625 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@fbfa2] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] bound to thread [http-8088-Processor25]<br />03:35:16,625 DEBUG HibernateTemplate:354 - Found thread-bound Session for HibernateTemplate<br />03:35:16,625 DEBUG SQL:393 - select hotel0_.HOTEL_ID as HOTEL1_3_0_, hotel0_.HOTEL_NAME as HOTEL2_3_0_, hotel0_.DESCRIPTION as DESCRIPT3_3_0_, hotel0_.CHARACTERISTIC as CHARACTE4_3_0_, hotel0_.STAR_CLASS as STAR5_3_0_, hotel0_.ESTABLISH_DATE as ESTABLISH6_3_0_, hotel0_.BUSINESS_LICENSE as BUSINESS7_3_0_, hotel0_.LEGAL_PERSON as LEGAL8_3_0_, hotel0_.ADDRESS as ADDRESS3_0_, hotel0_.PHONE as PHONE3_0_, hotel0_.EMAIL as EMAIL3_0_, hotel0_.INTERNET_ADDRESS as INTERNET12_3_0_, hotel0_.HOTEL_LOG as HOTEL13_3_0_, hotel0_.COPYRIGHT as COPYRIGHT3_0_, hotel0_.NOTES as NOTES3_0_, hotel0_.CUR_ORDERFORM_ID as CUR16_3_0_ from hbs.hbs_hotel hotel0_ where hotel0_.HOTEL_ID=?<br />03:35:16,625 DEBUG HibernateTemplate:378 - Not closing pre-bound Hibernate Session after HibernateTemplate<br />03:35:16,625 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@fbfa2] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] bound to thread [http-8088-Processor25]<br />03:35:16,625 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@fbfa2] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] bound to thread [http-8088-Processor25]<br />03:35:16,625 DEBUG HibernateTemplate:354 - Found thread-bound Session for HibernateTemplate<br />03:35:16,625 DEBUG SQL:393 - update hbs.hbs_hotel set HOTEL_NAME=?, DESCRIPTION=?, CHARACTERISTIC=?, STAR_CLASS=?, ESTABLISH_DATE=?, BUSINESS_LICENSE=?, LEGAL_PERSON=?, ADDRESS=?, PHONE=?, EMAIL=?, INTERNET_ADDRESS=?, HOTEL_LOG=?, COPYRIGHT=?, NOTES=?, CUR_ORDERFORM_ID=? where HOTEL_ID=?<br />03:35:16,640 DEBUG HibernateTemplate:378 - Not closing pre-bound Hibernate Session after HibernateTemplate<br />03:35:16,640 DEBUG TransactionInterceptor:305 - Completing transaction for [com.hbs.eventlog.EventLogService.eventLog]<br />03:35:16,640 DEBUG HibernateTransactionManager:776 - Triggering beforeCommit synchronization<br />03:35:16,640 DEBUG HibernateTransactionManager:789 - Triggering beforeCompletion synchronization<br />03:35:16,640 DEBUG HibernateTransactionManager:609 - Initiating transaction commit<br />03:35:16,640 DEBUG HibernateTransactionManager:557 - <span style="color: red">Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@eeb406]</span><br />03:35:16,671 DEBUG HibernateTransactionManager:802 - Triggering afterCommit synchronization<br />03:35:16,671 DEBUG HibernateTransactionManager:818 - Triggering afterCompletion synchronization<br />03:35:16,671 DEBUG TransactionSynchronizationManager:272 - Clearing transaction synchronization<br />03:35:16,671 DEBUG TransactionSynchronizationManager:190 - Removed value [org.springframework.orm.hibernate3.SessionHolder@fbfa2] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] from thread [http-8088-Processor25]<br />03:35:16,671 DEBUG TransactionSynchronizationManager:190 - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@16d03ba] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] from thread [http-8088-Processor25]<br />03:35:16,671 DEBUG HibernateTransactionManager:636 - <span style="color: red">Closing Hibernate Session [org.hibernate.impl.SessionImpl@eeb406] after transaction</span><br />03:35:16,671 DEBUG SessionFactoryUtils:781 - Closing Hibernate Session<br />03:35:16,671 DEBUG HibernateTransactionManager:870 - Resuming suspended transaction<br />03:35:16,671 DEBUG TransactionSynchronizationManager:166 - Bound value [org.springframework.orm.hibernate3.SessionHolder@1fee2db] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] to thread [http-8088-Processor25]<br />03:35:16,671 DEBUG TransactionSynchronizationManager:166 - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@1672c01] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] to thread [http-8088-Processor25]<br />03:35:16,671 DEBUG TransactionSynchronizationManager:219 - Initializing transaction synchronization<br /><span style="color: red">记录日志结束</span><br />03:35:16,671 DEBUG TransactionInterceptor:320 - Completing transaction for [com.hbs.customer.CustomerService.customerLogin] after exception: com.hbs.common.ResultException: 登录失败！用户名或密码不正确<br />03:35:16,671 DEBUG RuleBasedTransactionAttribute:130 - Applying rules to determine whether transaction should rollback on com.hbs.common.ResultException: 登录失败！用户名或密码不正确<br />03:35:16,687 DEBUG RuleBasedTransactionAttribute:148 - Winning rollback rule is: RollbackRuleAttribute with pattern [Exception]<br />03:35:16,687 DEBUG HibernateTransactionManager:789 - Triggering beforeCompletion synchronization<br />03:35:16,687 DEBUG HibernateTransactionManager:700 - Initiating transaction rollback<br />03:35:16,687 DEBUG HibernateTransactionManager:576 - Rolling back Hibernate transaction on Session [org.hibernate.impl.SessionImpl@dfbabd]<br />03:35:16,687 DEBUG HibernateTransactionManager:818 - Triggering afterCompletion synchronization<br />03:35:16,687 DEBUG TransactionSynchronizationManager:272 - Clearing transaction synchronization<br />03:35:16,687 DEBUG TransactionSynchronizationManager:190 - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@1672c01] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] from thread [http-8088-Processor25]<br />03:35:16,687 DEBUG HibernateTransactionManager:643 - Not closing pre-bound Hibernate Session [org.hibernate.impl.SessionImpl@dfbabd] after transaction<br />03:35:16,687&nbsp; WARN ActionMapping:74 - Unable to find 'null' forward.<br />03:35:16,687 DEBUG TransactionSynchronizationManager:190 - Removed value [org.springframework.orm.hibernate3.SessionHolder@1fee2db] for key [org.hibernate.impl.SessionFactoryImpl@1fe3238] from thread [http-8088-Processor25]<br />03:35:16,687 DEBUG OpenSessionInViewFilter:221 - Closing single Hibernate Session in OpenSessionInViewFilter<br />03:35:16,687 DEBUG SessionFactoryUtils:781 - Closing Hibernate Session<br /><br />从中可以看出，log异常拦截器在用户登录的事务回滚之前截获异常，在记录日志时，日志记录的service方法也在spring的事务管理之下，用户登录的事务还没有结束，根据REQUIRES_NEW特性，spring会新开一个事务，这时原来的数据库连接已经在一个事务中，一个连接不可能同时有两个事务，所以同时新创建一个session连接（虽然我使用了OpenSessionInViewFilter，并且session是单例的），日志记录就在新建的事务和session中进行，完了提交，并且会把新建的session连接关闭。<br />然后继续进行被中断的用户登录的事务管理操作，由于抛异常spring将用户登录的事务回滚。<br />这样能够实现预想的功能，但是如果我去掉指定的REQUIRES_NEW，那么log记录的操作会继续在用户登录的事务中进行，最后会被一起回滚。<br /><br />如果我把事务管理的order设为2，log拦截器的order设为1，也就是log拦截器在事务管理拦截器的外面，会在事务管理拦截器前后执行完了再执行log的异常拦截器，打印信息如下：<br />03:53:46,125 DEBUG OpenSessionInViewFilter:253 - Using SessionFactory 'sessionFactory' for OpenSessionInViewFilter<br />03:53:46,156 DEBUG OpenSessionInViewFilter:196 - Opening single Hibernate Session in OpenSessionInViewFilter<br />03:53:46,156 DEBUG SessionFactoryUtils:333 - Opening Hibernate Session<br />03:53:46,265 DEBUG TransactionSynchronizationManager:166 - Bound value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] to thread [http-8088-Processor25]<br />03:53:46,296&nbsp; INFO ComposableRequestProcessor:144 - Initializing composable request processor for module prefix ''<br />03:53:46,375&nbsp; INFO CreateAction:65 - Initialize action of type: org.springframework.web.struts.DelegatingActionProxy<br />03:53:46,406&nbsp; INFO JdbcTransactionObjectSupport:60 - JDBC 3.0 Savepoint class is available<br />03:53:46,406 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] bound to thread [http-8088-Processor25]<br />03:53:46,406 DEBUG HibernateTransactionManager:390 - Found thread-bound Session [org.hibernate.impl.SessionImpl@c21d01] for Hibernate transaction<br />03:53:46,406 DEBUG HibernateTransactionManager:292 - Using transaction object [org.springframework.orm.hibernate3.HibernateTransactionManager$HibernateTransactionObject@1fe6783]<br />03:53:46,406 DEBUG HibernateTransactionManager:320 - Creating new transaction with name [com.hbs.customer.CustomerService.customerLogin]<br />03:53:46,421 DEBUG HibernateTransactionManager:440 - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@c21d01]<br />03:53:46,468 DEBUG HibernateTransactionManager:510 - Exposing Hibernate transaction as JDBC transaction [org.apache.commons.dbcp.PoolableConnection@16c02df]<br />03:53:46,468 DEBUG TransactionSynchronizationManager:166 - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@30803a] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] to thread [http-8088-Processor25]<br />03:53:46,468 DEBUG TransactionSynchronizationManager:219 - Initializing transaction synchronization<br />03:53:46,484 DEBUG TransactionInterceptor:275 - Getting transaction for [com.hbs.customer.CustomerService.customerLogin]<br /><span style="color: red">用户登录</span><br />03:53:46,500 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] bound to thread [http-8088-Processor25]<br />03:53:46,500 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] bound to thread [http-8088-Processor25]<br />03:53:46,515 DEBUG HibernateTemplate:354 - Found thread-bound Session for HibernateTemplate<br />03:53:46,531 DEBUG SQL:393 - select this_.CUSTOMER_ID as CUSTOMER1_5_0_, this_.CUSTOMER_GROUP_ID as CUSTOMER2_5_0_, this_.CUSTOMER_PASSWORD as CUSTOMER3_5_0_, this_.CUSTOMER_NAME as CUSTOMER4_5_0_, this_.CUSTOMER_DESCRIPTION as CUSTOMER5_5_0_, this_.CUSTOMER_KIND as CUSTOMER6_5_0_, this_.CUSTOMER_SEX as CUSTOMER7_5_0_, this_.PHONE as PHONE5_0_, this_.MOBILE as MOBILE5_0_, this_.ADDRESS as ADDRESS5_0_, this_.EMAIL as EMAIL5_0_, this_.CONFIRM_TYPE as CONFIRM12_5_0_, this_.CREATE_TIME as CREATE13_5_0_, this_.GROUP_TIME as GROUP14_5_0_, this_.FIRST_TIME as FIRST15_5_0_, this_.LAST_TIME as LAST16_5_0_, this_.LOGIN_COUNT as LOGIN17_5_0_, this_.CREDIT_VALUE as CREDIT18_5_0_, this_.CUMULATE_VALUE as CUMULATE19_5_0_, this_.STATUS as STATUS5_0_, this_.NOTES as NOTES5_0_ from hbs.hbs_customer this_ where this_.CUSTOMER_ID=? and this_.CUSTOMER_PASSWORD=?<br />03:53:46,593 DEBUG HibernateTemplate:378 - Not closing pre-bound Hibernate Session after HibernateTemplate<br />03:53:46,593 DEBUG TransactionInterceptor:320 - Completing transaction for [com.hbs.customer.CustomerService.customerLogin] after exception: com.hbs.common.ResultException: 登录失败！用户名或密码不正确<br />03:53:46,609 DEBUG RuleBasedTransactionAttribute:130 - Applying rules to determine whether transaction should rollback on com.hbs.common.ResultException: 登录失败！用户名或密码不正确<br />03:53:46,609 DEBUG RuleBasedTransactionAttribute:148 - Winning rollback rule is: RollbackRuleAttribute with pattern [Exception]<br />03:53:46,609 DEBUG HibernateTransactionManager:789 - Triggering beforeCompletion synchronization<br />03:53:46,609 DEBUG HibernateTransactionManager:700 - Initiating transaction rollback<br />03:53:46,609 DEBUG HibernateTransactionManager:576 - <span style="color: red">Rolling back Hibernate transaction on Session [org.hibernate.impl.SessionImpl@c21d01]</span><br />03:53:46,609 DEBUG HibernateTransactionManager:818 - Triggering afterCompletion synchronization<br />03:53:46,609 DEBUG TransactionSynchronizationManager:272 - Clearing transaction synchronization<br />03:53:46,609 DEBUG TransactionSynchronizationManager:190 - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@30803a] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] from thread [http-8088-Processor25]<br />03:53:46,625 DEBUG HibernateTransactionManager:643 - Not closing pre-bound Hibernate Session [org.hibernate.impl.SessionImpl@c21d01] after transaction<br /><span style="color: red">记录日志开始</span><br />03:53:46,640 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] bound to thread [http-8088-Processor25]<br />03:53:46,640 DEBUG HibernateTransactionManager:390 - Found thread-bound Session [org.hibernate.impl.SessionImpl@c21d01] for Hibernate transaction<br />03:53:46,640 DEBUG HibernateTransactionManager:292 - Using transaction object [org.springframework.orm.hibernate3.HibernateTransactionManager$HibernateTransactionObject@1f68336]<br />03:53:46,640 DEBUG HibernateTransactionManager:320 - <span style="color: red">Creating new transaction with name [com.hbs.eventlog.EventLogService.eventLog]</span><br />03:53:46,640 DEBUG HibernateTransactionManager:440 - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@c21d01]<br />03:53:46,640 DEBUG HibernateTransactionManager:510 - Exposing Hibernate transaction as JDBC transaction [org.apache.commons.dbcp.PoolableConnection@16c02df]<br />03:53:46,640 DEBUG TransactionSynchronizationManager:166 - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@1205042] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] to thread [http-8088-Processor25]<br />03:53:46,656 DEBUG TransactionSynchronizationManager:219 - Initializing transaction synchronization<br />03:53:46,656 DEBUG TransactionInterceptor:275 - Getting transaction for [com.hbs.eventlog.EventLogService.eventLog]<br />03:53:46,656 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] bound to thread [http-8088-Processor25]<br />03:53:46,656 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] bound to thread [http-8088-Processor25]<br />03:53:46,656 DEBUG HibernateTemplate:354 - Found thread-bound Session for HibernateTemplate<br />03:53:46,656 DEBUG SQL:393 - select hotel0_.HOTEL_ID as HOTEL1_3_0_, hotel0_.HOTEL_NAME as HOTEL2_3_0_, hotel0_.DESCRIPTION as DESCRIPT3_3_0_, hotel0_.CHARACTERISTIC as CHARACTE4_3_0_, hotel0_.STAR_CLASS as STAR5_3_0_, hotel0_.ESTABLISH_DATE as ESTABLISH6_3_0_, hotel0_.BUSINESS_LICENSE as BUSINESS7_3_0_, hotel0_.LEGAL_PERSON as LEGAL8_3_0_, hotel0_.ADDRESS as ADDRESS3_0_, hotel0_.PHONE as PHONE3_0_, hotel0_.EMAIL as EMAIL3_0_, hotel0_.INTERNET_ADDRESS as INTERNET12_3_0_, hotel0_.HOTEL_LOG as HOTEL13_3_0_, hotel0_.COPYRIGHT as COPYRIGHT3_0_, hotel0_.NOTES as NOTES3_0_, hotel0_.CUR_ORDERFORM_ID as CUR16_3_0_ from hbs.hbs_hotel hotel0_ where hotel0_.HOTEL_ID=?<br />03:53:46,718 DEBUG HibernateTemplate:378 - Not closing pre-bound Hibernate Session after HibernateTemplate<br />03:53:46,734 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] bound to thread [http-8088-Processor25]<br />03:53:46,734 DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] bound to thread [http-8088-Processor25]<br />03:53:46,734 DEBUG HibernateTemplate:354 - Found thread-bound Session for HibernateTemplate<br />03:53:46,812 DEBUG SQL:393 - update hbs.hbs_hotel set HOTEL_NAME=?, DESCRIPTION=?, CHARACTERISTIC=?, STAR_CLASS=?, ESTABLISH_DATE=?, BUSINESS_LICENSE=?, LEGAL_PERSON=?, ADDRESS=?, PHONE=?, EMAIL=?, INTERNET_ADDRESS=?, HOTEL_LOG=?, COPYRIGHT=?, NOTES=?, CUR_ORDERFORM_ID=? where HOTEL_ID=?<br />03:53:46,812 DEBUG HibernateTemplate:378 - Not closing pre-bound Hibernate Session after HibernateTemplate<br />03:53:46,812 DEBUG TransactionInterceptor:305 - Completing transaction for [com.hbs.eventlog.EventLogService.eventLog]<br />03:53:46,812 DEBUG HibernateTransactionManager:776 - Triggering beforeCommit synchronization<br />03:53:46,812 DEBUG HibernateTransactionManager:789 - Triggering beforeCompletion synchronization<br />03:53:46,812 DEBUG HibernateTransactionManager:609 - Initiating transaction commit<br />03:53:46,812 DEBUG HibernateTransactionManager:557 - <span style="color: red">Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@c21d01]</span><br />03:53:46,859 DEBUG HibernateTransactionManager:802 - Triggering afterCommit synchronization<br />03:53:46,859 DEBUG HibernateTransactionManager:818 - Triggering afterCompletion synchronization<br />03:53:46,859 DEBUG TransactionSynchronizationManager:272 - Clearing transaction synchronization<br />03:53:46,859 DEBUG TransactionSynchronizationManager:190 - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@1205042] for key [org.apache.commons.dbcp.BasicDataSource@54cbb9] from thread [http-8088-Processor25]<br />03:53:46,859 DEBUG HibernateTransactionManager:643 - Not closing pre-bound Hibernate Session [org.hibernate.impl.SessionImpl@c21d01] after transaction<br />记录日志结束<br />03:53:46,859&nbsp; WARN ActionMapping:74 - Unable to find 'null' forward.<br />03:53:46,859 DEBUG TransactionSynchronizationManager:190 - Removed value [org.springframework.orm.hibernate3.SessionHolder@889c4e] for key [org.hibernate.impl.SessionFactoryImpl@cdf872] from thread [http-8088-Processor25]<br />03:53:46,859 DEBUG OpenSessionInViewFilter:221 - Closing single Hibernate Session in OpenSessionInViewFilter<br />03:53:46,875 DEBUG SessionFactoryUtils:781 - Closing Hibernate Session<br /><br />可以看出，用户登录的事务和日志记录的事务是前后两个不相关的事务，并且在日志记录事务中并不需要新建session连接，而是直接用在OpenSessionInViewFilter中创建的session。实际上这时也并不需要将propagation设为REQUIRES_NEW，使用默认的REQUIRES也照样能够正常工作。<br /><br />所以应该将该异常拦截器设在事务管理拦截器的外面，即使用Order接口排在前面。
          <br/><br/>
          <span style="color:red;">
            <a href="http://janh.javaeye.com/blog/71030#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 14 Apr 2007 03:17:22 +0800</pubDate>
        <link>http://janh.javaeye.com/blog/71030</link>
        <guid>http://janh.javaeye.com/blog/71030</guid>
      </item>
      </channel>
</rss>