hibernate

hibernate

Hibernate ORM enables developers to more easily write applications whose data outlives the application process. As an Object/Relational Mapping (ORM) framework, Hibernate is concerned with data persistence as it applies to relational databases.

This module setup and configure Hibernate ORM and JPA Provider.

NOTE: This module depends on jdbc module.

exports

  • SessionFactory / EntityManagerFactory
  • Session / EntityManager
  • UnitOfWork

dependency

<dependency>
 <groupId>org.jooby</groupId>
 <artifactId>jooby-hbm</artifactId>
 <version>1.6.6</version>
</dependency>

usage

{
  use(new Jdbc());
  use(new Hbm()
      .classes(Beer.class));

  get("/api/beer/", req -> {
    return require(UnitOfWork.class).apply(em -> {
      return em.createQuery("from Beer").getResultList();
    });
  });
}

unit of work

We provide an UnitOfWork to simplify the amount of code required to interact within the database.

For example the next line:

{
  require(UnitOfWork.class).apply(em -> {
    return em.createQuery("from Beer").getResultList();
  });
}

Is the same as:

{
   Session session = require(SessionFactory.class).openSession();
   Transaction trx = session.getTransaction();
   try {
     trx.begin();
     List<Beer> beers = em.createQuery("from Beer").getResultList();
     trx.commit();
   } catch (Exception ex) {
     trx.rollback();
   } finally {
     session.close();
   }
}

An UnitOfWork takes care of transactions and session life-cycle. It’s worth to mention too that a first requested UnitOfWork bind the Session to the current request. If later in the execution flow an UnitOfWork, Session and/or EntityManager is required then the one that belong to the current request (first requested) will be provided it.

open session in view

We provide an advanced and recommended Open Session in View pattern, which basically keep the Session opened until the view is rendered, but it uses two database transactions:

  • first transaction is committed before rendering the view and then

  • a read only transaction is opened for rendering the view

Here is an example on how to setup the open session in view filter:

{
   use(new Jdbc());
   use(new Hbm());
   use("*", Hbm.openSessionInView());
}

event listeners

JPA event listeners are provided by Guice, which means you can inject dependencies into your event listeners:

@Entity
@EntityListeners({BeerListener.class})
public class Beer {
}

public class BeerListener {

  @Inject
  public BeerListener(DependencyA depA) {
    this.depA = depA;
  }

  @PostLoad
  public void postLoad(Beer beer) {
    this.depA.complementBeer(beer);
  }
}

Hibernate event listeners are supported too via onEvent(EventType, Class):

{
   use(new Hbm()
       .onEvent(EventType.POST_LOAD, MyPostLoadListener.class));
}

Again, MyPostLoadListener will be provided by Guice.

persistent classes

Persistent classes must be provided at application startup time via classes(Class…):

{
  use(new Jdbc());
  use(new Hbm()
      .classes(Entity1.class, Entity2.class, ..., )
  );

}

Or via scan:

{
  use(new Jdbc());
  use(new Hbm()
      .scan()
  );

}

Which scan the application package defined by hibernate.packagesToScan property, or you can provide where to look:

{
  use(new Jdbc());
  use(new Hbm()
      .scan("foo.bar", "x.y.z")
  );

}

advanced configuration

Advanced configuration is provided via doWithXXX callbacks:

{
  use(new Hbm()
      .doWithBootstrap(bsrb -> {
        // do with bsrb
      })
      .doWithRegistry(ssrb -> {
        // do with ssrb
      })
  );

}

Or via hibernate.* property from your .conf file:

hibernate.hbm2ddl.auto = update

life-cycle

You are free to inject a SessionFactory or EntityManagerFactory and create a new EntityManagerFactory#createEntityManager(), start transactions and do everything you need.

For the time being, this doesn’t work for a Session or EntityManager. A Session and/or EntityManager is bound to the current request, which means you can’t freely access from every single thread (like manually started thread, started by an executor service, quartz, etc…).

Another restriction, is the access from Singleton services. If you need access from a singleton services, you need to inject a Provider.

@Singleton
public class MySingleton {

  @Inject
  public MySingleton(Provider<EntityManager> em) {
    this.em = em;
  }
}

Still, we strongly recommend to leave your services in the default scope and avoid Singleton objects, except of course for really expensive resources. This is also recommend approach by Guice.

Services in the default scope won’t have this problem and are free to inject the Session or EntityManager directly.

hbm.conf

These are the default properties for hbm:

hibernate.session_factory_name_is_jndi = false

hibernate.archive.autodetection = class

hibernate.current_session_context_class = managed

hibernate.packagesToScan = ${application.ns}