I have a mySQL database for manage user information, and I am using JTA datasource for my mySQL database, here is what the persistence.xml look like:
<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="SensorCloudPU" transaction-type="JTA">
<jta-data-source>java:/SensorCloudPU</jta-data-source>
<!-- <non-jta-data-source>java:/SensorCloudPU</non-jta-data-source> -->
<class>com.sensorhound.common.domain.impl.AnomalousInfo</class>
<class>com.sensorhound.common.domain.impl.Code</class>
<class>com.sensorhound.common.domain.impl.Device</class>
<class>com.sensorhound.common.domain.impl.Executable</class>
<class>com.sensorhound.common.domain.impl.Group</class>
<class>com.sensorhound.common.domain.impl.GroupAlert</class>
<class>com.sensorhound.common.domain.impl.GroupRule</class>
<class>com.sensorhound.common.domain.impl.GroupRuleDefinition</class>
<class>com.sensorhound.common.domain.impl.GroupRuleStatus</class>
<class>com.sensorhound.common.domain.impl.Node</class>
<class>com.sensorhound.common.domain.impl.NodeAlert</class>
<class>com.sensorhound.common.domain.impl.NodeRule</class>
<class>com.sensorhound.common.domain.impl.NodeRuleDefinition</class>
<class>com.sensorhound.common.domain.impl.Organization</class>
<class>com.sensorhound.common.domain.impl.PastGroupStatus</class>
<class>com.sensorhound.common.domain.impl.Trace</class>
<class>com.sensorhound.common.domain.impl.TrainingSession</class>
<class>com.sensorhound.common.domain.impl.User</class>
<class>com.sensorhound.common.domain.impl.Role</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value = "false" />
<property name="hibernate.connection.autocommit" value="true" />
<property name="hibernate.event.merge.entity_copy_observer" value="allow"/>
<property name="transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
<property name="jta.UserTransaction" value="java:jboss/UserTransaction"/>
</properties>
</persistence-unit>
</persistence>
And I have an endpoint like this:
#Path("/delete")
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
public Response deleteUser(#FormParam("organization_id") Integer organizationId,
#FormParam("username") String username) throws JsonProcessingException, NotSupportedException,
SystemException, SecurityException, IllegalStateException, RollbackException,
HeuristicMixedException, HeuristicRollbackException, NamingException {
Organization org = organizationDAO.getByOrganizationId(organizationId);
userDao.deleteUserByUserNameAndOrganization(username, org);
return Response.status(Response.Status.OK).build();
}
And the DAO is like this:
public class userDAO {
#PersistenceContext(unitName = "SensorCloudPU")
protected EntityManager em;
#Resource
protected UserTransaction utx;
public void deleteUserByUserNameAndOrganization(String userName, Organization org)
throws NotSupportedException, SystemException, SecurityException, IllegalStateException,
RollbackException, HeuristicMixedException, HeuristicRollbackException {
Query q = this.em.createNamedQuery(User.Q_GET_BY_USERNAME_AND_ORGANIZATION);
q.setParameter("organization", org);
q.setParameter("user_name", userName);
User u = this.executeQueryForSingleResult(q);
if (u == null) {
return;
}
utx.begin();
this.em.remove(u);
utx.commit();
}
}
But every time when I load the page and try to delete the from the database, I got this error:
Resource lookup for injection failed: java:jboss/UserTransaction]
UserTransaction [Root exception is java.lang.IllegalStateException: WFLYEJB0137: Only session and message-driven beans with bean-managed transaction demarcation are allowed to access UserTransaction]
You can't use UserTransaction in an EJB, unless you add #TransactionManagement(BEAN)
What TransactionManagement does is
Specifies whether a session bean or message driven bean has container managed transactions or bean managed transactions. If this annotation is not used, the bean is assumed to have container-managed transaction management.
#TransactionManagement(BEAN)
public class userDAO {
Since you are primarily concerned with transaction management I suggest that you convert your DAO into an EJB. It only takes one line:
#Stateless
public class userDAO {
#PersistenceContext(unitName = "SensorCloudPU")
protected EntityManager em;
public void deleteUserByUserNameAndOrganization(String userName, Organization org) {
Query q = this.em.createNamedQuery(User.Q_GET_BY_USERNAME_AND_ORGANIZATION);
q.setParameter("organization", org);
q.setParameter("user_name", userName);
User u = this.executeQueryForSingleResult(q);
if (u != null) {
this.em.remove(u);
}
}
}
and you can see that it simplifies things significantly. EJBs give you JTA transaction demarcation (and rollback if needed) for free.
This will work even if you are building a WAR only deployment.
You could also add #Stateless to your JAX-RS endpoint if you want. At the very least you will get a bit more monitoring than you may otherwise have.
Related
We have an existing fully functional web solution in production since a couple of years. The key technologies are
Wildfly 10.1.0
Java 8
Spring 4.3.3
Hibernate 5.0.10 ( JPA engine in Wildfly 10.1.0 )
Envers 5.0.10
So the upgrade was to go to the setting
Wildfly 15.0
Java 11
Spring 5.1.3
Hibernate 5.3.7 ( JPA engine in Wildfly 15 )
Just doing this switch and som minor code changes (very minor) all is well, the application starts and I can login with no problem, i.e. I can read and map to entities with no problem. But on update to database under #Transaction we have a very big issue that seems to be related to session being closed to early ( simlar to Hibernate bug HHH-11570 same discussion ). The update seems to go well, it is when Hibernate needs to start an a session for lazy load entity the session is closed an Hibernate throws an exception.
Please let me know if this is a bug? I tried to deploy the original non upgraded Java 8 war to the server but the problem is exactly the same so this is a good argument to say that the problem is Hibernate or Wildfly. I really need to upgrade the system. Let me know if you need more stacktrace, code or configuration.
And some configurations of Spring are
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="se.eaktiebok.repository")
public class JpaConfiguration {
#Bean
public DataSource dataSource() throws NamingException{
Context ctx = new InitialContext();
DataSource dataSource = (DataSource)ctx.lookup("java:/eaktiebok");
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager() throws NamingException{
JtaTransactionManager tm = new JtaTransactionManager();
tm.afterPropertiesSet();
return tm;
}
#Bean
public SharedEntityManagerBean entityManager() throws NamingException{
SharedEntityManagerBean entityManager = new SharedEntityManagerBean();
entityManager.setEntityManagerFactory(this.entityManagerFactory());
return entityManager;
}
#Bean
EntityManagerFactory entityManagerFactory() throws IllegalArgumentException, NamingException{
org.springframework.jndi.JndiObjectFactoryBean jndiObjectFactoryBean = new org.springframework.jndi.JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("java:app/JPADBFactory");
jndiObjectFactoryBean.setLookupOnStartup(true);
jndiObjectFactoryBean.setExpectedType(EntityManagerFactory.class);
jndiObjectFactoryBean.afterPropertiesSet();
return (EntityManagerFactory) jndiObjectFactoryBean.getObject();
}
}
AuthUser entity
#Entity
#Table(name=“auth_user”)
#Audited(withModifiedFlag=true)
public class AuthUser implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer iduser;
....
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="idclient", insertable=false, updatable = false)
private AuthClient authClient;
}
The wildfly datasource is defined in standalone.xml as
<datasource jndi-name="java:/eaktiebok" pool-name="eaktiebok">
<connection-url>jdbc:mysql://xxxxxxxxxxxxxxxxx:3306/eaktiebokdump?useSSL=false</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<driver>mysql</driver>
<security>
<user-name>xxxxxxx</user-name>
<password>xxxxxxx</password>
</security>
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
<background-validation>true</background-validation>
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
</validation>
</datasource>
persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<persistence-unit name="jpaEabMysqlUnit" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/eaktiebok</jta-data-source>
<properties>
<property name="hibernate.jdbc.use_streams_for_binary" value="true"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<property name="hibernate.temp.use_jdbc_metadata_defaults" value="false"/>
<property name="org.hibernate.envers.store_data_at_delete" value="false"/>
<property name="hibernate.generate_statistics" value="false"/>
<property name="jboss.entity.manager.jndi.name" value="java:app/JPADB"/>
<property name="jboss.entity.manager.factory.jndi.name" value="java:app/JPADBFactory"/>
</properties>
</persistence-unit>
And finally a little part of console with some stacktrace
09:05:39,320 DEBUG [org.springframework.transaction.jta.JtaTransactionManager.handleExistingTransaction] (default task-9) Participating in existing transaction
09:05:39,320 DEBUG [org.springframework.orm.jpa.EntityManagerFactoryUtils.closeEntityManager] (default task-9) Closing JPA EntityManager
09:05:39,321 DEBUG [org.springframework.transaction.jta.JtaTransactionManager.processCommit] (default task-9) Initiating transaction commit
09:05:39,321 DEBUG [org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes] (default task-9) Processing flush-time cascades
09:05:39,321 DEBUG [org.hibernate.event.internal.AbstractFlushingEventListener.prepareCollectionFlushes] (default task-9) Dirty checking collections
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy [se.eaktiebok.jpa.auth.AuthClient#1] - the owning Session was closed
at org.hibernate#5.3.7.Final//org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:172)
at org.hibernate#5.3.7.Final//org.hibernate.proxy.AbstractLazyInitializer.getIdentifier(AbstractLazyInitializer.java:89)
at org.hibernate#5.3.7.Final//org.hibernate.envers.internal.entities.mapper.id.SingleIdMapper.mapToMapFromEntity(SingleIdMapper.java:125)
at org.hibernate#5.3.7.Final//org.hibernate.envers.internal.entities.mapper.relation.ToOneIdMapper.mapToMapFromEntity(ToOneIdMapper.java:55)
at org.hibernate#5.3.7.Final//org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper.map(MultiPropertyMapper.java:90)
at org.hibernate#5.3.7.Final//org.hibernate.envers.internal.synchronization.work.ModWorkUnit.<init>(ModWorkUnit.java:43)
at org.hibernate#5.3.7.Final//org.hibernate.envers.event.spi.EnversPostUpdateEventListenerImpl.onPostUpdate(EnversPostUpdateEventListenerImpl.java:46)
at org.hibernate#5.3.7.Final//org.hibernate.action.internal.EntityUpdateAction.postUpdate(EntityUpdateAction.java:268)
at org.hibernate#5.3.7.Final//org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:215)
at org.hibernate#5.3.7.Final//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
at org.hibernate#5.3.7.Final//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478)
at org.hibernate#5.3.7.Final//org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356)
at org.hibernate#5.3.7.Final//org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate#5.3.7.Final//org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454)
... 131 more
Complete business function that starts transaction
#Override
#Transactional
public UserBean updateIntoUser(UserBean userBean, UserBean authenticatedUser) throws AuthUserException {
Assert.notNull(userBean, "userBean must not be null");
Assert.notNull(authenticatedUser, "authenticatedUser must not be null");
// If identity is set, check so no other user has it
if(userBean.getIdentity()!=null) {
UserBean ub = findAuthUserByIdentity(userBean.getIdentity());
if(ub!=null) {
if(!ub.getIduser().equals(userBean.getIduser())) {
throw new AuthUserException("Contact/Company taken by iduser "+ub.getIduser(), AuthUserExceptionCause.contactTaken);
}
}
OwnerEntityBean<?> o = companyDao.findOwnerEntityBean(userBean.getIdentity());
UserEntity ue = null;
if(o!=null) {
ue = new UserEntity();
ue.setAddressBean(o.getAddress());
ue.setEntityType(o.getEntityType());
ue.setIdentityNumber(o.getIdentityNumber());
ue.setName(o.getName());
}
userBean.setUserEntity(ue);
}
// If username changed check so it is free
if(!authenticatedUser.getUsername().equals(userBean.getUsername())) {
AuthUser user = userRepo.findUserByUsername(userBean.getUsername());
if(user!=null&&!user.getIduser().equals(userBean.getIduser())) {
throw new AuthUserException("Username already exists "+userBean.toString(), AuthUserExceptionCause.nonUniqueUsername);
}
}
// update AuthUser
userDao.updateIntoUser(userBean);
// Load AuthUser with contact and address
return userBean;
} // end function updateIntoUser
I am using hibernate in my Java EE application in combination with my Wildfly server to persist my Classes in a mysql Database.
So far this works fine but now I am writing unit tests and I am getting crazy about some error which I get.
I would like to test my DAO-Layer in my Unit-Tests but I get these errors:
Caused by: org.hibernate.engine.jndi.JndiException: Error parsing JNDI name [java:/MySqlDS]
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
my persistence.xml ist this:
<?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="primary">
<jta-data-source>java:/MySqlDS</jta-data-source>
<class>org.se.bac.data.Employee</class>
<properties>
<!-- Properties for Hibernate -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/empdb?useSSL=false"/>
<property name="hibernate.connection.username" value="student"/>
<property name="hibernate.connection.password" value="student"/>
<!--
SQL stdout logging
-->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>
</properties>
</persistence-unit>
</persistence>
So, Here I am using a jta-data-source> as you can see.
If I remove this line my tests are going fine! But I can not build my project with maven anymore.
Error:
Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
Caused by: org.hibernate.boot.registry.classloading.spi.ClassLoadingException: Unable to load class [com.mysql.jdbc.Driver]
Caused by: java.lang.ClassNotFoundException: Could not load requested class : com.mysql.jdbc.Driver"}}
He can not find the datasource because I removed the line in my persistence.xml
How can I manage to get both run in my application. The tests and of course the maven build?
Here is my test: (Setup is already causing the error):
package org.se.bac.data.dao;
import java.util.List;
import javax.persistence.EntityManager;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.se.bac.data.model.Employee;
public class EmployeeDAOTest
{
private static final JdbcTestHelper JDBC_HELPER = new JdbcTestHelper();
private final static JpaTestHelper JPA_HELPER = new JpaTestHelper();
private EntityManager em = JPA_HELPER.getEntityManager("primary");
private EmpDAO dao;
#BeforeClass
public static void init()
{
JDBC_HELPER.executeSqlScript("sql/test/dropEmployeeTable.sql");
JDBC_HELPER.executeSqlScript("sql/test/createEmployeeTable.sql");
}
#AfterClass
public static void destroy()
{
//JDBC_HELPER.executeSqlScript("sql/test/dropEmployeeTable.sql");
}
#Before
public void setUp()
{
JDBC_HELPER.executeSqlScript("sql/test/dropEmployeeTable.sql");
JDBC_HELPER.executeSqlScript("sql/test/createEmployeeTable.sql");
dao = new EmpDAOImpl();
dao.setEm(em);
JPA_HELPER.txBegin();
Employee emp2 = new Employee();
emp2.setFirstname("Max");
emp2.setLastname("Muster");
emp2.setHiredate("23-12-1991");
dao.insert(emp2);
}
And JPAHELPER Class:
package org.se.bac.data.dao;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaTestHelper
{
/*
* Property: persistenceUnitName
*/
private String persistenceUnitName;
public String getPersistenceUnitName()
{
return persistenceUnitName;
}
public void setPersistenceUnitName(String persistenceUnitName)
{
if(persistenceUnitName == null || persistenceUnitName.length() == 0)
throw new IllegalArgumentException("Illegal parameter persistenceUnitName = " + persistenceUnitName);
this.persistenceUnitName = persistenceUnitName;
}
/*
* Get an instance of the EntityManagerFactory.
*/
protected EntityManagerFactory getEnityManagerFactory()
{
if(persistenceUnitName == null)
throw new IllegalStateException("PersistenceUnitName must be set!");
return Persistence.createEntityManagerFactory(persistenceUnitName);
}
/*
* Manage an EntityManager.
*/
private EntityManager em;
public EntityManager getEntityManager()
{
if(em == null)
{
em = getEnityManagerFactory().createEntityManager();
}
return em;
}
public EntityManager getEntityManager(String persistenceUnitName)
{
setPersistenceUnitName(persistenceUnitName);
return getEntityManager();
}
public void closeEntityManager()
{
if(em != null)
em.close();
}
/*
* Handle Transactions
*/
protected void txBegin()
{
EntityTransaction tx = em.getTransaction();
tx.begin();
}
protected void txCommit()
{
EntityTransaction tx = em.getTransaction();
if(tx.getRollbackOnly())
{
tx.rollback();
}
else
{
tx.commit();
}
}
protected void txRollback()
{
EntityTransaction tx = em.getTransaction();
tx.rollback();
}
}
and my DAO:
package org.se.bac.data.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.se.bac.data.model.Employee;
class EmpDAOImpl // package private
implements EmpDAO
{
#PersistenceContext
private EntityManager em;
/*
* CRUD methods
*/
public Employee findById(int id)
{
System.out.println("empdaoimpl ID " + id);
return em.find(Employee.class, id);
}
public EntityManager getEm() {
return em;
}
public void setEm(EntityManager em) {
this.em = em;
}
}
Wildfly Datasource:
<datasources>
<datasource jta="true" jndi-name="java:/MySqlDS" pool-name="MySqlDS" enabled="true" use-ccm="false">
<connection-url>jdbc:mysql://localhost:3306/empdb?useSSL=false</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<driver>mysql-connector-java-5.1.44-bin.jar_com.mysql.jdbc.Driver_5_1</driver>
<security>
<user-name>student</user-name>
<password>student</password>
</security>
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
<background-validation>true</background-validation>
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
</validation>
</datasource>
</datasources>
He can not find the datasource because I removed the line in my persistence.xml
How can I manage to get both run in my application.
The problem is that the data source is managed by Wildfly which is not available on your test environment. So what you could do is define two separate persistence units (one for your production code and the other for the test) as follows:
<?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="primary">
<jta-data-source>java:/MySqlDS</jta-data-source>
<properties>
<!-- Properties for Hibernate -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<!-- SQL stdout logging -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>
</properties>
</persistence-unit>
<persistence-unit name="testPU" transaction-type="RESOURCE_LOCAL">
<class>org.se.bac.data.Employee</class>
<properties>
<!-- Properties for Hibernate -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/empdb?useSSL=false"/>
<property name="hibernate.connection.username" value="student"/>
<property name="hibernate.connection.password" value="student"/>
<!-- SQL stdout logging -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>
</properties>
</persistence-unit>
</persistence>
and then in your EmployeeDAOTest class modify the following line as:
private EntityManager em = JPA_HELPER.getEntityManager("testPU");
Note:
I removed the JDBC connection properties from the primary persistence unit because you don't need them as you already have the data source there on Wildfly.
I am working with Hibernate 4.3.8 on a web app and It looks like that the persist() method does not update the PersistentContext (cache level 1). Here is my configuration and the singleton to manage the persistent operations:
Hibernate configuration
<persistence-unit name="PersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
<property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/irm"/>
<property name="hibernate.connection.username" value="nrossi"/>
<property name="hibernate.connection.password" value="nicolas"/>
<property name="hibernate.connection.pool_size" value="5"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.max_fetch_depth" value="5"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
Persistence Manager
public class PersistenceManager
{
private static final Logger log = LogManager.getLogger();
private static final EntityManagerFactory emf;
private static final ThreadLocal<EntityManager> threadLocal;
static
{
emf = Persistence.createEntityManagerFactory("PersistenceUnit");
threadLocal = new ThreadLocal<EntityManager>();
}
public static EntityManager getEntityManager()
{
EntityManager em = threadLocal.get();
if (em == null)
{
em = emf.createEntityManager();
threadLocal.set(em);
}
return em;
}
public static <T>T get(Class<T> clazz, Object id)
{
return getEntityManager().find(clazz, id);
}
public static void save(Object object)
{
EntityManager em = getEntityManager();
EntityTransaction et = em.getTransaction();
et.begin();
try
{
em.persist(object);
et.commit();
}
catch (Exception e)
{
et.rollback();
throw new RuntimeException("Error saving object", e);
}
}
}
I update a model calling PersistenceManager.save(model) and it updates the record on the database, but after that when I call PersistenceManager.get(model.id) it returns the model from memory with the old values. It looks like the persist method is not updating the PersistenceCache.
By the way, If I call the PersistenceManager.get(model.id) on a new thread (i.e. incognito window) it returns the updated model.
I tried adding a refresh call em.refresh(model) after the commit and It is working, but I am not sure if this is the right way to get the context updated.
Updated info
I coded a simple JSP page just to reproduce the behavior. If I update the entity description and wait 5' and refresh the page It returns the old value:
<%#page import="com.identicum.framework.persistence.PersistenceManager"%>
<%#page import="com.identicum.irm.core.model.Entity"%>
<%
Entity entity = PersistenceManager.get(Entity.class, 1L);
String value = request.getParameter("entityName");
if(value != null)
{
entity.setDescription(value);
PersistenceManager.save(entity);
}
%>
<html>
<body>
Entity description: <b><%= entity.getDescription() %></b>
<br>
<br>
<form method="post">
Enter new entity description <br>
<input type="text" name="entityName"/>
<input type="submit" />
</form>
</body>
</html>
** New information **
My PersistenceManager is a copy of this suggestion. My application is an Application-managed EntityManager. I have to access to the same EntityManager during the lifecycle of each request. That's the reason this approach. Is there any other way to implement this ?
First of all persist() is for new entities (no record on database).
If you want to change an object you must use merge().
But your problem is that you probably are in a multi threaded environment.
So you cannot use ThreadLocal. Each thread will have his own PersistenceManager.
What app server or web container are you using?
I finally fixed it changing the way I get and close the EntityManager. Now I get the EntityManager from the EntityManagerFactory at the beginning of each request (on my Rest Dispatcher) and close it at the end. In this way each request has his own PersistentContext.
The PersistenceMangaer.java is almost the same. The main difference is that I removed the EntityManagerFactory reference to the ApplicationServiceContext and I call the setEntityManager() outside the class. Here is an example:
PersistenceManager.setEntityManager(PersistenceFactory.createEntityManager);
FooService.createObject1();
FooService.createObject2();
PersistenceManager.closeEntityManager();
These links where helpful to find the problem:
Best practice to get EntityManagerFactory
Should JPA Entity Manager be closed?
http://www.objectdb.com/java/jpa/start/connection
I am absolutely new to Hibernate and JPA and I have the following problem trying to implement a tutorial that uses Hibernate following the JPA specification.
I have these classes:
1) A HelloWorldClient class, the entry point of my application, containing the main method:
package client;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import entity.Message;
public class HelloWorldClient {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("merging");
EntityManager em = emf.createEntityManager();
EntityTransaction txn = em.getTransaction();
txn.begin();
Message message = new Message("Hello"); //transient state
em.persist(message); //persistent state
txn.commit();
em.close();
message.setText("Hi"); //modifying the detached state of message
EntityManager em2 = emf.createEntityManager();
EntityTransaction txn2 = em2.getTransaction();
txn2.begin();
//the returned mergedMessage is a persistent object
//any changes to mergedMessage will be dirty checked when the txn2 will be committed and updated in the database
Message mergedMessage = em2.merge(message);
txn2.commit();
em2.close();
//Detaching objects explicitly
/*
EntityManager em3 = emf.createEntityManager();
EntityTransaction txn3 = em.getTransaction();
txn3.begin();
Message msg = new Message("Howdy"); //transient state
em.persist(msg); //persistent state
em.detach(msg); //detaching the message object explicitly
txn3.commit();
em3.close();
*/
}
}
2) A Message entity class that is mapped on a database table:
package entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="message")
public class Message {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="ID")
private Long id;
#Column(name="TEXT")
private String text;
public Message() {}
public Message(String text) {
this.text = text;
}
public void setText(String text) {
this.text = text;
}
}
3) And finally I have the persistence.xml configuration file in the META-INF folder of my application:
<?xml version="1.0" encoding="UTF-8"?>
<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" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="merging" transaction-type="RESOURCE_LOCAL">
<properties>
<!-- Database connection settings -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/hello-world" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="myPassword" />
<!-- SQL dialect -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<!-- Create/update tables automatically using mapping metadata -->
<property name="hibernate.hbm2ddl.auto" value="update" />
<!-- Pretty print the SQL in the log file and console -->
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
When I try to run my application, I obtain this error message in the stacktrace:
Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
Exception in thread "main" java.lang.AbstractMethodError: org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.getConfigurationValues()Ljava/util/Map;
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:404)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:842)
at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:75)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:54)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:39)
at client.HelloWorldClient.main(HelloWorldClient.java:14)
I've had the same issue when trying to do hibernate tutorials. It usually means that there are compatibility issues between the jar files you added to your build path.
in your case, it would seem that
org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.getConfigurationValues()
appears in the latest version as an abstract method, hence the above error. Try changing the JPA jar to a previous version, that will probably solve the issue; that's what helped in my case.
I had the same problem and when I checked the Maven repository for hibernate-entity manager I found I wasn't specifying the latest version in my POM.xml. Once I updated to the latest version it fixed the problem.
I recently wrote a class, in my web application, for parsing a huge XML file and feed a db table with its content. My application is running on Wildfly9, and uses JPA with Hibernate provider to handle a MySQL DB.
The AS configuration is pretty standard, I just added my datasource conf:
<datasource jta="false" jndi-name="java:jboss/datasources/spazio_visione" pool-name="spazio_visione" enabled="true" use-ccm="false">
<connection-url>jdbc:mysql://127.0.0.1:3306/spazio_visione?zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<driver>mysql</driver>
<security>
<user-name>myuser</user-name>
<password>mypasswd</password>
</security>
<validation>
<validate-on-match>false</validate-on-match>
<background-validation>false</background-validation>
</validation>
<statement>
<share-prepared-statements>false</share-prepared-statements>
</statement>
</datasource>
And here's my persistence.xml:
<?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="backoffice" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/spazio_visione</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.jdbc.batch_size" value="100" />
<property name="hibernate.order_inserts" value="true" />
<property name="hibernate.order_updates" value="true" />
<!-- <property name="hibernate.show_sql" value="true"/> -->
<!-- <property name="hibernate.hbm2ddl.auto" value="validate"/> -->
</properties>
</persistence-unit>
</persistence>
Everything has always worked fine, using JPA entities to manage my domain model.
Back to my parser... actually, for many reasons, it needs to use native JDBC queries to insert my data in the db. Here's the code:
public class XMLFeedParser extends DefaultHandler {
#Inject Logger logger;
#Resource(lookup="java:jboss/datasources/spazio_visione") DataSource datasource;
private static final int STATEMENT_BATCH_THRESHOLD = 1000;
private MyXMLFeedItem item;
private Connection connection;
private PreparedStatement ps;
public XMLFeedParser() {
}
protected void initParser() throws SQLException {
connection = datasource.getConnection();
Statement deleteStatement = connection.createStatement();
deleteStatement.executeUpdate("DELETE FROM mytable WHERE id_feed = "+feed.getId());
deleteStatement.close();
ps = connection.prepareStatement(
"INSERT INTO mytable "
+ "( first, second, ...) "
+ "values ( ?, ?, ... )"
);
}
protected void finalizeParser() throws SQLException {
if (ps!=null) {
ps.executeBatch();
ps.close();
}
if (connection!=null) {
connection.close();
}
}
public void parseAndWriteToDatabase(String filePath) throws ParserConfigurationException, SAXException, IOException, SQLException {
File file = Paths.get(filePath).toFile();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
initParser();
saxParser.parse(file, this);
finalizeParser();
}
private void writeToDb(MyXMLFeedItem item) {
try {
ps.setString(1, "first");
ps.setString(2, "second");
// ...
ps.addBatch();
if ( counter % STATEMENT_BATCH_THRESHOLD == 0 ) {
ps.executeBatch();
}
} catch (SQLException e) {
logger.severe(e.getMessage());
}
}
#Override
public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes attrs) throws SAXException {
// ...parsing logic
}
#Override
public void characters(char[] ch, int start, int length) throws SAXException {
// ...parsing logic
}
#Override
public void endElement(String namespaceURI, String localName, String qualifiedName) throws SAXException {
// calls writeToDb() for each record found
}
}
My XMLFeedParser is injected (using #Inject) in one of my EJBs, which will call parseAndWriteToDatabase() method. It works!
The pain starts here. Since the end of the parsing, my application begins giving errors in other points, just randomly. The stacktrace looks like this:
Caused by: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:jboss/datasources/spazio_visione
at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:646)
at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:552)
at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.allocateConnection(AbstractConnectionManager.java:737)
at org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:138)
... 165 more
Caused by: javax.resource.ResourceException: IJ000655: No managed connections available within configured blocking timeout (30000 [ms])
at org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreArrayListManagedConnectionPool.getConnection(SemaphoreArrayListManagedConnectionPool.java:553)
at org.jboss.jca.core.connectionmanager.pool.AbstractPool.getSimpleConnection(AbstractPool.java:622)
at org.jboss.jca.core.connectionmanager.pool.AbstractPool.getConnection(AbstractPool.java:594)
at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:579)
... 168 more
It looks like I didn't close the connection, but that's not true!
Any suggestions?
There are a few possibilities what could go wrong. First, you are opening the connection in initParser(), but closing it in finalizeParser(), without using finally. If an exception is thrown, the connection is not closed. It would still be better to use try-with-resources.
Another potential problem is that the class is not thread-safe. For example, if an instance is used without synchronization, if you call XMLFeedParser.initParser() two times before finalizeParser(), you may lose your reference to connection which is then never closed (how does your EJB which injects the XMLFeedParser look like?)
edit: using try-with-resources:
it depends where you need your Connection. you could open the connection in parseAndWriteToDatabase() and pass it to the methods in which you need it. So you don't have to explicitly call close(). Also your PreparedStatements and ResultSets could be wrapped in try-with-resources.
for example something like:
public void parseAndWriteToDatabase(String filePath) throws ParserConfigurationException, SAXException, IOException, SQLException {
// ...
try (Connection connection = getDataSource().getConnection();)
{
initParser(connection);
saxParser.parse(file, this);
finalizeParser(connection);
}
}
So when your Connection and other variables are not members of the class, you don't have to worry about other threads accessing them.