Могу ли я использовать JPA, указав базу данных во время выполнения? - PullRequest
0 голосов
/ 24 января 2019

Я использую MySQL на своем производственном сервере, где у каждого клиента есть своя собственная база данных и собственный пользователь базы данных для подключения к своей базе данных.Каждая база данных имеет одинаковую структуру.Клиенты (и, следовательно, базы данных) добавляются в течение срока службы сервера.

В настоящее время я устанавливаю соединение с базой данных MySQL с классом java.sql.DriverManager, которое работает просто отлично.URL базы данных, пользователь, пароль и имя драйвера хранятся в нескольких файлах свойств.Сервлет читает файл свойств у текущего клиента и создает java.sql.Connection с java.sql.DriverManager.

Я действительно хочу использовать JPA, но насколько я знаю, JPA можно использовать только путем явного указания баз данных в конфигурации веб-контейнера (в моем случае Tomcat).Но это означает, что когда новый клиент регистрируется на моей платформе, я должен добавить новую базу данных в конфигурацию веб-контейнера и перезапустить веб-контейнер, что не очень хорошая идея.

Есть ли способ использовать JPA и при этом иметь возможность добавлять новые базы данных в течение срока службы веб-контейнера?

Ответы [ 2 ]

0 голосов
/ 24 января 2019

Вы определенно можете делать то, что просите, но демон в деталях.

Я бы назвал это JPA Multitenancy, а здесь - интересная статья, предоставленная случайным поиском, которая предлагает правильный метод с использованием CDI. Я начну с более общего подхода, принимая во внимание тот факт, что вам также потребуются дополнения во время выполнения новых арендаторов.

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

Основы

Это должно быть сделано с использованием отдельных, специфичных для арендатора экземпляров EntityManagerFactory. Точный URL-адрес соединения с БД может быть задан картой, переданной в Persistence.createEntityManagerFactory(). Например, если существует META-INF/persistence.xml, применяется следующий код:

HashMap props = new HashMap();
props.put("javax.persistence.jdbc.url", /* tenant-specific JDBC URL*/);
EntityManagerFactory tenantSpecificEntityManagerFactory =
    Persistence.createEntityManagerFactory("name-of-persistence-unit-from-persistence.xml", props);

props обычно переопределяет любые свойства, указанные в persistence.xml. Возможно, достаточно переопределить только URL JDBC, может быть, вам потребуется переопределить имя пользователя / пароль или другие вещи. С этого момента вы можете получить специфичный для арендатора контекст персистентности и работать с ним:

EntityManager tenantSpecificEm = tenantSpecificEntityManagerFactory.createEntityManager();

Улов: Эффективность

Вы можете выполнять приведенный выше код каждый раз, когда вам нужен специфичный для арендатора EntityManager (и затем также закрыть завод). Но это будет крайне неэффективно по следующим причинам:

  1. Создание EntityManagerFactory заново с каждым запросом происходит медленно
  2. Создание подключения к БД заново с каждым запросом происходит еще медленнее

Для решения этих проблем вам необходимо:

  1. Кэшировать EntityManagerFactory экземпляры
  2. Как-нибудь использовать пул соединений

Кэширование EntityManagerFactory экземпляров

Я предполагаю, что существует надежный механизм для связи каждого запроса с соответствующим арендатором. Кроме того, вам потребуется Map имя арендатора для соответствующего экземпляра EntityManagerFactory. Как это хранится и используется, зависит от приложения, например, есть ли какая-либо структура внедрения зависимости? Первая ссылка имеет решение с CDI, аналогичные решения будут применяться для других контейнеров DI.

Пул соединений

Серверы приложений предлагают пул соединений с БД. Tomcat тоже. Серверы приложений могут позволить вам добавлять пулы соединений с БД во время выполнения без необходимости перезапуска сервера. Я не знаю, поддерживает ли используемая вами версия Tomcat (думаю, нет, но, пожалуйста, исправьте меня). Итак:

  • Если сервер приложений (в данном случае Tomcat) поддерживает создание пула соединений во время выполнения, сделайте это и настройте props, чтобы использовать его
  • В противном случае вам придется использовать пользовательский пул соединений, который применяется к каждому EntityManagerFactory. По крайней мере, в Hibernate есть эта функция .

Сохранить настройки

Полагаю, вы уже поняли это, но параметры, применяемые во время выполнения (клиенты, имя клиента и сопоставления свойств соединения), должны как-то сохраняться, чтобы они применялись повторно при перезапуске сервера. Это может быть файл конфигурации или другая база данных «администрирования».

0 голосов
/ 24 января 2019

Не уверен, поможет ли это в вашей конкретной ситуации, но я настроил среду, в которой URL-адрес соединения JDBC указан в hibernate.cfg.xml

<property name="connection.url">
    jdbc:ucanaccess://C:/Users/Public/UCanHibernate.accdb;newDatabaseVersion=V2010
</property>

может быть переопределено с помощью свойства Java System, например:

StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder()
        .configure(); // configures settings from hibernate.cfg.xml

// allow tester to specify their own connection URL (via -D JVM argument)
String runtimeUrl = System.getProperty("HIBERNATE_CONNECTION_URL"); 
if (runtimeUrl != null) {
    ssrb.applySetting("hibernate.connection.url", runtimeUrl);
}

Скорее всего, вы получите информацию о переопределении из другого источника, но принцип все еще может применяться.

...