#Transactional not starting transactions with Spring Boot 3 / Hibernate 6 - spring

I am currently migrating to Spring Boot 3 / Hibernate 6.
Hibernate is correctly parsing all the entities and repos, connecting to the database, etc...
However, it seems #Transactional is not starting transactions correctly.
Small example:
#Component
public class Test {
#Autowired
private EntityManagerFactory entityManager;
#Transactional
public void test() {
Session s = entityManager.unwrap(SessionFactory.class).getCurrentSession();
s.createQuery("FROM sometable").list();
}
}
Error:
Caused by: org.hibernate.HibernateException: Calling method 'createQuery' is not valid without an active transaction (Current status: NOT_ACTIVE)
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:341)
Relevant Config:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="com.somepackage")
#EntityScan(basePackages="com.somepackage")
public class TransactionConfig {
...
}
session context class in application.properties
...
spring.jpa.properties.hibernate.current_session_context_class=thread
...
If I remove the above setting of session_content_class=thread,
I get this error:
Caused by: org.hibernate.HibernateException: No CurrentSessionContext configured
Edit 1:
The below still results in the same error "is not valid without an active transaction"
#PersistenceUnit
private EntityManagerFactory entityManager;
Edit 2:
If I do not unwrap a session and just call a class with extends extends JpaRepository, it works... but it creates a new transaction and ignores the parent #Transaction

Fix was the following:
#PersistenceContext
private EntityManager entityManager;
and to unwrap:
Session s = entityManager.unwrap(Session.class);

Related

Transactional test in Spring Boot - how to acquire current session?

Spring documentation warns about False Positives in Transactional tests and suggest the following:
// ...
#Autowired
SessionFactory sessionFactory;
#Transactional
#Test // no expected exception!
public void falsePositive() {
updateEntityInHibernateSession();
// False positive: an exception will be thrown once the Hibernate
// Session is finally flushed (i.e., in production code)
}
#Transactional
#Test(expected = ...)
public void updateWithSessionFlush() {
updateEntityInHibernateSession();
// Manual flush is required to avoid false positive in test
sessionFactory.getCurrentSession().flush();
}
// ...
I have the following base class:
#SpringBootTest
#Transactional
#AutoConfigureMockMvc
public abstract class BaseSpringBootTest {
and a class that extends it where I want to apply this practice of injecting the sessionFactory:
public class EmployeeRepositoryTest extends BaseSpringBootTest {
#Autowired
SessionFactory sessionFactory
but I am getting:
NoSuchBeanDefinitionException: No qualifying bean of type 'org.hibernate.SessionFactory' available
I also tried injecting
#Autowired
EntityManagerFactory entityManagerFactory;
and then calling:
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
sessionFactory.getCurrentSession();
but this throws the following Exception:
org.hibernate.HibernateException: No CurrentSessionContext configured!
How do I get a reference to currentSession in a test, so that I can finally call:
sessionFactory.getCurrentSession().flush();
as documented in Spring Boot documentation?

Serialization exception using spring-sessions

Primefaces/Joinfaces JSF app in Spring-Boot.
App is working fine running stand-alone, but I have recently started implementing session replication via Spring-Session. When the session is persisted to the session store, I get a not serializable exception.
Caused by: java.io.NotSerializableException:
com.company.application.service.dao.security.RoleBasedSecurityDao$$EnhancerBySpringCGLIB$$9de506c
Looking at that error message, it looks like the serialization exception is not for the class itself, but for something owned by the class. The only thing it has on it is the JDBCTemplate.
#Repository
public class RoleBasedSecurityDao {
private final static Logger log = LoggerFactory.getLogger(RoleBasedSecurityDao.class);
private NamedParameterJdbcTemplate jdbcTemplate;
#Autowired
#Qualifier("dataSource")
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
[...]
}
If I add "implements Serializable" to the class definition, the error changes:
Caused by: java.io.NotSerializableException:
org.springframework.dao.support.PersistenceExceptionTranslationInterceptor
I am not familiar with JSF, but from what I have read, the expectation is that all of your JSF classes are serializable. How can I make my DAO serializable, when it needs an instance of JdbcTemplate?
As #Selaron pointed out, the issue was non-transient spring beans on JSF controllers. Don't do that.

Spring Data JPA Transaction - No Transaction in progress - Spring Data Neo4j

I think i'm missing something obvious. Iam trying to make a entity persist into a database via a JUnit Test case, however it doesnt seem to be persisting due to no active transaction.
Configuration:
#Configuration
#EnableTransactionManagement
public class TransactionConfig {
#Inject
private EntityManagerFactory entityMangerFactory;
#Bean
public JpaTransactionManager transactionManager(){
return new JpaTransactionManager(entityMangerFactory);
}
TestCase:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = { Application.class })
#ActiveProfiles(CommonConstants.SPRING_PROFILE_TEST)
#IntegrationTest
#WebAppConfiguration
public class UserRepositoryTest {
#Inject
UserRepository userRepo;
#Test
#Rollback(false)
#Transactional("transactionManager")
public void addUser() {
User user = BootstrapDataPopulator.getUser();
userRepo.save(user);
System.out.println(user.getId()); //Successfully outputs the id generate by hibernate
assertNotNull(user.getId());
}
}
^This test case executed successfully however I do not see any entiites persisted in the database as expected.
When I change the from userRepo.save(user) to userRepo.saveAndFlush(user) I get the following exception:
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)
Spring Boot AutoConfiguration Report: http://dumptext.com/YcGaR3Wf
Names of all Spring Beans Initialized: http://dumptext.com/jp9O6l8v
I am using Spring Data Neo4j (SDN) in my application as well. SDN comes with a default class Neo4jConfiguration which has:
#Bean(name = {"neo4jTransactionManager","transactionManager"})
#Qualifier("neo4jTransactionManager")
public PlatformTransactionManager neo4jTransactionManager() throws Exception {
return new JtaTransactionManagerFactoryBean(getGraphDatabaseService()).getObject();
}
The "transactionManager" overrides the bean defined in my TransactionConfig class. Hence the reason no Entity transaction was in progress. I stopped using the SDN class Neo4jConfiguration. This resolved my issue.

Custom platform transaction manager in spring

I'm trying to implement custom transactional cache in a spring boot application. I've created my own implementation of AbstractPlatformTransactionManager and some unit tests, which show transactions are working as expected. However the real application ignores my transaction manager - it`s methods are never called. What I'm doing wrong? Thank you.
Transaction manager implementation:
#Component
public class CacheTransactionManager extends AbstractPlatformTransactionManager{
#Override
protected Object doGetTransaction() throws TransactionException {
...
}
...
}
Cache transaction configuration:
#Configuration
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
public class CacheTransactionConfiguration {
#Bean(name = "cacheTransactionManager")
public PlatformTransactionManager cacheTransactionManager() {
return new CacheTransactionManager();
}
}
Custom transactional annotation (I've tried also without this, but no difference):
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Transactional(value = "cacheTransactionManager", rollbackFor = Exception.class)
public #interface CacheTransactional {
}
Cache service:
#Component
public class CacheService {
#CacheTransactional
public void add(Object o){
...
}
}
Working JUnit test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestApplication.class)
#Configuration
#EntityScan(...)
#IntegrationTest
#TransactionConfiguration(defaultRollback = false)
public class CacheTransactionManagerTest {
#Autowired
private CacheService cacheService;
#Test
#CacheTransactional
public void transactionTest(){
cacheService.add(new Object());
}
}
Not working wicket application main class (ignores cacheTransactionManager):
#Configuration("MyApplication")
#EnableAutoConfiguration
#EntityScan(...)
#EnableJpaRepositories(...)
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#ComponentScan(...)
#ImportResource({...})
public class MyApplication extends AuthenticatedWebApplication {
...
}
My env: Java 8, Spring Boot 1.2.1, Spring 4.1.4, Spring data JPA 1.7.2, Hibernate 4.3.7, Apache Tomcat 8.0.15, Wicket 6.17.0
I found out some new facts:
when I remove AdviceMode.ASPECTJ from #EnableTransactionManagement on CacheTransactionConfiguration, transactions begin to work, but propagation of transaction is ignored - nested call from one #CacheTransactional method to another #CacheTransactional methods always creates new transaction. Same behavior in JUnit test and real application.
when AdviceMode.ASPECTJ is on CacheTransactionConfiguration setted, but I remove #CacheTransactional annotation from junit test, transaction stops working also in juint (in test body is a #CacheTransaction method called, so there should be a transaction created).
application log contains this entry:
o.s.c.a.ConfigurationClassBeanDefinitionReader isOverriddenByExistingDefinition:290 - Skipping bean definition for [BeanMethod:name=cacheTransactionManager,declaringClass=x.y.z.CacheTransactionConfiguration]: a definition for bean 'cacheTransactionManager' already exists. This top-level bean definition is considered as an override.
So I can get this working, but without propagation...
For propagation, you need to tell Spring's #Transactional what propagation mode to apply. You can define several tx annotations, each inherit from #Transactional, but with a different propagation mode.

No Session Hibernate in #PostConstruct

MyDao class have the methods to do whole persistence tasks through Hibernate SessionFactory, it works fine.
I inject MyDao in MyService as can see above, but when #PostConstruct init() method is called after injected MyDao (debugging I can see MyDao well injected) get the next Hibernate exception:
org.hibernate.HibernateException: No Session found for current thread
My service implementation.
#Service("myService")
#Transactional(readOnly = true)
public class MyServiceImpl implements MyService {
#Autowired
private MyDao myDao;
private CacheList cacheList;
#PostConstruct
public void init() {
this.cacheList = new CacheList();
this.cacheList.reloadCache(this.myDao.getAllFromServer());
}
...
}
WAY TO SOLVE
As #Yogi recommended above to me, I have used TransactionTemplate to get one valid/active transaction session, in this case I have implemented throught constructor and works fine for me.
#Service("myService")
#Transactional(readOnly = true)
public class MyServiceImpl implements MyService {
#Autowired
private MyDao myDao;
private CacheList cacheList;
#Autowired
public void MyServiceImpl(PlatformTransactionManager transactionManager) {
this.cacheList = (CacheList) new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
#Override
public Object doInTransaction(TransactionStatus transactionStatus) {
CacheList cacheList = new CacheList();
cacheList.reloadCache(MyServiceImpl.this.myDao.getAllFromServer());
return cacheList;
}
});
}
...
}
I don't think there is any transaction allowed on #PostConstruct level so #Transactional won't do much here unless mode is set to aspectj in <tx:annotation-driven mode="aspectj" />.
As per this discussion you can use TransactionTemplate to start manual transaction inside init() to bind session but if you intend to strictly adhere to declarative transaction you need to use ApplicationListener to register event and user ContextRefreshedEvent to initiate transaction.
Make sure you are running under transaction. I can see the transaction annotation but looks like you missed to activate the transaction management using annotation through the use of using <tx:annotation-driven/> tag in spring context.
This happens because MyServiceImpl.init() is called by Spring after MyServiceImpl related bean construction and #Transaction annotation is not used to manage session lifecycle.
A solution might be consider Spring AOP around methods that use the cache instead of #PostConstruct

Resources