I created a program using Hibernate.
The program reaches the main function end, nevertheless the program is running.
I wonder if it happens when SessionFactory is configured using Hibernate Version 4.x.
Is the way to configure wrong?
manual1_1_first_hibernate_apps.java
public static void main(String[] args) {
args[0] ="list";
if (args.length <= 0) {
System.err.println("argement was not given");
return;
}
manual1_1_first_hibernate_apps mgr = new manual1_1_first_hibernate_apps();
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
else if (args[0].equals("list")) {
mgr.<Event>listEvents().stream()
.map(e -> "Event: " + e.getTitle() + " Time: " + e.getDate())
.forEach(System.out::println);
}
Util.getSessionFactory().close();
}
private <T> List<T> listEvents() {
Session session = Util.getSessionFactory().getCurrentSession();
session.beginTransaction();
List<T> events = Util.autoCast(session.createQuery("from Event").list());
session.getTransaction().commit();
return events;
}
Util.java
private static final SessionFactory sessionFactory;
/**
* build a SessionFactory
*/
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
// hibernate version lower than 4.x are as follows
// # it successful termination. but buildSessionFactory method is deprecated.
// sessionFactory = new Configuration().configure().buildSessionFactory();
// version 4.3 and later
// # it does not terminate. I manually terminated.
Configuration configuration = new Configuration().configure();
StandardServiceRegistry serviceRegistry =
new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
/**
* #return built SessionFactory
*/
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
The following console log snippets when program terminate and use buildSessionFactory method.
2 08, 2014 8:42:25 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:derby:D:\Java\jdk1.7.0_03(x86)\db\bin\testdb]
but if do not use deprecated buildSessionFactory method and terminated(program is running), the above two lines do not appear.
ENVIRONMENT:
Hibernate 4.3.1
DERBY
JRE 1.8
IntelliJ IDEA 13
I met this problem also today, and I found the solution is, in the end of your main method (or thread), you should close your Session Factory, like:
sessionFactory.close();
And then, your program will terminate normally.
If You use JavaFX 8 in main method add:
#Override
public void stop() throws Exception {
sessionFactory.close();
}
This method will close session factory and destroy thread on program exit.
I had the same problem today, but I found another similar solution:
I inserted at the end of my code the following line:
StandardServiceRegistryBuilder.destroy(serviceRegistry);
And Ta-dah! the program ends.
Same problem in 4.3.4.Final.
Now after adding following code, the problem is gone.
public class Service {
private SessionFactory factory;
private ServiceRegistry serviceRegistry;
public void initialize() throws Exception{
Configuration configuration = new Configuration();
configuration.configure("com/jeecourse/config/hibernate.cfg.xml");
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(
configuration.getProperties()).build();
factory = configuration.buildSessionFactory(serviceRegistry);
}
public void close() throws Exception{
if(serviceRegistry!= null) {
StandardServiceRegistryBuilder.destroy(serviceRegistry);
}
}
.....
maybe, I solved this problem.
I saw the thread dump after Util.getSessionFactory().close() called, a thread named "pool-2-thread-1" state was TIMED_WAITING (parking).
The following snippets dump
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.0-b69 mixed mode):
"DestroyJavaVM" #16 prio=5 os_prio=0 tid=0x00000000020b9000 nid=0x3684 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"pool-2-thread-1" #15 prio=5 os_prio=0 tid=0x000000001bc27000 nid=0x3f0 waiting on condition [0x000000001ce6f000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000080be30a0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:744)
"derby.rawStoreDaemon" #14 daemon prio=5 os_prio=0 tid=0x000000001b059000 nid=0xa3c in Object.wait() [0x000000001ba1f000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000805f6190> (a org.apache.derby.impl.services.daemon.BasicDaemon)
at org.apache.derby.impl.services.daemon.BasicDaemon.rest(Unknown Source)
- locked <0x00000000805f6190> (a org.apache.derby.impl.services.daemon.BasicDaemon)
at org.apache.derby.impl.services.daemon.BasicDaemon.run(Unknown Source)
at java.lang.Thread.run(Thread.java:744)
"Timer-0" #13 daemon prio=5 os_prio=0 tid=0x000000001b08e800 nid=0x2160 in Object.wait() [0x000000001b6af000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000080608118> (a java.util.TaskQueue)
at java.lang.Object.wait(Object.java:502)
at java.util.TimerThread.mainLoop(Timer.java:526)
- locked <0x0000000080608118> (a java.util.TaskQueue)
at java.util.TimerThread.run(Timer.java:505)
I thought the cause is thread named "pool-2-thread-1" that created by buildSessionFactory method.
As a result of comparing the two buildSessionFactory method, I noticed that ServiceRegistry resources has not released.
Program successfully terminated by releasing it.
The following code, I adding.
Util.java
configuration.setSessionFactoryObserver(
new SessionFactoryObserver() {
#Override
public void sessionFactoryCreated(SessionFactory factory) {}
#Override
public void sessionFactoryClosed(SessionFactory factory) {
((StandardServiceRegistryImpl) serviceRegistry).destroy();
}
}
);
thanks.
It seems that Hibernate 4.3.1 introduced a bug. I create the connection in my application with:
EntityManagerFactory connection = Persistence.createEntityManagerFactory(...)
but even if the createEntityManagerFactory method fails with an exception, the service registry remains open. However, as you could see from the above code, I cannot terminate my application because as the method didn't succeed the variable connection wasn't assigned (it is null), so I cannot call connection.close() that would destroy the service registry. It seems that this is really a bug, because how will I be able to release resources without resorting to a hack, like using specific Hibernate APIs from a JPA application?
I met this problem also today, and I found that the solution like:
sessionFactory.close();
will work if you have
<property name="connection.pool_size">1</property>
I am using hibenate 5.2.12 with sqlite 3.20.1, managing the connection manually. In my case the problem was that not only the entity manager had to be closed but also the entity manager factory.
With these attributes:
EntityManager entityManager;
EntityTransaction entityTransaction;
This snippet is used when opening the DB and starting a transaction:
EntityManagerFactory emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME, map);
entityManager = emf.createEntityManager(map);
entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
This snipped is used to commit the transaction and close the DB:
entityTransaction.commit();
if ( entityManager.isOpen() ) {
entityManager.close();
}
EntityManagerFactory emf = entityManager.getEntityManagerFactory();
if ( emf.isOpen() ) {
emf.close();
}
Now with emf.close(); my application terminates as is should be.
I have just had the same problem. I was using Hibernate 4.1.1 and everything was working fine. Today I upgraded to Hibernate 4.3.1 and suddenly my application didn't terminate anymore. I investigated a little further and noticed that version 4.1.1 didn't have any problem with an open EntityManagerFactory. That's why my application always terminated. That's not the case with version 4.3.1 anymore. So I checked my application and made sure that the EntityManagerFactory was closed at the end (indeed I was not really closing it). Problem solved to me. Are you really sure there's nothing left open in your application? Hope this helps.
Marcos
i had the same problem, the solution is so simple , you have to add this property to configuration's file
<property name="hibernate.c3p0.timeout">0</property>
My answer is for Hibernate 4.3+ version, and I use it in my way.
A example of Spring Annotation configured with Hibernate:
//Using AutoCloseable 1.7 feature here to close context and
//suppress warning Resource leak: 'context' is never closed
//Creating AutoClosebale AbstractApplicationContext Object context
try (AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringAppConfig.class)) {
SnacksMenu snacks = context.getBean(SnacksMenu.class);
System.out.println(snacks.toString());
//Creating AutoClosebale SessionFactory Object factory
try (SessionFactory factory = getStandardServiceEnabledSessionFactory()){
//Creating AutoClosebale Session Object session
try (Session session = factory.openSession()) {
SessionCounter.analysisSession(session);
System.out.println("1: Opened Sessions under factory : " + SessionCounter.getOpenSessionsCount());
Transaction transaction = session.beginTransaction();
session.persist(snacks);
transaction.commit();
System.out.println(session.isConnected());
}//Session Object session resource auto closed
}//SessionFactory Object factory resource auto closed
System.out.println("2: Opened Sessions under factory : " + SessionCounter.getOpenSessionsCount());
}//AbstractApplicationContext Object context resource auto closed
We have multiple connections to database.
class ConnectionProviderFactory implements DataBaseConnectionProvider {
private EnumMap<SystemInstance, Properties> connectionsConfigs = new EnumMap<>(SystemInstance.class);
private Map<SystemInstance, EntityManager> entityManagers = new HashMap<>();
private Map<SystemInstance, ConnectionPerSystemInstance> connections = new HashMap<>();
#Getter
private static class ConnectionPerSystemInstance {
private String uuid = UUID.randomUUID().toString();
private final SessionFactory sessionFactory;
private final SystemInstance systemInstance;
private ConnectionPerSystemInstance(final SessionFactory sessionFactory, SystemInstance systemInstance){
this.sessionFactory = sessionFactory;
this.systemInstance = systemInstance;
}
static ConnectionPerSystemInstance createConnection(Properties properties, SystemInstance systemInstance) {
StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
registryBuilder.applySettings(toMap(properties));
StandardServiceRegistry registry = registryBuilder.build();
MetadataSources sources = new MetadataSources(registry);
Metadata metadata = sources.getMetadataBuilder().build();
SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();
return new ConnectionPerSystemInstance(sessionFactory, systemInstance);
}
private static Map<String, String> toMap(Properties properties) {
Map<String, String> map = new HashMap<>();
for (String name : properties.stringPropertyNames()) {
map.put(name, properties.getProperty(name));
}
return map;
}
EntityManager getEntityManager() {
if(sessionFactory == null) {
throw new IllegalStateException("Connection not initialized!");
}
return sessionFactory.createEntityManager();
}
void close() {
if(sessionFactory == null) {
throw new IllegalStateException("Connection not initialized!");
}
sessionFactory.close();
}
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ConnectionPerSystemInstance that = (ConnectionPerSystemInstance) o;
return uuid.equals(that.uuid);
}
#Override
public int hashCode() {
return uuid.hashCode();
}
}
#PostConstruct
private void init() {
String testQuery = "select sysdate from dual";
EntityManager e1TEST = connect(SystemInstance.TEST);
EntityManager e1PROD = connect(SystemInstance.PROD);
log.info("" + e1TEST.createNativeQuery(testQuery).getSingleResult());
log.info("" + e1PROD.createNativeQuery(testQuery).getSingleResult());
}
#PreDestroy
private void clean() {
entityManagers.forEach((key, value) -> value.close());
connections.forEach((systemInstance, connectionPerSystemInstance) -> {
connectionPerSystemInstance.close();
});
}
#Override
public EntityManager connect(final SystemInstance systemInstance) {
if (Optional.ofNullable(entityManagers.get(systemInstance)).isPresent()) {
entityManagers.get(systemInstance);
}
Properties properties = loadConnectionProperties(systemInstance);
ConnectionPerSystemInstance connection = ConnectionPerSystemInstance.createConnection(properties, systemInstance);
connections.put(systemInstance, connection);
entityManagers.put(systemInstance, connection.getEntityManager());
return entityManagers.get(systemInstance);
}
#Override
public void closeAllConnection() {
clean();
}
private Properties loadConnectionProperties(SystemInstance systemInstance) {
if (Optional.ofNullable(connectionsConfigs.get(systemInstance)).isPresent()) {
return connectionsConfigs.get(systemInstance);
}
return tryLoadConnectionProperties(systemInstance);
}
private Properties tryLoadConnectionProperties(final SystemInstance systemInstance) {
final String nameOfPropertyFile = getNameOfPropertyFile(systemInstance);
ClassPathResource classPathResource = new ClassPathResource(nameOfPropertyFile);
Properties properties = new Properties();
try {
properties.load(classPathResource.getInputStream());
addAdditionalConnectionSettings(properties);
connectionsConfigs.put(systemInstance, properties);
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e.getMessage()); //TODO chanfe exception
}
return properties;
}
private String getNameOfPropertyFile(SystemInstance systemInstance) {
if (systemInstance == SystemInstance.TEST)
return "db/db-test.properties";
if (systemInstance == SystemInstance.PROD)
return "db/db-prod.properties";
throw new IllegalArgumentException("Incorrect configuration");
}
private void addAdditionalConnectionSettings(Properties properties) {
properties.putIfAbsent("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
properties.putIfAbsent("hibernate.c3p0.timeout", "0");
}
}
In this example we have multiple databases so we can easily close all connections.
I faced the same issue, the reason is we need to close the resources properly.
try closing the resources in finally block
try{
Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
sessionFactory = cfg.buildSessionFactory();;
session = sessionFactory.openSession();
transaction = session.beginTransaction();
session.save(person);
transaction.commit();
}catch(Exception e){
System.out.println("Exception occured. "+e.getMessage());
}finally{
if(session.isOpen()){
System.out.println("Closing session");
session.close();
}
if(!sessionFactory.isClosed()){
System.out.println("Closing SessionFactory");
sessionFactory.close();
}
}
Ensure the same SessionFactory instance you are dealing with through out the program. i.e. The program must terminate if you are closing the same SessionFactory that you built and opened the Session in the program. The source code in the question part doesn't guaranty this and hence the trouble.
Please have a look at the following simple and straight forward solution,
//your class definition
public static void main(String args[]) {
SessionFactory sessionFactory = getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction().begin();
session.save(yourEntity);
session.beginTransaction().commit();
session.close();
sessionFactory.close();
}
public static SessionFactory getSessionFactory() {
Configuration cfg = new Configuration();
cfg.configure("com/srees/hibernate.cfg.xml");
SessionFactory sessionFactory = cfg.buildSessionFactory();
return sessionFactory;
}
//your class def ends here
Related
I working with PostgreSQL in multithread mode by Hibernate sessions. Init:
<property name="connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.current_session_context_class">thread</property>
(other strings is connection URL and entity xml describe))
HibernateUtil:
public class HibernateUtil {
private static SessionFactory sessionFactory;
static {
try {
Configuration configuration = new Configuration();
configuration.configure();
sessionFactory = configuration.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession() {
return sessionFactory.getCurrentSession();
}
public static void close() {
sessionFactory.close();
}
public static void setSessionFactory(SessionFactory factory) {
sessionFactory = factory;
}
}
All CRUD actions with Entities i do in separate class, where every action execute in one transaction.
public class SessionService {
public static <T> T get(Class<T> classvar, int id) {
Session session = HibernateUtil.getSession();
session.beginTransaction();
T result = session.get(classvar, id);
session.getTransaction().commit();
return result;
}
// Some other code
public static void update(Object obj) {
Session session = HibernateUtil.getSession();
session.beginTransaction();
session.update(obj);
session.getTransaction().commit();
}
}
There is no problems with multithreading work (or i not see it). But, some time ago i seen strange bug. I got one record from db by SessionService.get(Entity.class, 1). Change one field of this entity and save it by SessionService.update(entity). Update was successfull done without exceptions (logging approved). But changes not seen in database. In runtime entity has this changes, but not in db. I wait some time (for flush and others) but nothing changed.
Tried to repeat bug - all good, bug not found. In one day i catch this bug on production server with 19 objects from 200, but in other days i not seen this bug.
Last importan moment: i dont catched this bug with other entities (or i dont see them).
What may be reason of it? May be better use entity manager instead sessions?
Can you please post the entire hibernate.cfg.xml file (delete the username and password of course).
Also are you 100% sure that you do not get an error like (wild guess) :
A different object with the same identifier value was already associated with the session ?
There are errors while running Hibernate, that literary flashes in the console for half a second when running queries.
Had this problem a while back.
I'm new to Hibernate. I've created the SessionFactory and obtaining the Session like below. And I'm using c3p0 connection provider
public static SessionFactory getSessionFactory() {
try {
if (sessionFactory == null) {
Configuration configuration = loadDBConfiguration();
if (configuration != null) {
sessionFactory = configuration.buildSessionFactory();
} else {
log.info("---- Configuration failed ----");
}
}
} catch (Exception ex) {
log.info("---- Initial SessionFactory creation failed ----");
}
return sessionFactory;
}
public static EntityManagerFactory getEntityManagerFactory() {
try {
Session session = getSessionFactory().openSession();
entityManagerFactory = session.getEntityManagerFactory();
} catch (Exception e) {
log.error(e);
}
return entityManagerFactory;
}
public static EntityManager getEntityManager() {
try {
EntityManagerFactory entityManagerFactory = getEntityManagerFactory();
return entityManagerFactory.createEntityManager();
} catch (Exception e) {
log.error(e);
}
return null;
}
And i'm confused with below things.
Should i close the connection after commit(), or c3p0 connection provider will close after commit().
Shall i call getEntityManager() on each request or should i use like singleton
If i'm used as singleton, then does it affect any parallel transaction.begin() or transaction.commit() if server receives multiple request at same time.
Also in singleton the entity still persist in session until it gets closed or clear manually. So what i need to do in this case.
Currently the getEntityManager() gets called in all my EntityManager Classes like UserManager, AccountsManager. After some period of time in RDS it shows like 20connections, even my application not handling any user requests.
You should close your connections. Since your connections are wrapped by the database connection pool, they will not get physically closed, but they will return to the pool. This needs to be done otherwise the connection pool will think the connections are being used and when you hit the pool limit, no new connections will be open.
Keep the session factory as a singleton. You should open a new session for each thread.
Im working on a java standAlone project. I need to use hibernate in a MultiThread application but i just cant figure it out how to set up this correctly.
Each Thread deals with the same process of the others.
Everything goes Ok when i run it in a Non-Async way, but when i call the same thing using threads, hibernate just don't work fine.
Can anyone please explain me what's the correct way to use Hibernate in a multiThread Java Stand-Alone App?
Hibernate Util
public class HibernateUtil {
private static final Session session;
static {
try {
SessionFactory sessionFactory;
Properties properties = new Properties();
properties.load(new FileInputStream("middleware.properties"));
Configuration cfg = new Configuration().configure();
cfg.addProperties(properties);
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(cfg.getProperties()).build();
sessionFactory = cfg.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
} catch (IOException | HibernateException he) {
JOptionPane.showMessageDialog(null, DataBaseMessage.CONNECTION_ERROR.getMessage(), DataBaseMessage.CONNECTION_ERROR.getTitle(),JOptionPane.ERROR_MESSAGE);
throw new ExceptionInInitializerError(he);
}
}
public static Session getSession() {
return session;
}
The Error comes here
TbHistoDespachos despacho = Dao.findDespachoByTagId(element.getChild("tagID").getText());
public synchronized List<TbHistoDespachos> ExractDespachoAndNotify(String data, String nombreConexion) {
List<TbHistoDespachos> despachos = new ArrayList<>();
String nombreConexionUpp = nombreConexion.toUpperCase();
try {
Document doc = convertStringToDocument(data);
if (!doc.getRootElement().getChild("reply").getChild("readTagIDs")
.getChildren().isEmpty()) {
for (Element element : doc.getRootElement().getChild("reply").
getChild("readTagIDs").getChild("returnValue")
.getChildren()) {
TbHistoDespachos despacho = Dao.findDespachoByTagId(element.getChild("tagID").getText());
if (despacho != null) {
if(evaluateDespacho(nombreConexionUpp, despacho)){
despachos.add(despacho);
}
}
}
}
} catch (JDOMException | IOException ex) {
JOptionPane.showMessageDialog(null, FilesMessageWarnings.NOTIFICATION_SAP_WARNING.
getMessage().replace("&nombreConexion", nombreConexion).replace("&tagID", ""),
FilesMessageWarnings.NOTIFICATION_SAP_WARNING.getTitle(), JOptionPane.WARNING_MESSAGE);
}
return despachos;
}
Here is the DAO
public class Dao {
private static Session sesion;
public static TbHistoDespachos findDespachoByTagId(String tagId) {
TbHistoDespachos despacho = null;
try {
startTransmission();
despacho = (TbHistoDespachos)sesion.createQuery("FROM TbHistoDespachos WHERE TAG_ID =:tagId")
.setParameter("tagId", tagId)
.uniqueResult();
stopTransmission();
} catch (HibernateException he) {
System.out.println("error: " + he.getMessage());
JOptionPane.showMessageDialog(null, DataBaseMessage.QUERY_ERROR.getMessage(),
DataBaseMessage.QUERY_ERROR.getTitle(), JOptionPane.ERROR_MESSAGE);
}
return despacho;
}
private static void startTransmission() {
sesion = HibernateUtil.getSession();
sesion.getTransaction().begin();
}
private static void stopTransmission() {
sesion.getTransaction().commit();
sesion.getSessionFactory().getCurrentSession().close();
sesion.clear();
}
ANY IDEAS?
The problem stems from static Session variables. A SessionFactory is thread-safe and, generally speaking, you only need one (static) instance per database. A Session, on the other hand, is not thread-safe and is usually created (using a SessionFactory) and discarted/closed on the fly.
To solve your immediate problem, remove the static Session sesion variable from your Dao and also 'inline' the startTransmission and stopTransmission methods in the findDespachoByTagId method. This will ensure that each thread calling findDespachoByTagId creates and uses its own session instance. To analyze the current problem, imagine two threads calling findDespachoByTagId at the same time. Now the static session variable will be assigned a value twice by the startTransmission method. This means one session instance is lost almost immediatly after it was created while the other one is used by two threads at the same time. Not a good thing.
But there are other problems too: there are no finally blocks that guarantee transactions are closed and database connections are released (via the closing of sessions). Also, you will probably want to use a database pool as the one provided by Hibernate is not suitable for production. I recommend you have a look at HibHik: I created this project to show a minimal stand-alone Java application using Hibernate with a database pool (HikariCP) that uses the recommended patterns and practices (mostly shown in TestDbCrud.java). Use the relevant parts in your application, than write multi-threaded unit-tests to verify your database layer (DAO) is working properly, even in the case of failure (e.g. when the database is suddenly no longer available because the network-cable was unplugged).
I have buit a Webservice using these technologies + c3p0 for database handling. It works ok most of the time but I have a 3-5% ratio (sometimes even a 10%) of failed acces due to this error.
I am using Hibernate this way:
-Session Factory
private static SessionFactory buildSessionFactory() {
try {
Configuration configuration = new Configuration();
configuration.configure();
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
// Create the SessionFactory from hibernate.cfg.xml
return configuration
.buildSessionFactory(serviceRegistry);
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
//reabrimos la sesion si esta cerrada al liberar los recursos
if(sessionFactory.isClosed())
{
System.out.println("Reopen session");
sessionFactory.openSession();
}
return sessionFactory;
}
Then in my hibernate.cfg.xml I have the following line:
<property name="current_session_context_class">thread</property>
Finally in my endpoints I have defined a hibernate_session class which I use as follows:
#Path("/projects")
public class ProjectServiceImpl {
#Context
SecurityContext security;
Session hibernate_session = null;
#POST
#Path("sync.json")
#Produces(value = {"application/json",
"application/vnd.myapp-v1+json",
"application/vnd.myapp-v2+json"})
public Response syncProjects(
#DefaultValue("") #FormParam("projects") String in_projects_str,
#DefaultValue("0") #FormParam("last_sync") long last_sync,
#Context Request request) {
//...
hibernate_session = HibernateUtil.getSessionFactory()
.getCurrentSession();
if (hibernate_session == null) {
ResponseMessage rm = new ResponseMessage();
rm.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
rm.setMessage("Hibernate Session is Null");
rm.setType("ERROR");
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(rm)
.type("application/json").build();
}
try {
hibernate_session.beginTransaction();
//Database work...
hibernate_session.flush();
hibernate_session.getTransaction().commit();
}catch (RuntimeException | IllegalAccessException
| InvocationTargetException e) {
try {
if (hibernate_session.getTransaction() != null) {
hibernate_session.getTransaction().rollback();
}
} catch (RuntimeException rbe) {
System.err.println("Couldn’t roll back transaction");
}
e.printStackTrace();
ResponseMessage rm = new ResponseMessage();
rm.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
rm.setMessage(e.getMessage());
rm.setType("ERROR");
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(rm)
.type("application/json").build();
}
}
return Response.ok().entity(result_entity)
.type("application/json").build();
}
My hibernate_session is a class attribute, do I have to change it to a local variable?. As far as I know the end points will be executed in different threads so I have assumed that I am working with different instances of my endpoint container class and these class attributes will not get overriden by multiple request.
Any light you can shed on this topic will be appreciated,
Thanks in advance
Thanks all for your replies. I finally managed to solve the problem.
In one of my multiple entries there was a begin transaction (necessary to create criterias) but was not commited. The result was that a reused thread that had called that method before would throw a nested exception. By commiting the transaction the problem was solved :)
You aren't using openSession and getCurrentSession properly.
public static SessionFactory getSessionFactory() {
//reabrimos la sesion si esta cerrada al liberar los recursos
//change this: if(sessionFactory.isClosed()) to this:
if(sessionFactory == null || sessionFactory.isClosed())
{
System.out.println("Reopen session"); // Really setup session factory
//change this: sessionFactory.openSession(); to this:
sessionFactory = buildSessionFactory();
}
return sessionFactory;
}
That's not the problem though, your code there just isn't doing what it's supposed to. You need to change:
hibernate_session = HibernateUtil.getSessionFactory().getCurrentSession();
to
hibernate_session = HibernateUtil.getSessionFactory().openSession();
As per the SessionFactory Javadoc:
Obtains the current session. The definition of what exactly "current" means controlled by the CurrentSessionContext impl configured for use.
It's safe to assume your CurrentSessionContext is not thread safe.
It seems that a transaction is started, and before the transaction gets commited an attempt to start a new transaction is made.
This explains the error message that says that a nested transaction (the second transaction inside the ongoing transaction) is not supported.
This could be caused for example by incorrect error handling, for example starting a transaction, not catching an exception or catch and ignore and then try to begin a second transaction without having done either commit or rollback.
An idiom similar to this one should be used when doing programmatic transaction mananagement:
try {
sess.getTransaction().begin();
// do some work
sess.getTransaction().commit()
}
catch (RuntimeException e) {
sess.getTransaction().rollback();
throw e;
}
Also important to bear mind is that after a rollback the session cannot be reused, as it's in an inconsistent state.
If using a framework like Spring, the use of the annotation #Transactional for declarative transaction management solves most of these problems for us and leads to more maintainable code, EJB3 has also similar functionality.
My requirement is as follows:
I need to restart(or rebuild) hibernate session factory in my Spring web application at frequent intervals with the new HBM files I get from outside.
Currently my Sessionfactory class is as follows with a SessionFactory Proxy to intercept 'OpenSession' call.
There I am checking for a condition to restart and rebuilding sessionFactory.
My problem here is , in concurrent environment the other users who are in middle of other transaction is getting affected during this restart.
Is there anyway to carryout the restart by checking all the transactions and open sessions and carry out rebuilding session factory once all other completes?
or any other solution exists.
Code:
public class DataStoreSessionFactory extends LocalSessionFactoryBean
{
private boolean restartFactory = false;
#Override
protected void postProcessConfiguration(Configuration config) throws HibernateException
{
super.postProcessConfiguration(config);
updateHBMList(config);
}
private void updateHBMList(final Configuration config)
{
config.addXML(modelRegistry.generateMapping());
}
#Override
public SessionFactory getObject()
{
Object obj = super.getObject();
/*
* Invocation handler for the proxy
*/
SessionFactoryProxy proxy = new SessionFactoryProxy(this, (SessionFactory) obj);
/**
* All the methods invoked on the returned session factory object will pass through this proxy's invocation
* handler
*/
SessionFactory sessionFactory = (SessionFactory) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[] { SessionFactory.class },
proxy);
return sessionFactory;
}
static class SessionFactoryProxy implements InvocationHandler
{
private SessionFactory sessionFactory;
private LocalSessionFactoryBean factoryBean;
public SessionFactoryProxy(LocalSessionFactoryBean factoryBean, SessionFactory sessionFactory)
{
this.factoryBean = factoryBean;
this.sessionFactory = sessionFactory;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
/**
* Only if the method invoked is openSession - check if the session factory should be restarted, and only then
* invoke the requested method
*/
if (method.getName().equals("openSession"))
{
restartSessionFactoryIfNecessary();
}
return method.invoke(sessionFactory, args);
}
private void restartSessionFactoryIfNecessary()
{
restartSessionFactory();
/*if (((DataStoreSessionFactory) factoryBean).isRestartFactory())
{
restartSessionFactory();
}*/
}
private synchronized void restartSessionFactory()
{
log.info("Restarting session...");
factoryBean.destroy();
try
{
factoryBean.afterPropertiesSet();
sessionFactory = factoryBean.getObject();
}
catch (Exception e)
{
log.error("Error while restarting session: " + e.getMessage());
throw new RuntimeException(e);
}
}
}
Thanks,
Appasamy
You can following SessionFactoryUtils to determine whether a transaction is taking place in Session factory and then decide to restart the session factory or not:
You will need to import--> org.springframework.orm.hibernate.SessionFactoryUtils in you file, and use the following API.
static boolean hasTransactionalSession(SessionFactory sessionFactory);
Above API returns whether there is a transactional Hibernate Session for the current thread, that is, a Session bound to the current thread by Spring's transaction facilities.There is also another API just in case if you need to check if a session is transactional in session factory currently:
static boolean isSessionTransactional(Session session,SessionFactory sessionFactory);
Above API returns whether the given particular Hibernate Session is transactional, that is, bound to the current thread by Spring's transaction facilities.