I'm new to spring so I don't know if everything is configured correctly. Though I use the spring IDE and a unittest provided to be correct.
I'm having a User class
#Entity
#NamedQuery(name="findUser4Email",
query="SELECT * " +
"FROM User " +
"WHERE email = :userEmail")
public class User extends AbstractNamedDomain {
private String name;
private String email;
private String password;
...
I want to keep this class persistent with a DB. I use hsqldb to send queries to my db and so on.
Now I have a DAO layer for my user which looks like this :
public class UserManager implements IUserManager {
#PersistenceContext
private EntityManager em;
public User findUser4Email(String email) {
return (User) em.createNamedQuery("findUser4Email").setParameter("userEmail",
email).getSingleResult();
}
public User storeUser(User user) {
//User u = em.merge(user);
em.persist(user);
return user;
}
...
my beans look like this :
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="BankingWeb" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
<property name="databasePlatform" value="${hibernate.dialect}" />
</bean>
</property>
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean name="AccountManager" class="ssel.banking.dao.jpa.AccountManager" />
<bean name="UserManager" class="ssel.banking.dao.jpa.UserManager" />
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
When I try to store a User in a unittest (just by calling the store(User) from the dao the test fails. Nor do I see any table created in my DB when I look at it with hsqldb. What is wrong my code ? Or what do I miss?
this is what my datasourcebean looks like
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
You have to define a DataSource bean like this:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.0.0.1:3306/yourDB"/>
<property name="username" value=""/>
<property name="password" value=""/>
</bean>
(Or a JNDI lookup)
<jee:jndi-lookup jndi-name="jdbc/datasource" id="dataSource" />
You can follow this tutorial to help you setting everything up.
Related
I am learning JTA and I have two databases one from oracle and another one from mysql. I have noticed a weird behavior I hope someone can shed some light please, the code and spring config are shown below.
Please find use case below:
When two save operations (userRepository.save,tutorialsRepository.save) from different database are called from the method (checkDistributedTransactions) and a runtime exception is thrown at the end purposely of the method then both table operation rolls back which is as expected for JTA.
When a find method(findByActive) is added in between the two save methods and a runtime exception is thrown at the end, only the last save method(tutorialsRepository.save) rolls back and the first one does not roll back. The issue is that I would expect both save method to roll back or i am wrong?
Finally when the find method is added before the two save method and a runtime exception is thrown at the end then both save method roll back as expected.
Please find implementation class below:
#Component
public class UserServiceImp {
#Autowired
UserRepository userRepository;
UserTable user;
#Autowired
TutorialsRepository tutorialsRepository;
Tutor tutorials;
String name;
#Transactional()
public void checkDistributedTransactions() {
user = new UserTable();
user.setUsername("testFoo");
user.setFirstname("firstname");
user.setLastname("lastname");
user.setActive("Y");
userRepository.save(user);
List<UserTable> userTableList = userRepository.findByActive("Y");
System.out.println("userTableList " + userTableList.size());
tutorials = new Tutor();
tutorials.setTutorial_id("3");
tutorials.setTutorial_title("hrm");
tutorialsRepository.save(tutorials);
throw new EmptyStackException();
}
}
Please find app context below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:transaction="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:component-scan base-package="org.example" />
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="catalogEntityManagerFactory">
<property name="jpaProperties">
<map>
<entry key="hibernate.transaction.jta.platform" value-ref="jtaPlatform" />
</map>
</property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jtaDataSource">
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe" />
<property name="username" value="hr" />
<property name="password" value="unknown" />
</bean>
</property>
<property name="packagesToScan" value="org.example.domain.catalog" />
</bean>
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="directoryEntityManagerFactory">
<property name="jpaProperties">
<map>
<entry key="hibernate.transaction.jta.platform" value-ref="jtaPlatform" />
</map>
</property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jtaDataSource">
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/mysqlDb" />
<property name="username" value="root" />
<property name="password" value="unknown" />
</bean>
</property>
<property name="packagesToScan" value="org.example.domain.directory" />
</bean>
<jpa:repositories base-package="org.example.domain.directory"
entity-manager-factory-ref="directoryEntityManagerFactory" />
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
id="jpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
<property name="showSql" value="true" />
</bean>
<context:annotation-config />
<jpa:repositories base-package="org.example.domain.catalog"
entity-manager-factory-ref="catalogEntityManagerFactory" />
<transaction:annotation-driven />
<bean class="java.lang.String" id="jtaPlatform">
<constructor-arg value="com.atomikos.icatch.jta.hibernate4.AtomikosPlatform" />
</bean>
<bean class="org.springframework.transaction.jta.JtaTransactionManager"
id="transactionManager">
<property name="transactionManager">
<bean class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<property name="forceShutdown" value="false" />
</bean>
</property>
<property name="userTransaction">
<bean class="com.atomikos.icatch.jta.J2eeUserTransaction">
<property name="transactionTimeout" value="300" />
</bean>
</property>
<property name="allowCustomIsolationLevels" value="true" />
</bean>
</beans>
The method to call the implementation class below:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "springContext.xml" });
UserServiceImp userServiceImp = (UserServiceImp) context.getBean("userServiceImp");
userServiceImp.checkDistributedTransactions();
}
Any idea when the find operation is added in between the two save method then why only the last save method rolls back instead of both?
Thanks in advance
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.test.dao.CustomerDaoImpl</class>
<class>com.test.data.Customer</class>
<class>com.test.dto.CustomerDto</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.DefaultNamingStrategy"/>
<property name="hibernate.connection.charSet" value="UTF-8"/>
</properties>
</persistence-unit>
Class where I am using it:
public class CustomerDaoImpl implements CustomerDao {
#PersistenceContext(unitName = "persistenceUnit")
private EntityManager entityManager;
#Transactional
public List<CustomerDto> getCustomers() {
List<CustomerDto> customers = null;
List<Customer> cust = new ArrayList<Customer>();
Query q = entityManager.createQuery(
"SELECT c"
+ " FROM Customer c ");
EDIT ADDING SPRING-SERVLET.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<context:spring-configured />
<context:component-scan base-package="com.test"/>
<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/testdb" />
<property name="username" value="root" />
<property name="password" value="password" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="1800000" />
<property name="numTestsPerEvictionRun" value="3" />
<property name="minEvictableIdleTimeMillis" value="1800000" />
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean
class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean
class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/js/" />
<property name="suffix" value=".js" />
</bean>
After I tested this I recognized that Netbeans is giving me error in the class CustomerDaoImpl and it is saying that: "Class is listed in the persistence.xml but it is not annotated". What is correct annotation in this case?
I can see from log that there is correct entitymanager for pu: Closing JPA EntityManagerFactory for persistence unit 'persistenceUnit' and I am creating the dao like that:
#Autowired
CustomerDao customerDao;
When using #autowired annotation, customerDao is null, so I tried to create dao with
customerDao = new CustomerDaoImpl();
and then entitymanager is NULL.
In the persistence.xml you don't need
<class>com.test.dao.CustomerDaoImpl</class>
<class>com.test.dto.CustomerDto</class>
use only
<class>com.test.data.Customer</class>
I am using Spring 3.2 mvc and Hibernate 4 in my project.
hibernate.cfg.xml
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.autocommit">true</property>
<property name="show_sql">true</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.validator.apply_to_ddl">false</property>
<property name="hibernate.validator.autoregister_listeners">false</property>
servlet-context.xml
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<security:global-method-security pre-post-annotations="enabled"/>
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
<context:annotation-config />
<context:component-scan base-package="com.abc" />
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
</beans:beans>
DaoImpl Class:
public void add(Entity entity) {
try {
this.sessionFactory.getCurrentSession().save(entity);
this.sessionFactory.getCurrentSession().flush();
} catch (Exception e) {
logger.error("Exception occured " + e);
}
}
This is my project configuration and dao impl class file.
root-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!-- <tx:annotation-driven transaction-manager="transactionManager" /> -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<context:component-scan base-package="com.abc" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!-- <property name = "dataSource" ref = "dataSource"></property> -->
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="entityInterceptor" ref ="auditLogInterceptor"/>
</bean>
Issue
As of now in hibernate.cfg.xml, I have mentioned hibernate.connection.autocommit = true and in daoimpl while saving entity I need to call flush after .save .
If I remove hibernate.connection.autocommit = true and .flush from daoimpl class, I observed that .save method in daoimpl is not working, means my data is not inserting and even I cannot see insert query executed by hibernate on console.
hibernate.connection.autocommit = true should not be there in hibernate cfg xml as if I doing operation on multiple table in same transaction and if some error occurred then rollback will not happen.
I want that .save in daoimpl should work even I don't write hibernate.connection.autocommit = true in hibernate cfg xml and .flush.
I am using #Transactional annotation for transaction.
You haven't added any TransactionManager to your configuration:
Remove the following properties:
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.autocommit">true</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
Add a connection pooling DataSource (DBCP2 is a much better alternative than C3P0)
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="your-oracle-driver-url"/>
<property name="username" value="your-username"/>
<property name="password" value="your-password"/>
</bean>
Now add the Sessionfactory Spring proxy:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
</bean>
Add the TransactionManager bean
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource" ref="dataSource" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Update
If you have the transaction manager set in a separate spring application context (e.g. root-context.xml), then move these lines from your web context to where the back-end context:
<tx:annotation-driven transaction-manager="transactionManager"/>
<context:component-scan base-package="com.abc.service" />
<context:component-scan base-package="com.abc.dao" />
And only allow the web context to scan its own beans:
<context:component-scan base-package="com.abc.web" />
It's not good to mix the web and the back-end contexts responsibilities.
My problem solved, I just wrote the annotation #Transactional in the
#Repository
#Transactional
public class AbstractHibernateDao<T extends Serializable> {
private Class<T> clazz;
#Autowired
private SessionFactory sessionFactory;
And that solved that I couldnt Delete or Save without using Flush.
I create the configuration of Spring + JPA/Hibernate/c3p0 on this way:
Spring-Servlet.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<context:component-scan base-package="com.nassoft.erpweb"/>
<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/" />
<mvc:annotation-driven />
<mvc:interceptors>
<bean class="com.nassoft.erpweb.login.interceptor.AuthenticatorInterceptor" />
</mvc:interceptors>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="packagesToScan" value="com.nassoft.erpweb.*" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
</bean>
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/nsm_erp" />
<property name="user" value="root" />
<property name="password" value="1234" />
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<property name="maxStatements" value="50" />
<property name="idleConnectionTestPeriod" value="3000" />
<property name="loginTimeout" value="300" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven />
<bean id="persistenceExceptionTranslationPostProcessor" class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
</beans>
I'm not using persistence.xml because I read in some places its not necessary in Spring 4 with Hibernate.
When I start the server it still loading and don't start in 45s (nor 180s) in Tomcat7.
I create a factory of EntityManager to use in my project:
package com.nassoft.erpweb.factory;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceUnit;
public class ConnectionFactory {
#PersistenceUnit
private static EntityManagerFactory entityManagerFactory;
public static EntityManager getEntityManager(){
if (entityManagerFactory == null){
entityManagerFactory = Persistence.createEntityManagerFactory("ERPWeb");
}
return entityManagerFactory.createEntityManager();
}
}
I think my configuration is not correct, but I don't found any places with a good text about it.
Can someone help-me?
Edited.
Problem solved!
First: I applied Dependency Injection in each controller to bring the DAOs with IoC.
Second: I use the annotation #Repository to create a repository in each DAO that will receive my databases methods.
Third: I created the EntityManage in this way for each DAO:
#PersistenceContext
private EntityManager manager;
This is not a complete answer. I"m just pointing you to a direction.
Spring cannot find the ConnectionFactory class of yours, so it will not inject the entityManagerFactory. Its not required for you to again create a singleton for passing the entityManager, so no ConnectionFactory class is required. Spring will do it for you by injecting into the DAO or Controller etc., for example you have the following DAO that gets the data.
#Service
public class SomeDAO {
#AutoWire -- i'm not sure what you call for the entityManager.
private static EntityManagerFactory entityManagerFactory;
}
There is more info here. Instead of #autowiring he is using manual injection. I your case ,you can try with autowiring.
Also make sure that you have these classes in the <spring:component-scan /> path of the application context file or else the spring wont be able to recognize and inject the entity manager.
I'm using Spring JMS with the JPA Hibernate implementation and I'm seeing an intermittent issue with a insert and then instant read of the same record.
web application flow:
-Data gets posted to my web applications web service and the data is sent to a Glassfish OpenMQ queue (STUInputQ below).
-com.api.listener.backoffice.STUMessageListener reads the STUInputQ queue and does a insert into our Oracle Database and then sends a message (with the new database primary key) to another queue (ArchiveQ below).
-com.api.listener.backoffice.StorableMessageListener reads the ArchiveQ queue and attempts to do an read of the database using the primary key of the database record that was inserted by com.api.listener.backoffice.STUMessageListener.
Problem:
Sometimes (about 18%) the read operation in StorableMessageListener returns null, even though the record does exist. It seems to me the insert commit hasn't processed before the read occurs even though the insert returns the sequence generated primary key. I've put a unix timestamp at the end of the method that inserts the data and the one that reads it and when the issue occurs the unix timestamps are the same, so it seems as though the read gets the message before the commit is final.
Temporary Solution:
I've added some logic to sleep the thread and that ensures that I never get a null with the database read. I don't really think the thread sleep is a long term solution. Any ideas on why it seems the STUMessageListener isn't able to commit the transaction before the StorableMessageListener reads it?
Dependencies:
hibernate-core.3.3.2.GA
hibernate-entitymanager-3.4.0.GA
spring 3.0.6.RELEASE
Java 1.5
Spring Configuration:
<?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:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:hz="http://www.hazelcast.com/schema/spring"
xsi:schemaLocation="
http://www.hazelcast.com/schema/spring
http://www.hazelcast.com/schema/spring/hazelcast-spring-2.5.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- Generic -->
<context:annotation-config />
<context:component-scan base-package="myapp.api" />
<aop:aspectj-autoproxy/>
<!-- JPA -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="persistenceUnitName" value="MyApp" />
<property name="jpaProperties">
<props>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.archive.autodetection">class</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.provider_class">com.hazelcast.hibernate.provider.HazelcastCacheProvider</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_minimal_puts">true</prop>
</props>
</property>
</bean>
<hz:hazelcast id="instance">
<hz:config>
//rest of Hazelast config maps here
</hz:config>
</hz:hazelcast>
<hz:hibernate-region-factory id="regionFactory" instance-ref="instance"/>
<!-- Define JPA Provider Adapter -->
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.OracleDialect" />
</bean>
<bean id="dataSourceTarget" class="oracle.jdbc.pool.OracleDataSource" destroy-method="close">
<property name="URL" value="jdbc:oracle:thin:#server:1525:name" />
<property name="user" value="test" />
<property name="password" value="123" />
<property name="connectionCachingEnabled" value="true" />
<property name="connectionCacheProperties">
<props merge="default">
<prop key="MinLimit">5</prop>
<prop key="MaxLimit">50</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource" ref="dataSourceTarget"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false"/>
<bean id="genericDAO" class="myapp.api.dao.impl.GenericDAOImpl">
<constructor-arg>
<value>java.io.Serializable</value>
</constructor-arg>
</bean>
<bean id="springContextHolder" class="myapp.api.util.SpringContextHolder" factory-method="getInstance" />
<bean id="executionInterceptor" class="myapp.api.listener.backoffice.ExecutionInterceptor" />
<!-- JNDI-->
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"/>
<!-- JMS -->
<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName" value="${jms.jndi.qconnectionfactory}">
</property>
</bean>
<bean id="myJMSConnectionFactory" class="com.api.model.vo.backoffice.OpenMqConnectionFactoryBean">
<property name="imqAddressList" value="${jms.imq.url}" />
<property name="imqDefaultUsername" value="${jms.imq.user}" />
<property name="imqDefaultPassword" value="${jms.imq.password}" />
<property name="imqHost" value="${jms.imq.host}" />
<property name="imqPort" value="${jms.imq.port}" />
</bean>
<bean id="stuMessageListener" class="com.api.listener.backoffice.STUMessageListener" />
<bean id="storeListener" class="com.api.listener.backoffice.StorableMessageListener"/>
<bean id="executionInterceptor" class="com.api.listener.backoffice.ExecutionInterceptor" />
<bean id="stuJmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsQueueConnectionFactory"/>
<property name="destinationName" value="STUInputQ"/>
<property name="sessionTransacted" value="false"/>
<property name="messageListener" ref="stuMessageListener" />
<property name="concurrentConsumers" value="5" />
<property name="maxConcurrentConsumers" value="100" />
<property name="receiveTimeout" value="30000" />
<property name="cacheLevelName" value="CACHE_NONE" />
</bean>
<bean id="storeJmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsQueueConnectionFactory"/>
<property name="destinationName" value="ArchiveQ"/>
<property name="sessionTransacted" value="false"/>
<property name="messageListener" ref="storeListener" />
<property name="concurrentConsumers" value="5" />
<property name="maxConcurrentConsumers" value="100" />
<property name="receiveTimeout" value="30000" />
<property name="cacheLevelName" value="CACHE_NONE" />
</bean>
</beans>
Persistence Configuration:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
<persistence-unit name="com" transaction-type="RESOURCE_LOCAL">
</persistence-unit>
</persistence>
Classes that insert record:
public class STUMessageListener implements javax.jms.MessageListener{
#Autowired
StoringService storingService;
#Transactional
public void onMessage(Message message) throws RuntimeException {
try {
Object omsg = ((ObjectMessage) message).getObject();
if (omsg instanceof StorableMessage) {
StorableMessage storableMessage = (StorableMessage) omsg;
//StorableMessage insert into Database
storingService.store(storableMessage);
//jms logic here to send message to next queue (ArchiveQ)
}
catch (Throwable ex) {
throw new RuntimeException(ex);
}
}
#Service("storingService")
public class StoringServiceImpl{
#Autowired
MessagesDAO messagesDAO;
#Transactional
public StorableMessage store(StorableMessage storableMessage) {
messagesDAO.save(storableMessage);
}
}
#Repository("messagesDAO")
public class MessagesDAOImpl{
private Class<T> type
#PersistenceContext
EntityManager entityManager;
public void save(T object) {
entityManager.persist(object);
}
public T findById(Serializable id) {
return entityManager.find(type, id);
}
}
Classes that Read the Database Record:
public class StorableMessageListener implements javax.jms.MessageListener {
#Autowired
MessageDAO messageDAO;
#Transactional
public void onMessage(Message message) throws RuntimeException {
if (omsg instanceof StorableMessage) {
//this is where null is returned for the Messages object 18% of the time
//sleep thread by 1 second logic here helps eliminate the null Messages object
//uses same MessageDAO as above
Messages msg = messageDAO.findById(storableMessage.getMessageKey());
}
}
Try to change the annotation of the insert method as
#Transactional(propagation = Propagation.REQUIRES_NEW)
This will commit the insert as soon as the method finishes.