这是我最终做的,它工作得很好。实体管理器只能有一个bean用作数据源。所以我必须做的是在必要时创建一个在两者之间路由的bean。那一个是我用于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 - 你的JIST。
然后,我在tomcat中的context.xml文件中添加了一个资源链接,它将这些资源引用到资源中。
<ResourceLink name="readConnection" global="readConnection" type="javax.sql.DataSource"/>
<ResourceLink name="writeConnection" global="writeConnection" type="javax.sql.DataSource"/>
这些资源链接是弹簧在应用程序上下文中读取的内容。
在应用程序上下文中,我为每个资源链接添加了一个bean定义,并添加了一个额外的bean定义,该定义引用了我创建的数据源路由器bean,该bean在先前创建的两个bean(bean定义)的映射(enum)中。
<!--
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>
实体管理器bean定义然后引用dataSource bean。
<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>
我在属性文件中定义了一些属性,但是您可以用您自己的特定值替换$ {}值。所以现在我有一个bean使用两个代表我的两个数据源的bean。这个bean是我用于JPA的那个bean。它没有任何路由发生。
所以现在路由bean。
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();
}
}
我有我用它来处理各种日常JPA电话(getReference,坚持,createNamedQUery & getResultList等)方法的通用DAO实现。在调用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与READ或WRITE,它引用适当的数据源的枚举。您可以在应用程序上下文中的bean中定义的映射中看到。
哦,你需要确保MySQL JAR在Tomcat中,否则数据源(资源)将无法工作。 – Elrond 2012-02-21 17:23:37
谢谢!这是个好主意,我会试一试。 – Stony 2015-10-11 09:11:05
以下是使用自定义注释的此方法的一些增强功能:http://fedulov.website/2015/10/14/dynamic-datasource-routing-with-spring/ – 2015-10-16 10:43:06