我有一个Java应用程序,它使用Spring / Hibernate(在Amazon Elastic Beanstalk上运行)和普通的Amazon RDS实例。为了分配负载,我正在添加一个只读副本。我将DB.url交换到只读副本并测试了一个应只读取数据的方法(这些方法肯定不使用UPDATE,INSERT或DELETE)。但是,该应用程序返回错误。
我的问题:为什么只在读取数据时应用程序失败了?如何配置Spring / Hibernate以使用只读副本?
详细信息:
我得到的错误是:
INFO: Internal error in method IEngageManager.getSimpleExamActivityLiveResults, ARGUMENTS=[
Credentials {
(m) password=0c2a765c057a31c51b68d0f6c75cef93,
(m) email=w1jg
}
]
com.iengage.exceptions.IEException: Internal error in method IEngageManager.getSimpleExamActivityLiveResults, ARGUMENTS=[
Credentials {
(m) password=0c2a765c057a31c51b68d0f6c75cef93,
(m) email=w1jg
}
]
at com.iengage.exceptions.ExceptionWrapper.overridingInvoke(ExceptionWrapper.java:25)
at com.iengage.springobjects.SingletonMethodInterceptor.invoke(SingletonMethodInterceptor.java:18)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at com.iengage.managers.IEngageManager$$EnhancerByCGLIB$$c8a2c385.getSimpleExamActivityLiveResults(<generated>)
at com.iengage.services.IEngageService.getSimpleExamActivityLiveResults(IEngageService.java:379)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.sun.xml.ws.api.server.InstanceResolver$1.invoke(InstanceResolver.java:246)
at com.sun.xml.ws.server.InvokerTube$2.invoke(InvokerTube.java:146)
at com.sun.xml.ws.server.sei.EndpointMethodHandler.invoke(EndpointMethodHandler.java:257)
at com.sun.xml.ws.server.sei.SEIInvokerTube.processRequest(SEIInvokerTube.java:93)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:595)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:554)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:539)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:436)
at com.sun.xml.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:243)
at com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:444)
at com.sun.xml.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:244)
at com.sun.xml.ws.transport.http.servlet.ServletAdapter.handle(ServletAdapter.java:135)
at com.sun.xml.ws.transport.http.servlet.WSServletDelegate.doGet(WSServletDelegate.java:129)
at com.sun.xml.ws.transport.http.servlet.WSServletDelegate.doPost(WSServletDelegate.java:160)
at com.sun.xml.ws.transport.http.servlet.WSSpringServlet.doPost(WSSpringServlet.java:52)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:680)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:307)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:679)
调用DB的代码:
public LecturerBO getLecturerByCode(Credentials credentials) {
return findSingleResultByCriteria(LecturerBO.class,Restrictions.eq("loginCode",credentials.getPassword()));
}
,其中
protected <T> T findSingleResultByCriteria(Class<T> clazz, Criterion... criterion) {
Criteria crit = getSession().createCriteria(clazz);
for (Criterion c : criterion) {
crit.add(c);
}
return (T) crit.uniqueResult();
}
最后,Hibernate配置:
<bean id="generalManagerProxyTemplate"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="hibernateTransactionManager"/>
<property name="proxyTargetClass" value="true"/>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
<property name="preInterceptors">
<list>
<bean class="com.iengage.exceptions.ExceptionWrapper"/>
<bean class="com.iengage.utils.PerformanceLogger"/>
</list>
</property>
</bean>
<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="pooledDataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="transaction.auto_close_session">true</prop>
<prop key="transaction.flush_before_completion">true</prop>
<prop key="show_sql">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
<property name="annotatedClasses">
<bean class="com.iengage.dao.EntityList"/>
</property>
</bean>
<bean id="pooledDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
<property name="defaultAutoCommit" value="false"/>
<property name="testWhileIdle" value="true"/>
</bean>
<bean id="abstractDAO" class="com.iengage.dao.GeneralDAO" abstract="true">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="dataSource" ref="pooledDataSource"/>
</bean>
<bean id="dao" class="com.iengage.dao.IEngageDAO" parent="abstractDAO"/>
最后,作为一个完整性检查,我确实直接连接了读取副本,并通过执行我在第三方服务器上托管的SELECT查询的简单脚本。一切都按预期工作。
此外,在发布此问题之前的研究中,大多数答案都集中在@Transactional
注释上,我们没有使用它。悲伤的熊猫。
感谢您的帮助。
答案 0 :(得分:1)
要提供该类型的访问(仅读取副本),您必须实现此属性。
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly
</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly
</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly
</prop>
<prop key="store*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
</props>
</property>
在这里,我使用这两个来允许存储并添加到DB中。如果你不需要,你必须删除它。
<prop key="store*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>