具有多个数据源Oracle和H2的Spring Boot

我为我的角项目开发了一个Spring Boot RestController并遇到了问题。 在我的rest服务逻辑中,我使用了两个不同的数据库来获取数据。

在这里您可以看到数据源配置:

[application.properties]

#datasource1 spring.datasource.url=[url] spring.datasource.username=[username] spring.datasource.password=[password] spring.datasource.driverClassName=org.h2.Driver #datasource2 spring.secondDatasource.url=[url] spring.secondDatasource.username=[username] spring.secondDatasource.password=[password] spring.secondDatasource.driverClassName=oracle.jdbc.OracleDriver 

[DatasourceConfig.java]

 @Bean @Primary @ConfigurationProperties(prefix="spring.datasource") public DataSource h2DataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix="spring.secondDatasource") public DataSource oracleDataSource() { return DataSourceBuilder.create().build(); } 

记录输出:(此问题不会引发exception)

 ... 2016-11-22 13:20:25.853 [INFO ] 1 [main] dbsApplication : Started Application in 7.757 seconds (JVM running for 12.515) 2016-11-22 13:20:31.731 [INFO ] 62 [http-nio-8080-exec-1] oswsDispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started 2016-11-22 13:20:31.757 [INFO ] 62 [http-nio-8080-exec-1] oswsDispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 26 ms 2016-11-22 13:20:34.984 [INFO ] 65 [http-nio-8080-exec-5] dbsrRESTclient : /rest/getMyData 2016-11-22 13:20:34.992 [INFO ] 63 [http-nio-8080-exec-2] dbsrRESTclient : /rest/getMyData2 2016-11-22 13:20:34.993 [INFO ] 69 [http-nio-8080-exec-8] dbsrRESTclient : /rest/getMyData3 2016-11-22 13:20:35.004 [DEBUG] 65 [http-nio-8080-exec-5] dbsDataSourceService : getH2Connection() 2016-11-22 13:20:35.022 [DEBUG] 63 [http-nio-8080-exec-2] dbsDataSourceService : getOracleConnection() 2016-11-22 13:20:35.022 [DEBUG] 69 [http-nio-8080-exec-8] dbsDataSourceService : getH2Connection() 

问题是,rest服务的每个使用过的工作线程(http-nio-8080-?,…)都在我的DataSourceService中的datasource.getConnection()处挂起。 它会永远冻结并等待无法获取的连接。

 @Service public class DataSourceService { private final DataSource h2DataSource; private final DataSource oracleDataSource; @Autowired public DataSourceService(DataSource h2DataSource, DataSource oracleDataSource) { this.h2DataSource = h2DataSource; this.oracleDataSource = oracleDataSource; } public Connection getH2Connection() throws SQLException { LoggerUtil.logDebug(getClass(), "getH2Connection()"); return h2Connection.getConnection(); } public Connection getOracleConnection() throws SQLException { LoggerUtil.logDebug(getClass(), "getOracleConnection()"); return oracleConnection.getConnection(); } } 

关于它的奇怪之处在于,有时它可以在没有改变代码行的情况下工作(占所有时间的5%),并且大部分时间(95%的所有时间)都会挂起。

在我的pom.xml中,我有以下数据库驱动程序依赖项:

  com.oracle ojdbc6 11.2.0.4 runtime   com.h2database h2 1.4.193 runtime  

如果所有数据源都具有相同的驱动程序,这将永远不会发生,但在组合中我发现了这种奇怪的行为。

我的想法是,只有在oracle之前初始化h2Database时才会发生这种情况。 此时,spring类加载器加载h2驱动程序类,它们将被缓存。 接下来,oracle被初始化并且一些类将完全相同,因此一些h2类用于oracle。 因此,创建了奇怪的冻结状态。 (也许反之亦然)。

我现在的问题:为什么我不能在Spring-boot的同时使用H2和Oracle数据库?

编辑:

经过一些调试后,我发现了更多的信息:

[org.apache.tomcat.jdbc.pool.ClassLoaderUtil]

 Row 29: loadClass(...) ... Row 38: return Class.forName(className, true, cl); //className: "oracle.jdbc.OracleDriver" cl: Launcher$AppClassLoader 

在ClassLoaderUtil中是按类名查找Driver-Class。 可以毫无问题地解析className“org.h2.Driver”,但“oracle.jdbc.OracleDriver”不会。

之后我做了一个线程转储,你可以看到卡点。 此时有5条卡住的螺纹卡在不同的位置。

http-nio-8080-exec-2 @ 8307(runnable)(仅使用oracle数据库)

 "http-nio-8080-exec-2@8307" daemon prio=5 tid=0x40 nid=NA runnable java.lang.Thread.State: RUNNABLE blocks http-nio-8080-exec-6@8311 at java.lang.Class.forName0(Class.java:-1) at java.lang.Class.forName(Class.java:348) at org.apache.tomcat.jdbc.pool.ClassLoaderUtil.loadClass(ClassLoaderUtil.java:38) at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:271) at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:203) at org.apache.tomcat.jdbc.pool.ConnectionPool.createConnection(ConnectionPool.java:718) at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:650) at org.apache.tomcat.jdbc.pool.ConnectionPool.init(ConnectionPool.java:468) at org.apache.tomcat.jdbc.pool.ConnectionPool.(ConnectionPool.java:143) at org.apache.tomcat.jdbc.pool.DataSourceProxy.pCreatePool(DataSourceProxy.java:118) - locked  (a org.apache.tomcat.jdbc.pool.DataSource) at org.apache.tomcat.jdbc.pool.DataSourceProxy.createPool(DataSourceProxy.java:107) at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:131) at dbsDataSourceService.getConnection(DataSourceService.java:51) //<-- That's my package ... 

http-nio-8080-exec-3 @ 8308(runnable)(oracle和h2数据之间的比较)

 "http-nio-8080-exec-3@8308" daemon prio=5 tid=0x41 nid=NA runnable java.lang.Thread.State: RUNNABLE at oracle.jdbc.driver.OracleDriver.(OracleDriver.java:190) at java.lang.Class.forName0(Class.java:-1) at java.lang.Class.forName(Class.java:348) at org.apache.tomcat.jdbc.pool.ClassLoaderUtil.loadClass(ClassLoaderUtil.java:38) at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:271) at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:203) at org.apache.tomcat.jdbc.pool.ConnectionPool.createConnection(ConnectionPool.java:718) at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:650) at org.apache.tomcat.jdbc.pool.ConnectionPool.init(ConnectionPool.java:468) at org.apache.tomcat.jdbc.pool.ConnectionPool.(ConnectionPool.java:143) at org.apache.tomcat.jdbc.pool.DataSourceProxy.pCreatePool(DataSourceProxy.java:118) - locked  (a org.apache.tomcat.jdbc.pool.DataSource) at org.apache.tomcat.jdbc.pool.DataSourceProxy.createPool(DataSourceProxy.java:107) at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:131) at dbsDataSourceService.getConnection(DataSourceService.java:51) ... 

http-nio-8080-exec-4 @ 8309(runnable)(oracle和h2数据之间的比较)

(与exec-2相同)

http-nio-8080-exec-5 @ 8310(runnable)(仅使用h2数据库)

 "http-nio-8080-exec-5@8310" daemon prio=5 tid=0x43 nid=NA runnable java.lang.Thread.State: RUNNABLE at sun.reflect.GeneratedConstructorAccessor109.newInstance(Unknown Source:-1) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:-1) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at java.lang.Class.newInstance(Class.java:442) at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:380) at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404) at java.util.ServiceLoader$1.next(ServiceLoader.java:480) at java.sql.DriverManager$2.run(DriverManager.java:603) at java.sql.DriverManager$2.run(DriverManager.java:583) at java.security.AccessController.doPrivileged(AccessController.java:-1) at java.sql.DriverManager.loadInitialDrivers(DriverManager.java:583) at java.sql.DriverManager.(DriverManager.java:101) at org.h2.Driver.load(Driver.java:155) at org.h2.Driver.(Driver.java:41) at java.lang.Class.forName0(Class.java:-1) - locked  (a java.lang.Class) at java.lang.Class.forName(Class.java:348) at org.apache.tomcat.jdbc.pool.ClassLoaderUtil.loadClass(ClassLoaderUtil.java:38) at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:271) at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:203) at org.apache.tomcat.jdbc.pool.ConnectionPool.createConnection(ConnectionPool.java:718) at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:650) at org.apache.tomcat.jdbc.pool.ConnectionPool.init(ConnectionPool.java:468) at org.apache.tomcat.jdbc.pool.ConnectionPool.(ConnectionPool.java:143) at org.apache.tomcat.jdbc.pool.DataSourceProxy.pCreatePool(DataSourceProxy.java:118) at org.apache.tomcat.jdbc.pool.DataSourceProxy.createPool(DataSourceProxy.java:107) at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:131) - locked  (a org.apache.tomcat.jdbc.pool.DataSource) at dbsDataSourceService.getConnection(DataSourceService.java:51) ... 

http-nio-8080-exec-6 @ 8311(等待监视器输入)(oracle和h2数据之间的比较)

 "http-nio-8080-exec-6@8311" daemon prio=5 tid=0x44 nid=NA waiting for monitor entry java.lang.Thread.State: BLOCKED waiting for http-nio-8080-exec-2@8307 to release lock on  (a org.apache.tomcat.jdbc.pool.DataSource) at org.apache.tomcat.jdbc.pool.DataSourceProxy.pCreatePool(DataSourceProxy.java:115) at org.apache.tomcat.jdbc.pool.DataSourceProxy.createPool(DataSourceProxy.java:107) at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:131) at dbsDataSourceService.getConnection(DataSourceService.java:51) ... 

我遇到了和你一样的问题,但在h2和mysql中。我不是同时使用h2和oracle,在这篇文章中 ,也许你可以找到一些有用的东西。

这是我的双数据源配置:

maven依赖:

org.mybatis.spring.boot mybatis-spring-boot-starter 1.1.1 mysql mysql-connector-java runtime

“`

 datasource: km: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/km?useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: 123456 max-active: 100 max-idle: 10 max-wait: 10000 test-while-idle: true esb: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/esb?useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: 123456 max-active: 100 max-idle: 10 max-wait: 10000 test-while-idle: true 

“`

Datasource km配置类:“`

 import com.package.km.api.commons.config.datasource.annotation.UseDatasourceKM; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.lang.invoke.MethodHandles; import java.sql.SQLException; /** * km datasource config * Created by BeeNoisy on 16/5/23. */ @Configuration @MapperScan(basePackages = "package.km", annotationClass = UseDatasourceKM.class, sqlSessionFactoryRef = KMDatasourceConfig.SQL_SESSION_FACTORY_NAME) public class KMDatasourceConfig { public static final String SQL_SESSION_FACTORY_NAME = "sessionFactoryKm"; public static final String TX_MANAGER = "txManagerKm"; private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @Bean(name = "datasourceKm") @Primary @ConfigurationProperties(prefix = "datasource.km") public DataSource dataSourceKm() { return DataSourceBuilder.create().build(); } @Bean(name = TX_MANAGER) @Primary public PlatformTransactionManager txManagerKm() { return new DataSourceTransactionManager(dataSourceKm()); } @Bean(name = KMDatasourceConfig.SQL_SESSION_FACTORY_NAME) @Primary public SqlSessionFactory sqlSessionFactoryBean() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("/mybatis/mybatis-conf.xml")); sqlSessionFactoryBean.setDataSource(dataSourceKm()); return sqlSessionFactoryBean.getObject(); } } 

“`

这是datasource esb配置类:

“`

 import com.package.km.api.commons.condition.EsbEnabledCondition; import com.package.km.api.commons.config.datasource.annotation.UseDatasourceESB; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.lang.invoke.MethodHandles; /** * Created by BeeNoisy on 16/5/23. */ @Configuration @Conditional(EsbEnabledCondition.class) @MapperScan(basePackages = "package.esb", annotationClass = UseDatasourceESB.class, sqlSessionFactoryRef = EsbDatasourceConfig.SQL_SESSION_FACTORY_NAME) public class EsbDatasourceConfig { public static final String SQL_SESSION_FACTORY_NAME = "sessionFactoryEsb"; public static final String TX_MANAGER = "txManagerEsb"; private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @Bean(name = "datasourceEsb") @Conditional(EsbEnabledCondition.class) @ConfigurationProperties(prefix = "datasource.esb") public DataSource dataSourceEsb() { return DataSourceBuilder.create().build(); } @Bean(name = TX_MANAGER) @Conditional(EsbEnabledCondition.class) public PlatformTransactionManager txManagerEsb() { return new DataSourceTransactionManager(dataSourceEsb()); } @Bean(name = EsbDatasourceConfig.SQL_SESSION_FACTORY_NAME) @Conditional(EsbEnabledCondition.class) public SqlSessionFactory sqlSessionFactoryBean() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("/mybatis/mybatis-conf.xml")); sqlSessionFactoryBean.setDataSource(dataSourceEsb()); return sqlSessionFactoryBean.getObject(); } } 

“`

然后,您可以使用两个注释: UseDatasourceESBUseDatasourceKM来注释您的mapper类,如:

“`

 @UseDatasourceKM public interface GroupBaseDAO { public static final String COL_ALL = " id, name, create_time, last_update_time "; public static final String TABLE = " group_base "; @Select(" select " + COL_ALL + " from " + TABLE + " where id = #{id} ") public GroupBase findById(@Param("id") int id); @Select(" select " + COL_ALL + " from " + TABLE + " where id < #{lastId} " + " limit #{count} ") public List list( @Param("lastId") int lastId, @Param("count") int count ); ... 

“`

mysql \ h2和oracle中有两个数据源。 顺便说一句,您可以在调试模型中运行代码,并进入getConnection()方法以查找更多详细信息。