EntityManager persist and merge not working in #Transactional method - java

I have strange behaviour of #Transactional annotation and EntityManager persist and merge methods. I've annotated method as #Transactional and in it I call EntityManager.persists(entity)...and nothing happens. Entity is not saved to DB, no exception fired, totally nothing. I've read tons of examples, SO questions, and my code seems ok but not working.
Hibernate config:
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.jndi.JndiTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Properties;
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = {"com.mycompany"})
#DependsOn({"PropertiesConfig"})
public class HibernateConfig {
public HibernateConfig() {}
#Bean
public DataSource dataSource() throws NamingException {
return (DataSource) new JndiTemplate().lookup(PropertyService.getInstance().getProperty("jndi.jdbc.AGSQL"));
}
#Bean(name = "entityManager")
#DependsOn("dataSource")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
System.out.println("*** Init EntityManagerFactory ***");
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceProviderClass(HibernatePersistenceProvider.class);
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "com.mycompany" });
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.setJpaProperties(hibernateProperties());
return em;
}
#Bean(name = "tm")
public PlatformTransactionManager transactionManager() throws NamingException {
System.out.println("*** Init TransactionManager ***");
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private Properties hibernateProperties() {
return new Properties() {
{
setProperty("hibernate.dialect", PropertyService.getInstance().getProperty("hibernate.dialect"));
setProperty("show_sql", PropertyService.getInstance().getProperty("show_sql"));
setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_READ_UNCOMMITTED));
}
};
}
}
Entity:
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.time.LocalDateTime;
#Entity
#Table(name = "Vehicle", schema = "dbo")
public class VehicleEntity {
private Long deviceId;
private LocalDateTime dtEdit;
private String name;
#Id
#Column(name = "device_id")
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
#Basic
#Column(name = "dt_edit")
#UpdateTimestamp
public LocalDateTime getDtEdit() {
return dtEdit;
}
public void setDtEdit(LocalDateTime dtEdit) {
this.dtEdit = dtEdit;
}
#Basic
#Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Dao:
import com.mycompany.VehicleEntity;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
#Repository
public class VehicleDao {
#PersistenceContext(unitName = "entityManager")
private EntityManager entityManager;
protected EntityManager getEntityManager() {
return entityManager;
}
#Transactional(transactionManager = "tm")
public void persist(VehicleEntity entity) {
getEntityManager().persist(entity);
}
#Transactional("tm")
public void merge(VehicleEntity entity) {
getEntityManager().merge(entity);
}
}
And executing this code do totally nothing:
VehicleEntity ve = new VehicleEntity();
ve.setDeviceId(111L);
ve.setName("111");
vehicleDao.persist(ve);
I tried to check transaction status inside persist method:
((Session)this.getEntityManager().getDelegate()).getTransaction().getStatus() = NOT_ACTIVE
So I can conclude that transaction has not started, or started, but entitymanager did not see it automatically (see below). In one of the questions I saw that before calling persist(entity) entity manager was joined to the transaction and made the same thing:
#Transactional(transactionManager = "tm")
public void persist(T entity) {
getEntityManager().joinTransaction();
getEntityManager().persist(entity);
}
Aaaaand...it works. And transaction status become ACTIVE.
So my questions are:
- why it did not work without joinTransaction()? I've never seen it in examples...
- (possibly after solving 1st question this one will not make sense) persisting and merging actually are in abstract dao class, but I have many entities, many entities dao and many custom methods in them. And calling joinTransaction() in each of them is not good idea. How make it in pretty way?
EDIT:
Checked one more thing - added propagation property:
#Transactional(transactionManager = "tm", propagation = Propagation.MANDATORY)
public void persist(T entity) {
getEntityManager().persist(entity);
}
And it throws an exception:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
EDIT2:
Spring version 5.1.7.RELEASE
Hibernate version 5.4.10.Final
javax.persistence:javax.persistence-api:2.2 (dependency from hibernate-core)

Finally found the problem: I was migrating from SessionFactory to EntityManager and leave in config file old settings for SessionFactory. And the issue was that transaction managers has the same method names for SF and EM:
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(sessionFactory);
return transactionManager;
}
#Bean(name = "tm")
public PlatformTransactionManager transactionManager(#Qualifier("customEntityManager") EntityManagerFactory emf) {
System.out.println("*** Init TransactionManager ***");
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
This is mistake though bean names are different.
Changing SF transaction manager method to sessionTransactionManager(...) resolve the issue.

Related

hibernate 5 - Could not obtain transaction-synchronized Session for current thread;

I am able to use sessions if I open a new one, however, I am getting this error if I try to get the current session which I guess doesn't exist.
persistence-h2.properties
# jdbc.X
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/my-database
jdbc.eventGeneratedId=sa
jdbc.user=root
jdbc.pass=root_password
# hibernate.X
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=false
hibernate.hbm2ddl.auto=validate
# hibernate.search.X
hibernate.search.default.directory_provider = filesystem
hibernate.search.default.indexBase = /data/index/default
# envers.X
envers.audit_table_suffix=_audit_log
HibernateConfig.java
package com.buraktas.spring;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.Properties;
#Configuration
#EnableTransactionManagement
#PropertySource({ "classpath:persistence-h2.properties" })
#ComponentScan({ "com.buraktas" })
public class HibernateConfig {
#Autowired
private Environment env;
#Bean
public LocalSessionFactoryBean sessionFactory() {
final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.buraktas.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
final BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager() {
final HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private final Properties hibernateProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", "true");
// Envers properties
hibernateProperties.setProperty("org.hibernate.envers.audit_table_suffix", env.getProperty("envers.audit_table_suffix"));
return hibernateProperties;
}
}
PersonDaoImp.java
#Repository
public class PersonDaoImp implements PersonDao {
#Autowired
private SessionFactory sessionFactory;
public Person getPerson(int id) {
Session currentSession = sessionFactory.getCurrentSession();
return currentSession.get(Person.class, 1);
}
}
Here if I use sessionFactory.openSession() instead then I am able to query my database. But I don't want to create a new session and close it every time. I thought I needed to add current_session_context_class and connection.pool_size properties as well, but that didn't solve the problem. So long story short how I can initialize a connection pool or a session pool?
Thanks!
This is a late response, but as I mentioned in the comments adding #Transactional on class level worked.

Query is always execute before than AOP in SpringBoot and MyBatis application for dynamic datasource

Here , I want to make a SpringBoot and MyBatis application use dynamic datasource by AOP; But the AOP is always execute after query from database, so switch datasource is invalid because select is finished.
All my code is in https://github.com/helloworlde/SpringBoot-DynamicDataSource/tree/aspect_dao
My dependence is
compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-aop')
runtime('mysql:mysql-connector-java')
And application.properties
application.server.db.master.driver-class-name=com.mysql.jdbc.Driver
application.server.db.master.url=jdbc:mysql://localhost/redisapi?useSSL=false
application.server.db.master.port=3306
application.server.db.master.username=root
application.server.db.master.password=ihaveapen*^##
#application.server.db.master.database=123456
#
## application common config
application.server.db.slave.driver-class-name=com.mysql.jdbc.Driver
application.server.db.slave.url=jdbc:mysql:/localhost/redisapi2?useSSL=false
application.server.db.slave.port=3306
application.server.db.slave.username=root
application.server.db.slave.password=123456
#application.server.db.slave.database=redisapi
mybatis.type-aliases-package=cn.com.hellowood.dynamicdatasource.mapper
mybatis.mapper-locations=mappers/**Mapper.xml
Table
CREATE TABLE product(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
price DOUBLE(10,2) NOT NULL DEFAULT 0
);
DataSourceConfigur.java
package cn.com.hellowood.dynamicdatasource.configuration;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Configuration
public class DataSourceConfigurer {
#Bean("master")
#Primary
#ConfigurationProperties(prefix = "application.server.db.master")
public DataSource master() {
return DataSourceBuilder.create().build();
}
#Bean("slave")
#ConfigurationProperties(prefix = "application.server.db.slave")
public DataSource slave() {
return DataSourceBuilder.create().build();
}
#Bean("dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put("master", master());
dataSourceMap.put("slave", slave());
// Set master datasource as default
dynamicRoutingDataSource.setDefaultTargetDataSource(master());
// Set master and slave datasource as target datasource
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
// To put datasource keys into DataSourceContextHolder to judge if the datasource is exist
DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
return dynamicRoutingDataSource;
}
#Bean
#ConfigurationProperties(prefix = "mybatis")
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// Here is very important, if don't config this, will can't switch datasource
// put all datasource into SqlSessionFactoryBean, then will autoconfig SqlSessionFactory
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
return sqlSessionFactoryBean;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
DynamicRoutingDataSource.java
package cn.com.hellowood.dynamicdatasource.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Override
protected Object determineCurrentLookupKey() {
logger.info("Current DataSource is [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
DynamicDataSourceContextHolder.java
package cn.com.hellowood.dynamicdatasource.configuration;
import java.util.ArrayList;
import java.util.List;
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
#Override
protected String initialValue() {
return "master";
}
};
public static List<Object> dataSourceKeys = new ArrayList<>();
public static void setDataSourceKey(String key) {
contextHolder.set(key);
}
public static String getDataSourceKey() {
return contextHolder.get();
}
public static void clearDataSourceKey() {
contextHolder.remove();
}
public static boolean containDataSourceKey(String key) {
return dataSourceKeys.contains(key);
}
}
DynamicDataSourceAspect.java
package cn.com.hellowood.dynamicdatasource.configuration;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
#Aspect
#Order(-100) // To ensure execute before #Transactional
#Component
public class DynamicDataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
private final String QUERY_PREFIX = "select";
#Pointcut("execution( * cn.com.hellowood.dynamicdatasource.mapper.*.*(..))")
public void daoAspect() {
}
#Before("daoAspect()")
public void switchDataSource(JoinPoint point) {
if (point.getSignature().getName().startsWith(QUERY_PREFIX)) {
DynamicDataSourceContextHolder.setDataSourceKey("slave");
logger.info("Switch DataSource to [{}] in Method [{}]",
DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
}
}
#After("daoAspect())")
public void restoreDataSource(JoinPoint point) {
DynamicDataSourceContextHolder.clearDataSourceKey();
logger.info("Restore DataSource to [{}] in Method [{}]",
DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
}
}
And have Controller, Service and Dao for query, But although I set Order of aspect as -100, it still execute query before AOP, could anyone find where is wrong, Thank you very much.
This is log screenshot
Finally I fixed this issue, Because I injected Bean of DataSourceTransactionManager, So transaction will be open in Service, so the aspect of DAO is not work until transaction finished.
Delete this code:
#Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}

Using two EntityManagerFactory within one spring boot app

I am trying to configure two EntityManagerFactory for one spring boot application. Each of those EntityManagerFactorys should work with different databases. Also would be great if one of them would be used as a default (e.g. when not explicit information was provided during on which one should be used).
Here is my code:
pkg/Application.java
package pkg;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).run(args);
}
}
pkg/PrimaryDbConfig.java
package pkg;
import org.h2.jdbcx.JdbcDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
#Configuration
public class PrimaryDbConfig {
public static final String DB = "primary";
#Bean
#Primary
public DataSource primaryDataSource() {
JdbcDataSource ds = new JdbcDataSource();
ds.setURL("jdbc:h2:file:~\\db\\test;AUTO_SERVER=TRUE");
ds.setUser("sa");
ds.setPassword("sa");
return ds;
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName(DB);
factoryBean.setDataSource(primaryDataSource());
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factoryBean.setPackagesToScan("pkg.entities");
return factoryBean;
}
#Bean
#Primary
public PlatformTransactionManager primaryTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(primaryEntityManagerFactoryBean().getObject());
return transactionManager;
}
}
pkg/SecondaryDbConfig.java
package pkg;
import org.h2.jdbcx.JdbcDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
#Configuration
public class SecondaryDbConfig {
public static final String DB = "secondary";
#Bean
public DataSource secondaryDataSource() {
JdbcDataSource ds = new JdbcDataSource();
ds.setURL("jdbc:h2:file:~\\db\\test2;AUTO_SERVER=TRUE");
ds.setUser("sa");
ds.setPassword("sa");
return ds;
}
#Bean
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName(DB);
factoryBean.setDataSource(secondaryDataSource());
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factoryBean.setPackagesToScan("pkg.entities");
return factoryBean;
}
#Bean
public PlatformTransactionManager secondaryTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(secondaryEntityManagerFactoryBean().getObject());
return transactionManager;
}
}
pkg/repositories/PrimaryRepository.java
package pkg.repositories;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import pkg.entities.SomeEntity;
import javax.persistence.EntityManager;
#Repository
#Transactional
public class PrimaryRepository {
private final EntityManager entityManager;
public PrimaryRepository(EntityManager entityManager) {
this.entityManager = entityManager;
}
public SomeEntity find(Integer id) {
return entityManager.find(SomeEntity.class, id);
}
}
pkg/repositories/SecondaryRepository.java
package pkg.repositories;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import pkg.entities.SomeEntity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
#Repository
#Transactional("secondaryTransactionManager")
public class SecondaryRepository {
#PersistenceContext(unitName = "secondary")
private final EntityManager entityManager;
public SecondaryRepository(EntityManager entityManager) {
this.entityManager = entityManager;
}
public SomeEntity find(Integer id) {
return entityManager.find(SomeEntity.class, id);
}
}
build.gradle
plugins {
id 'org.springframework.boot' version '1.5.7.RELEASE'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile "com.h2database:h2:1.4.193"
compile "org.hibernate:hibernate-core:5.2.10.Final"
compile ("org.springframework.boot:spring-boot-starter-data-jpa:1.5.2.RELEASE") {
exclude module: 'tomcat-jdbc'
}
}
Running this app I am getting next error:
Description:
Parameter 0 of constructor in pkg.repositories.PrimaryRepository required a single bean, but 2 were found:
- org.springframework.orm.jpa.SharedEntityManagerCreator#0: defined by method 'createSharedEntityManager' in null
- org.springframework.orm.jpa.SharedEntityManagerCreator#1: defined by method 'createSharedEntityManager' in null

Spring Transactional annotation vs javax transactional

I am working with Spring 4.3.x and Hibernate 5.2.x. Since hibernate 5 JPA is standard way to user ORM with spring, I created a config to use Hibernate with JPA.
My spring configuration is,
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate5.HibernateExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.zaxxer.hikari.HikariDataSource;
#Configuration
#ComponentScan(basePackages = { "com.example.reports" })
#EnableTransactionManagement
#PropertySource(value = { "classpath:application.properties" })
public class SuperUserReportSpringConfiguration {
#Autowired
Environment environment;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendeorAdapter) {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setJpaVendorAdapter(jpaVendeorAdapter);
entityManagerFactory.setPackagesToScan("com.example.reports.domain");
entityManagerFactory.setPersistenceUnitName("onlineDB");
return entityManagerFactory;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(false);
adapter.setDatabasePlatform("org.hibernate.dialect.DB2400Dialect");
return adapter;
}
#Bean
DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setPoolName("hikariCP");
ds.setDriverClassName(this.environment.getProperty("jdbc.as400.db2.db.driver.classname"));
ds.setJdbcUrl(this.environment.getProperty("jdbc.as400.db2.db.url"));
ds.setUsername(this.environment.getProperty("jdbc.as400.db2.db.username"));
ds.setPassword(this.environment.getProperty("jdbc.as400.db2.db.password"));
ds.setMaximumPoolSize(10);
return ds;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager(emf);
return transactionManager;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
And I try to use #Transactional from javax.transaction.Transactional and org.springframework.transaction.annotation.Transactional
In both the case, transaction manager is not working and I am getting error when trying to get child element from User entity,
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.reports.domain.User.userInetAddrs, could not initialize proxy - no Session
I am not sure if I am missing any configuration.
Hibernate entity,
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private Set<UserInetAddr> userInetAddrs = new HashSet<UserInetAddr>(0);
Service class with #Transactional
User user = superUserReportDao.getUserSecurityRoles(id);
user.getUserInetAddrs().forEach(inetAddress -> {
});
And DAO Method with #Transactional
public User getUser(int userSeqId) {
return entityManager.find(User.class, userSeqId);
}
DAO Class transaction,
#Repository("superUserReportDao")
#Slf4j
#Transactional
public class SuperUserReportDaoImpl implements SuperUserReportDao {
#PersistenceContext(unitName="onlineDB")
private EntityManager onlineEntityManager;
And Service Class,
#Service("superUserReportService")
#Slf4j
public class SuperUserReportServiceImpl implements SuperUserReportService {
#Resource(name = "superUserReportDao")
private SuperUserReportDao superUserReportDao;
public void generateSuperUserReport() throws SQLException {
// -- generate Customer Super user report
generateSuperUserEmail(UserType.CUSTOMER);
}
#Transactional
private void generateSuperUserEmail(UserType userType) throws SQLException {
...
And my main class,
#Slf4j
#Component
public class SuperUserReportMain {
#Resource(name = "superUserReportService")
SuperUserReportService superUserReportService;
/**
* #param args
* #throws SQLException
*/
public static void main(String[] args) throws SQLException {
ApplicationContext context = new AnnotationConfigApplicationContext(SuperUserReportSpringConfiguration.class);
SuperUserReportMain reportMain = context.getBean(SuperUserReportMain.class);
reportMain.generateSuperUserReport();
((AnnotationConfigApplicationContext)context).close();
}
public void generateSuperUserReport() throws SQLException {
superUserReportService.generateSuperUserReport();
}
}

Hibernate, oauth2 could not obtain transaction-synchronized

I tried to make my own customDetailsService and when I tried to call server for token using http:
POST http://localhost:8080/SpringSecurityOAuth2Example/oauth/token?grant_type=password&username=bill&password=abc123
I got exception as a result:
<UnauthorizedException xmlns="">
<error>unauthorized</error>
<error_description>Could not obtain transaction-synchronized Session for current thread</error_description>
</UnauthorizedException>
What is more, when I debugged this I had determined that there is the problem with method userDaoImpl.findByLogin(login). But when I called it from my restController for test it worked and annotations were analogous.
#EnableTransactiomManagement is added in hibernate configuration and needed classes are annotated with #Transaction.
MyUserDetailsService
package com.maxim.spring.restcontroller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.maxim.spring.dao.UserDao;
import com.maxim.spring.model.User;
#Transactional
#Service("MyUserDetailsService")
public class MyUserDetailsService implements UserDetailsService {
#Autowired
#Qualifier("userDaoImpl")
private UserDao userDaoImpl;
#Transactional(readOnly=true)
#Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
User user = userDaoImpl.findByLogin(login);
if(user == null) {
System.out.println("User was not found ");
throw new UsernameNotFoundException(login);
} else {
System.out.println("Tu powinienem byc i jestem");
new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPass(), getUserRole(user));
}
return null;
}
public List<GrantedAuthority> getUserRole(User user) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_"+user.getRole()));
return authorities;
}
UserDaoImpl:
package com.maxim.spring.dao;
import java.util.List;
import org.springframework.transaction.annotation.Transactional;
import org.hibernate.Query;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;
import com.maxim.spring.model.User;
#Transactional
#Repository("userDaoImpl")
public class UserDaoImpl extends AbstractDao implements UserDao{
public User findById(Integer id) {
#Override
public User findByLogin(String login) {
System.out.println("w srodku1");
Query qr = (Query) getSession().getCurrentSession().createQuery("from User u where u.login=:login").setParameter("login", login);
System.out.println("w srodku2");
User u = (User) qr.list().get(0);
System.out.println("w srodku3");
return u;
}
}
The hibernate configuration:
#EnableTransactionManagement
#Configuration
#ComponentScan({ "com.maxim.spring.configuration" })
#PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {
#Autowired
private Environment environment;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.maxim.spring.model", "com.maxim.spring.services" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
How to make it work? What are my mistakes?
You are using getSession().getCurrentSession().......
if you put getSession().openSession().....It will works.
more info enter this discussion
Your #Transactional method loadUserByUsername() call #Transactional method findByLogin() that is conflict, can't open 2 transactions.
Idea for you:
Remove all #Transactional above class and add #Transactional on methods that you want to be transactional. Your method loadUserByUsername() must be transactional but your method findByLogin() can't be transactional because it is called from transactional method.

Categories

Resources