Spring data redis override default serializer - spring-boot

I am trying to create a RedisTemplate bean which will have the updated value serializer to serialize an object in JSON format in redis.
#Configuration
class RedisConfig {
#Bean(name = ["redisTemplate"])
#Primary
fun template(factory: RedisConnectionFactory): RedisTemplate<Any, Any> {
val template = RedisTemplate<Any, Any>()
template.connectionFactory = factory
template.valueSerializer = Jackson2JsonRedisSerializer(Object::class.java)
template.afterPropertiesSet()
return template
}
}
As per my understanding, spring should use the JSON serializer to serialize the object returned by the methods marked with Cacheable annotation. Despite this configuration, spring seems to be using the default Java serializer as this exception confirms this fact.
java.io.NotSerializableException: en.prateekj.vds.dto.Task
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at java.util.ArrayList.writeObject(ArrayList.java:766)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1128)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:46)
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:63)
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:35)
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:94)
at org.springframework.data.redis.serializer.DefaultRedisElementWriter.write(DefaultRedisElementWriter.java:43)
at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.write(RedisSerializationContext.java:219)
at org.springframework.data.redis.cache.RedisCache.serializeCacheValue(RedisCache.java:238)
at org.springframework.data.redis.cache.RedisCache.put(RedisCache.java:144)
at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:87)
at org.springframework.cache.interceptor.CacheAspectSupport$CachePutRequest.apply(CacheAspectSupport.java:770)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:398)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:314)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
Am I missing any configuration or something by which spring is not able to determine what RedisTemplate to use?

You have probably solved it meanwhile, but for further answer seekers.
According to spring data redis reference:
By default, RedisCache and RedisTemplate are configured to use Java native serialization.
From stacktrace I can see that you are actually using Redis for caching, so you need to configure RedisCache and not RedisTemplate. RedisCache is not picking up your #Bean because it is not using RedisTemplate internally.
Example how you can do it in Java:
#EnableCaching
#Configuration
public class CacheConfig {
#Bean
#Primary
public RedisCacheConfiguration defaultCacheConfig(ObjectMapper objectMapper) {
return RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)));
}
}

Related

Spring boot 2 Prometheus not pulling db metrics

I am using spring boot2 with Prometheus. we are using Postgres as db. The Prometheus url is not fetching db metrics.
Any references would be very helpful.
I have tried
#Configuration
#AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MyConfiguration {
#Autowired
HikariDataSource dataSource;
#Bean
PostgreSQLDatabaseMetrics dbMeterics() {
return new PostgreSQLDatabaseMetrics(dataSource, "database-name");
}
}
When I hit the end point /prometheus, I get this error
java.lang.NullPointerException: null
at io.micrometer.core.instrument.binder.db.PostgreSQLDatabaseMetrics.lambda$bindTo$1(PostgreSQLDatabaseMetrics.java:101)
at io.micrometer.core.instrument.internal.DefaultGauge.value(DefaultGauge.java:40)
at io.micrometer.prometheus.PrometheusMeterRegistry.lambda$newGauge$3(PrometheusMeterRegistry.java:235)
at io.micrometer.prometheus.MicrometerCollector.collect(MicrometerCollector.java:69)
at io.prometheus.client.CollectorRegistry$MetricFamilySamplesEnumeration.findNextElement(CollectorRegistry.java:183)
at io.prometheus.client.CollectorRegistry$MetricFamilySamplesEnumeration.nextElement(CollectorRegistry.java:216)
at io.prometheus.client.CollectorRegistry$MetricFamilySamplesEnumeration.nextElement(CollectorRegistry.java:137)
at io.prometheus.client.exporter.common.TextFormat.write004(TextFormat.java:22)
at org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint.scrape(PrometheusScrapeEndpoint.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282)
at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:76)
In line 101 the registry received via DI is used to build the pgMetrics object. Since you are getting the null pointer at this line, you might be missing the import of Prometheus registry as a dependency.

Create a spring session in amqp rpc client

I developped spring remoting amqp rpc applications.
That's works well for methods that don't use bean with Scope SESSION.
For the other methods, the client can't use spring session, and I get this exception
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.userSession': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:362) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) ~[spring-aop-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) ~[spring-aop-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at io.kzreactive.akwtype.akwtypeback.common.service.UserSession$$EnhancerBySpringCGLIB$$55d53e95.setUser(<generated>) ~[classes/:na]
at io.kzreactive.akwtype.akwtypeback.engine.service.AppService.login(AppService.kt:30) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.remoting.support.RemoteInvocation.invoke(RemoteInvocation.java:215) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.remoting.support.DefaultRemoteInvocationExecutor.invoke(DefaultRemoteInvocationExecutor.java:39) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at io.kzreactive.akwtype.akwtypeback.gateway.rabbitmq.SessionDefaultRemoteInvocationExecutor.invoke(RabbitMQSession.kt:48) ~[classes/:na]
at org.springframework.remoting.support.RemoteInvocationBasedExporter.invoke(RemoteInvocationBasedExporter.java:78) [spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.remoting.support.RemoteInvocationBasedExporter.invokeAndCreateResult(RemoteInvocationBasedExporter.java:114) [spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.amqp.remoting.service.AmqpInvokerServiceExporter.onMessage(AmqpInvokerServiceExporter.java:80) [spring-amqp-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1457) [spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1348) [spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1324) [spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1303) [spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:785) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:769) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:77) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1010) ~[spring-rabbit-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at java.base/java.lang.Thread.run(Thread.java:844) ~[na:na]
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:55) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:350) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
... 24 common frames omitted
So I would create and use a spring session over rabbit mq
First, I managed to pass the sessionId in RPC call
on the server I add an attribute
class SessionDefaultRemoteInvocationFactory : DefaultRemoteInvocationFactory() {
override fun createRemoteInvocation(methodInvocation: MethodInvocation?): RemoteInvocation {
return super.createRemoteInvocation(methodInvocation)
.apply { addAttribute("sessionId", RequestContextHolder.currentRequestAttributes().sessionId) }
}
}
on the client I can read it
class SessionDefaultRemoteInvocationExecutor : DefaultRemoteInvocationExecutor() {
override fun invoke(invocation: RemoteInvocation?, targetObject: Any?): Any {
if (invocation is RemoteInvocation) {
invocation.getAttribute("sessionId")?.let {
val sessionId = it.toString()
SecurityContextHolder.getContext().authentication = AnonymousAuthenticationToken(sessionId,
"anonymousUser", listOf(SimpleGrantedAuthority("ROLE_ANONYMOUS")))
val attr = RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes // <= ERROR here
attr.request.getSession(true)
}
}
return super.invoke(invocation, targetObject)
}
}
but I don't manage to use it to create a spring session
How can I create a spring session in this NON http context
I tried to create a request context listener
#Configuration
#WebListener
class MyRequestContextListener : RequestContextListener()
but same error
For the moment I bypass the spring session and I inject a bean with the same behavior
#Component
#Scope("singleton")
class EngineThread(var engineSession: ThreadLocal<EngineSession> = ThreadLocal()) : IEngineSession by engineSession.getOrSet( { EngineSession() })
#Component
#Profile("engine & !test")
class SessionDefaultRemoteInvocationExecutor(val engineThread: EngineThread) : DefaultRemoteInvocationExecutor() {
val engineSessionStore : MutableMap<String, EngineSession> = mutableMapOf()
override fun invoke(invocation: RemoteInvocation?, targetObject: Any?): Any {
if (invocation is RemoteInvocation) {
invocation.getAttribute(SESSION_ID)?.let {
val sessionId = it.toString()
SecurityContextHolder.getContext().authentication = AnonymousAuthenticationToken(sessionId,
"anonymousUser", listOf(SimpleGrantedAuthority("ROLE_ANONYMOUS")))
if (! engineSessionStore.contains(sessionId)) {
engineSessionStore.put(sessionId, EngineSession())
}
engineThread.engineSession.set(engineSessionStore.getValue(sessionId))
}
}
return super.invoke(invocation, targetObject)
}
}
The job is done but I'm not very well with this solution
(I will add clean by softreference… but all this work is reinvent session instead of use spring session)

How do you use both Spring Data JPA and Spring Data Elasticsearch repositories on the same domain class in a Spring Boot application?

I'm trying to use both Spring Data JPA and Spring Data Elasticsearch on the same domain object but it doesn't work.
When I tried to run a simple test, I got the following exception:
org.springframework.data.mapping.PropertyReferenceException: No
property index found for type Person! at
org.springframework.data.mapping.PropertyPath.(PropertyPath.java:75)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:327)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:307)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:270)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:241)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.Part.(Part.java:76)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.PartTree$OrPart.(PartTree.java:235)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.PartTree$Predicate.buildTree(PartTree.java:373)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.PartTree$Predicate.(PartTree.java:353)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.query.parser.PartTree.(PartTree.java:84)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.query.PartTreeJpaQuery.(PartTreeJpaQuery.java:61)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:95)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:206)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:73)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.(RepositoryFactorySupport.java:408)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:206)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:251)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:237)
~[spring-data-commons-1.11.0.RELEASE.jar:na] at
org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92)
~[spring-data-jpa-1.9.0.RELEASE.jar:na] at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
~[spring-beans-4.2.1.RELEASE.jar:4.2.1.RELEASE] at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
~[spring-beans-4.2.1.RELEASE.jar:4.2.1.RELEASE] ... 43 common frames
omitted
They work when disabling either one.
The project is based on Spring Boot 1.3.0.M5.
This is a sample project reproducing the situation:
https://github.com/izeye/spring-boot-throwaway-branches/tree/data-jpa-and-elasticsearch
Repositories in Spring Data are datasource agnostic, meaning that JpaRepository and ElasticsearchRepository both roll up into Repository interface. When this is the case, then auto-configuration of Spring Boot will cause Spring Data JPA to try and configure a bean for each repository in the project that inherits any Spring Data Commons base repository.
To fix this problem you need to move your JPA repository and Elasticsearch repository to separate packages and make sure to annotate your #SpringBootApplication application class with:
#EnableJpaRepositories
#EnableElasticsearchRepositories
Then you need to specify where the repositories are for each enable annotation. This ends up looking like:
#SpringBootApplication
#EnableJpaRepositories("com.izeye.throwaway.data")
#EnableElasticsearchRepositories("com.izeye.throwaway.indexing")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Then your application will be able to disambiguate which repositories are intended for which Spring Data project.
You can use like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchCrudRepository.class))
#EnableElasticsearchRepositories(includeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchCrudRepository.class))
public class DataConfiguration {
...
}
Or in SpringBoot:
#SpringBootApplication
#EnableJpaRepositories(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchCrudRepository.class))
#EnableElasticsearchRepositories(includeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchCrudRepository.class))
public class MyApplication {
...
}

JpaRepository: No Transaction in progress

I'm using Spring Data Jpa along with hibernate. I have a repository that extends JpaRepository and I'm using it to save records to the database. But when I call .flush(), the following error occurs.
I've seen other questions with a similar error but after trying to use #Transaction in the repository as well, I get the same error.
Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1171)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1332)
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:606)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:342)
at com.sun.proxy.$Proxy64.flush(Unknown Source)
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:606)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:262)
at com.sun.proxy.$Proxy64.flush(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.flush(SimpleJpaRepository.java:397)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:365)
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:606)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:333)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:318)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 75 more
The Repository
#Repository
#Transactional
public interface AttributeRepository extends JpaRepository<Attribute, Long> {
Attribute findById(long id);
}
My config contains the entity manager factory and the platform transaction manager that JPA requires,
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(BoneCPDataSource ds) throws IOException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ds);
em.setPackagesToScan(new String[] { "org.scripps.branch.entity" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
public PlatformTransactionManager transactionManager(
EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
The Service where the saveandFlush() method is called,
#Service
public interface FeatureService {
#Transactional
public List<Feature> rankFeatures(Instances data, List<String> entrezIds, Dataset d);
}
Just to add, I'm injecting the repository using #Autowired into the implementation of the service.
I think you forgot to enable Jpa Transaction or to associate Spring Data with the transaction manager:
Ex enable transaction management:
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
Ex associate transaction manager with Jpa Repositories:
#EnableJpaRepositories(transactionManagerRef="transactionManager",
entityManagerFactoryRef="entityManagerFactory",
value="com.repositories.pkg.location" )
Annotating your repository with #Transactional won't help; you have to annotate the service method with #Transactional. "service method" meaning the method encapsulating both, the call into the save method of the repo and the flush call.
I solved it. My configuration files were divided into 2 classes one for the Application Context and one for Persistence Context. I combined both of them and the error vanished.

Jooq, Spring, And BoneCP connection closed twice error

I am using Spring 4.0.0, along with jOOQ 3.2.0 and BoneCP 0.8.0 for a web application.
I have the PersistenceContext configured the same as this guide (please skim read it's a little too much code to paste here)
http://www.petrikainulainen.net/programming/jooq/using-jooq-with-spring-configuration/
but with a smaller number of max connections and closeConnectionWatch = true for error checking.
From what I can deduce, this guide is a non-XML version of the jOOQ website's own guide seen here
http://www.jooq.org/doc/3.2/manual/getting-started/tutorials/jooq-with-spring/
My problem comes from probably not knowing how to use the jOOQ generated DAOs or the #Transactional annotation. I am coming across loads of "connection closed twice" exceptions, which makes my application fundamentally broken. The following stack trace doesn't actually say it is closed twice, but the output of closeConnectionWatch says something along the lines of
bonecp connection closed twice detected: first location connection was closed in thread[blah]
closed again in thread[blah2]
Stack trace of SQL Exception after the connection watch does its thing:
Jan 28, 2014 10:51:51 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/application] threw exception [Request processing failed; nested exception is org.springframework.jdbc.UncategorizedSQLException: jOOQ; uncategorized SQLException for SQL
<snip> error code [0]; Connection is closed!; nested exception is java.sql.SQLException: Connection is closed!] with root cause java.sql.SQLException: Connection is closed!
at com.jolbox.bonecp.ConnectionHandle.checkClosed(ConnectionHandle.java:459)
at com.jolbox.bonecp.ConnectionHandle.prepareStatement(ConnectionHandle.java:1011)
at sun.reflect.GeneratedMethodAccessor40.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy$LazyConnectionInvocationHandler.invoke(LazyConnectionDataSourceProxy.java:376)
at com.sun.proxy.$Proxy73.prepareStatement(Unknown Source)
at sun.reflect.GeneratedMethodAccessor40.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy$TransactionAwareInvocationHandler.invoke(TransactionAwareDataSourceProxy.java:240)
at com.sun.proxy.$Proxy73.prepareStatement(Unknown Source)
at org.jooq.impl.ProviderEnabledConnection.prepareStatement(ProviderEnabledConnection.java:112)
at org.jooq.impl.SettingsEnabledConnection.prepareStatement(SettingsEnabledConnection.java:76)
at org.jooq.impl.AbstractResultQuery.prepare(AbstractResultQuery.java:224)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:295)
at org.jooq.impl.AbstractResultQuery.fetch(AbstractResultQuery.java:324)
at org.jooq.impl.SelectImpl.fetch(SelectImpl.java:1034)
at org.jooq.impl.DAOImpl.fetch(DAOImpl.java:249)
----> at com.myapplication.spring.services.UserService.authenticate(UserService.java:32)
at com.myapplication.spring.controllers.LoginController.doLogin(LoginController.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:214)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:748)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:833)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at com.github.dandelion.datatables.core.web.filter.DatatablesFilter.doFilter(DatatablesFilter.java:73)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:931)
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:1004)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
The line where I have arrowed is the line in the service that makes the call to the database. I have #Autowired DAO objects in the service, as below
#Service("UserService")
public class UserService implements UserServiceInterface{
#Autowired UsersDao userDao;
#Autowired PasswordServiceInterface passwordService;
#Override
public Users authenticate(String user,String password) {
boolean allowedIn = false;
List<Users> users = userDao.fetch(USERS.USERNAME, user);
//do something here
Other functions I use in similar services contain calls using the DSLContext object like
DSL.select(SOMETHING).from(SOMETABLE).fetch()
The DAO and DSLContext are stored as beans in PersistenceContext like so.
I am autowiring as a DSLContext and not a *Default*DSLContext as the jOOQ guide has a test method down the bottom showing only a DSLContext.
#Bean
public DefaultDSLContext dsl() {
return new DefaultDSLContext(configuration());
}
#Bean
public UsersDao userDao() { //bad because UsersDao isn't an interface???
return new UsersDao(configuration());
}
And this is the controller
#Controller
public class LoginController {
#Autowired UserServiceInterface userService;
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
return "login";
}
#RequestMapping(value = "/login/doLogin", method = RequestMethod.POST)
public String doLogin(#RequestParam("username")String username, #RequestParam("password") String password, HttpSession session) {
Users u = userService.authenticate(username, password);
if(u == null)
return "redirect:/error";
else {
session.setAttribute("user", u.getUserid());
session.setAttribute("role", u.getRoleid());
session.setAttribute("retailgroup", u.getGroupid());
return "redirect:/dashboard";
}
}
UserService is not the only service I get errors in -- all of my services are similar in that they contain one/both a DAO and a DSLContext Autowired object, with the same DAO configuration constructor as usersDao(), for example productsDao(). The Products service has this DAO and a DSLContext object, and much the same as the UsersService it makes calls to the database.
Sometimes I'll get this connection issue logging in, other times it will be fine and I can browse the site and look at products for a little while, but then randomly I'll get a 'connection is closed!' error coming from another service (there are about 5 that are written in the same way).
So my questions are
Where do I use the #Transactional annotation, and what does it actually do. Does my omission of the #Transactional annotation mean I am causing myself problems? I have previously added it in at all locations that use the DB, but I cant be sure if it was actually helping as I was still getting the same errors.
Is it an issue with my scope for something? I know beans are default as singleton - I've written my controllers in such a way that they use session stored attributes to pass to the services (which are all left as default singletons), so that they may only select data that a certain user is allowed to see.
Since the connectionPool is closing a connection twice, does this mean that the issue is that thread A and thread B go for a connection at the same time, do something with it, and then both close? Why is this happening using the configuration from the above guide? How do I ensure thread safety or is that not the problem?
Are the DAO beans supposed to be interfaces, as from my brief history with Spring I am led to believe a lot(many/all?) #Autowired beans should be? Am I supposed to be using the interface org.jooq.DAOImpl which is the interface that all the jOOQ generated DAOs seem to implement?
#Bean
public org.jooq.impl.DAOImpl usersDao() {
return new usersDao(configuration());
}
Apologies for the long question, any help would be greatly appreciated. Thanks.
Edit: This is my configuration in PersistenceContext class
#Configuration
#PropertySource("classpath:config.properties")
public class PersistenceContext {
#Autowired
private Environment env;
#Bean(destroyMethod = "close")
public DataSource dataSource() {
BoneCPDataSource dataSource = new BoneCPDataSource();
dataSource.setDriverClass(env.getRequiredProperty("db.driver"));
dataSource.setJdbcUrl(env.getRequiredProperty("db.url"));
dataSource.setUsername(env.getRequiredProperty("db.username"));
dataSource.setPassword(env.getRequiredProperty("db.password"));
dataSource.setMaxConnectionsPerPartition(20);
dataSource.setPartitionCount(2);
dataSource.setCloseConnectionWatch(true);
return dataSource;
}
#Bean
public LazyConnectionDataSourceProxy lazyConnectionDataSource() {
return new LazyConnectionDataSourceProxy(dataSource());
}
#Bean
public TransactionAwareDataSourceProxy transactionAwareDataSource() {
return new TransactionAwareDataSourceProxy(lazyConnectionDataSource());
}
#Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(lazyConnectionDataSource());
}
#Bean
public DataSourceConnectionProvider connectionProvider() {
return new DataSourceConnectionProvider(transactionAwareDataSource());
}
#Bean
public JOOQToSpringExceptionTransformer jooqToSpringExceptionTransformer() {
return new JOOQToSpringExceptionTransformer();
}
#Bean
public DefaultConfiguration configuration() {
DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
jooqConfiguration.set(connectionProvider());
jooqConfiguration.set(new DefaultExecuteListenerProvider(
jooqToSpringExceptionTransformer()
));
String sqlDialectName = env.getRequiredProperty("jooq.sql.dialect");
SQLDialect dialect = SQLDialect.valueOf(sqlDialectName);
jooqConfiguration.set(dialect);
return jooqConfiguration;
}
#Bean
public DefaultDSLContext dsl() {
return new DefaultDSLContext(configuration());
}
#Bean
public UsersDao userDao() {
return new UsersDao(configuration());
}
}
After re-reading your question and your chat, I can tell that it is really most likely due to your using version 3.2.0, which had this rather severe bug here:
https://github.com/jOOQ/jOOQ/issues/2863
That bug was fixed in 3.2.2, to which (or later) you should upgrade.

Resources