Статьи

Подключение к нескольким базам данных с использованием Hibernate

В недавнем проекте у меня было требование подключения к нескольким базам данных с помощью hibernate. Поскольку модуль tapestry-hibernate не обеспечивает встроенную поддержку, я подумал о добавлении одного. https://github.com/tawus/tapestry5

Теперь, когда приложение находится в производстве, я подумал написать простое «Как».

Я клонировал последний стабильный (5.3.2) проект гобелена в

https://github.com/tawus/tapestry5

и добавили поддержку нескольких баз данных к нему.

Единая база данных

Он почти полностью совместим с предыдущей интеграцией при использовании единой базы данных, за исключением нескольких вещей

1) HibernateConfigurer изменился

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) HibernateEntityPackageManager отсутствует, поскольку пакеты могут быть добавлены путем добавления большего количества HibernateConfigurers с теми же маркерами.

Несколько баз данных

Для нескольких баз данных необходимо использовать маркер для доступа к Session или HibernateSessionManager.

@Inject
@XDB
private Session session;

@Inject
@YDB
private HibernateSessionManager sessionManager;

@XDB
@CommitAfter
void myMethod(){

}

Также вы должны определить HibernateSessionManager и Session для вторичной базы данных в классе Module.

@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);
}

Обратите внимание на аннотацию @ DatabaseTwo.class. Это маркер Factory и используется для идентификации службы, связанной с конкретной SessionFactory.

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

}

Типичный AppModule для двух баз данных будет

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);
    }

}

Внедрение в Услуги

Вы можете добавить сеанс в сервис, используя маркер. Поскольку DatabaseOne используется в качестве конфигурации по умолчанию, для добавления его сеанса необходимо аннотировать его с помощью @DefaultFactory. Для DatabaseTwo вы можете использовать аннотацию @DatabaseTwo.

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);
    }
}


Использование @CommitAfter

Вы можете добавить совет так же, как раньше. Единственное изменение в @CommitAfter. Вы должны дополнительно аннотировать метод соответствующим маркером.

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

    List<EntityTwo> listTwos();

    @CommitAfter
    @DefaultFactory
    void save(EntityOne entityOne);

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

Вот пример .

 

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