Статьи

Hibernate — динамическая маршрутизация таблиц

Я искал метод для динамической маршрутизации объектов в базы данных во время выполнения, используя Hibernate, и недавно я нашел решение, которое отвечает всем требованиям. Этот пост исследует проблему и определяет одно из возможных решений.

С http://www.alvinsingh.org/blog

Эта проблема

У меня есть веб-приложение (использующее Spring + Hibernate для ORM), которое использует две схемы базы данных (в одной физической базе данных). Я указываю источник данных, доступный через JNDI (используя tomcat, достигнутый с помощью контекста xml ), поэтому в разных средах развертывания я могу использовать один и тот же файл войны. Это особенно важно, так как я не хочу использовать разные двоичные файлы для разных сред. Мой принцип — хранить любую динамическую информацию, относящуюся к среде развертывания, строго за пределами файла war точно по указанной выше причине — один и тот же файл war во всех развертываниях.

Я указываю один DS (источник данных), поскольку фабрика сессий Spring Hibernate ожидает один DS. Но это дает мне только половину пути — я указал только одну строку подключения к базе данных — как насчет другой базы данных? Вы можете иметь несколько фабрик сессий, но затем вам придется иметь дело с межсессионными транзакциями.

Мы можем решить эту проблему (а не основную проблему в этом сообщении), используя элементы конфигурации «схема» и «каталог» на наших объектах, которые находятся в БД №2. Ниже приведен пример, который я буду использовать для справки с 3 объектами, охватывающими 2 схемы. Он включает в себя обычную практику, при которой информация об управлении идентификационными данными хранится в отдельной базе данных для базы данных приложений. Я пропустил большую часть информации от объектов для краткости.

@Table
@Entity
public class ApplicationUser {

private IdentityUser identityUser;

}
@Table (schema="secure", catalog="identitymanagement")
@Entity
public class IdentityUser {

@OneToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "IdentityUserIdentityRole",
       schema="security",
       catalog="identitymanagement",
       joinColumns = {@JoinColumn(name = "IdentityUserID")},
               inverseJoinColumns = {@JoinColumn(name = "IdentityRoleID")})
    private Set<IdentityRole> roles;

}
@Table (schema="secure", catalog="identitymanagement")
@Entity
public class IdentityRole {

}

ApplicationUser находится в БД № 1 и настраивается с помощью моего источника данных JNDI. Его схема и каталог могут быть настроены с помощью свойств спящего режима по умолчанию . Как видно, я жестко запрограммировал значения схемы и каталога для объектов управления идентификацией IdentityUser и IdentityUserRole. Я не могу поместить динамические элементы в эти аннотации, а также отмечу, что использование файлов hbm.xml не позволяет обойти это (сохраняя при этом один двоичный файл в средах развертывания). Это суть проблемы.

Это означает, что у меня может быть несколько экземпляров БД № 1 и только один экземпляр БД № 2 (который должен называться secure.identitymanagement. *) В любой отдельной среде развертывания. Не очень надежный.

Решение

Способ, которым hibernate строит свои запросы, заключается в том, что он ссылается на имена схем и каталогов при обращении к таблицам, т.е. в моем примере мы получим запросы с эффектом «select * from secure.identitymanagement.IdentityUser». Нам нужен способ манипулирования этим построением запросов.

Введите спящий перехватчик .

public class HibernateInterceptor extends EmptyInterceptor {

@Override
    public String onPrepareStatement(String sql) {
          String prepedStatement = super.onPrepareStatement(sql);
          prepedStatement = prepedStatement.replaceAll("secure.identitymanagement", "my_dynamic_goodness");
          return prepedStatement;
    }

}

Этот интерфейс, вероятно, является одной из самых мощных функций гибернации в фреймворке. Это позволяет вам проникнуть в суть Hibernate и манипулировать свойствами во время выполнения — в этом случае выполнять динамическую маршрутизацию таблиц. «OnPrepareStatement» явно вызывается, когда Hibernate готовит операторы sql непосредственно перед отправкой в ​​БД. То, что делает код (если это не очевидно), является простой заменой точной строки. Эта точная строка является тем, что мы жестко кодируем (может быть любым, но должно быть идентификатором, который не будет встречаться в SQL-выражениях иначе) и должен быть заменен динамическим каталогом и именем схемы. Откуда берутся фактические значения имени каталога и схемы, зависит от разработчика. В случае веб-приложения я кодирую это в контексте как свойство String и просматриваю его через JNDI, аналогичную DS.

Я намеренно сделал приведенный выше код очень простым, чтобы показать основную концепцию. Можно использовать довольно сложную логику маршрутизации для выполнения других форм динамической маршрутизации (например, на основе зарегистрированного клиента, в зависимости от региона пользователя). Существуют и другие решения проблемы динамической маршрутизации, в которых рассматриваются несколько иные варианты использования — проверьте фрагменты Hibernate и динамические модели .

Я не уверен, что перехватчик был предназначен для того, что я «угоняю», но первая строка API дает некоторое представление …

Позволяет пользовательскому коду проверять и / или изменять значения свойств.