Keep Hibernate Initializer from Crashing Program
I have a Java program using a basic Hibernate session factory. I had an issue with a hibernate hbm.xml mapping file and it crashed my program even though I had the getSessionFactory()
call in a try catch
try
{
session = SessionFactoryUtil.getSessionFactory().openStatelessSession();
session.beginTransaction();
rh = getRunHistoryEntry(session);
if(rh == null)
{
throw new Exception("No run history information found in the database for run id " + runId_ + "!");
}
}
catch(Exception ex)
{
logger.error("Error initializing hibernate");
}
It still manages to break out of this try/catch and crash the main thread. How do I keep it from doing this? The main issue is I have a bunch of cleanup commands that NEED to be run before the main thread shuts down and need to be able to guarantee that even after a failure it still cleans up and goes down somewhat gracefully. The session factory looks like this:
public class SessionFactoryUtil {
private static final SessionFactory sessionFactory;
static {
try
{
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure().buildSessionFactory();
}
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);
}
}
public static SessionFactory getSessionFactory()
{ try
{
return sessionFactory;
}
catch(Exception ex)
{
return null;
}
}
}
The error thrown is the following, and I have fixed it, but I would like to safeguard against any hibernate initializing error from stopping the main thread.
Initial SessionFactory creation failed.org.hibernate.InvalidMappingException: Could not parse mapping document from resource hibernate/TmdIndataLine.hbm.xml
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.ryanco.db.SessionFactoryUtil.<clinit>(SessionFactoryUtil.java:19)
at com.ryanco.rta.RtaMain.main(RtaMain.java:148)
Caused by: org.hibernate.InvalidMappingException: Could not parse mapping document from resource hibernate/TmdIndataLine.hbm.xml
at org.hibernate.cfg.Configuration.addResource(Configuration.java:616)
at org.hibernate.cfg.Configuration.parseMappingElement(Configuration.java:1635)
at org.hibernate.cfg.Configuration.parseSessionFactory(Configuration.java:1603)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1582)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1556)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1476)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1462)
at com.ryanco.db.SessionFactoryUtil.<clinit>(SessionFactoryUtil.java:13)
... 1 more
Caused by: org.hibernate.InvalidMappingException: Could not parse mapping document from input stream
at org.hibernate.cfg.Configuration.addInputStream(Configuration.java:555)
at org.hibernate.cfg.Configuration.addResource(Configuration.java:613)
... 8 more
Caused by: org.dom4j.开发者_Go百科DocumentException: Error on line 114 of document : The element type "class" must be terminated by the matching end-tag "</class>". Nested exception: The element type "class" must be terminated by the matching end-tag "</class>".
at org.dom4j.io.SAXReader.read(SAXReader.java:482)
at org.hibernate.cfg.Configuration.addInputStream(Configuration.java:546)
... 9 more
The static initializer in SessionFactoryUtil has a try/catch for all instances of Throwable but your main try/catch block only catches Exceptions. I'd change it to catch Throwables as well and see if that solves the problem.
Well, depending on what type of application you write, static initializers doing anything that reaches outside of the JVM are usually a bad idea.
To the problem: if I read you code correctly, you first catch all Throwable
s in the static initalizer of SessionFactoryUtil
, write an error message to standard error an then throw a new ExceptionInInitializerError
yourself, wrapping the catched Throwable
in it.
IIRC static initializers are invoked first thing after the class was loaded. The stack trace tells us this happens probably from the main method in class RtaMain
, as SessionFactoryUtil.getSessionFactory()
seems to be invoked from there, which triggers the class load.
To catch this error you need a try/catch surrounding the call to getSessionFactory()
in the main method of your class RtaUtil
, catching Throwable, or at least the specific throwable you throw in your static initializer yourself, which is ExceptionInInitializerError
.
HTH
It still manages to break out of this try/catch and crash the main thread.
As mentioned, this bloc in your main method won't catch the ExceptionInInitializerError
thrown in your static initializer:
catch(Exception ex) {
logger.error("Error initializing hibernate")
}
If you want to catch both Exception
and Error
, you should catch (Throwable t)
(by the way, I noticed you said you changed that but I'd like to see what you changed exactly and the error you get.
But to be honest, if the SessionFactory
fails at being initialized, I wonder what you're going to do in your main
method and it's actually a common practice to let the program fail in that case.
Last thing, the try/catch
in this part seems totally useless:
public static SessionFactory getSessionFactory() {
try {
return sessionFactory;
} catch(Exception ex) {
return null;
}
}
You can remove it safely.
精彩评论