创建自定义 dataSourceClassName
Create a custom dataSourceClassName
我有一个使用 playFramework、Spring 和 Hibernate 的应用程序,如下所示:
我想用 AbstractRoutingDataSource 实现多个数据源,所以我有我的自定义 class:
package mx;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
*
*/
public class MyRouting extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
System.out.println("Logic to determine the datasource");
return "key";
}
}
但是当我尝试像这样在 application.conf 中设置数据源时:
db.routingDS.dataSourceClassName = mx.MyRouting
我得到一个
[error] play - Error while stopping the application
play.api.PlayException: Cannot load plugin[An exception occurred during Plugin [com.edulify.play.hikaricp.HikariCPPlugin] initialization]
at play.api.WithDefaultPlugins$$anonfun$plugins$$anonfun$apply.apply(Application.scala:154) ~[play_2.10-2.3.10.jar:2.3.10]
at play.api.WithDefaultPlugins$$anonfun$plugins$$anonfun$apply.apply(Application.scala:130) ~[play_2.10-2.3.10.jar:2.3.10]
at scala.collection.TraversableLike$$anonfun$map.apply(TraversableLike.scala:244) ~[scala-library.jar:0.13.5]
at scala.collection.TraversableLike$$anonfun$map.apply(TraversableLike.scala:244) ~[scala-library.jar:0.13.5]
at scala.collection.immutable.List.foreach(List.scala:318) ~[scala-library.jar:0.13.5]
Caused by: play.api.Configuration$$anon: Configuration error[java.lang.ClassNotFoundException: mx.MyRouting]
at play.api.Configuration$.play$api$Configuration$$configError(Configuration.scala:94) ~[play_2.10-2.3.10.jar:2.3.10]
at play.api.Configuration.reportError(Configuration.scala:743) ~[play_2.10-2.3.10.jar:2.3.10]
at com.edulify.play.hikaricp.HikariCPDBApi$$anonfun.apply(HikariCPDBApi.scala:64) ~[play-hikaricp_2.10-2.0.6.jar:2.0.6]
at com.edulify.play.hikaricp.HikariCPDBApi$$anonfun.apply(HikariCPDBApi.scala:44) ~[play-hikaricp_2.10-2.0.6.jar:2.0.6]
at scala.collection.TraversableLike$$anonfun$map.apply(TraversableLike.scala:244) ~[scala-library.jar:0.13.5]
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: mx.MyRouting
at com.zaxxer.hikari.util.UtilityElf.createInstance(UtilityElf.java:120) ~[HikariCP-2.3.8.jar:na]
at com.zaxxer.hikari.pool.PoolUtilities.initializeDataSource(PoolUtilities.java:102) ~[HikariCP-2.3.8.jar:na]
at com.zaxxer.hikari.pool.BaseHikariPool.<init>(BaseHikariPool.java:156) ~[HikariCP-2.3.8.jar:na]
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:60) ~[HikariCP-2.3.8.jar:na]
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:48) ~[HikariCP-2.3.8.jar:na]
Caused by: java.lang.ClassNotFoundException: mx.MyRouting
at java.net.URLClassLoader.run(URLClassLoader.java:372) ~[na:1.8.0_25]
at java.net.URLClassLoader.run(URLClassLoader.java:361) ~[na:1.8.0_25]
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_25]
at java.net.URLClassLoader.findClass(URLClassLoader.java:360) ~[na:1.8.0_25]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_25]
(我也在使用 HikariCP 的数据库配置)
我也尝试用 class 生成一个 jar,结果是一样的,有人知道吗?谢谢!
您无法通过直接播放应用程序配置来实现您想要实现的目标。主要问题是默认情况下播放需要 URL 和驱动程序来创建数据源(至少在播放 2.3 和播放 2.4 中)。
换句话说,play 总是期望您要创建数据源,您将使用连接池到真实数据库而不是 RoutingDatasource。
由于您使用的是 JPA 种子,您需要通过 JNDI 访问资源,因此您需要以某种方式将数据源添加到 JNDI。
这是如何实现的:
在 application.conf 中只声明将与数据库交互的数据源。数量不限:
# Database configuration
#
db.mydb1.driver=org.h2.Driver
db.mydb1.url="jdbc:h2:mem:play"
db.mydb1.jndiName=DB1DS
db.mydb2.driver=org.h2.Driver
db.mydb2.url="jdbc:h2:mem:play"
db.mydb2.jndiName=DB2DS
现在,在 persistence.xml 中确保您没有声明任何数据源。 (无论是 jta 还是不是 jta)
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
最后,在Global.java或Global.scala中,强制进入JNDI RoutingDatasource。令人讨厌的部分是,没有关于 Play 如何管理 JNDI 的文档或 post 任何软件。基本上,Play 使用 class "InitialContext" 创建内存 JNDI。这个上下文是可写的(感谢上帝)因为有许多应用程序服务器保护它们的初始上下文。
//This is the routing Datasource in spring context
@Bean
public RoutingDatasource routingDataSource() {
RoutingDatasource rd = new RoutingDatasource();
java.util.HashMap ds = new java.util.HashMap();
ds.put("DB1DS","DB1DS");
ds.put("DB2DS","DB2DS");
rd.setTargetDataSources(ds);
return rd;
}
@Bean
public EntityManagerFactory entityManagerFactory() {
java.util.Map props = new java.util.HashMap();
try{
//This is the magic line.
((javax.naming.Context)(new javax.naming.InitialContext())).bind("routingDataSource", ctx.getBean("routingDataSource"));
}catch(Exception e){}
props.put("javax.persistence.jtaDataSource", "routingDataSource");
return Persistence.createEntityManagerFactory(DEFAULT_PERSISTENCE_UNIT,props);
}
最后,正如您提到的,创建您自己的 RoutingDatasource:
public class RoutingDatasource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return selectRandom("DB1DS","DBDS2");
}
}
就是这样。希望对解决您的问题有所帮助。
我有一个使用 playFramework、Spring 和 Hibernate 的应用程序,如下所示:
我想用 AbstractRoutingDataSource 实现多个数据源,所以我有我的自定义 class:
package mx;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
*
*/
public class MyRouting extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
System.out.println("Logic to determine the datasource");
return "key";
}
}
但是当我尝试像这样在 application.conf 中设置数据源时:
db.routingDS.dataSourceClassName = mx.MyRouting
我得到一个
[error] play - Error while stopping the application
play.api.PlayException: Cannot load plugin[An exception occurred during Plugin [com.edulify.play.hikaricp.HikariCPPlugin] initialization]
at play.api.WithDefaultPlugins$$anonfun$plugins$$anonfun$apply.apply(Application.scala:154) ~[play_2.10-2.3.10.jar:2.3.10]
at play.api.WithDefaultPlugins$$anonfun$plugins$$anonfun$apply.apply(Application.scala:130) ~[play_2.10-2.3.10.jar:2.3.10]
at scala.collection.TraversableLike$$anonfun$map.apply(TraversableLike.scala:244) ~[scala-library.jar:0.13.5]
at scala.collection.TraversableLike$$anonfun$map.apply(TraversableLike.scala:244) ~[scala-library.jar:0.13.5]
at scala.collection.immutable.List.foreach(List.scala:318) ~[scala-library.jar:0.13.5]
Caused by: play.api.Configuration$$anon: Configuration error[java.lang.ClassNotFoundException: mx.MyRouting]
at play.api.Configuration$.play$api$Configuration$$configError(Configuration.scala:94) ~[play_2.10-2.3.10.jar:2.3.10]
at play.api.Configuration.reportError(Configuration.scala:743) ~[play_2.10-2.3.10.jar:2.3.10]
at com.edulify.play.hikaricp.HikariCPDBApi$$anonfun.apply(HikariCPDBApi.scala:64) ~[play-hikaricp_2.10-2.0.6.jar:2.0.6]
at com.edulify.play.hikaricp.HikariCPDBApi$$anonfun.apply(HikariCPDBApi.scala:44) ~[play-hikaricp_2.10-2.0.6.jar:2.0.6]
at scala.collection.TraversableLike$$anonfun$map.apply(TraversableLike.scala:244) ~[scala-library.jar:0.13.5]
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: mx.MyRouting
at com.zaxxer.hikari.util.UtilityElf.createInstance(UtilityElf.java:120) ~[HikariCP-2.3.8.jar:na]
at com.zaxxer.hikari.pool.PoolUtilities.initializeDataSource(PoolUtilities.java:102) ~[HikariCP-2.3.8.jar:na]
at com.zaxxer.hikari.pool.BaseHikariPool.<init>(BaseHikariPool.java:156) ~[HikariCP-2.3.8.jar:na]
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:60) ~[HikariCP-2.3.8.jar:na]
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:48) ~[HikariCP-2.3.8.jar:na]
Caused by: java.lang.ClassNotFoundException: mx.MyRouting
at java.net.URLClassLoader.run(URLClassLoader.java:372) ~[na:1.8.0_25]
at java.net.URLClassLoader.run(URLClassLoader.java:361) ~[na:1.8.0_25]
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_25]
at java.net.URLClassLoader.findClass(URLClassLoader.java:360) ~[na:1.8.0_25]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_25]
(我也在使用 HikariCP 的数据库配置)
我也尝试用 class 生成一个 jar,结果是一样的,有人知道吗?谢谢!
您无法通过直接播放应用程序配置来实现您想要实现的目标。主要问题是默认情况下播放需要 URL 和驱动程序来创建数据源(至少在播放 2.3 和播放 2.4 中)。
换句话说,play 总是期望您要创建数据源,您将使用连接池到真实数据库而不是 RoutingDatasource。
由于您使用的是 JPA 种子,您需要通过 JNDI 访问资源,因此您需要以某种方式将数据源添加到 JNDI。
这是如何实现的:
在 application.conf 中只声明将与数据库交互的数据源。数量不限:
# Database configuration
#
db.mydb1.driver=org.h2.Driver
db.mydb1.url="jdbc:h2:mem:play"
db.mydb1.jndiName=DB1DS
db.mydb2.driver=org.h2.Driver
db.mydb2.url="jdbc:h2:mem:play"
db.mydb2.jndiName=DB2DS
现在,在 persistence.xml 中确保您没有声明任何数据源。 (无论是 jta 还是不是 jta)
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
最后,在Global.java或Global.scala中,强制进入JNDI RoutingDatasource。令人讨厌的部分是,没有关于 Play 如何管理 JNDI 的文档或 post 任何软件。基本上,Play 使用 class "InitialContext" 创建内存 JNDI。这个上下文是可写的(感谢上帝)因为有许多应用程序服务器保护它们的初始上下文。
//This is the routing Datasource in spring context
@Bean
public RoutingDatasource routingDataSource() {
RoutingDatasource rd = new RoutingDatasource();
java.util.HashMap ds = new java.util.HashMap();
ds.put("DB1DS","DB1DS");
ds.put("DB2DS","DB2DS");
rd.setTargetDataSources(ds);
return rd;
}
@Bean
public EntityManagerFactory entityManagerFactory() {
java.util.Map props = new java.util.HashMap();
try{
//This is the magic line.
((javax.naming.Context)(new javax.naming.InitialContext())).bind("routingDataSource", ctx.getBean("routingDataSource"));
}catch(Exception e){}
props.put("javax.persistence.jtaDataSource", "routingDataSource");
return Persistence.createEntityManagerFactory(DEFAULT_PERSISTENCE_UNIT,props);
}
最后,正如您提到的,创建您自己的 RoutingDatasource:
public class RoutingDatasource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return selectRandom("DB1DS","DBDS2");
}
}
就是这样。希望对解决您的问题有所帮助。