I am new to Spring but in general I am aware of it's features so I decided to use it in one of my projects. Main problem however is with Hibernate. Before this idea of introducing spring the premise is this:
My application (not web) had to connect to a DB and gather information from it using "persistence_1.xml" with it's own set of entity classes. In other words everything related to "persistence_1.xml" was read only so that no tragedies would occur. Also "persistence_1.xml" with persistence-unit of name "p1" came from web-app dependencies. So picture is this: my app (not-web) written with the support of maven, took dependencies of the other application to access database and gather information.
And the other "persistence_2.xml" with persistence-unit name of "p2" and it's own subset of entities was created by me to store gathered and processed information into the same database.
So originally I had two entity managers one responsible for "p1" another for "p2".
I have seen some material on the internet where they show how to configure two entity managers with different dataSources but I can not figure out how to create two entity managers in SPRING using their own set of ENTITIES.
Let's say "test" is only associated with "UserEntity" and "dummy" is only associted with "DumbEntity".
Now everything get's mashed up along the way and no matter which PU name I type in in #PersistenceContext(name = "test") - it can query for any entity in database.
This is example of persistence.xml:
<persistence-unit name="test" type="RESOURCE_LOCAL">
<class>org.test.db.UserEntity</class>
</persistence-unit>
<persistence-unit name="dummy" type="RESOURCE_LOCAL">
<class>org.test.db.DumbEntity</class>
</persistence-unit>
Bean definition:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="test" />
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<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="org.hibernate.dialect.MySQL5Dialect" />
</bean>
</property>
</bean>
<bean id="entityManagerFactory2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="dummy" />
<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="org.hibernate.dialect.MySQL5Dialect" />
</bean>
</property>
</bean>
<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory2"/>
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean name="userDao" class="org.test.services.UserDaoImpl" />
My UserDaro service
public class UserDaoImpl implements UserDao {
#PersistenceContext(unitName = "test")
private EntityManager entityManager;
public UserDaoImpl() {
}
public UserDaoImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Override
public void saveUser(UserEntity user) {
entityManager.persist(user);
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Override
public void saveUser(DumbEntity user) {
entityManager.persist(user);
}
#Override
public List<UserEntity> fetchAllUsers() {
String sql = "FROM UserEntity";
Query query = entityManager.createQuery(sql);
return query.getResultList();
}
#Override
public List<DumbEntity> fetchAllUsers2() {
String sql = "FROM DumbEntity";
Query query = entityManager.createQuery(sql);
return query.getResultList();
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
}
In any way whether or not I use fetchAllUsers() or fetchAllUsers2() I get the result, but I would like that each of these would only work with entityManager that only has the knowledge about about specific entities.
I would like you to share your thoughts on this one. Thank You.
Related
I have embedded H2 database for my Spring unit tests. The tables and data should be initialized with Liquibase. However, when a test is running, there are no data in the table.
I'm using Spring 4.2.1, Liquibase 4.7.1 and H2 2.1.210.
Below is my implementation:
The datasource is declare in .xml file
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:test;MODE=Oracle;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" />
<property name="username" value="test" />
<property name="password" value="test" />
</bean>
<bean id="liquibase" class="liquibase.integration.spring.SpringLiquibase">
<property name="dataSource" ref="dataSource" />
<property name="changeLog" value="classpath:liquibase/changelog/sil_client_init.h2.sql" />
<property name="contexts" value="test" />
</bean>
<!-- Session factory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate-config.xml" />
</bean>
<!-- Transaction manager -->
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
The sil_client_init.h2.sql file contains SQL contains changeset for creating and inserting several data in the table.
I have a super class which load the context, including the "liquibase" bean declared above.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:applicationContext.xml" })
#Ignore
public class SpringApplicationContextIT {
#Before
public void setUp() {
BasicConfigurator.configure();
FacesContextMocker.mockFacesContext();
assertNotNull(applicationContext);
}
#Autowired
private ApplicationContext applicationContext;
}
From which my test class inherits
public class ClientDAOTest extends SpringApplicationContextIT {
#Autowired
private IClientDAO simpleClientDAO;
#Test
#Transactional
public void getAllClients() throws ClientDAOException {
simpleClientDAO.find();
}
When running getAllClients(), the context are loaded and there is log showing that my changesets Liquibase were running. However, when the test arrives at simpleClientDAO.find(), my Client table is empty.
Any idea of how to fix, how this could happen or how I could investigate the reason would be appreciated. Thank you so much in advance!
For those who might have the same problem, turn out the datasource is properly loaded but my sessions are not correctly connected.
I had to specify a package to scan for my sessionFactory bean:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<value>com.myApp.dao.entity</value>
</property>
<property name="configLocation" value="/WEB-INF/classes/hibernate-config.xml" />
</bean>
Spring and hibernate are both new tools for me and I struggle a bit using both at the same time.
spring.xml :
<bean id="my.supervisionFramesProcess" class="supervision.frames.SupervisionFramesProcess"
init-method="startup" destroy-method="shutdown">
<property name="SupervisionDAO">
<bean class="supervision.dao.jpa.JpaSupervisionDao">
<property name="entityManager" ref="my.entity-manager-factory" />
</bean>
</property>
</bean>
<bean id="my.dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://XXXX/supervision?autoReconnect=true" />
<property name="username" value="***" />
<property name="password" value="***" />
<property name="maxIdle" value="5" />
<property name="maxActive" value="100" />
<property name="maxWait" value="30000" />
<property name="testOnBorrow" value="true" />
<property name="validationQuery" value="SELECT 1" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"></bean>
<bean id="my.entity-manager-factory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml" />
<property name="dataSource" ref="my.dataSource" />
<property name="persistenceUnitName" value="SUPERVISION-1.0" />
</bean>
JpaSupervisionDao.xml :
#PersistenceContext(unitName = "SUPERVISION-1.0")
private EntityManager entityManager;
public JpaSupervisionDao() {
if (logger.isDebugEnabled())
logger.debug("New instance DAO : " + this);
}
protected void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
protected EntityManager getEntityManager() {
return entityManager;
}
#Override
public SupervisionDbObject selectSupervisionDbObject(SupervisionDbObject supervision) {
Query query = getEntityManager().createQuery(SELECT_SUPERVISION);
}
persistence.xml :
<persistence-unit name="SUPERVISION-1.0">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>supervision.dao.SupervisionDbObject</class>
</persistence-unit>
Using JDBC, my DataSource can be instanciated and is fully working but using Hibernate and the entityManager, i only get an error :
Bean property 'entityManager' is not writable or has an invalid setter method.
I have tried to use the EntityManagerFactory object instead but the same error occurs.
Can someone help me out ?
The addition of the <context:annotation-config/> fixed my issue thanks to M. Deinum's answer.
If this can help someone else, I also had an issue with an import, I was importing
org.hibernate.annotations.Entity instead of javax.persistence.Entity.
I have a problem with transaction auto committed after call another dao native query.
Both service and dao signed as #Transactional.
What am I doing wrong here?
Spring 4.2.x
Hibernate 5.1.0
Atomikos 3.9.3
This is my setup:
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="jtaPlatformAdapter" class="com.xxx.JtaPlatformAdapter">
<property name="jtaTransactionManager" ref="transactionManager" />
</bean>
<bean class="com.atomikos.icatch.jta.UserTransactionManager" destroy-method="close" id="atomikosTransactionManager" init-method="init">
<property name="forceShutdown" value="true" />
<property name="startupTransactionService" value="true" />
</bean>
<bean class="com.atomikos.icatch.jta.UserTransactionImp" id="atomikosUserTransaction" />
<bean class="org.springframework.transaction.jta.JtaTransactionManager" id="transactionManager">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
</bean>
<bean id="datasouce" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
...
</bean>
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" id="JPAVendorAdapter">
...
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="emf" depends-on="transactionManager,jtaPlatformAdapter">
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<property name="packagesToScan" value="com.xxx.server"/>
<property name="dataSource" ref="datasouce" />
<property name="persistenceUnitName" value="pun" />
<property name="jpaVendorAdapter" ref="JPAVendorAdapter" />
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.connection.release_mode" value="on_close" />
<entry key="hibernate.transaction.jta.platform" value="com.xxx.server.JtaPlatformAdapter" />
</map>
</property>
</bean>
persistence.xml
<persistence-unit name="pun" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
service
#Transactional
#Scope("prototype")
public synchronized void save(EntityObj model) throws Exception {
model.setX(30);
model.setY(40);
EntityObj oldModel = entityObjDAO.findById(model.getId());
// after call findById, the model had been commit to DB
...
...
...
entityObjDAO.store(model); // this will call entityManager.merge(model)
entityObjDAO.flush();
}
DAO
#Transactional
public EntityObj findById(String id) {
EntityObj model = null;
String sql = "select id,x,y from EntityObj where id = :id"; // this is a native sql query
Query query = this.entityManager.createNativeQuery(sql);
query.setParameter("id", id);
Object[] rs = (Object[]) query.getSingleResult();
if (rs != null) {
model = new EntityObj();
model.setId(id);
model.setX(rs[1] == null ? null : (Integer) rs[1]);
model.setY(rs[2] == null ? null : (Integer) rs[2]);
}
return model;
}
thanks!
If you are using #Transactional in your code, Spring creates a proxy object for your Dao object (wrapper).
So it looks like this if your application is running:
public EntityObj proxyMethodForFindById(String id) {
try {
// 1. start transaction
startTransaction();
// 2. execute your code
return yourDaoObject.findById(id);
} finally { // [!] PSEUDO CODE: NO EXCEPTION HANDLING
// commit transaction
commitTransaction();
}
}
So what happens in your code?
Your save method is marked also as #Transactional. So if you are changing your object by setting:
model.setX(30);
model.setY(40);
Spring creates two proxies. One for Service and one four your Dao. On the End of the findById-Transaction this changes will be commited. Nested transactions is the keyword.
You should remove #Transaction in your findById-Method or better in the whole Dao object. Service should be transactional, not Dao-layer.
Nothing about Activiti is being saved to database. Application entities ARE being saved to database. Below, in order are, the spring file, persitence.xml file and the test case.
Using SQL Server profiler, I see a separate database transaction being started for the database interaction caused by Activiti and further I see that separate transaction being rolled back instead of being committed. Other application db interaction is happening on another transaction and this particular transaction is being committed.
I thought, given my configurations, Activiti database interaction would happen on the same transaction as the rest of the application. I have gone through my configuration files and code numerous times and do not see anything wrong therein. Any ideas why a separate transaction is being started for the Activiti db interactions?
Of course the previous item is the critical question. However, it would also be interesting to know why is that separate transaction being rolled back?
Spring file :
<context:annotation-config/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="ActivitiTrialDataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver" />
<property name="url" value="jdbc:jtds:sqlserver://localhost:1433/ActivitiTrial" />
<property name="username" value="ActivitiTrial" />
<property name="password" value="ActivitiTrial" />
<property name="defaultAutoCommit" value="false" />
<property name="initialSize" value="5" />
</bean>
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="ActivitiTrialDataSource" />
<property name="persistenceUnitName" value="ActivitiTrial"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- Activiti -->
<bean id="activitiDataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="ActivitiTrialDataSource" />
</bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="databaseType" value="mssql" />
<property name="dataSource" ref="activitiDataSource" />
<property name="transactionsExternallyManaged" value="true" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="false" />
<property name="history" value="audit" />
<property name="jobExecutorActivate" value="false" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="activitiRepositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="activitiRuntimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="activitiTaskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="activitiHistoryService" factory-bean="processEngine" factory-method="getHistoryService" />
<bean id="activitiManagementService" factory-bean="processEngine" factory-method="getManagementService" />
persitence.xml file:
<persistence-unit name="ActivitiTrial">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.archive.autodetection" value="hbm,class"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.hbm2ddl.auto" value="none"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.ejb.metamodel.generation" value="disabled"/>
</properties>
</persistence-unit>
Test case:
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionConfiguration(defaultRollback=false)
#ContextConfiguration({"classpath:/springApplicationContext.xml"})
public class TrialTest {
#Autowired
RepositoryService activitiRepositoryService;
#Autowired
RuntimeService activitiRuntimeService;
#Autowired
TaskService activitiTaskService;
#PersistenceContext(unitName="ActivitiTrial")
EntityManager entityManager;
#Test
#Transactional
public void trialTest() throws Exception {
long entryMilliseconds = new Date().getTime();
activitiRepositoryService.createDeployment().addClasspathResource("process-definitions/neville.bpmn20.xml").deploy();
ApplicationEntity applicationEntity1 = new ApplicationEntity();
applicationEntity1.name = "App entity 1";
applicationEntity1.createDate = new Date();
Session hibernateSessionBeforeActiviti = ((Session) entityManager.getDelegate());
entityManager.persist(applicationEntity1);
entityManager.flush();
Map<String, Object> processVariables = new HashMap<String, Object>();
processVariables.put("ApplicationEntityID", applicationEntity1.id);
ProcessInstance processInstance = activitiRuntimeService.startProcessInstanceByKey("neville", processVariables);
String processInstanceId = processInstance.getId();
Task userTask = activitiTaskService.createTaskQuery().processInstanceId(processInstanceId).list().get(0);
ApplicationEntity applicationEntity2 = new ApplicationEntity();
applicationEntity2.name = "App entity 2";
applicationEntity2.createDate = new Date();
Session hibernateSessionAfterActiviti = ((Session) entityManager.getDelegate());
entityManager.persist(applicationEntity2);
entityManager.flush();
System.out.println("Leaving trialTest() in : " + (new Date().getTime() - entryMilliseconds) + " milliseconds.");
}
}
I've solved this problem by resolving conflict between MyBatis (JDBC) and Hibernate (JPA):
You should add jpaVendorAdapter property to entityManagerFactory bean:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="ActivitiTrialDataSource" />
<property name="persistenceUnitName" value="ActivitiTrial"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
For more details see answer of this question.
I've already waste a lot of time trying to pass common transaction manager to Spring and Activiti - and no result. No mean of full answer to your question, but I hope it will save you some time
Activiti forum:
http://forums.activiti.org/content/activiti-doesnt-submit-rollback
http://forums.activiti.org/content/activitispringhibernate-common-transaction-manager
Demo:
These repos illustrate that Activiti don't accept jpa/hibernate transactions:
https://github.com/JOLO-/activiti-jpa-spring-transaction-demo.git
https://github.com/JOLO-/activiti-hibernate-spring-transaction-demo.git
You also can see demo https://github.com/Activiti/Activiti (Activiti + Spring + Bitronix) it may help (I have no time to check it yet).
P.S.
As an alternative to common transaction I see compensation events.
I am trying to implement Spring MVC 3 +EclipseLink JPA 2
When I call saveUser for example it returns that
NullPointerException, EntityManager is
null
:
public class UserDAO {
#PersistenceContext
private EntityManager em;
public void setEntityManager(EntityManager em) {
this.em = em;
}
#Transactional
public User saveUser(User user){
return em.merge(user);
}
My config is:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="application" />
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform" />
<property name="generateDdl" value="false" />
</bean>
</property>
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver" />
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="username" value=".." />
<property name="password" value=".." />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://..." />
</bean>
Also have
<context:component-scan base-package="com.elasticbeanstalk.mypackage" />
<context:annotation-config />
It does initialize JPA during Tomcat startup. Why am I see NPE? Could I miss something?
If you instantiate the UserDAO manually, nothing will be injected by Spring. If the DAO is in a package which is under your base-package of <context:component-scan ../> then you can simply autowire it into your Controller. If not, either modify the base-package or also you can define the DAO in the appcontext manually, then you can autowire as well.