Spring cache support requires ApplicationContext? - java

I'm running into an issue trying to use Spring caching with ehcache in my application. For reasons that I can't elaborate on, my application uses a graph of BeanFactories instead of ApplicationContexts. This approach has worked well as long as we manually register our BeanPostProcessors, as is called out in the Spring documentation.
We are now adding caching to the app. When we used the simplest annotation configuration, it works.
// This works
package com.x.y.z;
public class RoleManager {
private String user;
public RoleManager( String user ) {
this.user = user;
}
public String getName() {
return user;
}
#Cacheable("user")
public boolean isAllowed(String permissionId, Map<String,?> params)
{
... lengthy and expensive operation to determine if user is permitted to do something
}
}
We configure this to using spring xml for this bean factory:
<?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:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven/>
<bean id="roleManager" class="com.x.y.z.RoleManager" scope="prototype"/>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
</bean>
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="file:${conf.dir}/ehcache.xml"/>
<property name="shared" value="true"/>
</bean>
</beans>
... unrelated business beans elided ...
We are using Spring 4.1.9 and ehcache 2.10.2
The above code works quite well. Our ehcache instance for "user" begins to fill as we get cache misses, and returns cached values for hits.
Once this was running correctly, we found that it isn't possible to evict all the entries for a particular user because the cache key is a concatenation of the permissionid and the Map::toString result. We decided to create a cache per user so we would have more control over eviction. To use Spring, we need to use a CacheResolver to accomplish this.
package com.x.y.z;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.AbstractCacheResolver;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import java.util.Collection;
import java.util.Collections;
public class MyCacheResolver extends AbstractCacheResolver {
public MyCacheResolver() {
}
public MyCacheResolver(CacheManager cacheManager) {
super(cacheManager);
}
#Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> cacheOperationInvocationContext) {
if(cacheOperationInvocationContext.getTarget() instanceof RoleManager) {
return Collections.singleton(((RoleManager) cacheOperationInvocationContext.getTarget()).getName());
}
return Collections.singleton("user");
}
}
We wire this up by adding a new bean definition
<bean id="myCacheResolver" class="com.x.y.z.MyCacheResolver">
<constructor-arg index="0" ref="cacheManager"/>
</bean>
And change the annotation in RoleManager to
#Cacheable(cacheResolver="myCacheResolver")
Once we do this, however, we get the following exception when the isAllowed method is invoked:
java.lang.NullPointerException
at org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.qualifiedBeanOfType(BeanFactoryAnnotationUtils.java:57)
at org.springframework.cache.interceptor.CacheAspectSupport.getBean(CacheAspectSupport.java:282)
at org.springframework.cache.interceptor.CacheAspectSupport.getCacheOperationMetadata(CacheAspectSupport.java:254)
at org.springframework.cache.interceptor.CacheAspectSupport.getOperationContext(CacheAspectSupport.java:226)
at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContexts.<init>(CacheAspectSupport.java:500)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:299)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy61.isAllowed(Unknown Source)
at com.x.y.z.RoleManager.isAllowed(CompositeRoleManager.java:61)
When I look at the CacheAspectSupport class from the stack trace, I see that it has a member, applicationContext, which is null.
protected <T> T getBean(String beanName, Class<T> expectedType) {
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.applicationContext, expectedType, beanName);
}
This seems like a bug in Spring to me since we do not use ApplicationContexts, and yet caching works until we need to use a CacheResolver. I've looked over the documentation and I see no mention that one must use ApplicationContexts in order to use the Spring caching abstraction.
I guess my question is, has anyone experienced this problem, and if so, what did you do to resolve it? We absolutely cannot use ApplicationContexts in our application, and I'd rather not throw out a perfectly usable abstraction and code directly to the ehcache (or JSR-107) APIs.
Thanks in advance!

Spring 4.3 has fixed the problem by adding a setBeanFactory() method and using the BeanFactory thus set to call the CacheResolvers. Unfortunately I am unable to update our Spring library code to 4.3 at this time, but it will work when we are able to upgrade in the future.

Related

Delete by username spring boot [duplicate]

I get this error when trying to invoke "persist" method to save entity model to database in my Spring MVC web application. Can't really find any post or page in internet that can relate to this particular error. It seems like something's wrong with EntityManagerFactory bean but i'm fairly new to Spring programming so for me it seems like everything is initialized fine and according to various tutorial articles in web.
dispatcher-servlet.xml
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/data/repository
http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.2.xsd">
<context:component-scan base-package="wymysl.Controllers" />
<jpa:repositories base-package="wymysl.repositories"/>
<context:component-scan base-package="wymysl.beans" />
<context:component-scan base-package="wymysl.Validators" />
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.orm.hibernate4.HibernateExceptionTranslator"/>
<bean id="passwordValidator" class="wymysl.Validators.PasswordValidator"></bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe" />
<property name="username" value="system" />
<property name="password" value="polskabieda1" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:./META-INF/persistence.xml" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
</props>
</property>
</bean>
<mvc:annotation-driven />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
</bean>
<bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:resources mapping="/resources/*" location="/resources/css/"
cache-period="31556926"/>
</beans>
RegisterController.java
#Controller
public class RegisterController {
#PersistenceContext
EntityManager entityManager;
#Autowired
PasswordValidator passwordValidator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(passwordValidator);
}
#RequestMapping(value = "/addUser", method = RequestMethod.GET)
public String register(Person person) {
return "register";
}
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String register(#ModelAttribute("person") #Valid #Validated Person person, BindingResult result) {
if(result.hasErrors()) {
return "register";
} else {
entityManager.persist(person);
return "index";
}
}
I had the same problem and I annotated the method as #Transactional and it worked.
UPDATE: checking the spring documentation it looks like by default the PersistenceContext is of type Transaction, so that's why the method has to be transactional (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html):
The #PersistenceContext annotation has an optional attribute type,
which defaults to PersistenceContextType.TRANSACTION. This default is
what you need to receive a shared EntityManager proxy. The
alternative, PersistenceContextType.EXTENDED, is a completely
different affair: This results in a so-called extended EntityManager,
which is not thread-safe and hence must not be used in a concurrently
accessed component such as a Spring-managed singleton bean. Extended
EntityManagers are only supposed to be used in stateful components
that, for example, reside in a session, with the lifecycle of the
EntityManager not tied to a current transaction but rather being
completely up to the application.
I got this exception while attempting to use a deleteBy custom method in the spring data repository. The operation was attempted from a JUnit test class.
The exception does not occur upon using the #Transactional annotation at the JUnit class level.
This error had me foxed for three days, the situation I faced produced the same error. Following all the advice I could find, I played with the configuration but to no avail.
Eventually I found it, the difference, the Service I was executing was contained in a common jar, the issue turned out to be AspectJ not treating the Service instantiation the same. In effect the proxy was simply calling the underlying method without all the normal Spring magic being executed before the method call.
In the end the #Scope annotation placed on the service as per the example solved the issue:
#Service
#Scope(proxyMode = ScopedProxyMode.INTERFACES)
#Transactional
public class CoreServiceImpl implements CoreService {
#PersistenceContext
protected EntityManager entityManager;
#Override
public final <T extends AbstractEntity> int deleteAll(Class<T> clazz) {
CriteriaDelete<T> criteriaDelete = entityManager.getCriteriaBuilder().createCriteriaDelete(clazz);
criteriaDelete.from(clazz);
return entityManager.createQuery(criteriaDelete).executeUpdate();
}
}
The method I have posted is a delete method but the annotations affect all persistence methods in the same way.
I hope this post helps someone else who has struggled with the same issue when loading a service from a jar
boardRepo.deleteByBoardId(id);
Faced the same issue. GOT javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread
I resolved it by adding #Transactional annotation above the controller/service.
You need to add #Transactional to your methode
I had the same error because I switched from XML- to java-configuration.
The point was, I didn't migrate <tx:annotation-driven/> tag, as Stone Feng suggested.
So I just added #EnableTransactionManagement as suggested here
Setting Up Annotation Driven Transactions in Spring in #Configuration Class, and it works now
Adding the org.springframework.transaction.annotation.Transactional annotation at the class level for the test class fixed the issue for me.
I had the same problem and I added tx:annotation-driven in applicationContext.xml and it worked.
I had the same error when accessing an already transactional-annotated method from a non-transactional method within the same component:
Before:
#Component
public class MarketObserver {
#PersistenceContext(unitName = "maindb")
private EntityManager em;
#Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
public void executeQuery() {
em.persist(....);
}
#Async
public void startObserving() {
executeQuery(); //<-- Wrong
}
}
//In another bean:
marketObserver.startObserving();
I fixed the error by calling the executeQuery() on the self-referenced component:
Fixed version:
#Component
public class MarketObserver {
#PersistenceContext(unitName = "maindb")
private EntityManager em;
#Autowired
private GenericApplicationContext context;
#Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
public void executeQuery() {
em.persist(....);
}
#Async
public void startObserving() {
context.getBean(MarketObserver.class).executeQuery(); //<-- Works
}
}
Just a note for other users searching for answers for thie error. Another common issue is:
You generally cannot call an #transactional method from within the same class.
(There are ways and means using AspectJ but refactoring will be way easier)
So you'll need a calling class and class that holds the #transactional methods.
If you have
#Transactional // Spring Transactional
class MyDao extends Dao {
}
and super-class
class Dao {
public void save(Entity entity) { getEntityManager().merge(entity); }
}
and you call
#Autowired MyDao myDao;
myDao.save(entity);
you won't get a Spring TransactionInterceptor (that gives you a transaction).
This is what you need to do:
#Transactional
class MyDao extends Dao {
public void save(Entity entity) { super.save(entity); }
}
Unbelievable but true.
Without #Transactional annotation you can achieve the same goal with finding the entity from the DB and then removing that entity you got from the DB.
CrudRepositor -> void delete(T var1);
For us, the problem came down to same context settings in multiple configuration files. Check you've not duplicated the following in multiple config files.
<context:property-placeholder location="classpath*:/module.properties"/>
<context:component-scan base-package="...." />
I had the same error code when I used #Transaction on a wrong method/actionlevel.
methodWithANumberOfDatabaseActions() {
methodA( ...)
methodA( ...)
}
#Transactional
void methodA( ...) {
... ERROR message
}
I had to place the #Transactional just above the method methodWithANumberOfDatabaseActions(), of course.
That solved the error message in my case.
I removed the mode from
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
to make this work
I already had the #Transactional but still wasn't working. Turns out I had to get rid of parallelism to make it work.
If you are doing things in parallel, DON'T.
I had this issue for days and nothing I found anywhere online helped me, I'm posting my answer here in case it helps anyone else.
In my case, I was working on a microservice being called through remoting, and my #Transactional annotation at the service level was not being picked up by the remote proxy.
Adding a delegate class between the service and dao layers and marking the delegate method as transactional fixed this for me.
This helped us, maybe it can help others in the future. #Transaction was not working for us, but this did:
#ConditionalOnMissingClass("org.springframework.orm.jpa.JpaTransactionManager")
I got the same error when I executed the Spring JPA deleteAll() method from Junit test cases. I simply used the deleteInBatch() & deleteAllInBatch() and its perfectly works. We do not need to mark #Transactional at the test cases level.
For anyone with the same issue as I had, I was calling a public method method1 from within another class.
method1 then called another public method method2 within the same class.
method2 was annotated with #Transactional, but method1 was not.
All that method1 did was transform some arguments and directly call method2, so no DB operations here.
The issue got solved for me once I moved the #Transactional annotation to method1.
Not sure the reason for this, but this did it for me.
Calling the repository method was being called within a class with #Component, taking that method out of that class and placing it inside another with #Service worked.
It's like you are using the shared EntityManager when you are getting it Autowired so for persisting spring tells that this EntityManager bean is a shared bean and for persisting it needs a hold of this bean till the data persist doesn't get completed so for that we have to use #Transactional so that it gonna start and commit the persistence in a transaction so the data or operation gets completely saved or get rollback completely.
To fix this in a test, you can use #DataJpaTest or #AutoConfigureTestDatabase.

Using a spring bean with #Transactional is giving TransactionRequiredException: No EntityManager with actual transaction available for current thread [duplicate]

I get this error when trying to invoke "persist" method to save entity model to database in my Spring MVC web application. Can't really find any post or page in internet that can relate to this particular error. It seems like something's wrong with EntityManagerFactory bean but i'm fairly new to Spring programming so for me it seems like everything is initialized fine and according to various tutorial articles in web.
dispatcher-servlet.xml
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/data/repository
http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.2.xsd">
<context:component-scan base-package="wymysl.Controllers" />
<jpa:repositories base-package="wymysl.repositories"/>
<context:component-scan base-package="wymysl.beans" />
<context:component-scan base-package="wymysl.Validators" />
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.orm.hibernate4.HibernateExceptionTranslator"/>
<bean id="passwordValidator" class="wymysl.Validators.PasswordValidator"></bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe" />
<property name="username" value="system" />
<property name="password" value="polskabieda1" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:./META-INF/persistence.xml" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
</props>
</property>
</bean>
<mvc:annotation-driven />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
</bean>
<bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:resources mapping="/resources/*" location="/resources/css/"
cache-period="31556926"/>
</beans>
RegisterController.java
#Controller
public class RegisterController {
#PersistenceContext
EntityManager entityManager;
#Autowired
PasswordValidator passwordValidator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(passwordValidator);
}
#RequestMapping(value = "/addUser", method = RequestMethod.GET)
public String register(Person person) {
return "register";
}
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String register(#ModelAttribute("person") #Valid #Validated Person person, BindingResult result) {
if(result.hasErrors()) {
return "register";
} else {
entityManager.persist(person);
return "index";
}
}
I had the same problem and I annotated the method as #Transactional and it worked.
UPDATE: checking the spring documentation it looks like by default the PersistenceContext is of type Transaction, so that's why the method has to be transactional (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html):
The #PersistenceContext annotation has an optional attribute type,
which defaults to PersistenceContextType.TRANSACTION. This default is
what you need to receive a shared EntityManager proxy. The
alternative, PersistenceContextType.EXTENDED, is a completely
different affair: This results in a so-called extended EntityManager,
which is not thread-safe and hence must not be used in a concurrently
accessed component such as a Spring-managed singleton bean. Extended
EntityManagers are only supposed to be used in stateful components
that, for example, reside in a session, with the lifecycle of the
EntityManager not tied to a current transaction but rather being
completely up to the application.
I got this exception while attempting to use a deleteBy custom method in the spring data repository. The operation was attempted from a JUnit test class.
The exception does not occur upon using the #Transactional annotation at the JUnit class level.
This error had me foxed for three days, the situation I faced produced the same error. Following all the advice I could find, I played with the configuration but to no avail.
Eventually I found it, the difference, the Service I was executing was contained in a common jar, the issue turned out to be AspectJ not treating the Service instantiation the same. In effect the proxy was simply calling the underlying method without all the normal Spring magic being executed before the method call.
In the end the #Scope annotation placed on the service as per the example solved the issue:
#Service
#Scope(proxyMode = ScopedProxyMode.INTERFACES)
#Transactional
public class CoreServiceImpl implements CoreService {
#PersistenceContext
protected EntityManager entityManager;
#Override
public final <T extends AbstractEntity> int deleteAll(Class<T> clazz) {
CriteriaDelete<T> criteriaDelete = entityManager.getCriteriaBuilder().createCriteriaDelete(clazz);
criteriaDelete.from(clazz);
return entityManager.createQuery(criteriaDelete).executeUpdate();
}
}
The method I have posted is a delete method but the annotations affect all persistence methods in the same way.
I hope this post helps someone else who has struggled with the same issue when loading a service from a jar
boardRepo.deleteByBoardId(id);
Faced the same issue. GOT javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread
I resolved it by adding #Transactional annotation above the controller/service.
You need to add #Transactional to your methode
I had the same error because I switched from XML- to java-configuration.
The point was, I didn't migrate <tx:annotation-driven/> tag, as Stone Feng suggested.
So I just added #EnableTransactionManagement as suggested here
Setting Up Annotation Driven Transactions in Spring in #Configuration Class, and it works now
Adding the org.springframework.transaction.annotation.Transactional annotation at the class level for the test class fixed the issue for me.
I had the same problem and I added tx:annotation-driven in applicationContext.xml and it worked.
I had the same error when accessing an already transactional-annotated method from a non-transactional method within the same component:
Before:
#Component
public class MarketObserver {
#PersistenceContext(unitName = "maindb")
private EntityManager em;
#Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
public void executeQuery() {
em.persist(....);
}
#Async
public void startObserving() {
executeQuery(); //<-- Wrong
}
}
//In another bean:
marketObserver.startObserving();
I fixed the error by calling the executeQuery() on the self-referenced component:
Fixed version:
#Component
public class MarketObserver {
#PersistenceContext(unitName = "maindb")
private EntityManager em;
#Autowired
private GenericApplicationContext context;
#Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
public void executeQuery() {
em.persist(....);
}
#Async
public void startObserving() {
context.getBean(MarketObserver.class).executeQuery(); //<-- Works
}
}
Just a note for other users searching for answers for thie error. Another common issue is:
You generally cannot call an #transactional method from within the same class.
(There are ways and means using AspectJ but refactoring will be way easier)
So you'll need a calling class and class that holds the #transactional methods.
If you have
#Transactional // Spring Transactional
class MyDao extends Dao {
}
and super-class
class Dao {
public void save(Entity entity) { getEntityManager().merge(entity); }
}
and you call
#Autowired MyDao myDao;
myDao.save(entity);
you won't get a Spring TransactionInterceptor (that gives you a transaction).
This is what you need to do:
#Transactional
class MyDao extends Dao {
public void save(Entity entity) { super.save(entity); }
}
Unbelievable but true.
Without #Transactional annotation you can achieve the same goal with finding the entity from the DB and then removing that entity you got from the DB.
CrudRepositor -> void delete(T var1);
For us, the problem came down to same context settings in multiple configuration files. Check you've not duplicated the following in multiple config files.
<context:property-placeholder location="classpath*:/module.properties"/>
<context:component-scan base-package="...." />
I had the same error code when I used #Transaction on a wrong method/actionlevel.
methodWithANumberOfDatabaseActions() {
methodA( ...)
methodA( ...)
}
#Transactional
void methodA( ...) {
... ERROR message
}
I had to place the #Transactional just above the method methodWithANumberOfDatabaseActions(), of course.
That solved the error message in my case.
I removed the mode from
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
to make this work
I already had the #Transactional but still wasn't working. Turns out I had to get rid of parallelism to make it work.
If you are doing things in parallel, DON'T.
I had this issue for days and nothing I found anywhere online helped me, I'm posting my answer here in case it helps anyone else.
In my case, I was working on a microservice being called through remoting, and my #Transactional annotation at the service level was not being picked up by the remote proxy.
Adding a delegate class between the service and dao layers and marking the delegate method as transactional fixed this for me.
This helped us, maybe it can help others in the future. #Transaction was not working for us, but this did:
#ConditionalOnMissingClass("org.springframework.orm.jpa.JpaTransactionManager")
I got the same error when I executed the Spring JPA deleteAll() method from Junit test cases. I simply used the deleteInBatch() & deleteAllInBatch() and its perfectly works. We do not need to mark #Transactional at the test cases level.
For anyone with the same issue as I had, I was calling a public method method1 from within another class.
method1 then called another public method method2 within the same class.
method2 was annotated with #Transactional, but method1 was not.
All that method1 did was transform some arguments and directly call method2, so no DB operations here.
The issue got solved for me once I moved the #Transactional annotation to method1.
Not sure the reason for this, but this did it for me.
Calling the repository method was being called within a class with #Component, taking that method out of that class and placing it inside another with #Service worked.
It's like you are using the shared EntityManager when you are getting it Autowired so for persisting spring tells that this EntityManager bean is a shared bean and for persisting it needs a hold of this bean till the data persist doesn't get completed so for that we have to use #Transactional so that it gonna start and commit the persistence in a transaction so the data or operation gets completely saved or get rollback completely.
To fix this in a test, you can use #DataJpaTest or #AutoConfigureTestDatabase.

EntityManager.merge() is not being committed (Wildfly, JPA, JTA)

I can persist new data, but I cannot do updates. There are no errors, just no transactions committing the changes. I'm assuming this has something to do with the way that I've set up transactions. I'm trying a bunch of relatively new (to me) set of technologies. Below are the details.
I'm using the following tools/technologies:
Wildfly 8 and Java 7 (which is what my hosting service uses)
Annotations, with minimal XML being the goal
Struts 2.3 (using the convention plugin)
Spring 3.2
Hibernate 4.3
JTA (with container managed transactions (CMT))
JPA 2 (with a Container Managed Persistence Context)
EJBs (I have a remote client app that runs htmlunit tests)
Three WAR files and one EJB JAR file deployed
SpringBeanAutowiringInterceptor to autowire the EJBs (could there be an error in here where transactions don't commit?)
beanRefContext.xml (required by SpringBeanAutowiringInterceptor)
<beans>
<bean
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg value="classpath:campaignerContext.xml" />
</bean>
</beans>
campaignerContext.xml
<beans>
<context:component-scan base-package="..." />
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/CampaignerDS"/>
<tx:annotation-driven/>
<tx:jta-transaction-manager/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="campaigner" />
</bean>
<bean id="ehCacheManager" class="net.sf.ehcache.CacheManager" factory-method="create">
<constructor-arg type="java.net.URL" value="classpath:/campaigner_ehcache.xml"/>
</bean>
</beans>
persistence.xml
<persistence>
<persistence-unit name="campaigner" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:/jdbc/CampaignerDS</jta-data-source>
<class>....UserRegistration</class>
...
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
</properties>
</persistence-unit>
</persistence>
SecurityServiceBean.java
#EnableTransactionManagement
#TransactionManagement(value = TransactionManagementType.CONTAINER)
#TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW)
#Stateless
#Interceptors(SpringBeanAutowiringInterceptor.class)
#DeclareRoles("Security Admin")
public class SecurityServiceBean extends AbstractCampaignerServiceImpl implements
SecurityServiceLocal, SecurityServiceRemote
{
#Override
#PermitAll
#Transactional(propagation = Propagation.REQUIRES_NEW)
public UserRegistration confirmRegistration(
String confirmationCode) throws ApplicationException
{
UserRegistration userRegistration = this.userRegistrationDAO
.find(new UserRegistrationQuery(null, confirmationCode)).uniqueResult(); // Should be attached now
...
userRegistration.setConfirmationDate(new Date());
userRegistration.setState(State.CONFIRMED);
userRegistration = this.userRegistrationDAO.saveOrUpdate(userRegistration);
...
}
}
UserRegistrationDAO.java
#Override
public UserRegistration saveOrUpdate(
UserRegistration obj) throws DAOException
{
log.debug("[saveOrUpdate] isJoinedToTransaction? "
+ (this.em.isJoinedToTransaction() ? "Y " : "N"));
try
{
if (obj.getId() == null)
{
this.em.persist(obj);
log.debug("[saveOrUpdate] called persist()");
return obj;
}
else
{
UserRegistration attached = this.em.merge(obj);
log.debug("[saveOrUpdate] called merge()");
return attached;
}
}
catch (PersistenceException e)
{
throw new DAOException("[saveOrUpdate] obj=" + obj.toString() + ",msg=" + e.getMessage(), e);
}
}
Are there any settings in Wildfly's standalone.xml that you need to see or that I should be setting?
BTW, this is incredibly annoying and frustrating. This should be an easy one-time setup that I can do and then forget about as I move on to creating my website, which should be where most of my time is spent. The lack of comprehensive documentation anywhere is AMAZING. Right now, development has been halted until this is solved
/rant
UPDATES
I tried switching to an XA data source, because some sites claimed that was necessary, but that didn't work (didn't think so but had to try). Also tried configuring emf with dataSource instead of persistenceUnitName as some other sites have. No joy.
I tried replacing the transactionManager with JpaTransactionManager, but that just led to this exception: A JTA EntityManager cannot use getTransaction()
The answer, thanks to M. Deinum, is that I was using the wrong #Transactional. I should have been using javax.transaction.Transactional but was using the Spring one instead. Note that the correct one will look like "#Transactional(TxType.REQUIRES_NEW)" instead of "#Transactional(propagation = Propagation.REQUIRES_NEW)"

How mock spring component during tests without extracting interface from it?

There is a spring componet with #Component annotation, it is just java class (not interface) with annotated #Autowired fields. I am trying to create mock like that:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"
>
<bean id="myComponent" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.MyComponent"/>
</bean>
</beans>
And got an exception that some of fields are not autowired. This happens, because when Spring see <constructor-arg value="com.MyComponent"/> it try to instantiate MyComponent bean and pass it to factory method.
I have tried to extract interface from component, in that case mocking works, but is there a way to make it working without extracting interface?
Also
I have tried adding type="java.lang.Class" but got same errors.
<bean id="myComponent" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg type="java.lang.Class" value="com.MyComponent"/>
</bean>
Any ideas?
Spring cannot autowire the bean because the type of the created bean is java.lang.Object and not com.myComponent. Apparently the order of the definitions in the XML matters as well.
Jayway has a very nice blog post about this: Spring Integration Tests - Creating Mock Objects
You can create a FactoryBean which returns the correct class:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private Class<T> classToBeMocked;
/**
* Creates a Mockito mock instance of the provided class.
* #param classToBeMocked The class to be mocked.
*/
public MockitoFactoryBean(Class<T> classToBeMocked) {
this.classToBeMocked = classToBeMocked;
}
#Override
public T getObject() throws Exception {
return Mockito.mock(classToBeMocked);
}
#Override
public Class<?> getObjectType() {
return classToBeMocked;
}
#Override
public boolean isSingleton() {
return true;
}
}
Using this factory bean, you can create a config like this:
<bean id="myComponent" class="com.yourpackage.MockitoFactoryBean">
<constructor-arg name="classToBeMocked" value="com.myComponent" />
</bean>
This will mock your component and the object type will com.myComponent.
Update
This is the original answer and explanation (using EasyMock): Autowiring of beans generated by EasyMock factory-method?

Can instantiate hibernate session Factory directly but cannot do it through spring

I have started learning hibernate and spring through an assignment in which i am trying to use session factory instance through spring. I understood the hibernate part but just cant go on with spring. I have tried numerous tutorials and examples but just cant get my spring working. Although it works when i instantiate it directly.
Here are the problem related details of my project...
applicationContext.xml (inside WEB-INF)
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<context:annotation-config />
<context:component-scan
base-package="com.nagarro.training.assignment6.dao.entity.Student" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:annotation-driven />
<bean id="studentDAO"
class="com.nagarro.training.assignment6.dao.impl.StudentDAOImplementation">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
Edit: including changes as suggested
StudentDAOImplementaion.java (the file where it is being used )
public class StudentDAOImplementation implements StudentDAO {
/**
* single instance of hibernate session
*/
#Autowired
private SessionFactory sessionFactory;
// HibernateUtil.getSessionFactory().openSession()
/**
* #param sessionFactory
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* #return the sessionFactory
*/
public SessionFactory getSessionFactory() {
return sessionFactory;
}
private Session getSession() {
return this.sessionFactory.getCurrentSession();
}
/**
* #return list of all the students
*/
public List<Student> getStudentList() {
System.out.println(this.getSession().getStatistics());
return (List<Student>) this.getSEssion.createQuery("from Student")
.list();
}
}
And here is the snip of my jar files in lib folder:
I think that i dont need to include hibernate files and beans as it is working fine without spring. Its the spring i cant get to work.I have tried many different implementation from the web but i just cannot get it working. It just says null pointer exception on ** System.out.println(sessionFactory.getStatistics());** line in StudentDAOImplementation.
Test class that calls StudentDAO
public class Test {
public static void main(String[] args) {
StudentDAOImplementation sd = new StudentDAOImplementation();
List<Student> list = sd.getStudentList();
for(Student s : list) {
System.out.println(s.getName());
}
}
}
Stack trace
Exception in thread "main" java.lang.NullPointerException
at StudentDAOImplementation.getStudentList(StudentDAOImplementation.java:116)
at Test.main(Test.java:13)
Your sessionFactory variable is misleadingly named, since the type is actually Session. Session != SessionFactory. You're getting a NPE on sessionFactory.getStatistics() because there's no way that Spring can autowire a Session into a DAO like that. If you're not seeing an error before the NPE, then you're not actually instantiating the DAO with Spring, or else you'd get an error about not being able to find a dependency of type Session. The appropriate way to use a Hibernate-based DAO is to inject it with a SessionFactory and call getCurrentSession() in your methods where you need a Session. See "Implementing DAOs based on plain Hibernate 3 API" and following for details about this approach and about setting up appropriate transaction management.
Update: On a second glance, I see that the package for your component-scan is set to com.nagarro.training.assignment6.dao.entity.Student, which looks exactly like a class, not a package. It's also not even close to anything you'd actually want to component-scan. Maybe you don't understand what component-scan is for. It's covered under "Annotation-based container configuration" in the reference guide.
Update 2: About your "test" code: you're not using Spring at all, so you might as well remove the XML and save yourself the trouble. On the other hand, if you'd like to actually use Spring, you'd need to create a context in your main method based on said XML file, such as:
ApplicationContext context = new FileSystemXmlApplicationContext(locationOfXmlFile);
Then if you want a Spring-managed DAO, you can't just create one with new. Spring isn't magic. It doesn't grab control away from you just because you have it loaded somewhere in the same JVM.* You have to ask Spring for the DAO that it created, like:
StudentDAO dao = context.getBean(StudentDAO.class);
Note that I used the interface type, not the concrete type. That's always an advisable practice for numerous reasons.
This (not starting Spring) is your first problem. As soon as you do this, you're going to run into other problems with your configuration. You should post a new question if you need help solving one of them.
*Unless you're using AspectJ weaving to inject arbitrary objects.
You are injecting a Session instead of SessionFactory with #Autowired private Session sessionFactory; in the DAO class. It needs to be a SessionFactory , like this
#Autowired SessionFactory sessionFactory;
And then use it like this to do a DAO operations like save
Session session = sessionFactory.getCurrentSession()
session.persist(entity);
EDIT
Your testcase should be something like this
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:<path_to_your_appcontext.xml>" })
public class StudentDAOTest {
#Autowired
private StudentDAO studentDAO
#Test
public void test() {
List<Student> list = studentDAO.getStudentList();
assertNotNull(list)
}
}

Categories