为了账号安全,请及时绑定邮箱和手机立即绑定

Spring Boot,Spring Data JPA,带有多个DataSources

/ 猿问

Spring Boot,Spring Data JPA,带有多个DataSources

qq_遁去的一_1 2019-09-02 08:31:49

我正在尝试使用Spring Boot和Spring Data JPA将每个@Repositories连接到不同的DataSource。我使用以下内容http://xantorohara.blogspot.com/2013/11/spring-boot-jdbc-with-multiple.html作为参考。以下是我试图使用Spring Data JPA实现类似解决方案时使用的代码。


CustomerDbConfig.java(第一个数据源连接)


@Configuration

@EnableJpaRepositories(

        entityManagerFactoryRef = "orderEntityManager",

        transactionManagerRef = "orderTransactionManager",

        basePackages = {"com.mm.repository.customer"})

public class CustomerDbConfig {


    @Bean(name = "customerEntityManager")

    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){

        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();

        em.setDataSource(dataSource());

        em.setPackagesToScan(new String[] {"com.mm.domain.customer"});


        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();

        em.setJpaVendorAdapter(vendorAdapter);

        em.setJpaProperties(additionalJpaProperties());

        em.setPersistenceUnitName("customerPersistence");

        em.setPackagesToScan("com.mm.domain.customer");


        return em;

    }


    Properties additionalJpaProperties(){

        Properties properties = new Properties();

        properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");

        properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");

        properties.setProperty("hibernate.show_sql", "true");


        return properties;

    }


    @Bean

    public DataSource dataSource(){

        return DataSourceBuilder.create()

                .url("jdbc:h2:mem:customer:H2")

                .driverClassName("org.h2.Driver")

                .username("sa")

                .password("")

                .build();

    }   

查看完整描述

3 回答

?
慕田峪9158850

通过使用@EnableAutoConfiguration和application.properties,还有另一种方法可以拥有多个dataSource。


基本上在application.properties上放置多个dataSource配置信息,并通过@EnableAutoConfiguration自动为第一个dataSource生成默认设置(dataSource和entityManagerFactory)。但对于下一个dataSource,请通过属性文件中的信息手动创建dataSource,entityManagerFactory和transactionManager。


下面是我设置两个dataSource的示例。第一个dataSource由@EnableAutoConfiguration设置,只能为一个配置而不是多个配置。这将由DataSourceTransactionManager生成“transactionManager” ,它看起来是由注释生成的默认transactionManager。但是,我已经看到事务没有从预定的线程池中开始出现问题,仅针对默认的DataSourceTransactionManager以及有多个事务管理器时。所以我通过JpaTransactionManager手动创建transactionManager也为第一个分配'transactionManager'bean名称和默认entityManagerFactory的dataSource。第一个dataSource的JpaTransactionManager肯定会解决来自ScheduledThreadPool的线程上的奇怪事务问题。


Spring Boot 1.3.0.RELEASE的更新


我发现我之前使用@EnableAutoConfiguration配置的默认dataSource在使用Spring Boot 1.3版本查找entityManagerFactory时遇到了问题。在我介绍自己的transactionManager之后,可能默认的entityManagerFactory不是由@EnableAutoConfiguration生成的。所以现在我自己创建entityManagerFactory。所以我不需要使用@EntityScan。因此看起来我越来越多地通过@EnableAutoConfiguration进行设置了。


第二个dataSource是在没有@EnableAutoConfiguration的情况下设置的,并通过手动方式创建“anotherTransactionManager”。


由于从PlatformTransactionManager扩展了多个transactionManager,我们应该指定在每个@Transactional注释上使用哪个transactionManager


默认存储库配置


@Configuration

@EnableTransactionManagement

@EnableAutoConfiguration

@EnableJpaRepositories(

        entityManagerFactoryRef = "entityManagerFactory",

        transactionManagerRef = "transactionManager",

        basePackages = {"com.mysource.repository"})

public class RepositoryConfig {

    @Autowired

    JpaVendorAdapter jpaVendorAdapter;


    @Autowired

    DataSource dataSource;


    @Bean(name = "entityManager")

    public EntityManager entityManager() {

        return entityManagerFactory().createEntityManager();

    }


    @Primary

    @Bean(name = "entityManagerFactory")

    public EntityManagerFactory entityManagerFactory() {

        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();

        emf.setDataSource(dataSource);

        emf.setJpaVendorAdapter(jpaVendorAdapter);

        emf.setPackagesToScan("com.mysource.model");

        emf.setPersistenceUnitName("default");   // <- giving 'default' as name

        emf.afterPropertiesSet();

        return emf.getObject();

    }


    @Bean(name = "transactionManager")

    public PlatformTransactionManager transactionManager() {

        JpaTransactionManager tm = new JpaTransactionManager();

        tm.setEntityManagerFactory(entityManagerFactory());

        return tm;

    }

}

另一个存储库配置


@Configuration

@EnableTransactionManagement

@EnableJpaRepositories(

        entityManagerFactoryRef = "anotherEntityManagerFactory",

        transactionManagerRef = "anotherTransactionManager",

        basePackages = {"com.mysource.anothersource.repository"})

public class AnotherRepositoryConfig {

    @Autowired

    JpaVendorAdapter jpaVendorAdapter;


    @Value("${another.datasource.url}")

    private String databaseUrl;


    @Value("${another.datasource.username}")

    private String username;


    @Value("${another.datasource.password}")

    private String password;


    @Value("${another.dataource.driverClassName}")

    private String driverClassName;


    @Value("${another.datasource.hibernate.dialect}")

    private String dialect;


    public DataSource dataSource() {

        DriverManagerDataSource dataSource = new DriverManagerDataSource(databaseUrl, username, password);

        dataSource.setDriverClassName(driverClassName);

        return dataSource;

    }


    @Bean(name = "anotherEntityManager")

    public EntityManager entityManager() {

        return entityManagerFactory().createEntityManager();

    }


    @Bean(name = "anotherEntityManagerFactory")

    public EntityManagerFactory entityManagerFactory() {

        Properties properties = new Properties();

        properties.setProperty("hibernate.dialect", dialect);


        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();

        emf.setDataSource(dataSource());

        emf.setJpaVendorAdapter(jpaVendorAdapter);

        emf.setPackagesToScan("com.mysource.anothersource.model");   // <- package for entities

        emf.setPersistenceUnitName("anotherPersistenceUnit");

        emf.setJpaProperties(properties);

        emf.afterPropertiesSet();

        return emf.getObject();

    }


    @Bean(name = "anotherTransactionManager")

    public PlatformTransactionManager transactionManager() {

        return new JpaTransactionManager(entityManagerFactory());

    }

}

application.properties


# database configuration

spring.datasource.url=jdbc:h2:file:~/main-source;AUTO_SERVER=TRUE

spring.datasource.username=sa

spring.datasource.password=

spring.datasource.driver-class-name=org.h2.Driver

spring.datasource.continueOnError=true

spring.datasource.initialize=false


# another database configuration

another.datasource.url=jdbc:sqlserver://localhost:1433;DatabaseName=another;

another.datasource.username=username

another.datasource.password=

another.datasource.hibernate.dialect=org.hibernate.dialect.SQLServer2008Dialect 

another.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

为@Transactional注释选择适当的transactionManager


为第一个数据源提供服务


@Service("mainService")

@Transactional("transactionManager")

public class DefaultDataSourceServiceImpl implements DefaultDataSourceService       

{


   //


}

为另一个数据源提供服务


@Service("anotherService")

@Transactional("anotherTransactionManager")

public class AnotherDataSourceServiceImpl implements AnotherDataSourceService 

{


   //


}


查看完整回答
反对 回复 2019-09-02
?
慕仰0522570

这是我的解决方案。基于spring-boot.1.2.5.RELEASE。


application.properties


first.datasource.driver-class-name=com.mysql.jdbc.Driver

first.datasource.url=jdbc:mysql://127.0.0.1:3306/test

first.datasource.username=

first.datasource.password=

first.datasource.validation-query=select 1


second.datasource.driver-class-name=com.mysql.jdbc.Driver

second.datasource.url=jdbc:mysql://127.0.0.1:3306/test2

second.datasource.username=

second.datasource.password=

second.datasource.validation-query=select 1

DataSourceConfig.java


@Configuration

public class DataSourceConfig {

    @Bean

    @Primary

    @ConfigurationProperties(prefix="first.datasource")

    public DataSource firstDataSource() {

        DataSource ds =  DataSourceBuilder.create().build();

        return ds;

    }


    @Bean

    @ConfigurationProperties(prefix="second.datasource")

    public DataSource secondDataSource() {

        DataSource ds =  DataSourceBuilder.create().build();

        return ds;

    }

}


查看完整回答
反对 回复 2019-09-02
?
慕莱坞森

我检查了你在GitHub上提供的源代码。配置中有几个错误/拼写错误。


在CustomerDbConfig / OrderDbConfig中,您应该引用customerEntityManager,并且包应该指向现有的包:


@Configuration

@EnableJpaRepositories(

    entityManagerFactoryRef = "customerEntityManager",

    transactionManagerRef = "customerTransactionManager",

    basePackages = {"com.mm.boot.multidb.repository.customer"})

public class CustomerDbConfig {

要在customerEntityManager和orderEntityManager中扫描的包都没有指向正确的包:


em.setPackagesToScan("com.mm.boot.multidb.model.customer");

注入适当的EntityManagerFactory也不起作用。它应该是:


@Bean(name = "customerTransactionManager")

public PlatformTransactionManager transactionManager(EntityManagerFactory customerEntityManager){


}

以上是导致问题和例外。在@Bean方法中提供名称时,您确定可以注入适当的EMF。


我做的最后一件事是禁用自动配置JpaRepositories:


@EnableAutoConfiguration(exclude = JpaRepositoriesAutoConfiguration.class)

通过所有修复程序,应用程序就像您预期的那样启动!


查看完整回答
反对 回复 2019-09-02

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信