Aspects of using Hibernate with CaptainCasa Enterprise Client

4 downloads 27743 Views 94KB Size Report
Hibernate consist out of quite a lot libraries + dependent libraries. Add all these libraries ..... Now that during the request processing we made sure that we have a shared Session and a ... An easy way to do so, is the usage of a servlet filter:.
Aspects of using Hibernate with CaptainCasa Enterprise Client We all know: there are a lot of frameworks that deal with persistence in the Java environment – one of them being Hibernate. And there are a lot of different ways how to work with Hibernate. So this document only does not tell you the one and only way to use Hibernate within a CaptainCasa environment, but it talks about one way of using it. Nevertheless there are some principal thoughts that should always be of interest... ...and, please note: the server side of CaptainCasa is based on JSF. So, what we talk about in this document is nothing, which is specific to CaptainCasa.

Adding Hibernate to your Project Libraries Hibernate consist out of quite a lot libraries + dependent libraries. Add all these libraries into the WEB-INF/lib folder of your web content.

CaptainCasa Enterprise Client

The following screen shot shows the WEB-INF/lib folder after having added all Hibernate libraries:

1

You also see a “hsqldb.jar” which is a driver to the database that we are using (a small HSQL database).

Configuration Files Add the following configuration files to the source folder of your project:

A typical content of the log4j.properties files is: ### direct log messages to stdout ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### direct messages to file hibernate.log ### #log4j.appender.file=org.apache.log4j.FileAppender #log4j.appender.file.File=hibernate.log #log4j.appender.file.layout=org.apache.log4j.PatternLayout #log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### set log levels - for more verbose logging change 'info' to 'debug' ### log4j.rootLogger=warn, stdout log4j.logger.org.hibernate=info #log4j.logger.org.hibernate=debug ### log HQL query parser activity #log4j.logger.org.hibernate.hql.ast.AST=debug ### log just the SQL #log4j.logger.org.hibernate.SQL=debug ### log JDBC bind parameters ### log4j.logger.org.hibernate.type=info #log4j.logger.org.hibernate.type=debug

### log HQL parse trees #log4j.logger.org.hibernate.hql=debug ### log cache activity ### #log4j.logger.org.hibernate.cache=debug ### log transaction activity #log4j.logger.org.hibernate.transaction=debug ### log JDBC resource acquisition #log4j.logger.org.hibernate.jdbc=debug ### enable the following line if you want to track down connection ### ### leakages when using DriverManagerConnectionProvider ### #log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace

A typical content of the hibernate.cfg.xml file is:

2

CaptainCasa Enterprise Client

### log schema export/update ### log4j.logger.org.hibernate.tool.hbm2ddl=debug

org.hsqldb.jdbcDriver jdbc:hsqldb:hsql://localhost/ISA SA 1 org.hibernate.dialect.HSQLDialect thread org.hibernate.cache.NoCacheProvider true update

The hibernate configuration points to locally running hypersonic database (HSQL). There is a pointing to a mapping file contained in the source directory “entities/database.hbm.xml” - so all this is normal Hibernate...

The typical HibernateUtil class Add the typical HibernateUtil class to the project for having a singleton-access to the Hibernate SessionFactory: package hibernate;

CaptainCasa Enterprise Client

import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static final SessionFactory s_sessionFactory; static { try { // Create the SessionFactory from hibernate.cfg.xml s_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() { return s_sessionFactory; }

3

}

First – test your Environment... After having done the copying, it's not too bad to do some testing, if you can access the database... • Write an entity bean (in example below “PersonEntity”) • Set up the mapping (or use Hibernate annotations) • Write a mini test page with an action listener doing some Hibernate operations • Deploy this by “reloading” the application in the Layout Editor The entity bean might look like: package entities; public class PersonEntity { String m_id; String m_firstName; String m_lastName; Boolean m_sex; String m_geburtsland; public String getGeburtsland() { return m_geburtsland; } public void setGeburtsland(String geburtsland) { m_geburtsland = geburtsland; } public public public public public public public public

Boolean getSex() { return m_sex; } void setSex(Boolean sex) { m_sex = sex; } String getId() { return m_id; } void setId(String id) { m_id = id; } String getFirstName() { return m_firstName; } void setFirstName(String firstName) { m_firstName = firstName; } String getLastName() { return m_lastName; } void setLastName(String lastName) { m_lastName = lastName; }

}

The mapping file (in the example: entities/database.hbm.xml) might look like:



And the simple bean that is bound to user interface (“managed bean”) might have the following action listener: public class TestUI { public void onTest(ActionEvent event) { try { Session s = HibernateContext.getCurrentSession(); // query List pes = (List)s.createQuery

4

CaptainCasa Enterprise Client



( "from PersonEntity" ).list(); for (PersonEntity pe: pes) { System.out.println(pe); } // update Transaction t = HibernateContext.beginTransaction(); PersonEntity pe = new PersonEntity(); pe.setId(""+System.currentTimeMillis()); pe.setFirstName("First"); pe.setLastName("Last"); s.save(pe); HibernateContext.commit(); } catch (Throwable t) { t.printStackTrace(); } } }

When executing the action listener from the UI several times, you should see an increasing number of persons that are output to the console... Typical Usage Patterns In principal you now can work with Hibernate in any way you like. You have the UI-classes (managed beans) that are bound to the UI processing, within these classes you can invoke any Java function that internally operated with the Hibernate objects, such as SessionFactory, Session, Transaction, Query etc. But: of course there are some usage patterns that you should be aware of: • Typically there is one (and exactly one) SessionFactory for the database that you access. This is ensured due the HibernateUtil class that was mentioned in a previous chapter.

CaptainCasa Enterprise Client

• Typically the life cycle of a Session-object should be very short. The Session-object should be valid during one request only, so it should be closed when the request that comes from the user interface client is finished and the response to the client is sent. It is an absolute anti-pattern to have Session-objects that span multiple requests. Don't do this by accident...! • Typically all the code that is executed during a request should access the same Sessionobject. • Same with transactions: everyone typically should used a shared transaction for one request, so that all updates that are done are in the same transaction.

Now adding some typical Hibernate Usage Patterns Bind Hibernate Context Info to Thread In order to achieve the goals listed in the text above, it is a common pattern that these objects that are be shared during request processing are bound to the request thread. The concrete objects that we take about are: • Session • Transaction The binding to the thread is done by a class HibernateContext:

5

package hibernate; import java.util.Hashtable; import java.util.Map; import org.hibernate.Session; import org.hibernate.Transaction; public class HibernateContext { static Map s_contextPerThread = new Hashtable(); Session m_currentSession; Transaction m_currentTransaction; public static Session getCurrentSession() { HibernateContext hc = getCurrentContext(true); if (hc.m_currentSession == null) hc.m_currentSession = HibernateUtil.getSessionFactory().openSession(); return hc.m_currentSession; } public static Transaction beginTransaction() { HibernateContext hc = getCurrentContext(true); if (hc.m_currentSession == null) getCurrentSession(); if (hc.m_currentTransaction == null) hc.m_currentTransaction = hc.m_currentSession.beginTransaction(); return hc.m_currentTransaction; }

public static void rollback() { HibernateContext hc = getCurrentContext(false); if (hc == null) return; if (hc.m_currentTransaction != null) { hc.m_currentTransaction.rollback(); hc.m_currentTransaction = null; } } public static void close() { HibernateContext hc = getCurrentContext(false); if (hc == null) return; if (hc.m_currentTransaction != null) hc.m_currentTransaction.rollback(); if (hc.m_currentSession != null) hc.m_currentSession.close(); s_contextPerThread.remove(Thread.currentThread()); System.out.println("HibernateContext - closed"); } private static HibernateContext getCurrentContext(boolean createNewIfNotExists) { HibernateContext hc = s_contextPerThread.get(Thread.currentThread()); if (createNewIfNotExists == true && hc == null) { hc = new HibernateContext(); s_contextPerThread.put(Thread.currentThread(),hc); } return hc; } }

6

CaptainCasa Enterprise Client

public static void commit() { HibernateContext hc = getCurrentContext(false); if (hc == null) return; if (hc.m_currentTransaction != null) { hc.m_currentTransaction.commit(); hc.m_currentTransaction = null; } }

The typical lifecycle within the request processing is: ... HibernateContext.getCurrentSession()