Over a million developers have joined DZone.

Connecting to Multiple Databases Using Hibernate

· Java Zone

Easily build powerful user management, authentication, and authorization into your web and mobile applications. Download this Forrester report on the new landscape of Customer Identity and Access Management, brought to you in partnership with Stormpath.

In a recent project, I had a requirement of connecting to multiple databases using hibernate. As tapestry-hibernate module does not provide an out-of-box support, I thought of adding one. https://github.com/tawus/tapestry5

Now that the application is in production, I thought of writing a simple “How to”.

I have cloned the latest stable(5.3.2) tapestry project at

https://github.com/tawus/tapestry5

and have added multiple database support to it.

Single Database

It is almost fully compatible with the previous integration when using a single database except for a few things

1) HibernateConfigurer has changed

public interface HibernateConfigurer
{
   /**
    * Passed the configuration so as to make changes.
    */
   void configure(Configuration configuration);

   /**
    * Factory Id for which this configurer is meant for
    */
   Class<? extends Annotation> getMarker();

   /**
    * Entity package names
    * 
    * @return
    */
   String[] getPackageNames();
}

2) There is no HibernateEntityPackageManager, as the packages can be contributed by adding more HibernateConfigurers with the same Markers.

Multiple databases

For multiple database, a marker has to be used for accessing Session or HibernateSessionManager

@Inject
@XDB
private Session session;

@Inject
@YDB
private HibernateSessionManager sessionManager;

@XDB
@CommitAfter
void myMethod(){

}

Also you have to define a HibernateSessionManager and a Session for the secondary database in the Module class.

@Scope(ScopeConstants.PERTHREAD)
@Marker(DatabaseTwo.class)
public static HibernateSessionManager buildHibernateSessionManagerForFinacle(
    HibernateSessionSource sessionSource,
    PerthreadManager perthreadManager)
{
    HibernateSessionManagerImpl service = new HibernateSessionManagerImpl(sessionSource,
        DatabaseTwo.class);

    perthreadManager.addThreadCleanupListener(service);

    return service;
}

@Marker(DatabaseTwo.class)
public static Session buildSessionForFinacle(
    @Local HibernateSessionManager
        sessionManager,
    PropertyShadowBuilder propertyShadowBuilder)
{
    return propertyShadowBuilder.build(sessionManager, "session", Session.class);
}

Notice an annotation @DatabaseTwo.class. This is a Factory marker and is used to identify a service related to a particular SessionFactory.

@Retention(RetentionPolicy.RUNTIME)
@Target( {ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@FactoryMarker
@Documented
public @interface DatabaseTwo
{

}

A typical AppModule for two databases will be

public class AppModule
{

    public static void bind(ServiceBinder binder)
    {
        binder.bind(DemoService.class, DemoServiceImpl.class);
    }

    @Contribute(HibernateSessionSource.class)
    public static void configureHibernateSources(OrderedConfiguration<HibernateConfigurer>
        configurers)
    {
        configurers.add("databaseOne", new HibernateConfigurer()
        {
            public void configure(org.hibernate.cfg.Configuration configuration)
            {
                configuration.configure("/databaseOne.xml");
            }

            public Class<? extends Annotation> getMarker()
            {
                return DefaultFactory.class;
            }

            public String[] getPackageNames()
            {
                return new String[] {"org.example.demo.one"};
            }
        });

        configurers.add("databaseTwo", new HibernateConfigurer()
        {
            public void configure(org.hibernate.cfg.Configuration configuration)
            {
                configuration.configure("/databaseTwo.xml");
            }

            public Class<? extends Annotation> getMarker()
            {
                return DatabaseTwo.class;
            }

            public String[] getPackageNames()
            {
                return new String[] {"org.example.demo.two"};
            }
        });
    }

    @Contribute(SymbolProvider.class)
    @ApplicationDefaults
    public static void addSymbols(MappedConfiguration<String, String> configuration)
    {
        configuration.add(HibernateSymbols.DEFAULT_CONFIGURATION, "false");
        configuration.add("tapestry.app-package", "org.example.demo");
    }


    @Scope(ScopeConstants.PERTHREAD)
    @Marker(DatabaseTwo.class)
    public static HibernateSessionManager buildHibernateSessionManagerForFinacle(
        HibernateSessionSource sessionSource,
        PerthreadManager perthreadManager)
    {
        HibernateSessionManagerImpl service = new HibernateSessionManagerImpl(sessionSource,
            DatabaseTwo.class);

        perthreadManager.addThreadCleanupListener(service);

        return service;
    }

    @Marker(DatabaseTwo.class)
    public static Session buildSessionForFinacle(
        @Local HibernateSessionManager
            sessionManager,
        PropertyShadowBuilder propertyShadowBuilder)
    {
        return propertyShadowBuilder.build(sessionManager, "session", Session.class);
    }

}

Injecting into Services

You can inject a session in a service using the marker. As DatabaseOne is being used as the default configuration, in order to inject its Session, you have to annotate it with @DefaultFactory. For DatabaseTwo, you can use @DatabaseTwo annotation.

public class DemoServiceImpl implements DemoService
{
    private Session sessionOne;

    private Session sessionTwo;

    public DemoServiceImpl(
        @DefaultFactory Session sessionOne,
        @DatabaseTwo Session sessionTwo)
    {
        this.sessionOne = sessionOne;
        this.sessionTwo = sessionTwo;
    }

    @SuppressWarnings("unchecked")
    public List<EntityOne> listOnes()
    {
        return sessionOne.createCriteria(EntityOne.class).list();
    }

    @SuppressWarnings("unchecked")
    public List<EntityTwo> listTwos()
    {
        return sessionTwo.createCriteria(EntityTwo.class).list();
    }

    public void save(EntityOne entityOne)
    {
        sessionOne.saveOrUpdate(entityOne);
    }

    public void save(EntityTwo entityTwo)
    {
        sessionTwo.saveOrUpdate(entityTwo);
    }
}


Using @CommitAfter

You can add an advice the same way you used to. The only change is in @CommitAfter. You have to additionally annotate the method with the respective marker.

public interface DemoService
{
    List<EntityOne> listOnes();

    List<EntityTwo> listTwos();

    @CommitAfter
    @DefaultFactory
    void save(EntityOne entityOne);

    @CommitAfter
    @DatabaseTwo
    void save(EntityTwo entityTwo);
}

Here is an example.

 

From http://tawus.wordpress.com/2012/03/03/tapestry-hibernate-multiple-databases/

The Java Zone is brought to you by Stormpath—a complete, pre-built User Management API. Want to learn how to use JWTs to protect microservices from CSRF and more? Check out this on-demand webinar with our Java Developer Evangelist, Micah Silverman.

Topics:

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}