HQL ingoring transaction - spring

im building an Spring 4 + Hibernate 4 app. My config is this:
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(packagesDaoAnotaciones);
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
// ConfiguraciĆ³n del DataSource por el Servidor.
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
DataSource dataSource = dsLookup.getDataSource(environment.getRequiredProperty("jndi.datasource"));
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"));
properties.put("hibernate.generate_statistics", "true");
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
I have a Service with this method:
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void processThis() throws Exception {
List<String> myList = new ArrayList<>();
myList.add("1234567890");
myDao.update(myList,'01', "1234567890");
LOGGER.info("buahhhhhhhhhhhhhhhhhhhhhhh");
}
The Dao executes an HQL query like this:
#Override
public int update(List<String> data, String data1, String data2) {
StringBuffer hql = new StringBuffer();
hql.append("UPDATE MyTablenp SET np.field1 = :d1, field2= sysdate, field3= :d2, field4= sysdate, field5= null,field6= null WHERE np.data IN (:num)");
this.getSession().getTransaction().isActive();
Query query = this.getSession().createQuery(hql.toString());
query.setParameterList("num", data);
query.setParameter("d1", data1);
query.setParameter("d2", data2);
return query.executeUpdate();
}
When i execute this, i can see the data modified in the BBDD just after dao method ends, but the service method didnt finish yet, so its commiting the HQL without finish the transaction method. Do anyone knows how to avoid that commit and commit only when service method ends?

You should use the existing session opened by hibernate at the start of the annotated method proccessThis().
In your DAO do the following:
Remove this: this.getSession().getTransaction().isActive();
and instead get current session from the session factory(assuming you have extended HibernateDaoSupport):
Session session = this.getHibernateTemplate().getSessionFactory().getCurrentSession();

Related

C3P0 exhausted after call some #Transactional methods

I've implemented a multi tenant architecture and I've introduced a C3P0 connection pool.
By GUI I try to query my application with a simple search form. THis form has a button to call a back end method to extract my result.
If I click several times my search I've my application freeze because C3P0 connection pool is exhausted.
Because C3P0 increment connections managed with acquireIncrement parameter but when it arrived MAX_SIZE crashes
So my scenario is the follow:
I've a class annoted as #Service and a method annoted as #Transactional where I'm executing my query, as follow:
#Override
#Transactional
public String findPatientCountDao(Paziente paziente,
boolean searchForAllPatients, boolean enabledDemetra) {
String querySqlOrHql = this.createQuery(paziente, searchForAllPatients, true, 0, 0);
Query q = getSession().createQuery(querySqlOrHql);
List<Object[]> obj = q.list();
return "" + obj.get(0);
}
My Hibernate configuration has managed by HibernateConf class, as follow:
#Configuration
#EnableTransactionManagement
public class HibernateConf {
#Autowired
DataSourceBasedMultiTenantConnectionProviderImpl dataSources;
#Autowired
TenantSchemaResolver tenantSchemaResolver;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
if (TenantContext.getCurrentTenant()==null)
TenantContext.setCurrentTenant("tenant_1");
sessionFactory.setDataSource(dataSources.selectDataSource(TenantContext.getCurrentTenant()));
sessionFactory.setPackagesToScan("it.spi");
org.hibernate.cfg.Configuration configuration = new org.hibernate.cfg.Configuration();
configuration.setProperties(hibernateProperties());
//sessionFactory.setHibernateProperties(hibernateProperties());
sessionFactory.setConfigLocation(new ClassPathResource("hibernate_ehr/hibernate.cfg.xml"));
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties()).build();
System.out.println("Hibernate Java Config serviceRegistry created");
sessionFactory = (LocalSessionFactoryBean) configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
#Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager transactionManager =
new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
private final Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
hibernateProperties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, dataSources);
hibernateProperties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantSchemaResolver);
hibernateProperties.put(Environment.USE_STREAMS_FOR_BINARY, true);
hibernateProperties.put(Environment.DIALECT, "org.hibernate.dialect.Oracle10gDialect");
hibernateProperties.put(Environment.DRIVER, "oracle.jdbc.OracleDriver");
hibernateProperties.put(Environment.SHOW_SQL, false);
hibernateProperties.put(Environment.FORMAT_SQL, false);
hibernateProperties.put(Environment.USE_SQL_COMMENTS, false);
hibernateProperties.put(Environment.C3P0_MIN_SIZE, 50); //Minimum size of pool
hibernateProperties.put(Environment.C3P0_MAX_SIZE, 100); //Maximum size of pool
hibernateProperties.put(Environment.C3P0_ACQUIRE_INCREMENT, 10);//Number of connections acquired at a time when pool is exhausted
hibernateProperties.put(Environment.C3P0_TIMEOUT, 1800); //Connection idle time
hibernateProperties.put(Environment.C3P0_MAX_STATEMENTS, 600); //PreparedStatement cache size
return hibernateProperties;
}
My Datasource configuration:
#Primary
#Bean(name = { "dataSource", "dataSource1" })
#ConfigurationProperties(prefix = "spring.multitenancy.datasource1")
public DataSource dataSource1() {
DataSourceBuilder factory = DataSourceBuilder
.create(this.multitenancyProperties.getDatasource1().getClassLoader())
.driverClassName(this.multitenancyProperties.getDatasource1().getDriverClassName())
.username(this.multitenancyProperties.getDatasource1().getUsername())
.password(this.multitenancyProperties.getDatasource1().getPassword())
.url(this.multitenancyProperties.getDatasource1().getUrl()).type(ComboPooledDataSource.class);
return factory.build();
}
My database.properties file
spring.multitenancy.datasource.type = com.mchange.v2.c3p0.ComboPooledDataSource
spring.multitenancy.datasource.url=jdbc:oracle:thin:#xx.xx.xx.xx:1521/xxx
spring.multitenancy.datasource.username=xxx
spring.multitenancy.datasource.password=yyy
spring.multitenancy.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.multitenancy.datasource.testWhileIdle = true
spring.multitenancy.datasource.validationQuery = SELECT 1
spring.multitenancy.datasource.hibernate.c3p0.max_size=100
spring.multitenancy.datasource.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory

Wrong transaction manger is being used in Spring

I have two spring transaction managers.
txManager1 - ChainedKafkaTransactionManager (KafkaTransactionManager,JpaTransactionManager) configured with datasource DB1
txManager2 - JpaTransactionManager configured with datasource DB2
The problem is that I perform some operation using txManager2 but somehow txManager1 is being used instead of txManager2 and the data is getting committed to DB1 instead of DB2.
#Autowired
#Qualifier("common-tx")
private PlatformTransactionManager txManager2 ;
#KafkaListener(topics = "${kafka.topic.name}", groupId = "group-1", containerFactory = "customKafkaListenerContainerFactory")
public void topicListener(String message, Acknowledgment ack)
throws InterruptedException, ClassNotFoundException, IOException {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName(domainEvent.getEventId());
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus commonStatus = txManager2 .getTransaction(def);
someService.doSomething();
txManager2 .commit(commonStatus);
ack.acknowledge();
}
In doSomething() I am just persisting an entity, upon debugging it is found that while saving the entity via Spring data repository, the transaction manager
determined in invokeWithinTransaction() of org.springframework.transaction.interceptor.TransactionAspectSupport is wrong, i.e. txManager1 is selected instead of txManager2, is it a configuration issue or am I missing something?
txManager1 configuration :
#Configuration
#EnableTransactionManagement
#PropertySource(value = {"classpath:application-${spring.profiles.active}.properties"})
#Profile({"development","production","qa"})
#EnableJpaRepositories(basePackages={"xxx.xxx.xxxx"},excludeFilters=#ComponentScan.Filter(type=FilterType.REGEX, pattern="xxx.xxx.xxxx.module2.*"))
public class JPAConfig1 {
#Value("${jndi.name}")
private String jndiName;
#Value("${hibernate.dialect}")
private String hibernateDialect;
#Value("${hibernate.show_sql}")
private String showSql;
#Value("${hibernate.format_sql}")
private String formatSql;
#Value("${hibernate.hbm2ddl.auto}")
private String hiberanteUpdate;
#Value("${javax.persistence.validation.mode}")
private String hibernateValidation;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory(MultiTenantConnectionProviderImpl tenantConnection, CurrentTenantIdentifierResolver currentTenantIdentifierResolver)
{
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(vendorAdapter);
emf.setJpaProperties(jpaProperties(tenantConnection, currentTenantIdentifierResolver));
emf.setPackagesToScan(new String[] {"xxx.xxx.xxxx"});
return emf;
}
private Properties jpaProperties(MultiTenantConnectionProviderImpl tenantConnection, CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect",hibernateDialect);
properties.setProperty("hibernate.show_sql",showSql);
properties.setProperty("hibernate.format_sql",formatSql);
properties.setProperty("hibernate.hbm2ddl.auto",hiberanteUpdate);
properties.setProperty("javax.persistence.validation.mode",hibernateValidation);
properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);
properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, tenantConnection);
return properties;
}
#Bean
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver(TenantContext tenantContext) {
return new CurrentTenantIdentifierResolverImpl(tenantContext);
}
#Bean(name="tenantConnection")
public MultiTenantConnectionProviderImpl getMultiTenantConnectionProvider(TenantContext tenantContext) {
return new MultiTenantConnectionProviderImpl(false,tenantContext);
}
#Bean
#Primary
public PlatformTransactionManager transactionManager(EntityManagerFactory factory,ProducerFactory producerFactory){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(factory);
KafkaTransactionManager tm = new KafkaTransactionManager(producerFactory);
return new ChainedKafkaTransactionManager(tm,transactionManager);
}
#Bean
public TenantContext getTenantContext() {
return new TenantContextImpl();
}
}
txManager2 configuration :
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="xxx.xxx.xxxx.module2",
entityManagerFactoryRef="common-factory",transactionManagerRef="common-tx")
#PropertySource(value = {"classpath:common-${spring.profiles.active}.properties"})
#Profile({"development","production","qa"})
public class JPAConfig2 {
#Value("${common.jndi.name}")
private String jndiName;
#Value("${common.hibernate.dialect}")
private String hibernateDialect;
#Value("${common.hibernate.show_sql}")
private String showSql;
#Value("${common.hibernate.format_sql}")
private String formatSql;
#Value("${common.hibernate.hbm2ddl.auto}")
private String hiberanteUpdate;
#Value("${common.javax.persistence.validation.mode}")
private String hibernateValidation;
#Bean(name="common-factory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(#Qualifier("common-ds") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(new String[] {"xxx.xxx.xxxx.module2"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(jpaProperties());
em.setPersistenceUnitName("common");
return em;
}
#Bean("common-ds")
public DataSource dataSource() throws NamingException {
return (DataSource) new JndiTemplate().lookup(jndiName);
}
private Properties jpaProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect",hibernateDialect);
properties.setProperty("hibernate.show_sql",showSql);
properties.setProperty("hibernate.format_sql",formatSql);
properties.setProperty("hibernate.hbm2ddl.auto",hiberanteUpdate);
properties.setProperty("javax.persistence.validation.mode",hibernateValidation);
return properties;
}
#Bean(name="common-tx")
public PlatformTransactionManager transactionManager(#Qualifier("common-factory") EntityManagerFactory factory){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(factory);
return transactionManager;
}
}

Spring Boot - using JPA Repositories with Hibernate

I have a simple Spring Boot application with the following auto-configuration properties:
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/mywebapp
spring.datasource.username=username
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=validate
These work fine and I'm able to setup Spring Data JpaRepositories:
public interface UserRepository extends JpaRepository<User, String>
{
User findByName(String name);
}
...for the following entities:
#Entity
public class User
{
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
protected String uuid;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String email;
}
...and use them like this:
#Transactional
public void updatePassword(String username, String password)
{
User user = userRepository.findByName(username);
user.setEmail("test#example.com"); // This gets persisted automatically by the JpaRepository.
}
Now I'm struggling to configure the same thing manually. I've tried the following:
#Configuration
#EnableTransactionManagement
public class PersistenceConfig
{
#Bean
public DataSource dataSource()
{
DataSource dataSource = new DataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/mywebapp");
dataSource.setUsername("username");
dataSource.setPassword("password");
return dataSource;
}
#Bean
public LocalSessionFactoryBean sessionFactory()
{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan("com.example.persistent");
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "validate");
sessionFactoryBean.setHibernateProperties(hibernateProperties);
return sessionFactoryBean;
}
#Bean
public HibernateTransactionManager transactionManager()
{
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
}
...but while no exceptions are thrown and i can now successfully read from the database, it seems that none of the changes I make to the entities are being persisted.
Does anyone have an idea what I'm missing for the persistence to work?
OP here.
I seem to have misunderstood that JPA needs an EntityManager instead of a session.
The following configuration works:
#Configuration
#EnableTransactionManagement
public class PersistenceJpaConfig
{
#Bean
public DataSource dataSource()
{
DataSource dataSource = new DataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/mywebapp");
dataSource.setUsername("username");
dataSource.setPassword("password");
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
{
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.example.persistent");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
properties.setProperty("hibernate.hbm2ddl.auto", "validate");
em.setJpaProperties(properties);
return em;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf)
{
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation()
{
return new PersistenceExceptionTranslationPostProcessor();
}
}

Spring Data JPA, cannot write to db from CommandLineRunner Bean

I'm following the Accessing Data with JPA guide and having an issues executing the CommandLineRunner Bean. The problem occurs when manually configuring my database component class. Basically, it works when I have the configuration as below and fails to write when the commented block is included.
Edit - With setShowSql enabled on the JpaVendorAdaptor, I can see select statements being by hibernate attempted, but no attempts at inserts. There are no errors on calling the repository to save, and the ID's are staying at 0 when checked after save.
I don't know if something is configured improperly or if I'm just completely missing a bean or something else.
#Component
public class RepoConfig {
#Value("${spring.datasource.driver-class-name}") private String driverClassName;
#Value("${spring.datasource.url}") private String url;
#Value("${spring.datasource.username}") private String username;
#Value("${spring.datasource.password}") private String password;
#Bean
DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(driverClassName);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
// #Bean
// public JpaVendorAdapter jpaVendorAdapter() {
// HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
// adapter.setDatabase(Database.SQL_SERVER);
// adapter.setDatabasePlatform("org.hibernate.dialect.SQLServer2012Dialect");
// adapter.setShowSql(true);
// adapter.setGenerateDdl(false);
// return adapter;
// }
//
// #Bean
// public EntityManagerFactory entityManagerFactory() {
// LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
// emfb.setJpaVendorAdapter(jpaVendorAdapter());
// emfb.setDataSource(dataSource());
// emfb.setPackagesToScan("model.package");
// emfb.afterPropertiesSet();
// return emfb.getObject();
// }
//
// #Bean
// public PlatformTransactionManager transactionManager() {
// JpaTransactionManager txManager = new JpaTransactionManager();
// txManager.setEntityManagerFactory(entityManagerFactory());
// return txManager;
// }
//
#Bean
Flyway flyway() {
Flyway flyway = new Flyway();
flyway.setDataSource(dataSource());
flyway.migrate();
return flyway;
}
}

Automatic hibernate schema creation not working (junit+spring)

I am running a JUnit test case with the following spring config. THe automatic schema creation is not happening.
#Configuration
public class DatabaseConfiguration {
#Bean
DataSource dataSource() {
BasicDataSource ret = new BasicDataSource();
ret.setDriverClassName("com.mysql.jdbc.Driver");
ret.setUrl("jdbc:mysql://localhost:3306/mydb");
ret.setUsername("root");
ret.setPassword("root");
return ret;
}
#Bean
SessionFactory sessionFactoryBean(DataSource dataSource) {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(
dataSource);
sessionBuilder.scanPackages("com.mypackage.domain");
sessionBuilder.addProperties(getHibernateProperties());
return sessionBuilder.buildSessionFactory();
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", "true");
properties.put("hbm2ddl.auto", "create");
properties.put("hibernate.dialect",
"org.hibernate.dialect.MySQLDialect");
return properties;
}
#Bean
HibernateTransactionManager transactionManager(SessionFactory lsfb) {
HibernateTransactionManager mgr = new HibernateTransactionManager();
mgr.setSessionFactory(lsfb);
return mgr;
}
}
I have debugged to make sure that the schema creation is actually getting skipped.
Any idea why the schema creation might get skipped?
Try with hibernate.hbm2ddl.auto instead of hbm2ddl..auto

Resources