在Spring Data JPA中配置多个数据源的问题

时间:2015-04-10 04:43:28

标签: spring-boot spring-data-jpa

当我配置两个数据源时,spring boot会抛出错误:

2015-04-10 10:01:50.750  INFO 8824 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2015-04-10 10:01:50.765  INFO 8824 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.0.20
2015-04-10 10:01:51.155  WARN 8824 --- [ost-startStop-1] o.a.catalina.core.NamingContextListener  : Failed to register in JMX: javax.naming.NamingException: Could not create resource factory instance [Root exception is java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory]

pom.xml包含以下条目:

<?xml version="1.0"?>
<project
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
    xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>qcom.cas.spring.reference.app</groupId>
        <artifactId>spring-reference-app</artifactId>
        <version>1.0-RELEASE</version>
    </parent>
    <artifactId>persistence</artifactId>
    <name>persistence</name>
    <packaging>jar</packaging>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.7</java.version>
        <gorm.version>1.1.0.RELEASE</gorm.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.grails</groupId>
            <artifactId>gorm-hibernate4-spring-boot</artifactId>
            <version>${gorm.version}</version>
        </dependency>

        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
        </dependency>

        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>

    </dependencies>

</project> 

应用程序属性文件包含:

myapp.db.jndi = java:comp/env/jdbc/myapp
pds.db.jndi = java:comp/env/jdbc/pds 

// this is another team's DB. Will use select queries directly.
pds.jndi.factory = org.apache.commons.dbcp.BasicDataSourceFactory
pds.url = jdbc:mysql://pds.com:3306/test
pds.username = abc
pds.password = xyz
pds.driverClassName = com.mysql.jdbc.Driver

local.db.url = jdbc:mysql://localhost:3306/myapp
local.db.username = abc
local.db.password = xyz
local.db.driverClassName = com.mysql.jdbc.Driver
local.db.jndi.factory = org.apache.commons.dbcp.BasicDataSourceFactory

我已经定制了嵌入式tomcat,如下所示:

@Component
@Profile("local")
public class EmbeddedServerConfig implements EmbeddedServletContainerCustomizer {

@Value("${local.db.url}") 
private String dbUrl;

@Value("${local.db.username}") 
private String dbUserName;

@Value("${local.db.password}") 
private String dbPassword;

@Value("${local.db.driverClassName}") 
private String dbDriverClassName;

@Value("${local.db.jndi.factory}") 
private String dbJNDIFactory;

@Value("${pds.jndi.factory}") 
private String pdsJNDIFactory;

@Value("${pds.url}") 
private String pdsUrl;

@Value("${pds.username}") 
private String pdsUserName;

@Value("${pds.password}") 
private String pdsPassword;

@Value("${pds.driverClassName}") 
private String pdsDriverClassName;

public void customize(ConfigurableEmbeddedServletContainer container) {
    container.setPort(9000);
}

@Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
    return new TomcatEmbeddedServletContainerFactory() {

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
            tomcat.enableNaming();
            TomcatEmbeddedServletContainer container = super.getTomcatEmbeddedServletContainer(tomcat);
            for (Container child: container.getTomcat().getHost().findChildren()) {
                if (child instanceof Context) {
                    ClassLoader contextClassLoader =  ((Context)child).getLoader().getClassLoader();
                    Thread.currentThread().setContextClassLoader(contextClassLoader);
                    break;
                }
            }
            return container;
            //return super.getTomcatEmbeddedServletContainer(tomcat);
        }

        @Override
        protected void postProcessContext(Context context) {
            ContextResource appDBResource = new ContextResource();
            appDBResource.setName("jdbc/myapp");
            appDBResource.setType(DataSource.class.getName());
            appDBResource.setProperty("factory", dbJNDIFactory);
            appDBResource.setProperty("driverClassName", dbDriverClassName);
            appDBResource.setProperty("url", dbUrl);
            appDBResource.setProperty("username", dbUserName);
            appDBResource.setProperty("password", dbPassword);
            context.getNamingResources().addResource(appDBResource);

            ContextResource pdsDBResource = new ContextResource();
            pdsDBResource.setName("jdbc/pds");
            pdsDBResource.setType(DataSource.class.getName());
            appDBResource.setProperty("factory", pdsJNDIFactory);
            pdsDBResource.setProperty("driverClassName", pdsDriverClassName);
            pdsDBResource.setProperty("url", pdsUrl);
            pdsDBResource.setProperty("username", pdsUserName);
            pdsDBResource.setProperty("password", pdsPassword);
            context.getNamingResources().addResource(pdsDBResource);
        }
    };
}

@Configuration
@EnableJpaRepositories(basePackages = {"qcom.cas.myapp.repo" })
public class PersistenceConfig {

    private final String entityPackages = "qcom.cas.myapp";

    @Value("${hibernate.mysql.dialect}") 
    private String dialect;

    @Value("${hibernate.hbm2ddl.auto}") 
    private String ddl;

    @Value("${hibernate.ejb.naming_strategy}") 
    private String naming;

    @Value("${hibernate.show_sql}") 
    private String showSql;

    @Value("${hibernate.format_sql}") 
    private String formatSql;

    @Value("${myapp.db.jndi}") 
    private String appDB;

    private static final Logger log = Logger.getLogger(PersistenceConfig.class);

    @Bean
    @Primary
    public DataSource dataSource() throws IllegalArgumentException, NamingException {
        log.info("Setting up application data source");
        JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
        bean.setJndiName(appDB);
        bean.setLookupOnStartup(false);
        bean.setProxyInterface(DataSource.class);
        bean.setResourceRef(true);
        bean.afterPropertiesSet();
        return (DataSource) bean.getObject();
    }

    @Bean(name = "entityManagerFactory")
    @Primary 
    public LocalContainerEntityManagerFactoryBean entityManagerFactory( ) throws IllegalArgumentException, NamingException {
        log.info("Setting up entity manager factory");
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPackagesToScan(entityPackages);
        entityManagerFactoryBean.setPersistenceUnitName("myapp");
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());      
        entityManagerFactoryBean.setJpaProperties(getJpaProperties());
        return entityManagerFactoryBean;
    }

    private Properties getJpaProperties() {
        Properties jpaProperties = new Properties();
        jpaProperties.put("hibernate.dialect", dialect);
        jpaProperties.put("hibernate.hbm2ddl.auto", ddl);
        jpaProperties.put("hibernate.ejb.naming_strategy", naming);
        jpaProperties.put("hibernate.show_sql", showSql);
        jpaProperties.put("hibernate.format_sql", formatSql);
        return jpaProperties;
    }

}

@Configuration
public class PdsConfig {

    private static final Logger log = Logger.getLogger(PdsConfig.class);

    @Value("${pds.db.jndi}") 
    private String pdsDB;

    @Bean(name="pdsDataSource")
    public DataSource pdsDataSource() throws IllegalArgumentException, NamingException {
        log.info("Setting up PDS data source.");
        JndiObjectFactoryBean bean = new JndiObjectFactoryBean();       
        bean.setJndiName(pdsDB);
        bean.setLookupOnStartup(false);
        bean.setProxyInterface(DataSource.class);
        bean.setResourceRef(true);
        bean.afterPropertiesSet();
        return (DataSource) bean.getObject();
    }

}

如果我只使用1个数据源,那么app运行正常。

当我尝试访问第二个数据源时,下面是完整的堆栈跟踪:

2015-04-10 15:56:37.886 ERROR 7588 --- [nio-9000-exec-1] o.a.c.c.C.[.[.[/].[jerseyServlet]        : Servlet.service() for servlet [jerseyServlet] in context with path [] threw exception [org.springframework.jndi.JndiLookupFailureException: JndiObjectTargetSource failed to obtain new target object; nested exception is javax.naming.NamingException: Could not create resource factory instance [Root exception is java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory]] with root cause

java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:191)
    at org.apache.naming.factory.ResourceFactory.getDefaultFactory(ResourceFactory.java:47)
    at org.apache.naming.factory.FactoryBase.getObjectInstance(FactoryBase.java:90)
    at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:841)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:152)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:829)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:152)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:829)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:152)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:829)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:166)
    at org.apache.naming.SelectorContext.lookup(SelectorContext.java:157)
    at javax.naming.InitialContext.lookup(InitialContext.java:411)
    at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:155)
    at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:87)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:179)
    at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:95)
    at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:106)
    at org.springframework.jndi.JndiObjectTargetSource.getTarget(JndiObjectTargetSource.java:135)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
    at com.sun.proxy.$Proxy62.getConnection(Unknown Source)
    at groovy.sql.Sql$36.run(Sql.java:4165)
    at groovy.sql.Sql$36.run(Sql.java:4163)
    at java.security.AccessController.doPrivileged(Native Method)
    at groovy.sql.Sql.createConnection(Sql.java:4163)
    at groovy.sql.Sql$AbstractQueryCommand.execute(Sql.java:4548)
    at groovy.sql.Sql.rows(Sql.java:1954)
    at groovy.sql.Sql.firstRow(Sql.java:2192)
    at groovy.sql.Sql.firstRow(Sql.java:2230)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:189)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
    at qcom.cas.myapp.service.pds.PdsService.getUserDetails(PdsService.groovy:27)
    at qcom.cas.myapp.api.PdsResource.user(PdsResource.java:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:151)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:172)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:195)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:104)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:384)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:342)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:101)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:297)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:254)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1030)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:377)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:381)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:344)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:221)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:291)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:102)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration$MetricsFilter.doFilterInternal(MetricFilterAutoConfiguration.java:90)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

2 个答案:

答案 0 :(得分:0)

我可以看到你正在使用jpa,如果你使用的是spring-boot,那么你可以使用spring-boot-starter-jpa来自动配置你的数据源。您将需要额外的配置(请参阅此link

添加此依赖项:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

答案 1 :(得分:0)

可以通过在类路径中添加以下jar来解决此问题:

Tomcat DBCP