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
Related
Full MCVE is at the bottom of this post.
I am trying to run a very basic Hibernate example as I work through a tutorial. However, I am receiving the following error: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]:
Exception in thread "main" java.lang.ExceptionInInitializerError
at hibernateTutorial.util.HibernateUtil.buildSessionFactory(HibernateUtil.java:50)
at hibernateTutorial.util.HibernateUtil.getSessionFactory(HibernateUtil.java:30)
at hibernateTutorial.main.HibernateMain.main(HibernateMain.java:21)
Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:275)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:152)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:176)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:127)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:86)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:479)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:85)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:709)
at hibernateTutorial.util.HibernateUtil.buildSessionFactory(HibernateUtil.java:47)
... 2 more
Caused by: org.vibur.dbcp.ViburDBCPException: java.lang.NullPointerException
at org.vibur.dbcp.ViburDBCPDataSource.start(ViburDBCPDataSource.java:233)
at org.hibernate.vibur.internal.ViburDBCPConnectionProvider.configure(ViburDBCPConnectionProvider.java:57)
at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:107)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:246)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.buildJdbcConnectionAccess(JdbcEnvironmentInitiator.java:145)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:66)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:101)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
... 15 more
Caused by: java.lang.NullPointerException
at java.util.Hashtable.put(Hashtable.java:460)
at java.util.Properties.setProperty(Properties.java:166)
at org.vibur.dbcp.pool.Connector$Builder$Driver.<init>(Connector.java:66)
at org.vibur.dbcp.pool.Connector$Builder$Driver.<init>(Connector.java:56)
at org.vibur.dbcp.pool.Connector$Builder.buildConnector(Connector.java:48)
at org.vibur.dbcp.ViburDBCPDataSource.doStart(ViburDBCPDataSource.java:248)
at org.vibur.dbcp.ViburDBCPDataSource.start(ViburDBCPDataSource.java:226)
... 24 more
My database is on a Microsoft SQL Server 2017 instance that uses Windows Authentication. I have added both the MSSQL JDBC (v6.4.0) updated IntelliJ with the -Djava.library.path VM option so that sqljdbc_auth.dll (required for Windows Authentication) is accessible.
From what I can tell in my research into this ambiguous error message, it could have many different causes, but ultimately boils down to just not being able to connect to the database somehow. Most of the other Q&A I've found seems to be specific to other databases or SQL Server that doesn't use Windows Authentication.
I have copied the JDBC URL directly from my datasource config in IntelliJ so I know that it is correct and works properly in the IDE.
What else is required to properly configure Hibernate to connect to SQL Server?
HibernateMain.java:
package hibernateTutorial.main;
import hibernateTutorial.model.Employee;
import hibernateTutorial.util.HibernateUtil;
import org.hibernate.Session;
import java.time.LocalDateTime;
public class HibernateMain {
public static void main(String[] args) {
Employee emp = new Employee();
emp.setName("Nathan");
emp.setRole("CEO");
emp.setInsertTime(LocalDateTime.now());
// **********************************************************************************************
// Get Session
// **********************************************************************************************
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
// **********************************************************************************************
// Start transaction
// **********************************************************************************************
session.beginTransaction();
// **********************************************************************************************
// Save the model object
// **********************************************************************************************
session.save(emp);
// **********************************************************************************************
// Commit the transaction
// **********************************************************************************************
session.getTransaction().commit();
System.out.println("Employee ID: " + emp.getId());
// **********************************************************************************************
// Terminate the session factory or the program won't end
// **********************************************************************************************
HibernateUtil.getSessionFactory().close();
}
}
HibernateUtil.java:
package hibernateTutorial.util;
import hibernateTutorial.model.Employee1;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import java.util.Properties;
public class HibernateUtil {
// **********************************************************************************************
// XML-based configuration
// **********************************************************************************************
private static SessionFactory sessionFactory;
// **********************************************************************************************
// Annotation-based configuration
// **********************************************************************************************
private static SessionFactory sessionAnnotationFactory;
// **********************************************************************************************
// Property-based configuration
// **********************************************************************************************
private static SessionFactory sessionJavaConfigFactory;
public static SessionFactory getSessionFactory() {
if (sessionFactory == null) sessionFactory = buildSessionFactory();
return sessionFactory;
}
private static SessionFactory buildSessionFactory() {
try {
// **********************************************************************************************
// Create the SessionFactory from hibernate.cfg.xml
// **********************************************************************************************
Configuration configuration = new Configuration();
configuration.configure("hibernate.cfg.xml");
System.out.println("Hibernate configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate serviceRegistry created");
return configuration.buildSessionFactory(serviceRegistry);
} catch (Throwable e) {
System.err.println("Initial SessionFactory creation failed. " + e);
throw new ExceptionInInitializerError(e);
}
}
public static SessionFactory getSessionAnnotationFactory() {
if (sessionAnnotationFactory == null) sessionAnnotationFactory = buildSessionAnnotationFactory();
return sessionAnnotationFactory;
}
private static SessionFactory buildSessionAnnotationFactory() {
try {
// **********************************************************************************************
// Create the SessionFactory from hibernate-annotation.cfg.xml
// **********************************************************************************************
Configuration configuration = new Configuration();
configuration.configure("hibernate-annotation.cfg.xml");
System.out.println("Hibernate Annotation configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate Annotation serviceRegistry created");
return configuration.buildSessionFactory(serviceRegistry);
} catch (Throwable e) {
System.err.println("Initial SessionFactory creationg failed." + e);
throw new ExceptionInInitializerError(e);
}
}
public static SessionFactory getSessionJavaConfigFactory() {
if (sessionJavaConfigFactory == null) sessionJavaConfigFactory = buildSessionJavaConfigFactory();
return sessionJavaConfigFactory;
}
private static SessionFactory buildSessionJavaConfigFactory() {
try {
Configuration configuration = new Configuration();
//Create Properties, can be read from property files too
Properties props = new Properties();
props.put("hibernate.connection.driver_class", "com.microsoft.sqlserver.jdbc.SQLServerDriver");
props.put("hibernate.connection.url", "jdbc:sqlserver://FADBOKT2493V\\KRAFTLAKEODB:51678;database=Dev_RepAssistDB;integratedSecurity=true");
props.put("hibernate.current_session_context_class", "thread");
configuration.setProperties(props);
//we can set mapping file or class with annotation
//addClass(Employee1.class) will look for resource
// com/journaldev/hibernate/model/Employee1.hbm.xml (not good)
configuration.addAnnotatedClass(Employee1.class);
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate Java Config serviceRegistry created");
return configuration.buildSessionFactory(serviceRegistry);
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
}
Employee.java:
package hibernateTutorial.model;
import java.time.LocalDateTime;
public class Employee {
private int id;
private String name;
private String role;
private LocalDateTime insertTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public LocalDateTime getInsertTime() {
return insertTime;
}
public void setInsertTime(LocalDateTime insertTime) {
this.insertTime = insertTime;
}
}
Employee1.java:
package hibernateTutorial.model;
import javax.persistence.*;
import java.time.LocalDateTime;
#Entity
#Table(name = "tmp_employees",
uniqueConstraints =
{#UniqueConstraint(columnNames = {"id"})})
public class Employee1 {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, unique = true, length = 11)
private int id;
#Column(name = "name", length = 20, nullable = true)
private String name;
#Column(name = "role", length = 20, nullable = true)
private String role;
#Column(name = "insert_time", nullable = true)
private LocalDateTime insertTime;
}
employee.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="hibernateTutorial.model.Employee" table="tmp_employees">
<id name="id" type="int">
<column name="id"/>
<generator class="increment"/>
</id>
<property name="name" type="java.lang.String">
<column name="name"/>
</property>
<property name="role" type="java.lang.String">
<column name="role"/>
</property>
<property name="insertTime" type="java.time.LocalDateTime">
<column name="insert_time"/>
</property>
</class>
</hibernate-mapping>
hibernate.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection properties - Driver, URL, user, password -->
<property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="hibernate.connection.url">jdbc:sqlserver://servername:port;
database=test_db;integratedSecurity=true
</property>
<!-- Connection Pool Size -->
<property name="hibernate.connection.pool_size">5</property>
<!-- org.hibernate.HibernateException: No CurrentSessionContext configured! -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- Outputs the SQL queries, should be disabled in Production -->
<property name="hibernate.show_sql">true</property>
<!-- Dialect is required to let Hibernate know the Database Type, MySQL, Oracle etc
Hibernate 4 automatically figure out Dialect from Database Connection Metadata -->
<property name="hibernate.dialect">org.hibernate.dialect.SQLServer2012Dialect</property>
<!-- mapping file, we can use Bean annotations too -->
<mapping resource="hibernateTutorial/employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
hibernate-annotation.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection properties - Driver, URL, user, password -->
<property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="hibernate.connection.url">jdbc:sqlserver:servername:port;database=test_db;integratedSecurity=true</property>
<!-- Connection Pool Size -->
<property name="hibernate.connection.pool_size">5</property>
<!-- org.hibernate.HibernateException: No CurrentSessionContext configured! -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- Outputs the SQL queries, should be disabled in Production -->
<property name="hibernate.show_sql">true</property>
<!-- Dialect is required to let Hibernate know the Database Type, MySQL, Oracle etc
Hibernate 4 automatically figure out Dialect from Database Connection Metadata -->
<property name="hibernate.dialect">org.hibernate.dialect.SQLServer2012Dialect</property>
<!-- mapping with model class containing annotations -->
<mapping class="hibernateTutorial.model.Employee1"/>
</session-factory>
</hibernate-configuration>
I'm using Spring and Hibernate (hibernate-core 3.3.1.GA), and as a result of a web call, the code does a transaction with several inserts. Sometimes, one of the inserts fails with Hibernate saying 'Duplicate entry ... for key 'PRIMARY'. I have not been able to identify any pattern on when this happens -- it may work for 4 - 5 requests, and then it fails, then works on retrying, and then may fail on the next request.
Below are the relevant parts of the code:
Controller
#RequestMapping(value = "/users", method = RequestMethod.POST)
public #ResponseBody Map<Object, Object> save(<params>) throws IllegalArgumentException {
...
try {
map = userHelper.save(<parameters>);
...
} catch (Exception e) {
e.printStackTrace();
}
}
The exception is thrown in the above part.
UserHelper.save() method
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public HashMap<String, Object> save(<parameters>) throws NumberParseException, IllegalArgumentException, HibernateException {
....
userService.save(<parameters>);
return save;
}
UserService
HBDao dao;
#Autowired
public UserService(org.hibernate.SessionFactory sessionFactory) {
dao = new HBDao(sessionFactory);
}
...
#Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public HashMap<String, Object> save(<parameters>) throws NumberParseException {
...
User user;
// several lines to create User object
dao.save(user);
...
lookupService.saveUserConfigurations(user, userType, loginById);
...
return response;
}
HBDao
This class wraps hibernate sessions.
public HBDao(SessionFactory sf) {
this.sessionFactory = sf;
}
private Session getSession() {
sessionFactory.getCurrentSession();
}
public void save(Object instance) {
try {
getSession().saveOrUpdate(instance);
} catch (RuntimeException re) {
throw re;
}
}
lookupService.saveUserConfigurations(user, userType, loginById) call results in the below methods in LookupRepository class to be executed:
LookupRepository
#Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public LookupMapping save(LookupMapping configuration) {
dao.save(configuration);
return configuration;
}
public Collection<LookupMapping> saveAll(Collection<LookupMapping> configurations) {
configurations.forEach(this::save);
return configurations;
}
LookupMapping
#Entity
public class LookupMapping {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long configId;
...
}
Hibernate Mapping for LookupMapping class
<hibernate-mapping package="com...configuration.domain">
<class name="LookupMapping" table="lookup_mapping" mutable="false">
<id column="id" name="configId" type="long">
<generator class="increment"/>
</id>
...
</class>
</hibernate-mapping>
Hibernate config
<hibernate-configuration>
<session-factory name="sosFactory">
<!-- Database connection settings -->
...
<property name="connection.pool_size">2</property>
<!-- SQL dialect -->
<property name="dialect">com. ... .CustomDialect</property>
<!-- Enable Hibernate's current session context -->
<property name="current_session_context_class">org.hibernate.context.ManagedSessionContext</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
...
</session-factory>
</hibernate-configuration>
Below are the lines from the log:
2018-05-04 10:24:51.321 7|13|60f566fa-4f85-11e8-ba9b-93dd5bbf4a00 ERROR [http-nio-8080-exec-1] org.hibernate.util.JDBCExceptionReporter - Duplicate entry '340932' for key 'PRIMARY'
2018-05-04 10:24:51.321 7|13|60f566fa-4f85-11e8-ba9b-93dd5bbf4a00 WARN [http-nio-8080-exec-1] org.hibernate.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
2018-05-04 10:24:51.322 7|13|60f566fa-4f85-11e8-ba9b-93dd5bbf4a00 ERROR [http-nio-8080-exec-1] org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:167) ~[hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321) [hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50) [hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027) [hibernate-core-3.3.1.GA.jar:3.3.1.GA]
at com.arl.mg.helpers.UserHelper.save(UserHelper.java:329) [classes/:?]
...
I'm working on a legacy codebase (so cannot upgrade Hibernate easily), and the code that I wrote are in LookupRepository class (and LookupService which is called in UserService).
The Duplicate entry error happens while persisting the LookupMapping objects. There are always two of this object being persisted, and when the error occurs, the duplicate ID is created same as the last entry. That is, if for the first request, IDs 999 and 1000 were inserted, and if the error occurs for the next request, the duplicate ID will be 1000 (and not 999).
Another, possibly important thing to note is that Hibernate shows this line:
org.hibernate.jdbc.ConnectionManager [] - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
This is all the info that I have so far, and I hope I've covered the relevant code as well. Any help will be much appreciated. Do let me know if I have to give more info.
Thanks!
The problem was with the ID generation strategy defined in the Hibernate mapping file.
The strategy was set as increment, which seems to work only when there are no other processes inserting to the table. In my case, it seems that sometimes there were previously open sessions, and new requests ended up inserting to the table simultaneously.
The solution was to change the strategy to native, which uses the underlying database's strategy to generate ID.
<hibernate-mapping package="com...configuration.domain">
<class name="LookupMapping" table="lookup_mapping" mutable="false">
<id column="id" name="configId" type="long">
<generator class="native"/>
</id>
...
</class>
</hibernate-mapping>
I agree with response by #shyam I would switch to some sequence generator.
But also have a look at this peace of code:
User user;
// several lines to create User object
dao.save(user);
...
lookupService.saveUserConfigurations(user, userType, loginById);
In this case you are sending user to saveUserConfigurations which is not managed, and within saveUserConfigurations you might calling merge method. This will cause additional insert statement. Consider refactoring your code to:
User user;
// several lines to create User object
// dao.save should return the stored value of user.
user = dao.save(user);
...
lookupService.saveUserConfigurations(user, userType, loginById);
With such constructions you will be using stored entity (i.e. managed by current hibernate's session). and have a look at all your code and prevent usage of not managed entities once those have been stored.
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.
I'm creating an application with Hibernate JPA and I use c3p0 for connection pooling with MySQL. I have an issue with the number of connections to the MySQL database as it hits the 152 opened connections, this is not wanted since I define in my c3p0 config file the max pool size to 20, and of course I close every entity manager I get from the EntityManagerFactory after committing every transaction.
For every time a controller is executed, I notice more than 7 connections are opened, and if I refresh, then 7 connections are opened again without the past idle connections being closed. And in every DAO function I call, the em.close() is executed. I admit here that the issue is in my code, but I don't know what I am doing wrong here.
This is the Sondage.java entity:
#Entity
#NamedQuery(name="Sondage.findAll", query="SELECT s FROM Sondage s")
public class Sondage implements Serializable {
private static final long serialVersionUID = 1L;
public Sondage() {}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private byte needLocation;
//bi-directional many-to-one association to ResultatSondage
#OneToMany(mappedBy = "sondage", cascade = CascadeType.ALL)
#OrderBy("sondage ASC")
private List<ResultatSondage> resultatSondages;
//bi-directional many-to-one association to SondageSection
#OneToMany(mappedBy = "sondage", cascade = CascadeType.ALL)
private List<SondageSection> sondageSections;
}
And here's my DAO class:
#SuppressWarnings("unchecked")
public static List<Sondage> GetAllSondage() {
EntityManager em = PersistenceManager.getEntityManager();
List<Sondage> allSondages = new ArrayList<>();
try {
em.getTransaction().begin();
Query query = em.createQuery("SELECT s FROM Sondage s");
allSondages = query.getResultList();
em.getTransaction().commit();
} catch (Exception ex) {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
allSondages = null;
} finally {
em.close();
}
return allSondages;
}
As you see, em is closed. In my JSP, I do this: I know this is not the good way of doing thing in the view side.
<body>
<div class="header">
<%#include file="../../../Includes/header.jsp" %>
</div>
<h2 style="color: green; text-align: center;">الاستمارات</h2>
<div id="allsurveys" class="pure-menu custom-restricted-width">
<%
List<Sondage> allSondages = (List<Sondage>) request.getAttribute("sondages");
for (int i = 0; i < allSondages.size(); i++) {
%>
<%= allSondages.get(i).getName()%>
<%
if (request.getSession().getAttribute("user") != null) {
Utilisateur user = (Utilisateur) request.getSession().getAttribute("user");
if (user.getType().equals("admin")) {
%>
تعديل
<%
}
}
%>
<br />
<%
}
%>
</div>
</body>
I'm guessing that every time I call user.getType(), a request is established ? If so, how can I prevent this?
For c4p0 config file, I included it in persistence.xml, I saw several posts saying that I need to put the c3p0 config file in c3p0-config.xml, but with my setup the c3p0 is initialized with the values I pass in the persistence.xml file, also the mysql connections are reaching 152 connections but the maxpoolsize is at 20, here's the persistence.xml file
<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="CAOE" transaction-type="RESOURCE_LOCAL">
<class>com.caoe.Models.ChoixQuestion</class>
<class>com.caoe.Models.Question</class>
<class>com.caoe.Models.Reponse</class>
<class>com.caoe.Models.ResultatSondage</class>
<class>com.caoe.Models.Section</class>
<class>com.caoe.Models.Sondage</class>
<class>com.caoe.Models.SondageSection</class>
<class>com.caoe.Models.SousQuestion</class>
<class>com.caoe.Models.Utilisateur</class>
<properties>
<property name="hibernate.connection.provider_class"
value=" org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider" />
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.connection.url"
value="jdbc:mysql://localhost:3306/caoe?useUnicode=yes&characterEncoding=UTF-8"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.c3p0.max_size" value="50" />
<property name="hibernate.c3p0.min_size" value="3" />
<property name="hibernate.c3p0.max_statements" value="20" />
<property name="hibernate.c3p0.acquire_increment" value="1" />
<property name="hibernate.c3p0.idle_test_period" value="30" />
<property name="hibernate.c3p0.timeout" value="35" />
<property name="hibernate.c3p0.checkoutTimeout" value="60000" />
<property name="hibernate.connection.release_mode" value="after_statement" />
<property name="debugUnreturnedConnectionStackTraces"
value="true" />
</properties>
</persistence-unit>
</persistence>
EDIT: I'm deploying the Application to a red hat server with Tomcat and MySQL Installed. I'm just wondering why Hibernate is opening too much connections to MySQL, with all entity managers closed no connection will remain open, but this is not the case. I'm guessing and correct me if I'm true that the connections are opened when I do something like this:
List<Sondage> allSondages = SondageDao.getAllSondages();
for (Sondage sondage : allSondages) {
List<Question> questions = sondage.getQuestions();
//code to display questions for example
}
Here when I use sondage.getQuestions(), does Hibernate open a connection to the database and doesn't close it after, am I missing something in the configuration file that close or return connection to pool when it's done with it. Thanks in advance for any help.
EDIT2 :
Since people are asking for versions, here they are :
JAVA jre 1.8.0_25
Apache Tomcat v7.0
hibernate-core-4.3.10
hibernate c3p0 4.3.10.final
hibernate-jpa 2.1
Thanks in advance
The mysql version is Mysql 5.6.17 if that can help...
EDIT 4: as people are getting confused about witch version of the code I posted is buggy, let me edit this so you'll know what happens exactly:
First I'll start by showing what's the buggy code, as you guys don't care about what's working:
#SuppressWarnings("unchecked")
public static List<Sondage> GetAllSondage() {
EntityManager em = PersistenceManager.getEntityManager();
List<Sondage> allSondages = new ArrayList<>();
try {
em.getTransaction().begin();
Query query = em.createQuery("SELECT s FROM Sondage s");
allSondages = query.getResultList();
em.getTransaction().commit();
} catch (Exception ex) {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
allSondages = null;
} finally {
em.close();
}
return allSondages;
}
So this is basically what I did for all my dao functions, I know transaction is not needed here, since I saw questions pointing that transactions are important for connection to close. beside this , I getEntityManager from PersistenceManager class that has an EntityManagerFactory singleton Object, so getEntityManager creates an entityManager from the EntityManagerFactory singleton Object:=> code is better than 1000 word :
PesistenceManager.java:
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class PersistenceManager
{
private static EntityManagerFactory emf = null;
public static EntityManager getEntityManager()
{
return getEntityManagerFactory().createEntityManager();
}
public static EntityManagerFactory getEntityManagerFactory()
{
if(emf == null) {
emf = Persistence.createEntityManagerFactory("CAOE");
return emf;
}
else
return emf;
}
}
Yes this is cool and all good, but where's the problem?
The problem here is that this version opens the connections and never close them, the em.close() have no effect, it keeps the connection open to the database.
The noob fix:
What I did to fix this issue is create an EntityManagerFactory for every request, it mean that the dao looks something like this:
#SuppressWarnings("unchecked")
public static List<Sondage> GetAllSondage() {
//this is the method that return the EntityManagerFactory Singleton Object
EntityManagerFactory emf = PersistenceManager.getEntitManagerFactory();
EntityManager em = emf.createEntityManager();
List<Sondage> allSondages = new ArrayList<>();
try {
em.getTransaction().begin();
Query query = em.createQuery("SELECT s FROM Sondage s");
allSondages = query.getResultList();
em.getTransaction().commit();
} catch (Exception ex) {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
allSondages = null;
} finally {
em.close();
emf.close();
}
return allSondages;
}
Now this is bad and I'll just keep it while I don't have answer for this question (it seems like forver :D ). So with this code basically All connections gets closed after hibernate doesn't need them. Thanks in advance for any efforts you put in this question :)
I think that Hibernate and C3P0 are behaving correctly here. In fact you should see that there are always at least three connections to the database open as per your C3P0 configuration.
When you execute a query Hibernate will use a connection from the pool and then return it when it is done. It will not close the connection. C3P0 might shrink the pool if the min size is exceeded and some of the connections time out.
In your final example you see the connections closed because you've shut down your entity manager factory and therefore your connection pool as well.
You call Persistence.createEntityManagerFactory("CAOE") every time. It is wrong. Each call createEntityManagerFactory creates new (indepented) connection pool. You should cache EntityManagerFactory object somewhere.
EDIT:
Also you should manually shutdown EntityManagerFactory. You can do it in #WebListener:
#WebListener
public class AppInit implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {}
public void contextDestroyed(ServletContextEvent sce) {
PersistenceManager.closeEntityMangerFactory();
}
}
Otherwise each case of redeploy is source of leaked connections.
Can you try the following:
<property name="hibernate.connection.release_mode" value="after_transaction" />
<property name="hibernate.current_session_context_class" value="jta" />
instead of your current release mode?
Since sibnick has already answered the technical questions I'll try to address some points you seem to be confused about. So let me give you some ideas on how a hibernate application and connection-pool is intended to work:
Opening a database connection is an "expensive" operation. In order to avoid having to pay that cost for each and every request, you use a connection-pool. The pool opens a certain number of connections to the database in advance and when you need one you can borrow one of those existing connections. At the end of the transaction, these connections will not be closed but returned to the pool so they can be borrowed by the next request. Under heavy load, there might be too few connections to serve all requests so the pool might open additional connections that might be closed later on but not at once.
Creating an EntityManagerFactory is even more expensive (it will create caches, open a new connection-pool, etc.), so, by all means, avoid doing it for every request. Your response-times will become incredibly slow. Also creating too many EntityManagerFactories might exhaust your PermGen-space. So only create one EntityManagerFactory per application/persistence-context, create it at application startup (otherwise the first request will take too long) and close it upon application shutdown.
Bottom line: When using a connection-pool you should expect a certain number of DB-connections to remain open for the lifetime of your application. What must not happen is that the number increases with every request. If you insist on having the connections closed at the end of the session don't use a pool and be prepared to pay the price.
I ran into the same problem and was able to fix it by creating a singleton wrapper class for the EntityManagerFactory and creating the EntityManager where it's needed. You're having the connection overload problem because you're wrapping the EntityManager creation in the singleton class, which is wrong. The EntityManager provides the transaction scope (should not be re-used), the EntityManagerFactory provides the connections (should be re-used).
from: https://cloud.google.com/appengine/docs/java/datastore/jpa/overview
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public final class EMF {
private static final EntityManagerFactory emfInstance =
Persistence.createEntityManagerFactory("CAOE");
private EMF() {}
public static EntityManagerFactory get() {
return emfInstance;
}
}
and then use the factory instance to create an EntityManager for each request.
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import EMF;
// ...
EntityManager em = EMF.get().createEntityManager();
In my application property i have some datasource related parameter. Those are given bellow:
# DataSource Parameter
minPoolSize:5
maxPoolSize:100
maxIdleTime:5
maxStatements:1000
maxStatementsPerConnection:100
maxIdleTimeExcessConnections:10000
Here, **maxIdleTime** value is the main culprit. It takes value in second. Here maxIdleTime=5 means after 5 seconds if connection is not using then it will release the connection and it will take the minPoolSize:5 connection. Here maxPoolSize:100 means it will take maximum 100 connection at a time.
In my DataSource Configuration class i have a bean. Here is the example code:
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.core.env.Environment;
import org.springframework.beans.factory.annotation.Autowired;
#Autowired
private Environment env;
#Bean
public ComboPooledDataSource dataSource(){
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass(env.getProperty("db.driver"));
dataSource.setJdbcUrl(env.getProperty("db.url"));
dataSource.setUser(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
dataSource.setMinPoolSize(Integer.parseInt(env.getProperty("minPoolSize")));
dataSource.setMaxPoolSize(Integer.parseInt(env.getProperty("maxPoolSize")));
dataSource.setMaxIdleTime(Integer.parseInt(env.getProperty("maxIdleTime")));
dataSource.setMaxStatements(Integer.parseInt(env.getProperty("maxStatements")));
dataSource.setMaxStatementsPerConnection(Integer.parseInt(env.getProperty("maxStatementsPerConnection")));
dataSource.setMaxIdleTimeExcessConnections(10000);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
}
Hope this will solve your problem :)
Looks like issue is related to Hibernate bug. Please try to specify fetch strategy EAGER in your OneToMany annotations.
#OneToMany(mappedBy = "sondage", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
Previously I used JDBC in my application and it was running very fast, but I have modified it to use Hibernate which made it too slow, especially when it needs to open a page that has a dropdown box in it. It takes much longer in compare to JDBC, to open this kind of pages.
If I try to access a table with foreign keys it takes much longer.
My server is GlassFish and I am usingfollowing version of hibernate.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.10.Final</version>
<type>jar</type>
</dependency>
The questions are why is it to slow in compare to JDBC and do I need to have the following lin before each session.beginTransaction() ?
session = HibernateUtil.getSessionFactory().openSession();
Take the following one as an example, it has a dropdown box that need to be populated once the page is opened.
HibernateUtil.java
package com.myproject.util;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry;
private static SessionFactory configureSessionFactory() {
try {
System.out.println("1");
Configuration configuration = new Configuration();
configuration.configure();
serviceRegistry = new
ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
System.out.println("2");
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
System.out.println("3");
return sessionFactory;
} catch (HibernateException e) {
System.out.append("** Exception in SessionFactory **");
e.printStackTrace();
}
return sessionFactory;
}
public static SessionFactory getSessionFactory() {
return configureSessionFactory();
}
}
MyClassModel.java
public class MyClassModel extends HibernateUtil {
private Session session;
public Map populatedropdownList() {
Map map = new HashMap();
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
List<MyListResult> temp = null;
try{
temp = retrieveItems();
System.err.println("size:" + temp.size());
for(int i=0;i<temp.size();i++){
map.put(temp.get(i).getId(),temp.get(i).getName());
}
session.getTransaction().commit();
session.close();
return map;
}catch(Exception e){
e.printStackTrace();
}
return map;
}
private List <MyListResult> retrieveItems(){
Criteria criteria = session.createCriteria(MyTable.class, "MyTable");
ProjectionList pl = Projections.projectionList();
pl.add(Projections.property("MyTable.id").as("id"));
pl.add(Projections.property("MyTable.name").as("name"));
criteria.setProjection(pl);
criteria.setResultTransformer(new
AliasToBeanResultTransformer(MyListResult.class));
return criteria.list();
}
MyListResult.java
public class MyListResult implements Serializable {
private int id;
private String Name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return Name;
}
public void setName(String Name) {
this.Name = Name;
}
}
Hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="connection.url">
jdbc:mysql://localhost:3306/MyDatabase
</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">
org.hibernate.cache.NoCacheProvider
</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">update</property>
<mapping class="com.MyProject.MyTable" />
</session-factory>
</hibernate-configuration>
Console is as following
INFO: in myform
INFO: 1
INFO: HHH000043: Configuring from resource: /hibernate.cfg.xml
INFO: HHH000040: Configuration resource: /hibernate.cfg.xml
INFO: HHH000041: Configured SessionFactory: null
INFO: 2
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
INFO: HHH000115: Hibernate connection pool size: 1
INFO: HHH000006: Autocommit mode: false
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL
[jdbc:mysql://localhost:3306/MyDatabase]
INFO: HHH000046: Connection properties: {user=root, password=****}
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
INFO: HHH000399: Using default transaction strategy (direct JDBC transactions)
INFO: HHH000397: Using ASTQueryTranslatorFactory
INFO: HHH000228: Running hbm2ddl schema update
INFO: HHH000102: Fetching database metadata
INFO: HHH000396: Updating schema
INFO: HHH000261: Table found: MyDatabase.MyTable
INFO: HHH000037: Columns: [id, name, age, xx, yy]
INFO: HHH000126: Indexes: [primary]
INFO: HHH000232: Schema update complete
INFO: Hibernate: select this_.id as y0_, this_.name as y1_ from MyTable this_
SEVERE: size:4
Usually in the application you shouldn't build the session factory each time when you need the session. That's what actually needed by application to use the hibernate. If you want to manage the session manually then you write the HibernateUtil making it singleton. Initially, build the session factory in the static initializer block
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<>();
private static SessionFactory sessionFactory;
static {
try {
sessionFactory = configureSessionFactory();
} catch (Exception e) {
System.err.println("%%%% Error Creating SessionFactory %%%%");
e.printStackTrace();
}
}
private HibernateUtil() {
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession() throws HibernateException {
Session session = threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession() : null;
threadLocal.set(session);
}
return session;
}
public static void rebuildSessionFactory() {
try {
sessionFactory = configureSessionFactory();
} catch (Exception e) {
System.err.println("%%%% Error Creating SessionFactory %%%%");
e.printStackTrace();
}
}
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.close();
}
}
think that's enough addition to your code to run your application.
While I don't have any direct experience with Hibernate, I do with NHibernate, which is modeled after the Java version, so this should still be correct...
Building up the SessionFactory takes time, all the more so since, if I'm reading the configuration right, you're having it check for schema changes between your models and the database when it does start.
What you should do instead is create the SessionFactory once on startup (or upon the first use of data access, depending on when you want to pay the cost of setting up Hibernate) and use that through the lifetime of your app.
What I tend to do is something roughly like this (rough psuedocode, since my Java is rusty):
Session getSession()
{
if(sessionFactory == null)
buildSessionFactory()
return sessionFactory.OpenSession()
}
(Of course this doesn't take into account any possible race conditions with threading). This should hopefully see a rather large performance boost. Keep in mind that ORMs are almost never as fast as hand-coded SQL due to the additional mapping layers, but it's a trade off between performance and ease of coding.