dropwizard/dropwizard

View on GitHub
docs/source/manual/hibernate.rst

Summary

Maintainability
Test Coverage
.. _man-hibernate:

####################
Dropwizard Hibernate
####################

.. highlight:: text

.. rubric:: The ``dropwizard-hibernate`` module provides you with managed access to Hibernate_, a
            powerful, industry-standard object-relation mapper (ORM).

.. _Hibernate: http://www.hibernate.org/

Configuration
=============

To create a :ref:`managed <man-core-managed>`, instrumented ``SessionFactory`` instance, your
:ref:`configuration class <man-core-configuration>` needs a ``DataSourceFactory`` instance:

.. code-block:: java

    public class ExampleConfiguration extends Configuration {
        @Valid
        @NotNull
        private DataSourceFactory database = new DataSourceFactory();

        @JsonProperty("database")
        public DataSourceFactory getDataSourceFactory() {
            return database;
        }

        @JsonProperty("database")
        public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
            this.database = dataSourceFactory;
        }
    }

Then, add a ``HibernateBundle`` instance to your application class, specifying your entity classes
and how to get a ``DataSourceFactory`` from your configuration subclass:

.. code-block:: java

    private final HibernateBundle<ExampleConfiguration> hibernate = new HibernateBundle<ExampleConfiguration>(Person.class) {
        @Override
        public DataSourceFactory getDataSourceFactory(ExampleConfiguration configuration) {
            return configuration.getDataSourceFactory();
        }
    };

    @Override
    public void initialize(Bootstrap<ExampleConfiguration> bootstrap) {
        bootstrap.addBundle(hibernate);
    }

    @Override
    public void run(ExampleConfiguration config, Environment environment) {
        final PersonDAO dao = new PersonDAO(hibernate.getSessionFactory());
        environment.jersey().register(new UserResource(dao));
    }

This will create a new :ref:`managed <man-core-managed>` connection pool to the database, a
:ref:`health check <man-core-healthchecks>` for connectivity to the database, and a new
``SessionFactory`` instance for you to use in your DAO classes.

Your application's configuration file will then look like this:

.. code-block:: yaml

    database:
      # the name of your JDBC driver
      driverClass: org.postgresql.Driver

      # the username
      user: pg-user

      # the password
      password: iAMs00perSecrEET

      # the JDBC URL
      url: jdbc:postgresql://db.example.com/db-prod

      # any properties specific to your JDBC driver:
      properties:
        charSet: UTF-8
        hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect

      # the maximum amount of time to wait on an empty pool before throwing an exception
      maxWaitForConnection: 1s

      # the SQL query to run when validating a connection's liveness
      validationQuery: "/* MyApplication Health Check */ SELECT 1"

      # the minimum number of connections to keep open
      minSize: 8

      # the maximum number of connections to keep open
      maxSize: 32

      # whether or not idle connections should be validated
      checkConnectionWhileIdle: false

Usage
=====

Data Access Objects
-------------------

Dropwizard comes with ``AbstractDAO``, a minimal template for entity-specific DAO classes. It
contains type-safe wrappers for ``SessionFactory``'s common operations:

.. code-block:: java

    public class PersonDAO extends AbstractDAO<Person> {
        public PersonDAO(SessionFactory factory) {
            super(factory);
        }

        public Person findById(Long id) {
            return get(id);
        }

        public long create(Person person) {
            return persist(person).getId();
        }

        public List<Person> findAll() {
            return list(namedTypedQuery("com.example.helloworld.core.Person.findAll"));
        }
    }

Transactional Resource Methods
------------------------------

Dropwizard uses a declarative method of scoping transactional boundaries. Not all resource methods
actually require database access, so the ``@UnitOfWork`` annotation is provided:

.. code-block:: java

    @GET
    @Path("/{id}")
    @Timed
    @UnitOfWork
    public Person findPerson(@PathParam("id") LongParam id) {
        return dao.findById(id.get());
    }

This will automatically open a session, begin a transaction, call ``findById``, commit the
transaction, and finally close the session. If an exception is thrown, the transaction is rolled
back.

.. important:: The Hibernate session is closed **before** your resource method's return value (e.g.,
               the ``Person`` from the database), which means your resource method (or DAO) is
               responsible for initializing all lazily-loaded collections, etc., before returning.
               Otherwise, you'll get a ``LazyInitializationException`` thrown in your template (or
               ``null`` values produced by Jackson).

Transactional Resource Methods Outside Jersey Resources
-------------------------------------------------------

Currently creating transactions with the `@UnitOfWork` annotation works out-of-box only for resources
managed by Jersey. If you want to use it outside Jersey resources, e.g. in authenticators, you should
instantiate your class with ``UnitOfWorkAwareProxyFactory``.

.. code-block:: java

     SessionDao dao = new SessionDao(hibernateBundle.getSessionFactory());
     ExampleAuthenticator exampleAuthenticator = new UnitOfWorkAwareProxyFactory(hibernateBundle)
                    .create(ExampleAuthenticator.class, SessionDao.class, dao);

It will create a proxy of your class, which will open a Hibernate session with a transaction around
methods with the ``@UnitOfWork`` annotation.

Prepended Comments
==================

Dropwizard automatically configures Hibernate to prepend a comment describing the context of all
queries:

.. code-block:: sql

    /* load com.example.helloworld.core.Person */
    select
        person0_.id as id0_0_,
        person0_.fullName as fullName0_0_,
        person0_.jobTitle as jobTitle0_0_
    from people person0_
    where person0_.id=?

This will allow you to quickly determine the origin of any slow or misbehaving queries.