2013-05-10 66 views
5

我使用Spring 3与JPA,我在我的web应用程序中看到一个间歇性问题。 我有调用基础EntityManager crud方法的JPA EntityManager的包装。我有时看到NPE,当我打电话给entityManager.persist(object);它看起来数据库连接丢失了,但我不是100%,原因是什么。任何人有关于可能导致以下例外的任何细节?Spring 3 NPE LazyConnectionDataSourceProxy autoCommit

春季版本:3.0.6.RELEASE

春3 LazyConnectionDataSourceProxy.java行416:

if (this.autoCommit != null && this.autoCommit != this.target.getAutoCommit()) { 
    this.target.setAutoCommit(this.autoCommit); 
} 

例外:

Caused by: java.lang.NullPointerException 
     at org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy$LazyConnectionInvocationHandler.getTargetConnection(LazyConnectionDataSourceProxy.java:416) 
     at org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy$LazyConnectionInvocationHandler.invoke(LazyConnectionDataSourceProxy.java:376) 
     at $Proxy64.prepareStatement(Unknown Source) 
     at org.hibernate.jdbc.AbstractBatcher.getPreparedStatement(AbstractBatcher.java:534) 
     at org.hibernate.jdbc.AbstractBatcher.prepareSelectStatement(AbstractBatcher.java:145) 
     at org.hibernate.id.SequenceGenerator.generate(SequenceGenerator.java:96) 
     at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:122) 
     at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49) 
     at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154) 
     at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110) 
     at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61) 
     at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:646) 
     at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:620) 
     at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:624) 
     at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220) 
     at sun.reflect.GeneratedMethodAccessor101.invoke(Unknown Source) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
     at java.lang.reflect.Method.invoke(Method.java:592) 
     at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240) 
     at $Proxy79.persist(Unknown Source) 
     at myapp.api.dao.impl.GenericDAOImpl.save(GenericDAOImpl.java:50) 
     at sun.reflect.GeneratedMethodAccessor100.invoke(Unknown Source) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
     at java.lang.reflect.Method.invoke(Method.java:592) 
     at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309) 
     at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) 
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) 
     at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) 
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
     at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 
     at $Proxy119.save(Unknown Source) 
     at myapp.api.service.impl.backoffice.StoringServiceImpl.store(StoringServiceImpl.java:89) 
     at myapp.api.service.impl.backoffice.StoringServiceImpl.storeIncludedFeatureMessage(StoringServiceImpl.java:68) 
     at sun.reflect.GeneratedMethodAccessor124.invoke(Unknown Source) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
     at java.lang.reflect.Method.invoke(Method.java:592) 
     at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309) 
     at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) 
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) 
     at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) 
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
     at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 
     at $Proxy168.storeIncludedFeatureMessage(Unknown Source) 
     at myapp.api.listener.backoffice.StorableMessageListener.processNew(StorableMessageListener.java:136) 
     at myapp.api.listener.backoffice.StorableMessageListener.onMessage(StorableMessageListener.java:187) 
     ... 34 more 

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:util="http://www.springframework.org/schema/util" 
     xmlns:context="http://www.springframework.org/schema/context" 
     xmlns:aop="http://www.springframework.org/schema/aop" 
     xmlns:tx="http://www.springframework.org/schema/tx" 
     xmlns:hz="http://www.hazelcast.com/schema/spring" 
     xsi:schemaLocation=" 
     http://www.hazelcast.com/schema/spring 
     http://www.hazelcast.com/schema/spring/hazelcast-spring-2.5.xsd 
     http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
     http://www.springframework.org/schema/util 
     http://www.springframework.org/schema/util/spring-util-3.0.xsd 
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd 
     http://www.springframework.org/schema/tx 
     http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
     http://www.springframework.org/schema/aop 
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 

     <!-- Generic --> 
     <context:annotation-config /> 
     <context:component-scan base-package="myapp.api" /> 
     <aop:aspectj-autoproxy/> 

     <!-- JPA --> 
     <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> 

     <tx:annotation-driven /> 

     <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" /> 

     <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
      <property name="dataSource" ref="dataSource" /> 
      <property name="jpaVendorAdapter" ref="jpaVendorAdapter" /> 
      <property name="persistenceUnitName" value="MyApp" /> 
      <property name="jpaProperties"> 
       <props> 
        <prop key="hibernate.use_sql_comments">true</prop> 
        <prop key="hibernate.generate_statistics">true</prop> 
        <prop key="hibernate.archive.autodetection">class</prop> 
        <prop key="hibernate.cache.use_second_level_cache">true</prop> 
        <prop key="hibernate.cache.provider_class">com.hazelcast.hibernate.provider.HazelcastCacheProvider</prop> 
        <prop key="hibernate.cache.use_query_cache">true</prop> 
        <prop key="hibernate.cache.use_minimal_puts">true</prop> 
       </props> 
      </property> 
     </bean> 


     <hz:hazelcast id="instance"> 
      <hz:config> 
       //rest of Hazelast config here 
      </hz:config> 
     </hz:hazelcast> 

     <hz:hibernate-region-factory id="regionFactory" instance-ref="instance"/> 

     <!-- Define JPA Provider Adapter --> 
     <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
      <property name="showSql" value="true" /> 
      <property name="generateDdl" value="true" /> 
      <property name="databasePlatform" value="org.hibernate.dialect.OracleDialect" /> 
     </bean> 

     <bean id="dataSourceTarget" class="oracle.jdbc.pool.OracleDataSource" destroy-method="close"> 
      <property name="URL" value="jdbc:oracle:thin:@server:1525:name" /> 
      <property name="user" value="test" /> 
      <property name="password" value="123" /> 
      <property name="connectionCachingEnabled" value="true" /> 
      <property name="connectionCacheProperties"> 
       <props merge="default"> 
        <prop key="MinLimit">5</prop> 
        <prop key="MaxLimit">50</prop> 
       </props> 
      </property> 
     </bean> 

     <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"> 
      <property name="targetDataSource" ref="dataSourceTarget"/> 
     </bean> 


     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
      <property name="entityManagerFactory" ref="entityManagerFactory"/> 
      <property name="dataSource" ref="dataSource"/> 
     </bean> 

     <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false"/> 

     <bean id="genericDAO" class="myapp.api.dao.impl.GenericDAOImpl"> 
      <constructor-arg> 
       <value>java.io.Serializable</value> 
      </constructor-arg> 
     </bean> 

     <bean id="springContextHolder" class="myapp.api.util.SpringContextHolder" factory-method="getInstance" /> 

<bean id="executionInterceptor" class="myapp.api.listener.backoffice.ExecutionInterceptor" /> 

     </beans> 

AOP错误处理程序:

package myapp.api.listener.backoffice; 


import javax.jms.Message; 

import org.aspectj.lang.JoinPoint; 
import org.aspectj.lang.ProceedingJoinPoint; 
import org.aspectj.lang.annotation.AfterReturning; 
import org.aspectj.lang.annotation.AfterThrowing; 
import org.aspectj.lang.annotation.Around; 
import org.aspectj.lang.annotation.Aspect; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.jms.core.JmsTemplate; 
import org.springframework.util.StopWatch; 

@Aspect 
public class ExecutionInterceptor extends BaseListener{ 
    //protected Log log = LogFactory.getLog(this.getClass()); 
    private String errorDestination="ErrorQ"; 
    @Autowired 
    @Qualifier("jmsTemplate") 
    JmsTemplate jmsTemplate; 

    @Around("execution(* onMessage(javax.jms.Message))") 
    public Object aroundOnMessage(ProceedingJoinPoint pjp) throws Throwable{ 
     StopWatch stopWatch = new StopWatch(); 
     stopWatch.start(); 
     Object retVal = pjp.proceed(); 
     stopWatch.stop(); 
     log.trace(pjp.getSignature().getName() + " Execution Time: " + stopWatch.getTotalTimeMillis()+" ms"); 
     return retVal; 
    } 
    @AfterReturning("execution(* onMessage(javax.jms.Message))") 
    public void afterOnMessage(){ 
     // logic to capture time 
     log.debug("*****************************EXIT ONMESSAGE*******************************"); 

    } 

    @Around("execution(* commit(..))") 
    public Object aroundCommit(ProceedingJoinPoint pjp) throws Throwable{ 
     try{ 
      return pjp.proceed(); 
     }catch(Throwable ex){ 
      log.error("Unexpected Error occured during database commit routing message to ErrorQ", ex); 
      jmsTemplate.convertAndSend(errorDestination, ex); 
      throw ex; 
     }finally{ 
      log.trace("Commiting Transaction..."); 
     } 

    } 
    @AfterThrowing(value="execution(* onMessage(javax.jms.Message) throws java.lang.RuntimeException)",throwing="ex") 
    public void afterThrowingOnMessage(JoinPoint jp,RuntimeException ex) throws RuntimeException{ 
     log.trace("Unexpected Error occured during onMessage processing routing to ErrorQ"); 
     log.error("{{ERROR}}", ex); 
     Object[] args = jp.getArgs(); 
     if (args!=null && args[0] instanceof javax.jms.Message){ 
      Message msg = (Message)args[0]; 
      jmsTemplate.convertAndSend(errorDestination, msg); 
      log.info("Unexpected Error occured successfully routed to ErrorQ"); 
     }else 
      log.info("Unexpected Error occured failed to route to ErrorQ"); 
    } 
} 
+0

你正在使用哪个确切的弹簧版本? – tstorms 2013-05-14 12:13:33

+0

@tstorms 3.0.6.RELEASE – c12 2013-05-14 12:18:33

+0

你有没有考虑使用最新的春季版本?现在是3.2.2.RELEASE。这样,我们排除了可能的固定错误。 – tstorms 2013-05-14 12:25:34

回答

3

看来,NPE被抛出,因为连接为空(可变this.target)S形。

从数据源获得的连接前几行(LazyConnectionDataSourceProxy.java):

// Fetch physical Connection from DataSource. 
this.target = (this.username != null) ? getTargetDataSource().getConnection(this.username, this.password) : getTargetDataSource().getConnection(); 

谷歌搜索我发现,Oracle JDBC驱动程序可能会返回在以下情况下一个空连接(link):

ConnectionWaitTimeout

指定在请求连接的高速缓存行为,有 已激活MaxLimit连接。如果ConnectionWaitTimeout为大于零的 ,则每个连接请求都会等待指定的秒数或直到连接返回到 缓存。如果在超时 过去之前没有连接返回到缓存,则连接请求将返回空值。

默认值:0(无超时)

所以,我想,连接超时可以解释你的不一致的问题。

+0

我会尝试将ConnectionWaitTimeout增加到30秒,除非您有超时值的其他建议?我也可以将MaxLimit从50增加到75. – c12 2013-05-14 17:42:39

+0

更改这些参数应该可以减少超时的次数,但是您仍应该处理应用程序代码中的异常(例如,重试,向用户显示消息......取决于您的应用程序) 。 – 2013-05-16 14:17:43

+0

由于我们将消息放入可以重新处理的错误队列中,因此处理异常。 – c12 2013-05-16 17:28:53

1

你有正确应用您的交易?

你可以显示数据源的conifg文件吗?

而且它看起来你正在使用AOP与这些文件 在

myapp.api.listener.backoffice.StorableMessageListener.processNew(StorableMessageListener.java:136) 

myapp.api.listener.backoffice.StorableMessageListener.onMessage(StorableMessageListener.java:187) 
+0

我发布了我的Spring配置。在过去的一年中,我们的生产环境也采用了相同的配置,而且配置方面没有任何变化。它可能在网络或数据库级别,但不幸的是我无法深入挖掘这些领域。关于如何查看活动连接的任何想法,也许我已经达到了Glassfish所允许的最大值。任何想法为什么Spring会抛出异常? – c12 2013-05-14 12:38:43

+0

StorableMessageListener中的代码是做什么的?这看起来像它的射击感谢一些AOP ... – NimChimpsky 2013-05-14 12:41:00

+0

它是一个Spring JMS侦听器,它正在处理来自OpenMQ消息队列的消息并将它们存储在Oracle数据库中。 – c12 2013-05-14 12:44:52