One of the awesome capabilities of Dropwizard is the ability to incorporate CLI commands into your application. But when using Hibernate within Dropwizard to connect to your database, you may run into problems with session management.
Commands in Dropwizard
For your CLI command, you will likely want to use a ConfiguredCommand, thereby utilizing your yaml configuration. Your command will extend this class, and implement this function:
protected void run(Bootstrap<MyConfiguration> bootstrap, Namespace namespace, MyConfiguration configuration) { // command behavior }
To break this function down, it is given the same Bootstrap that your Application receives, your configuration class, and any arguments given through the CLI command through the Namespace.
Hibernate in Dropwizard
Dropwizard provides a clear method for using Hibernate in an application. The intention for Dropwizard’s session management is driven through the resources via the @UnitOfWork
annotation on resource methods.
But Hibernate in Dropwizard CLI commands are not executed via a resource, and therein lies the problem. The error you will see when getCurrentSession()
is called:
org.hibernate.HibernateException: No session currently bound to execution context
Solution
There are a couple things you need to do:
1. Setup the HibernateFactory
As the the docs indicate, and like you do in your Application, you will construct a HibernateBundle in your command’s run
method:
HibernateBundle<MyConfiguration> hibernate = new HibernateBundle<MyConfiguration>(Person.class) { @Override public DataSourceFactory getDataSourceFactory(MyConfiguration configuration) { return configuration.getDatabase(); } };
2. Configure Hibernate Properties
In order to have a session, you need to control when the session is constructed. In this circumstance, we want the current session bound to the current thread, and so you will want to use the ThreadLocalSessionContext. Add this to your run
method after initializing the HibernateBundle:
configuration.getDatabase().getProperties().put("hibernate.current_session_context_class", "org.hibernate.context.internal.ThreadLocalSessionContext");
3. Initialize
Lastly, the HibernateBundle in your Application is included in the environment as managed. But for your command, there is no management provided. So you will have to initialize yourself! There is no environment provided either, but you can create one using the bootstrap:
hibernate.run(configuration, new Environment("EnvName", bootstrap.getObjectMapper(), bootstrap.getValidatorFactory().getValidator(), bootstrap.getMetricRegistry(), bootstrap.getClassLoader()));
Now you have it!
You will of course need to manage the transaction yourself, but you now have all you need to start and end the session for your command. There wasn’t much documentation when I went through this exercise, so I hope this helps.
Thank you (and search engines) for writing this up – exactly what I needed.
I struggled for quite a while getting Hibernate working in a ConfiguredCommand – before realising that it just doesn’t work, and that your command has to be a subclass of EnvironmentCommand, so that the HibernateBundle gets wired up and initialised correctly.
Thanks for your blog post on this!