Hibernate and JDBC in one transaction - java

I have a method, marked as #Transactional.
It consists of several functions, one of them uses JDBC and the second one - Hibernate, third - JDBC.
The problem is that changes, made by Hibernate function are not visible in the last functions, that works with JDBC.
#Transactional
void update() {
jdbcUpdate1();
hibernateupdate1();
jdbcUpdate2(); // results of hibernateupdate1() are not visible here
}
All functions are configured to use the same datasource:
<bean id="myDataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="targetDataSource"/>
</bean>
<bean id="targetDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" lazy-init="true" scope="singleton">
<!-- settings here -->
</bean>
myDataSource bean is used in the code. myDataSource.getConnection() is used to work with connections in jdbc functions and
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
...
}
});
is used in hibernate function.
Thanks.

First, avoid using JDBC when using hibernate.
Then, if you really need it, use to Session.doWork(..). If your hibernate version does not yet have this method, obtain the Connection from session.connection().

You can use JDBC and Hibernate in the same transaction if you use the right Spring setup:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="myDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target">
<bean class="MyDaoImpl">
<property name="dataSource" ref="dataSource"/>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
This assumes that the JDBC portion of your DAO uses JdbcTemplate. If it doesn't you have a few options:
Use DataSourceUtils.getConnection(javax.sql.DataSource) to get a connection
Wrap the DataSource you pass to your DAO (but not necessarily the one you pass to the SessionFactory) with a TransactionAwareDataSourceProxy
The latter is preferred since it hidse the DataSourceUtils.getConnection inside the proxy datasource.
This is of course the XML path, it should be easy to convert this to annotation based.

The problem is, the operations on Hibernate engine does not result in immediate SQL execution. You can trigger it manually calling flush on Hibernate session. Then the changes made in hibernate will be visible to the SQL code within the same transaction. As long as you do DataSourceUtils.getConnection to get SQL connection, because only then you'll have them run in the same transaction...
In the opposite direction, this is more tricky, because you have 1nd level cache (session cache), and possibly also 2nd level cache. With 2nd level cache all changes made to database will be invisible to the Hibernate, if the row is cached, until the cache expires.

Related

How to execute several sql-statements in one session with mybatis-string

I am setting up an application with mybatis-spring, which should execute several sql-statements (mostly selects) and print the result to the console.
My applicationContext.xml looks like this:
<context:property-placeholder location="classpath:application.properties"/>
<!-- BEANS -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${spring.datasource.driverClassName}"/>
<property name="url" value="${spring.datasource.url}"/>
<property name="username" value="${spring.datasource.username}"/>
<property name="password" value="${spring.datasource.password}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:sql_mapper.xml"/>
</bean>
<bean id="organisationMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="mapper.OrganisationMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
I have noticed that whenever I execute a sql-statement the session is created just for this statement and closes directly after execution.
Is there a way to execute multiple sql-statements in just one session, which closes itself not until all methods/statements are executed?
Thanks and Greetings.
It seems that transactions are not demarcated properly and the default behaviour (one call to mapper - one transaction - one mybatis session) is used.
In spring-mybatis the session bound to spring transaction. If transactions are not demarcated then a transaction is created (and hence mybatis SqlSession) for every call to mybatis mapper method.
In order to change this you need to properly configure spring so that:
transactions can be used at all
configure transaction boundaries that is what calls to the database should be executed in one transaction
There are many ways to configure transactions for details see documentation. I'll show here the simple way that uses xml configuration.
Firstly, add transaction manager to the spring configuration:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
Then you need to specify transactions boundaries. The simple way is to use declarative transaction management:
Add this to spring configuration
<tx:annotation-driven/>
And then use #Transactional annotation on methods that should be executed transactionally:
#Service
class SomeService {
#Autowired MyMapper mapper;
#Transactional
SomeResult someMethod () {
Piece1 piece1 = mapper.getSome();
Piece2 piece2 = mapper.getMore();
SomeResult result = SomeResult(piece1, piece2);
}
}

Manually open a hibernate session with Spring config

The issue I am having is that I use Spring to manage and load hibernate for my web application. I am currently using OpenSessionInViewFilter. This works as intended when I am viewing the application, but not so well when I am trying to access hibernate from non-view related activities such as a Quartz task or some Runnable thread I create to help with some tasks. This causes the Lazy initialize exception and no session available exceptions to occur.
Here is how I currently use Spring to manage Hibernate
<bean id="mainDataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
[..DB config..]
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="mainDataSource"/>
</property>
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
<property name="dataSource"><ref local="mainDataSource"/></property>
</bean>
I then configure DAO objects which extend HibernateDaoSupport and inject them into service classes
<bean id="myDAO"
class="package.myDAO">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<bean id="mySvcTarget" class="package.myService">
<property name="myDAO"><ref bean="myDAO"/></property>
</bean>
<bean id="myService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="txManager"/>
</property>
<property name="target">
<ref bean="mySvcTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
So then in my application, myService is injected into my controller classes so I use that to get access to my DAO's. For my situation though it appears I need to access my DAO's (or service preferably) some other way and manually open and close my hibernate sessions since my service classes only seem to be open during view session. I am not exactly sure the best way to do this. All the hibernate configurations are there already in Spring so I'm assuming its just a matter or calling them somehow.
First of all those additional services that you're using (non-views) should be visible by Spring. The simplest way to do it is to use #Service annotation. And to make it work you can add <context:component-scan base-package="your.package"> in your configuration.
After this, if Spring sees your service as a bean, it should be enough to use #Transactional annotation to have Hibernate session in it.

JPA with Spring Data

I'm a newbie with Java and I need to create a console application that is going to connect with 4 databases (access, vfp, mysql and sqlserver).
I started with hibernate.cfg.xml files and managed to configure them, one for each database. Then I realized that jpa was a better solution. So I changed all my hibernate files to a persistence.xml file with 4 persistence-unit.
The databases are working well, but to use them I have to create a lot of code. This is an example:
EntityManagerFactory dbPersistence =
Persistence.createEntityManagerFactory("oneOfMyDatabases");
EntityManager em = dbPersistence.createEntityManager();
Query query = em.createQuery("from ProductEntity").setMaxResults(10);
for (Object o : query.getResultList()) {
ProductEntity c = (ProductEntity) o;
System.out.println("Product " + c.getName());
}
cgPersistence.close();
I need to update one of the databases with data from the other databases.
It's a pain to create all the code like this, so I was thinking about creating repositories but I can't see how to create them with different entityManagers for each database.
I tried to inject the managers with google guice without success, but I couldn't handle how to close or where to close the persistence connection.
Finally, I've found Spring Data and it seems to be what I need, but I don't really understand how to use it.
I need some guide to get it working because I've read tons of tutorials and each of them appear to be different:
· Can I use the same persistence.xml or do I need another configuration file? I've seen that Spring Data has jpa compatibility but I'm not sure how it works.
· Is Spring Context the IOC Container of Spring Framework? Do I need it to work with Spring Data?
· What else do I need?
Thank you in advance
Each database is going to be represented by a different data source. For every data source you need a different session factory/entity manager factory.
If you want to save in more than one data source in a single transaction you then need XA transactions, therefore a Java EE or a stand-alone transaction manager, like Bitronix or Atomikos.
You have 4 different entity manager factories, you also need specific repositories for each of those:
<jpa:repositories base-package="your.company.project.repository.access" entity-manager-factory-ref="accessEntityManagerFactory"/>
<jpa:repositories base-package="your.company.project.repository.sqlserver" entity-manager-factory-ref="sqlserverEntityManagerFactory"/>
Then your application doesn't have to care which repository it uses.
The JpaTransactionManager requires one entityManagerFactory, but since you have 4 you may end up creating for of those, which I think it's undisirable.
It's better to switch to JTA instead:
<!-- 1. You define the Bitronix config ->
<bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
<property name="serverId" value="spring-btm"/>
<property name="warnAboutZeroResourceTransaction" value="true"/>
<property name="logPart1Filename" value="${btm.config.logpart1filename}"/>
<property name="logPart2Filename" value="${btm.config.logpart2filename}"/>
<property name="journal" value="${btm.config.journal:disk}"/>
</bean>
<!-- 2. You define all your data sources ->
<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init"
destroy-method="close">
<property name="className" value="${jdbc.driverClassName}"/>
<property name="uniqueName" value="dataSource"/>
<property name="minPoolSize" value="0"/>
<property name="maxPoolSize" value="5"/>
<property name="allowLocalTransactions" value="false"/>
<property name="driverProperties">
<props>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
<prop key="url">${jdbc.url}</prop>
</props>
</property>
</bean>
<!-- 3. For each data source you create a new persistenceUnitManager and you give its own specific persistence.xml ->
<bean id="persistenceUnitManager" depends-on="transactionManager"
class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml"/>
<property name="defaultDataSource" ref="dataSource"/>
<property name="dataSourceLookup">
<bean class="org.springframework.jdbc.datasource.lookup.BeanFactoryDataSourceLookup"/>
</property>
</bean>
<!-- JpaDialect must be configured for transactionManager to make JPA and JDBC share transactions -->
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
<!-- 4. For each data source you create a new entityManagerFactory ->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="persistenceUnitManager" ref="persistenceUnitManager"/>
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<!-- 5. You have only one JTA transaction manager ->
<bean id="jtaTransactionManager" factory-method="getTransactionManager"
class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig, dataSource"
destroy-method="shutdown"/>
<!-- 6. You have only one Spring transaction manager abstraction layer ->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="jtaTransactionManager"/>
<property name="userTransaction" ref="jtaTransactionManager"/>
</bean>
If this proves too much work for your current problem, you can give a try to having 4 JPA transaction managers and see how it works.
If your schema is same across all the databases, then the best approach is to use hibernate,because hibernate provides portability between multiple databases. once you create the hbm files and pojo classes, the only thing you need to change is the dialect and your datasource.
The transactionmanager support can be provided by spring.

Using HibernateTransactionManager for jdbc template

I was working on an spring application before which uses HibernateTransactionManager for transactions with queries/updates using jdbc template and works fine as well.
Below was the code used
<bean id="sybaseDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="MySybaseDS"/>
<property name="lookupOnStartup" value="false"/>
<property name="cache" value="true" />
<property name="proxyInterface" value="javax.sql.DataSource" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="sybaseDataSource" />
<property name="mappingResources">
<list>..........</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.SybaseAnywhereDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
I am creating a new application and confused now whether to use the HibernateTransactionManager or JpaTransactionManager or both(possible?).
The application will use both HibernateTemplate and JdbcTemplate(for bulk updates) for database operations and need to be transactional.
I mean, can/should I use HibernateTransactionManager for transactions with jdbctemplate and HibernateTemplate database operations? Will there be any performance bottleneck of using one transaction manager over another?
The short answer is that it will work. See also this thread for a description of the low-level details.
If the application is JPA based (an entity manager factory is configured), then the JpaTransactionManager should be used.
This transaction manager binds an entity manager to the thread during the duration of the transaction.
The HibernateTransactionManager is for applications that don't use JPA but use instead use Hibernate directly (a session factory is configured).
This transaction manager binds a hibernate session to the thread instead of an entity manager.
They do basically the same, it's just one handles a Session while the other handles an EntityManager. Their choice is simply dictated by the fact that JPA is used or not.
Both transaction managers are compatible with the JdbcTemplate, according to their respective javadocs.
There are no performance consequences of using one transaction manager versus the other.
Internally the entity manager uses a Hibernate session, so the end result is the same: a hibernate session is binded to the thread directly or indirectly during the duration of the transaction.

Spring Transactions in different DAOs does not work anyway?

Here is my implementation in summary
1) all DAOs implemented using HibernateDAO support/ #Transational annotation is only used in Service layer
2) Use MySQL / HibernateTransactionManager
3) Test using main(String args[]) method (do transactions work using this method? )
Transactions does not get rollbacked and invalid entried can be seen in the database.
What am I doing wrong here?
Detailed information is given below.
1) I have configured transactions using #Transactional annotation in service layer as:
#Transactional(readOnly=false, rollbackFor={DuplicateEmailException.class,DuplicateLoginIdException.class,IdentityException.class},propagation=Propagation.REQUIRES_NEW)
public void createUserProfile(UserProfile profile)
throws DuplicateEmailException, DuplicateLoginIdException,
IdentityException {
//Accessing DAO (implemented using HibernateDAOSupport)
identityDAO.createPrincipal(profile.getSecurityPrincipal());
try {
//Accessing DAO
userProfileDAO.createUserProfile(profile);
} catch (RuntimeException e) {
throw new IdentityException("UseProfile create Error", e);
}
}
2) My Transation Manager configuration / datasource is as follows :
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/ibmdusermgmt" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="defaultAutoCommit" value="false"/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingLocations"
value="classpath*:hibernate/*.hbm.xml" />
<property name="hibernateProperties">
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.query.substitutions">
true=1 false=0
</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.use_outer_join">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
3) Test using main() method as follows :
public static void main(String[] args) {
ApplicationContext cnt=new ClassPathXmlApplicationContext("testAppContext.xml");
IUserProfileService upServ=(IUserProfileService)cnt.getBean("ibmdUserProfileService");
UserProfile up=UserManagementFactory.createProfile("testlogin");//
up.setAddress("address");
up.setCompany("company");
up.setTelephone("94963842");
up.getSecurityPrincipal().setEmail("security#email.com");
up.getSecurityPrincipal().setName("Full Name");
up.getSecurityPrincipal().setPassword("password");
up.getSecurityPrincipal().setSecretQuestion(new SecretQuestion("Question", "Answer"));
try {
upServ.createUserProfile(up);
} catch(Exception e){
e.printStackTrace();
}
As far as I know, MySQL's default storage engine MyISAM does not support transactions.
MySQL Server (version 3.23-max and all versions 4.0 and above) supports transactions with the InnoDB and BDB transactional storage engines. InnoDB provides full ACID compliance. ... The other nontransactional storage engines in MySQL Server (such as MyISAM) follow a different paradigm for data integrity called “atomic operations.” In transactional terms, MyISAM tables effectively always operate in autocommit = 1 mode
In order to get transactions working, you'll have to switch your storage engine for those tables to InnoDB. You may also want to use Hibernate's MySQLInnodDBDialect if you generate your tables (hdm2ddl) from your mappings:
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
As mentioned by #gid, it's not a requirement for transactions though.
Yes. you can call transactional objects from main().
#sfussenegger is correct in that MyISAM doesn't support transactions, use of the MySQLDialect dialect doesn't exclude the use of transactions it only means that the hdm2ddl table generation will create InnoDB type tables. (which of course could be your issue).
Can you confirm you are using a transactional type table in MySQL?
Assuming this is true the next thing to do is get some debug output from org.springframework.transaction.support.AbstractPlatformTransactionManager. How to do this depends on which logging framework you are using but it should be straight forward.

Categories

Resources