我已经“继承”了一个项目,该项目使用Spring批注来管理Hibernate的事务/会话。或至少是注定的。当前,Hibernate会话永远不会被刷新(它们设置为FLUSH_MODE_NEVER),并且DAO需要手动刷新才能将任何数据写入数据库。
同样,所有DTO对象都驻留在hibernate的内存中,最终导致OutOfMemory错误。
我相信我需要告诉Spring / Hibernate关闭会话或提交事务。在我的控制器类中,我具有带注释的方法来处理请求:
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { ... ... }
我相信我在applicationContetxt.xml文件中设置了hibernate事务管理器,并告诉spring使用注释:
<!-- hibernate3 transaction manager --> <bean id="transactionManagerLocal" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="dataSource" ref="${local.data.source}" /> <property name="sessionFactory" ref="localSessionFactory" /> </bean> <!-- Demarcate using @Transactional annotation --> <tx:annotation-driven transaction-manager="transactionManagerLocal" order="200" />
我不仅可以确定配置是否错误,而且在没有手动调用每个DAO上的flush的情况下不会将数据写入数据库,从日志文件中我们还可以看到事务管理器已禁用了flushing和关闭会话关闭:
INFO [http-8080-2] TransactionFactoryFactory - Transaction strategy: org.springframework.orm.hibernate3.SpringTransactionFactory INFO [http-8080-2] TransactionManagerLookupFactory - No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended) INFO [http-8080-2] SettingsFactory - Automatic flush during beforeCompletion(): disabled INFO [http-8080-2] SettingsFactory - Automatic session close at end of transaction: disabled
要使Spring / hibernate自动刷新DAO和/或关闭会话以防止Hibernate使用大量内存,需要做些什么?
欢呼丹
<!-- MySQL/InnoDB session factory --> <bean id="localSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="${local.data.source}"/> <property name="mappingResources"> <list> <value>net/company/projectname/domain/ExchangeRate.hbm.xml</value> <!-- Other --> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop> <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop> <prop key="hibernate.connection.zeroDateTimeBehavior">convertToNull</prop> <!-- DOES NOTHING <prop key="hibernate.transaction.flush_before_completion">true</prop> <prop key="hibernate.transaction.auto_close_session">true</prop> --> </props> </property> </bean> <!-- hibernate3 transaction manager --> <bean id="transactionManagerLocal" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="dataSource" ref="${local.data.source}" /> <property name="sessionFactory" ref="localSessionFactory" /> </bean> <!-- Demarcate using @Transactional annotation --> <tx:annotation-driven transaction-manager="transactionManagerLocal" order="200" /> <bean id="exchangeRateDAO" class="net.company.project.dao.hibernate.impl.ExchangeRateDAOImpl"> <property name="sessionFactory" ref="localSessionFactory"/> </bean>
我的一位聪明的同事发现了问题所在。
实际的问题是,您声明为@Transactional的方法是从基类调用的继承方法,这意味着Spring无法截获对该方法的调用并将其包装在事务中。 Spring将事务管理作为方面来实现,而方面则通过代理来实现。这样做的局限性在于,如果一个对象本身调用了一个方法(由于继承而在这里发生了这种情况),则代理看不到该调用(因为它发生在类内部,就像调用私有方法一样),并且对此无能为力。
实际的问题是,您声明为@Transactional的方法是从基类调用的继承方法,这意味着Spring无法截获对该方法的调用并将其包装在事务中。
Spring将事务管理作为方面来实现,而方面则通过代理来实现。这样做的局限性在于,如果一个对象本身调用了一个方法(由于继承而在这里发生了这种情况),则代理看不到该调用(因为它发生在类内部,就像调用私有方法一样),并且对此无能为力。
这是有道理的,但似乎非常危险,因为它无法在没有任何错误消息或警告的情况下写入任何数据。