how to load beans on demand in spring boot application - spring

>
Hi, I am new to Spring and Hibernate and require a positive response. As per my project requirement , I have a spring boot application and want to load Hibernate Configuration like datasource bean , session factory bean on demand when get the values from user . Based on users database specification values it will create connection there and create tables and perform further tasks . Here is my sample code in which I am excluding the db configuration on initial run and after that when call the hibernate configure method it creates separate context and connection with db .The problem is that it is not able to save user values in db and unable to have session factory object autowired in application
Main Application
#SpringBootApplication
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class TestApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
Hibernate Persistence.XMl This are the four beans that i want to load on demand
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="${spring.datasource.driver-class-name}" p:url="${spring.datasource.url}"
p:username="${spring.datasource.username}"
p:password="${spring.datasource.password}"
p:initialSize="1" p:maxActive="2"
destroy-method="close">
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"
p:dataSource-ref="dataSource">
<property name="hibernateProperties">
<value>
hibernate.dialect=${spring.jpa.properties.hibernate.dialect}
hibernate.format_sql=true
hibernate.show_sql=false
hibernate.hbm2ddl.auto=update
</value>
</property>
<property name="mappingResources">
<list>
<value>entity-schema-hbm.xml</value>
</list>
</property>
</bean>
<bean id="testDao" class="com.app.dao.AbstractGenericDao" abstract="true">
<property name="entityManager">
<bean class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="persistenceUnitName" value="persistence-test-unit" />
</bean>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven />
Defined method in util for initializing beans it is able to create the separate context and connection with db but not merged with the current application and communicate with db . I want to create connection later on and will be available to spring boot application
public static EntityManager configureHibernate() {
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
"hibernate-persistence.xml");
dataSource = (DataSource) appContext.getBean("dataSource");
sessionFactory = (SessionFactory) appContext.getBean("sessionFactory");
transactionManager = (TransactionManager) appContext.getBean("transactionManager");
entityManager = (EntityManager) appContext.getBean("testDao");
UserEntity userEntity=new UserEntity("admin","secret","admin#gmail.com");
sessionFactory.getCurrentSession().saveOrUpdate(userEntity);
try{
transactionManager.commit();
}catch (Exception e){
}
return entityManager;
}

Related

Hibernate Transaction Manager not committing data changes

I'm using Hibernate 4 to write data to an H2 embedded in-memory database and there seems to be a problem with transactions. The application already uses Oracle and H2 has been added with a separate DataSource, SessionFactory, and TransactionManager. The original TransactionManager is marked as default and the H2 TransactionManager has the qualifier memTransactions
The following code - specifically the load function - correctly populates the memEvents variable at the end with the written data.
#Repository
#Transactional(transactionManager = "memTransactions")
public class EventMemDaoHibernate implements EventMemDao {
#Autowired
#Qualifier(value = "memSessionFactory")
private SessionFactory memSessionFactory;
#Override
public List<EventMem> getEvents() {
return memSessionFactory.getCurrentSession().createCriteria(EventMem.class).list();
}
#Override
public void load(List<Event> allEvents) {
Session session = memSessionFactory.getCurrentSession();
for (Event e : allEvents) {
EventMem memEvent = new EventMem(e);
session.save(memEvent);
}
List<EventMem> memEvents = getEvents(); // correct
}
}
However the following code produces an empty memEvents list
#Autowired
private EventMemDao eventMemDao;
List<Event> allEvents = eventDao.getAllEvents();
eventMemDao.load(allEvents); // calls the load function shown above
List<EventMem> memEvents = eventMemDao.getEvents(); // empty
I assume this is related to transaction management (e.g.: data is not auto-committed after the call to .save()). However when I tried explicitly beginning and committing a transaction within EventMemDaoHibernate#load, I receive this error:
nested transactions not supported
So, from what I can tell the TransactionManager is working.
My TransactionManager and related bean definitions are shown below.
<bean
id="memTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="memSessionFactory" />
<qualifier value="memTransactions"/>
</bean>
<bean id="hDataSource" class="org.h2.jdbcx.JdbcDataSource">
<property name="url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM 'classpath:scripts/init-h2.sql'" />
<property name="user" value="sa" />
<property name="password" value="" />
</bean>
<bean
id="memSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="hDataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
</props>
</property>
</bean>
This was due to my configuration error (of course). I didn't fully grasp that the connection URL was evaluated every time a session was opened against H2 and that means init-h2.sql was executed repeatedly. init-h2.sql included a truncate followed by an insert so it was dropping and recreating data every time Hibernate opened a session.

Is it necessary to autowire session factory in DAO layer if i am using hibernate transaction manager with session factory set in its property?

Is it necessary to autowire session factory in dao layer if i am using hibernate transcation manager with session factory set in its property?
Here is my xml config for session factory and transaction manager. You can see that i set session factory in hibernate transaction manager property:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" destroy-method="destroy">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan">
<list>
<value>com.model</value>
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
Here is my code in dao layer. You can see here that i am injecting an instance of session factory,so my question is, is this necessary? or the transaction manager config is enough? thank you!
#Autowired
#Required
#Override
public void setSessionFactory(SessionFactory sessionFactory) {
super.setSessionFactory(sessionFactory);
}

How to configure multiple MyBatis datasources in Spring Boot?

With MyBatis-Spring-Boot-Starter, we can easily integrate MyBatis with Spring Boot, it works perfectly for one data source. However, now we'd like to add an extra data source in our project, unfortunately it seems not easy.
In MyBatis official documentation, I see the following content:
MyBatis-Spring-Boot-Starter will:
Autodetect an existing DataSource.
Will create and register an instance of a SqlSessionFactoryBean passing that DataSource as an input.
Will create and register an instance of a SqlSessionTemplate got out of the SqlSessionFactoryBean.
It looks like MyBatis-Spring-Boot-Starter supports only one data source at this moment. So, the question is how to configure multiple MyBatis datasources in Sping Boot?
You outlined 3 beans that are needed for MyBatis+Spring integration. These are automatically created for single data source.
If you need two data sources, you need to create 3 beans for each data source explicitly. So you'll be creating 6 beans (2 of type DataSource, 2 of type SqlSessionFactoryBean and 2 of type SqlSessionFactoryBean).
To bind DAO with certain datasource, you will need to use sqlSessionTemplateRef or sqlSessionFactoryRef parameter of #MapperScan annotation.
Also I don't recommend to go down the XML hell. I was using it this way in PROD, with two data sources, without any ugly XML configs on various projects. Also SQL queries were annotated.
Shame is that MyBatis documentation is not great and most examples out there are in XML.
Something this like this to your spring servlet.xml:
<bean id="db2dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${db2.database.driver}</value></property>
<property name="url"><value>${db2.database.url}</value></property>
<property name="username"><value>${db2.database.username}</value></property>
<property name="password"><value>${db2.database.password}</value></property>
<property name="maxActive"><value>${db2.database.maxactiveconnections}</value></property>
<property name="maxIdle"><value>${db2.database.idleconnections}</value></property>
<property name="initialSize"><value>${db2.database.initialSize}</value></property>
</bean>
<bean id="db2SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="db2dataSource" />
<property name="configLocation" value="/WEB-INF/mybatis-config.xml"/>
</bean>
<bean id="db2Dao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="db2SqlSessionFactory"/>
<property name="mapperInterface" value="com.dao.db2Dao" />
</bean>
<bean id="oracledataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${oracle.database.driver}</value></property>
<property name="url"><value>${oracle.database.url}</value></property>
<property name="username"><value>${oracle.database.username}</value></property>
<property name="password"><value>${oracle.database.password}</value></property>
<property name="maxActive"><value>${oracle.database.maxactiveconnections}</value></property>
<property name="maxIdle"><value>${oracle.database.idleconnections}</value></property>
<property name="initialSize"><value>${oracle.database.initialSize}</value></property>
</bean>
<bean id="oracleSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="oracledataSource" />
<property name="configLocation" value="/WEB-INF/mybatis-config.xml"/>
</bean>
<bean id="oracleoardDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="oracleSqlSessionFactory"/>
<property name="mapperInterface" value="com.lodige.clcs.dao.oracleoardDao" />
</bean>
Maybe this is what you need
#Configuration
#MapperScan(basePackages = "com.neo.mapper.test1", sqlSessionTemplateRef =
"test1SqlSessionTemplate")
public class DataSource1Config {
#Bean(name = "test1DataSource")
#ConfigurationProperties(prefix = "spring.datasource.test1")
#Primary
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "test1SqlSessionFactory")
#Primary
public SqlSessionFactory testSqlSessionFactory(#Qualifier("test1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
#Bean(name = "test1TransactionManager")
#Primary
public DataSourceTransactionManager testTransactionManager(#Qualifier("test1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
#Bean(name = "test1SqlSessionTemplate")
#Primary
public SqlSessionTemplate testSqlSessionTemplate(#Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}

Spring Hibernate "No Current Transaction" for getCurrentSession method

I am using the Spring configuration to test Spring-Hibernate Transactions.
<beans ...>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- hibernate 4 onwards annotationsessionfactorybean is replaced with localsessionfactory bean -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.fg.arch.test.transaction.Foo</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<!-- <prop key="hibernate.current_session_context_class">org.hibernate.context.internal.ThreadLocalSessionContext</prop> -->
</props>
</property>
</bean>
</beans>
My service layer is annotated with #Transactional.
This is my DAO:
public class FooHibernateDaoImpl implements FooDao {
private SessionFactory sessionFactory;
public void testFoo(Foo foo) throws Throwable {
System.out.println(" --- ");
sessionFactory.openSession().save(foo);
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
Explicitly opening the session using the openSession() method does not cause a problem however when I change to getCurrentSession() I am getting an exception.
I have two questions.
Is it good practice to call openSession() in every DAO method.
How can I make getCurrentSession() work so that it will not give me an exception like no active transaction present ?
Thanks.
To answer your questions:
No, its not. The #Transactional annotation that should be on your service class method calling testFoo() is opening the session for you. You should use getCurrentSession() in the DAO to get this session.
You can, but you shouldn't. That's the entire point of using the Hibernate SessionFactory with annotation based transaction management. As long as you are marking your service methods transactional, you shouldn't have a problem.
As a side note, why are you not Autowiring your SessionFactory? Don't use setters to set something that should be Autowired. Otherwise you may as well not use Spring.

Groovy and spring under same transaction

I'm struggling with legacy code. I'm creating unit tests so I've decided to use groovy to fill database with required legacy data. Normally in my code I using ibatis for persistence. I'd like to rollback test in the end. Problem is that when I create row via groovy then I use it's id to create row via ibatis I get constraint violation exception - parent key not found.
When I use groovy to persist parent and than create child based on parents id it works perfectly fine.
Also I can't use #Transactional because of problems with XML parser (legacy code FTW :/ )
#ContextConfiguration(locations = [ "../dao/impl/ibatis/spring-data-context-config.xml", "classpath:/pl/com/betacom/treq/dao-context.xml"])
#RunWith(SpringJUnit4ClassRunner.class)
public class FinancingForIltCreationTest {
#Autowired
IFinancingForIltDAO financingForIltDAO;
#Autowired
Sql sql;
#Autowired
DataSourceTransactionManager transactionManager;
private TransactionStatus transactionStatus;
#Before
public void setUp() {
transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
}
#After
public void tearDown() {
transactionManager.rollback(transactionStatus);
transactionStatus = null;
}
#Test
public void shallCreateFinancingForIlt() throws Exception {
//given
IltOffering offering = new IltOffering("GOING_DOWN_TO_UBERGROUND", offeringTempId, java.sql.Date.valueOf("2011-07-21"), java.sql.Date.valueOf("2012-07-21"));
offering.insert(sql); // it's inserted by groovy
//when
FinancingForIltDTO financingForIltDTO = createFinancingForIlt(offering.id).build(financingForIltDAO); // it's my assembler inserting via iBatis
//then
assertNotNull(financingForIltDTO.id);
}
Configuration looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dataSourceIn"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>####</value>
</property>
<property name="url">
<value>####</value>
</property>
<property name="username">
<value>####</value>
</property>
<property name="password">
<value>####</value>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dataSourceIn" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
<bean id="sql" class="groovy.sql.Sql">
<constructor-arg ref="dataSource" />
</bean>
Unfortunately it was a database schema issue.

Resources