Tranaction Manager in Spring JPA DAO - spring

I've used the following configuration in spring and JPA,
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="entityManger"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="JPA-01" />
</bean>
<bean id="dao" class="springdao.MessageDAO" />
</beans>
and my persistence.xml is,
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="JPA-01">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.archive.autodetection" value="class, hbm" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/Hibernate" />
<property name="hibernate.connection.username" value="root" />
<property name="hibernate.connection.password" value="password" />
<property name="hibernate.c3p0.min_size" value="5" />
<property name="hibernate.c3p0.max_size" value="20" />
<property name="hibernate.c3p0.timeout" value="300" />
<property name="hibernate.c3p0.max_statements" value="50" />
<property name="hibernate.c3p0.idle_test_period" value="3000" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<!-- <property name="hibernate.hbm2ddl.auto" value="create" />
-->
</properties>
</persistence-unit>
</persistence>
and my junit is,
package test;
import junit.framework.Assert;
import model.Message;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import dao.MessageDAO;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:SpringDAOTest.xml"})
public class SpringDAOTest {
#Autowired
springdao.MessageDAO dao;
#Test
public void testGetMessageById() {
Message message = dao.getMessageById("1");
Assert.assertNotNull(message);
System.out.println(message);
}
#Test
public void testPersistMEssage() {
Message message = new Message();
message.setMessage("Hello World This is the second Message");
dao.persistMessage(message);
}
#Test
public void testUpdateMessage() {
String updatedMessage = "Updated Message for ID 1";
dao.updateMessage(updatedMessage, 1);
}
// TODO updateMessageID
#Test
public void testUpdateMessageID() {
String updatedMessage = "Updated Message for ID 1 with 25";
dao.updateMessageID(updatedMessage, 1);
}
}
I've following questions,
I've autowired the persistence context. I want to assert that the current persistent context is associtated or not?
I've not used the transaction manager configured in the applicationcontext.xml. Just by annotating the classes, I'm able to configure transactions to the services.
I want to get the entity transaction id, so that I want to assert that for the entire service (which involves a lot of daos) uses the same transaction.

1) Provide a getter method for EntityManager in your MessageDAO and use assertNotNull (Although not advisable and not required to test this scenario; you should be testing only your business logic and trust the framework will correctly associate the EntityManager)
2) Reading data from database doesn't require transaction. However, writing data to database does require transaction. Therefore you need to configure a transaction manager for your persistent operation like below
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
You can make a method execute under a transaction in two ways
XML configuration
#Transactional annotation
Annotating your method using #Transactional without a TransactionManager will silently ignore it.
3) I'm not aware of retrieving a transaction id.
However, the primary intention of using a framework like Spring is that someone has already tested infrastructure code for you and you can concentrate on testing only your business logic. Trying to retest may not be a good idea.

As you are using entity manager as the data-access API you can use following transaction manager configuration
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource"/>
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
By default this transaction manager picks up the datasource bean if named "dataSource" and "entityManagerFactory". This transaction manager variety binds the entity manager from the specified entitymanager factory with the current thread. Also it would be worthwhile to have a look at its documentation.

Related

test hibernate entities with spring

I try to tests how hibernate Entities are persisted to database. I have 5 tests for 3 Entitites. All my attempts are unsuccessful((( I tried to use #Transactional annotation to bind each test to one transaction. But I could not (tried all combinations). Now I try to test it manually creating sessions and transactions. But now problem is that I dont know how to create one session for all tests. I tried #BeforeClass, but here problem is that it is static and I use Spring beans for session creation. Any ideas how I can test hibernate Entities?
You can use a dedicated database like HSQLDB for testing you entities via DAO test classes.
You will need a:
Maven test profile
Spring configuration file (test context)
Script SQL to insert some data
DAO class test.
Maven test profile :
<profile>
<id>test</id>
<properties>
<jpa.dialect>org.springframework.orm.jpa.vendor.HibernateJpaDialect</jpa.dialect>
<jpa.vendor.adapter>HibernateJpaVendorAdapter</jpa.vendor.adapter>
<jdbc.dialect>org.hibernate.dialect.HSQLDialect</jdbc.dialect>
<jdbc.url>jdbc:hsqldb:mem:testDatabase</jdbc.url>
<jdbc.driver>org.hsqldb.jdbcDriver</jdbc.driver>
<jdbc.username>sa</jdbc.username>
<jdbc.password></jdbc.password>
<jdbc.format_sql>true</jdbc.format_sql>
<jdbc.show_sql>true</jdbc.show_sql>
<!-- import.sql is read : it must have this name and be in classpath -->
<jpa.generateDdl>true</jpa.generateDdl>
<hibernate.format_sql>true</hibernate.format_sql>
<hibernate.hbm2ddl.auto>create</hibernate.hbm2ddl.auto>
</properties>
</profile>
Spring test-applicationContext.xml file :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<!-- enables interpretation of the #PersistenceUnit/#PersistenceContext
annotations providing convenient access to EntityManagerFactory/EntityManager -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<context:component-scan base-package="foo.bar.dao">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Repository" />
</context:component-scan>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="jpaLibrary" />
<property name="jpaDialect">
<bean class="${jpa.dialect}" />
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.${jpa.vendor.adapter}">
<property name="showSql" value="${jdbc.show_sql}" />
<property name="databasePlatform" value="${jdbc.dialect}" />
<!-- On genere la BDD au demarrage -->
<property name="generateDdl" value="${jpa.generateDdl}" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager" />
<context:spring-configured />
<context:annotation-config />
</beans>
import.sql (authors for instance):
INSERT INTO `testDatabase`.`authors`(author_id, name) VALUES
(1, "JRR Tolkien"),
(2, "Albert Camus"),
(3, "Victor Hugo");
...
Finally, the class to test your DAO (then entities) :
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import foo.bar.dao.BookDao;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/test-applicationContext.xml" })
public class BookDaoTest extends AbstractTransactionalJUnit4SpringContextTests {
#Autowired
private BookDao bookDao;
private int initialSize = 0;
#Before
public void init() {
initialSize = bookDao.findAll().size();
}
#Test
public void getAllBooks() {
assertEquals(12, initialSize);
}
...
}

Unable to get correct session factory

I am using hibernate along side spring to configure the session factory and hibernate transaction manager, below mentioned is the apllication-context file---
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd">
<beans profile="nondev,dev">
<context:annotation-config />
<context:component-scan base-package="com.woodmac" />
<import resource="classpath:spring/applicationContext-soap.xml" />
<import resource="classpath:spring/applicationContext-cache.xml" />
<import resource="classpath:spring/applicationContext-mail.xml" />
<import resource="classpath:spring/applicationContext-batchJobs.xml" />
<bean name="ecommSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="ecommDataSource" />
<property name="hibernateProperties" ref="hibernateProperties" />
<property name="packagesToScan" value="com.woodmac" />
</bean>
<bean name="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties" ref="hibernateProperties" />
<property name="packagesToScan" value="com.woodmac" />
</bean>
<bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.use_sql_comments">${hibernate.use_sql_comments}</prop>
<prop key="hibernate.connection.release_mode">after_statement</prop>
</props>
</property>
</bean>
<!--
Source: http://static.springsource.org/spring/docs/3.0.0.RC3/reference/html/ch12s03.html
A DataSource is part of the JDBC specification and is a generalized connection factory. It allows a container or a
framework to hide connection pooling and transaction management issues from the application code.
NOTE: DriverManagerDataSource
class is not an actual connection pool; it does not actually pool Connections. It just serves as simple replacement for a
full-blown connection pool, implementing the same standard interface, but creating new Connections on every call. -->
<!--
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="minPoolSize" value="${hibernate.c3p0.minPoolSize}" />
<property name="maxPoolSize" value="${hibernate.c3p0.maxPoolSize}" />
<property name="maxIdleTime" value="${hibernate.c3p0.idleTestPeriod}" />
<property name="maxConnectionAge" value="${hibernate.c3p0.maxConnectionAge}" />
<property name="maxStatements" value="${hibernate.c3p0.max_statement}" />
<property name="testConnectionOnCheckout" value="${hibernate.c3p0.testConnectionOnCheckout}" />
<property name="preferredTestQuery" value="${hibernate.c3p0.preferredTestQuery}" />
<property name="debugUnreturnedConnectionStackTraces" value="${hibernate.c3p0.debug}" />
</bean>
-->
<!-- This bean is the Spring Hibernate transaction manager that will handle transaction related boilerplate code and wiring
for us. We needed to define the session factory that the transaction manager will use to create sessions -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" depends-on="sessionFactory">
</bean>
<!-- This bean is the spring hibernate transaction manager for new session factory -->
<bean id="ecommTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="ecommSessionFactory" depends-on="sessionFactory">
</bean>
<!-- Transaction Management tx:annotation-driven element defines that we are declaring transactions using annotations in
our classes. -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<!-- Transaction management for the ecommerce transactions -->
<tx:annotation-driven transaction-manager="ecommTransactionManager" proxy-target-class="true" />
<!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get', 'select', 'find' are read-only -->
<tx:method name="get*" read-only="true" propagation="SUPPORTS" />
<tx:method name="select*" read-only="true" propagation="SUPPORTS" />
<tx:method name="find*" read-only="true" propagation="SUPPORTS" />
<tx:method name="fetch*" read-only="true" propagation="SUPPORTS" />
<!-- other methods use the default transaction settings -->
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
</beans>
<!-- Note: Only use the DriverManagerDataSource class should only be used for testing purposes since it does not provide
pooling and will perform poorly when multiple requests for a connection are made. -->
<beans profile="dev-notinuse">
<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}">
</bean> -->
<!-- <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="${portal.ds.jndi}"/>
</bean> -->
</beans>
<beans profile="nondev, dev">
<jee:jndi-lookup id="ecommDataSource" jndi-name="${ecommerce.ds.jndi}" lookup-on-startup="true" lazy-init="false" expected-type="javax.sql.DataSource" proxy-interface="javax.sql.DataSource"/>
<jee:jndi-lookup id="dataSource" jndi-name="${portal.ds.jndi}" lookup-on-startup="true" lazy-init="false" expected-type="javax.sql.DataSource" proxy-interface="javax.sql.DataSource"/>
</beans>
</beans>
Now in the test DAO that I have written to call a stored procedure ---
package com.woodmac.component.event.dao.impl;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import oracle.jdbc.OracleTypes;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.woodmac.component.event.dao.IEventDAO;
import com.woodmac.generic.dao.impl.EcommDaoImpl;
/**
* The Class EventDAOImpl.
*/
#Component
#Qualifier("eventDAO")
#Transactional
#DependsOn("ecommSessionFactory")
public class EventDAOImpl extends EcommDaoImpl implements IEventDAO{
public Integer callSqlBlock(){
Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
final Integer outputValue =1;
session.doWork(new Work() {
#Override
public void execute(Connection conn)
throws SQLException {
CallableStatement stmt = conn.prepareCall("? = call test(?)");
stmt.registerOutParameter(1, OracleTypes.INTEGER);
stmt.setString(2, "callIndex");
stmt.execute();
Integer output = stmt.getInt(1);
System.out.println("outputValue is"+outputValue);
}
});
return outputValue;
}
}
But unfortunately this is giving me a different session factory than expected that is the connection string I am getting is of the other schema henceforth I am getting a bad sql exception.
Where I am going wrong on this?
So what you're saying is that getHibernateTemplate() returns a template with the wrong session factory? You're creating 2 session factories but not explicitly stating which one gets assigned to the EventDAOImpl instance.
#DependsOn only states that the object will be created by Spring after the ecommSessionFactory, that does not mean that it will be given that one.
See here
Beans on which the current bean depends. Any beans specified are guaranteed to be created by the container before this bean. Used infrequently in cases where a bean does not explicitly depend on another through properties or constructor arguments, but rather depends on the side effects of another bean's initialization.
May be used on any class directly or indirectly annotated with Component or on methods annotated with Bean.
I'd create the EventDAOImpl in my XML file and specify the SessionFactory bean that you want injected in the XML (either the ecommSessionFactory or the sessionFactory, I'm not sure which you want). Something like this:
<bean name="eventDAO" class=".....EventDaoImpl">
<property name="hibernateTemplate" ref="whicheverOneItShouldBe" />

database not updated using JPA and spring when new entries are made

I am configuring a simple JPA application using Spring framework.
My goal is to populate the db with data during JUnit Test runs. I understand this is not ideal. But I want it for different purposes.
Here is my persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
version="1.0">
<persistence-unit name="tothought-tutorial-test" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<!-- <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<property name="hibernate.hbm2ddl.auto" value="create-drop" /> -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
test-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.2.xsd">
<!-- Database -->
<!-- <jdbc:embedded-database id="datasource" type="H2"></jdbc:embedded-database> -->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/to_thought_tutorial" />
<property name="username" value="demo" />
<property name="password" value="demo" />
</bean>
<!-- Entity Manager -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="datasource" />
<property name="persistenceUnitName" value="tothought-tutorial-test" />
</bean>
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- Jpa Repositories -->
<jpa:repositories base-package="com.cloudfoundry.tothought.repositories"></jpa:repositories>
</beans>
Here is junitTest class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="classpath:META-INF/test-context.xml")
#Transactional
public class PostPartRepositoryTest {
#Autowired
PostPartRepository repository;
#Test
public void test() {
PostPart postPart = new PostPart();
String body = "Hello";
postPart.setBody(body);
repository.save(postPart);
PostPart dbPostPart = repository.findOne(postPart.getPostPartId());
assertNotNull(dbPostPart);
assertEquals(body, dbPostPart.getBody());
}
#Test
public void insertTest(){
Post post = new Post();
post.setPostDate(new Date());
post.setTitle("First Post");
PostPart postPart = new PostPart();
String body = "Hello";
postPart.setBody(body);
postPart.setPost(post);
repository.save(postPart);
PostPart dbPostPart = repository.findOne(postPart.getPostPartId());
assertNotNull(dbPostPart);
assertNotNull(dbPostPart.getPost());
assertEquals(body, dbPostPart.getBody());
}
}
Both the test passes. But I do not see any entry in tables, although tables are created the first time.
You won't see any entries because you are running the test with #Transactional using the SpringJUnit4ClassRunner. The default for running tests with #Transactional is for any database changes to be automatically rolled back once the test is finished. For more information see this question here
The order in which these things happen is as follows:
Test starts, setup scripts are run, which creates your tables.
Tests start running in transactional context.
Tests finish running, and any database changes made during your test, are automatically rolled back, leaving the database in the state it was in before the tests ran.
You can change this default behaviour you can use #TransactionConfiguration(defaultRollback=false) at the top of the class. However this is bad practice because unit tests should not depend on each other and shouldn't be permanently modifying application state. Also, if you are depending on one test to set something in the environment and use it in the next test, there is no guarantee which order the tests are executed by test runner.

JBoss AS7 + Oracle 11g + Spring 3.1 + JPA 2 - Multiple DSs, PUs, EMs, TMs

I'm building an application that needs CRUD operations on two separate databases. The transactions are applied to one database or the other (never both...so no need for JTA is my understanding). My setup is pretty close to what is found here: Multiple database with Spring+Hibernate+JPA
The problem: My server (JBoss AS7) starts up fine. The application reads from both datasources, say DS1 and DS2, BUT it can only manipulate data from DS1. I can see sequences (Oracle 11g) being updated but no table updates. There are no errors/exceptions thrown. I suspect one of my transaction managers isn't committing.
Below is a list of technologies used and configuration settings...
Tech Stack
JBoss AS7
Oracle 11g
Spring 3.1
JPA 2
Hibernate 4.1
persistence-ds1.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="pu1">
<class>com.somepackage.EntityA</class>
<class>com.somepackage.EntityB</class>
<class>com.somepackage.EntityC</class>
<validation-mode>CALLBACK</validation-mode>
<properties>
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.DefaultNamingStrategy" />
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="hibernate.hbm2ddl.auto" value="validate" />
</properties>
</persistence-unit>
</persistence>
persistence-ds2.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="pu2">
<class>com.somepackage.EntityD</class>
<class>com.somepackage.EntityE</class>
<class>com.somepackage.EntityF</class>
<validation-mode>CALLBACK</validation-mode>
<properties>
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.DefaultNamingStrategy" />
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="hibernate.hbm2ddl.auto" value="validate" />
</properties>
</persistence-unit>
</persistence>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="ds1" jndi-name="java:jboss/datasources/DS1"
expected-type="javax.sql.DataSource" />
<jee:jndi-lookup id="ds2" jndi-name="java:jboss/datasources/DS2"
expected-type="javax.sql.DataSource" />
<bean id="em1" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="emf1" />
<property name="persistenceUnitName" value="pu1" />
</bean>
<bean id="em2" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="emf2" />
<property name="persistenceUnitName" value="pu2" />
</bean>
<bean id="emf1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence-ds1.xml"/>
<property name="dataSource" ref="ds1" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
<bean id="emf2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence-ds2.xml"/>
<property name="dataSource" ref="ds2" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
<tx:annotation-driven transaction-manager="txm1" />
<tx:annotation-driven transaction-manager="txm2" />
<bean id="txm1" class="org.springframework.orm.jpa.JpaTransactionManager">
<qualifier value="txMgr1"/>
<property name="entityManagerFactory" ref="emf1" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
<bean id="txm2" class="org.springframework.orm.jpa.JpaTransactionManager">
<qualifier value="txMgr2"/>
<property name="entityManagerFactory" ref="emf2" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
</beans>
In my DAOs, I reference the transaction managers at the class-level as follows.
#Transactional("txm1")
public class DAO1 { ... }
#Transactional("txm2")
public class DAO2 { ... }
I resolved my issue!
In my applicationContext.xml, I removed the following.
<tx:annotation-driven transaction-manager="txm1" />
<tx:annotation-driven transaction-manager="txm2" />
And used the following instead.
<tx:annotation-driven />
But here's what I believe was the kicker (main problem). In my DAOs, I was assigning the two transaction managers at the class-level. But then I was overriding them with the way I was declaring my methods.
#Transactional(readOnly = false, value = "txm1")
public abstract class AbstractJpaDAO1<T extends Serializable> {
...
#Transactional(readOnly = true)
public T findById(final Long id) {...}
#Transactional
public boolean insert(final T entity) {...}
As you can see, the #Transaction annotations on the methods were overriding the the one at the class-level. And because there was no transaction manager specified on the methods, Spring defaulted to "transactionManager", which I didn't (and still don't) have declared in my applicaitonContext.xml. So, it was trying to commit using a transaction manager that didn't exist.
For the resolution, I just removed the #Transitional annotations on the methods, and kept the one at the class-level.
#Transactional(readOnly = false, value = "txm1")
public abstract class AbstractJpaDAO1<T extends Serializable> {
...
public T findById(final Long id) {...}
public boolean insert(final T entity) {...}
Now everything works! I can read/write to two separate databases.

Could not open JPA EntityManager for transaction in Spring Test

I have a java project using Spring 3.0 , JPA 2.0 with MyEclipse IDE
Trying to convert some basic dao integration tests to spring and have run into a few issues. Here's my setup:
LevelDAO
public class LevelDAO extends JpaDaoSupport implements ILevelDAO
{
public void save(Level entity) {
logger.info("saving Level instance");
try {
getJpaTemplate().persist(entity);
logger.info("save successful");
} catch (RuntimeException re) {
logger.error("save failed", re);
throw re;
}
}
}
Unit Test
#Transactional
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:applicationContext.xml"})
#TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
public class LevelDaoImplTests {
LevelDAO levelDao;
#Test
public void shouldSaveNewLevels() {
levelDao= new LevelDAO();
Level l = new Level();
l.setName = "test";
levelDao.save(l);
}
}
Persistence.Xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="t1" transaction-type="RESOURCE_LOCAL">
<provider>
org.eclipse.persistence.jpa.PersistenceProvider
</provider>
<class>com.nlg.model.Level</class>
<properties>
<property name="javax.persistence.jdbc.driver"
value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://myserver.rds.amazonaws.com:3306/" />
<property name="javax.persistence.jdbc.user" value="myuser" />
<property name="javax.persistence.jdbc.password" value="mypass" />
</properties>
</persistence-unit>
applicationContext.xml
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="t1" />
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory"
ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean
id="LevelDAO" class="com.nlg.model.LevelDAO">
<property name="entityManagerFactory"
ref="entityManagerFactory" />
</bean>
At first I recieved this
Value '2.0' of attribute 'version' of element 'persistence' is not valid
So reading another post which suggested changing this to "1.0"
and now recieve
java.lang.IllegalStateException: Failed to load ApplicationContext
That issue was resolved with the < provider > tag however hasn't fixed my issue:
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is Exception [EclipseLink-4021] (Eclipse Persistence Services - 1.0.2 (Build 20081024)): org.eclipse.persistence.exceptions.DatabaseException
Exception Description: Unable to acquire a connection from driver [null], user [null] and URL [null].
My concern is that by downgrading the version no. to "1.0" that I'm also overlooking the issue of compatibility within this stack. I have non-spring versions of these tests working, so any ideas on where I've gone wrong appreciated.
Thanks
Unable to acquire a connection from driver [null], user [null] and URL [null]
These are JPA 2.0 properties:
<property name="javax.persistence.jdbc.driver"
value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://myserver.rds.amazonaws.com:3306/" />
<property name="javax.persistence.jdbc.user" value="myuser" />
<property name="javax.persistence.jdbc.password" value="mypass" />
So if you switched back to 1.0, you'd probably want:
<property name="eclipelink.jdbc.url" value="jdbc:mysql://myserver.rds.amazonaws.com:3306/"/>
<property name="eclipelink.jdbc.user" value="myuser"/>
<property name="eclipelink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="eclipelink.jdbc.password" value="mypass"/>
But then I would recommend to solve "that other problem" and stay with 2.0
Here is an example of JPA 2.0 over EclipseLink that works ( notice persistence version="2.0" ):
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="eclipselinktest" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<!-- list all classes -->
<class>com.codesmuggler.model.User</class>
<properties>
<!-- some properties needed by persistence provider:
- driver
- db url
- db user name
- db user password -->
<property name="javax.persistence.target-database" value="PostgreSQL"/>
<property name="javax.persistence.logging.level" value="INFO"/>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/testdb"/>
<property name="javax.persistence.jdbc.user" value="testuser"/>
<property name="javax.persistence.jdbc.password" value="testpassword"/>
<!-- for testing purpose every time application is launched drop and create tables
in production mode - this line should be removed or commented out
-->
<property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
</properties>
</persistence-unit>
</persistence>
Take a look at the example in full

Resources