New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for several Hibernate bundles #1276

Merged
merged 1 commit into from Oct 12, 2015

Conversation

Projects
None yet
7 participants
@arteam
Member

arteam commented Oct 10, 2015

In some cases one need to simultaneously connect to several relational databases. Users want to use Hibernate to access each database. The problem that the current implementation of
UnitOfWorkApplicationListener supports only one session factory, which ultimately restricts ability to support several session factories.

Another problem is that users should have the ability to specify the name of datasource, against which a transaction should be opened in a resource method.

This change:

  • Adds the method value to the @UnitOfWork annotation. It allows to specify the name of Hibernate bundle (session factory). By default it specifies the default bundle.
  • Replaces the single session factory in UnitOfWorkApplicationListener to a map of session factories by names of bundles.
  • The correct session factory is determined in runtime, based on the value method in the corresponding @UnitOfWork annotation.

In the result users can simultaneously use declarative transactions on several databases, access to which is managed by Hibernate.

Fixes #1217

Add support for several Hibernate bundles
In some cases one need to simultaneously connect to several
relational databases. Users want to use Hibernate to access each
database. The problem that the current implementation of
`UnitOfWorkApplicationListener` supports only one session factory,
which ultimately restricts ability to support several session factories.

Another problem is that users should have the ability to specify the
name of datasource, against which a transaction should be opened
in a resource method.

This change:

* Adds the method `value` to the `@unitofwork`
annotation. It allows to specify the name of Hibernate bundle
(session factory). By default it specifies the default bundle.

* Replaces the single session factory in
`UnitOfWorkApplicationListener` to a map of session factories by
names of bundles.

* The correct session factory is determined in runtime, based on
the `value` method in the corresponding `@unitofwork` annotation.

In the result users can simultaneously use declarative transactions
on several databases, access to which is managed by Hibernate.

Fixes #1217
@nickbabcock

This comment has been minimized.

Contributor

nickbabcock commented Oct 12, 2015

Excellent, I like it. Backwards compatible as well!

nickbabcock added a commit that referenced this pull request Oct 12, 2015

Merge pull request #1276 from arteam/serveral_hibernate_bundles
Add support for several Hibernate bundles

@nickbabcock nickbabcock merged commit 025714f into dropwizard:master Oct 12, 2015

@jplock

This comment has been minimized.

Member

jplock commented Oct 12, 2015

Should this be in 0.9.0 or 1.0.0? (related, we really need to release 0.9.0 if there aren't any show stoppers)

@nickbabcock

This comment has been minimized.

Contributor

nickbabcock commented Oct 12, 2015

I think this is fine, there have been other semi-major-but-backwards-compatible features since the latest rc: (changes since 0.9.4-rc4). But I'm fine if a release wants to be cut and save some of the features for 0.9.x

@lecummins

This comment has been minimized.

lecummins commented Nov 10, 2015

This is great, I have been looking to be able to do this for a while. Just one question - is it possible to use two different session factories within a single resource method? I have a number of resources that need to be able to access user settings from one database and information from a different one. I would have thought that this would be possible since each of the DAOs takes in their relevant bundle that they will use.

@nathan-olson

This comment has been minimized.

nathan-olson commented May 4, 2016

This totally breaks if you need to use both databases within one resource call by having the UnitOfWork value coupled directly with the bundle name. My use case is such that I need to update a legacy database with a bit of data, but also create an entry within my new database within a POST. However, since the transaction has to be tied to one, and only one, bundle (even if I set 'transactional' to false), then I only have context into one of my databases, making it impossible to solve my use case

@dineshbhagat

This comment has been minimized.

dineshbhagat commented Apr 29, 2017

It's awesome that Dropwizard hibernate supports multiple hibernate bundle.
Could you please share document where I can use multiple bundles. I could not find any reference of this here: http://www.dropwizard.io/0.6.2/manual/hibernate.html
I am using dropwizard-hibernate-0.9.2.jar.
Consider I have working REST resource method for path /getResult, just that I need to use slave DB along with existing master DB for this resource.
I am still getting exception:

! org.hibernate.HibernateException: No session currently bound to execution context
! at org.hibernate.context.internal.ManagedSessionContext.currentSession(ManagedSessionContext.java:75)
! at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
! at io.dropwizard.hibernate.AbstractDAO.currentSession(AbstractDAO.java:36)
! at io.dropwizard.hibernate.AbstractDAO.namedQuery(AbstractDAO.java:57)

Suggest If I am missing anything.
Referred following site for the issue.
http://stackoverflow.com/questions/30212125/no-session-currently-bound-to-execution-context

Tried,
ResourceClass method has
@path("/getResult")
@get
@unitofwork

Configuration.class has DB related configs.

public class MyApplication extends Application<Configuration> {
 private final HibernateBundle<Configuration> slaveHibernateBundle =
			new HibernateBundle<Configuration>(TableClass.class) {
				@Override
				public DataSourceFactory getDataSourceFactory(Configuration configuration) {
					return configuration.getDataSourceFactory1();
				}
				@Override
				protected String name() {
					return "slave-hibernate";
				}
	};
 public static void main(String[] args) throws Exception {
		new MyApplication().run(args);
	}

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

 @Override
 public void run(Configuration configuration, Environment environment) throws Exception {
     GenericDBDAO<TableClass> slaveDbTable = new GenericDBDAO<>
       (slaveHibernateBundle.getSessionFactory(), "TableClass");
    environment.jersey().register(new AbstractBinder() {
			@Override
			protected void configure() {
                                bind(slaveDbTable).to(GenericDBDAO.class);
	                }
                  }
 }
}
@arteam

This comment has been minimized.

Member

arteam commented Apr 29, 2017

I think you need to set the name of the slave database in the @UnitOfWork annotation on your resource.

@dineshbhagat

This comment has been minimized.

dineshbhagat commented May 2, 2017

Yes, It is correct that
if we do not specify a value in @unitofwork then it considers default hibernate bundle.
But If I specify like @unitofwork(value = "slave-hibernate") then it consideres only slave hibernate session.
But, I am trying to connect to both master and slave DB within the same resource "/getResult".
Is there any way to achieve this in Dropwizard-hibernate?
Please suggest an alternative if exists.

@yateam

This comment has been minimized.

yateam commented May 2, 2017

You could work this around by splitting your resource method into several parts, each of them talks to a different database. Then annotate each of these parts (functions or services) with @unitofwork(value = 'needed-db'). And remove the top UnitOfWork annotation from the resource method.

it could be something like this:

@Path("/getResult")
@GET
//@UnitOfWork -- comment this out
Result getResult() {
   service.talkToDb1();
   service.talkToDb2();
}

class Service {
  @UnitOfWork(value = "db1")
   void talkToDb1() {}

  @UnitOfWork(value = "db2")
   void talkToDb2() {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment