Вот что я в итоге сделал, и все получилось очень хорошо. У менеджера объектов может быть только один компонент для использования в качестве источника данных. Так что я должен был сделать, чтобы создать фасоль, которая маршрутизировалась между ними там, где это необходимо. Этот ben - тот, который я использовал для менеджера сущности JPA.
Я устанавливаю два разных источника данных в tomcat. В server.xml я создал два ресурса (источники данных).
<Resource name="readConnection" auth="Container" type="javax.sql.DataSource"
username="readuser" password="readpass"
url="jdbc:mysql://readipaddress:3306/readdbname"
driverClassName="com.mysql.jdbc.Driver"
initialSize="5" maxWait="5000"
maxActive="120" maxIdle="5"
validationQuery="select 1"
poolPreparedStatements="true"
removeAbandoned="true" />
<Resource name="writeConnection" auth="Container" type="javax.sql.DataSource"
username="writeuser" password="writepass"
url="jdbc:mysql://writeipaddress:3306/writedbname"
driverClassName="com.mysql.jdbc.Driver"
initialSize="5" maxWait="5000"
maxActive="120" maxIdle="5"
validationQuery="select 1"
poolPreparedStatements="true"
removeAbandoned="true" />
Вы могли бы иметь таблицы базы данных на одном сервере, и в этом случае IP-адреса или домена будет такой же, просто разные DBS - вы получите тэк.
Затем я добавил ссылку ресурса в файле context.xml в tomcat, который ссылался на эти ресурсы.
<ResourceLink name="readConnection" global="readConnection" type="javax.sql.DataSource"/>
<ResourceLink name="writeConnection" global="writeConnection" type="javax.sql.DataSource"/>
Эти ссылки ресурсов - это то, что весна читает в контексте приложения.
В контексте приложения я добавил определение компонента для каждой ссылки ресурса и добавил еще одно определение bean-компонента, которое ссылалось на bean-компонент Datasource, который я создал, который принимает карту (перечисление) двух ранее созданных bean-компонентов (определение bean-компонента).
<!--
Data sources representing master (write) and slaves (read).
-->
<bean id="readDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="readConnection" />
<property name="resourceRef" value="true" />
<property name="lookupOnStartup" value="true" />
<property name="cache" value="true" />
<property name="proxyInterface" value="javax.sql.DataSource" />
</bean>
<bean id="writeDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="writeConnection" />
<property name="resourceRef" value="true" />
<property name="lookupOnStartup" value="true" />
<property name="cache" value="true" />
<property name="proxyInterface" value="javax.sql.DataSource" />
</bean>
<!--
Provider of available (master and slave) data sources.
-->
<bean id="dataSource" class="com.myapp.dao.DatasourceRouter">
<property name="targetDataSources">
<map key-type="com.myapp.api.util.AvailableDataSources">
<entry key="READ" value-ref="readDataSource"/>
<entry key="WRITE" value-ref="writeDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="writeDataSource"/>
</bean>
Определения компонента управления сущностью, а затем ссылаются на компонент dataSource.
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="${jpa.persistenceUnitName}" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="${jpa.dialect}"/>
<property name="showSql" value="${jpa.showSQL}" />
</bean>
</property>
</bean>
Я определил некоторые свойства в файле свойств, но вы можете заменить значения $ {} вашими конкретными значениями. Итак, теперь у меня есть один компонент, который использует два других компонента, которые представляют мои два источника данных. Один компонент - тот, который я использую для JPA. Это не замечает никакой маршрутизации.
Итак, теперь фасоль.
public class DatasourceRouter extends AbstractRoutingDataSource{
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException{
// TODO Auto-generated method stub
return null;
}
@Override
protected Object determineCurrentLookupKey(){
return DatasourceProvider.getDatasource();
}
}
Переопределенный метод вызывается менеджером сущности для определения источника данных в основном. DatasourceProvider имеет локальное (потокобезопасное) свойство потока с методом getter и setter, а также чистый метод источника данных для очистки.
public class DatasourceProvider{
private static final ThreadLocal<AvailableDataSources> datasourceHolder = new ThreadLocal<AvailableDataSources>();
public static void setDatasource(final AvailableDataSources customerType){
datasourceHolder.set(customerType);
}
public static AvailableDataSources getDatasource(){
return (AvailableDataSources) datasourceHolder.get();
}
public static void clearDatasource(){
datasourceHolder.remove();
}
}
У меня есть родовое реализацию DAO с методами, которые я использую для обработки различных рутинных вызовов JPA (getReference, сохраняются, createNamedQUery & getResultList и т.д.). Прежде чем он сделает вызов entityManager, чтобы сделать все, что ему нужно, я установил источник данных DatasourceProvider для чтения или записи. Метод может обрабатывать переданное значение, чтобы сделать его немного более динамичным. Вот пример метода.
@Override
public List<T> findByNamedQuery(final String queryName, final Map<String, Object> properties, final int... rowStartIdxAndCount)
{
DatasourceProvider.setDatasource(AvailableDataSources.READ);
final TypedQuery<T> query = entityManager.createNamedQuery(queryName, persistentClass);
if (!properties.isEmpty())
{
bindNamedQueryParameters(query, properties);
}
appyRowLimits(query, rowStartIdxAndCount);
return query.getResultList();
}
В AvailableDataSources это перечисление с чтения или записи, которая ссылается на соответствующий источник данных. Вы можете видеть это на карте, определенной в моем компоненте в контексте приложения.
О, и вам нужно убедиться, что MySQL JAR находится в Tomcat, иначе источники данных (ресурс) не будут работать. – Elrond
Спасибо! Это хорошая идея, я попробую. – Stony
Вот некоторые улучшения этого метода с использованием пользовательских аннотаций: http://fedulov.website/2015/10/14/dynamic-datasource-routing-with-spring/ –