Java 使用相同存储库和模型类的多个数据源进行Spring引导?

Java 使用相同存储库和模型类的多个数据源进行Spring引导?,java,spring,jpa,spring-boot,spring-data-jpa,Java,Spring,Jpa,Spring Boot,Spring Data Jpa,我必须做一个SpringBootVersion1.5应用程序,它可以这样做:它创建一个对象并尝试持久化到两个数据源(例如:Postgresql中名为test_book_1和test_book_2的两个数据库) 我发现一个例子可以用于两个不同的对象(作者:A,图书:B),它们可以存储在不同的数据库中(A到test_Book_1,B到test_Book_2)。这是一个很好的例子,但它不是我想要的。 我的想法是,我需要定义2个自定义JPA数据库配置,并需要对它们进行配置以管理相同的存储库和域类。然而

我必须做一个SpringBootVersion1.5应用程序,它可以这样做:它创建一个对象并尝试持久化到两个数据源(例如:Postgresql中名为test_book_1和test_book_2的两个数据库)

我发现一个例子可以用于两个不同的对象(作者:A,图书:B),它们可以存储在不同的数据库中(A到test_Book_1,B到test_Book_2)。这是一个很好的例子,但它不是我想要的。

我的想法是,我需要定义2个自定义JPA数据库配置,并需要对它们进行配置以管理相同的存储库和域类。然而,Spring只使用第二个类作为限定符来注入JPA存储库(我知道当两个配置都指向同一个类时,第二个可以重写)

问题是,我如何告诉Spring让它知道何时应该从想要的数据源注入正确的Bean(BookRepository)(我想要将对象持久化到两个数据源,而不仅仅是第二个数据源)

下面是上面示例链接中修改的代码

@Service
@Transactional(rollbackFor = Exception.class)
public class StorageEntryService {

    @Autowired
    private StorageEntryRepository storageEntryRepository;

    @PersistenceContext(unitName = "target")
    private EntityManager targetEntityManager;

    public void save(StorageEntry storageEntry) throws Exception {

        // this.storageEntryRepository.save(storageEntry);

        // Load an stored entry from the source database
        StorageEntry storedEntry = this.storageEntryRepository.findOne(12L);                
        //this.storageEntryRepository.save(storageEntry);
        // Save also to a different database
        final Session targetHibernateSession = targetEntityManager.unwrap(Session.class);
        Criteria criteria = targetHibernateSession.createCriteria(StorageEntry.class);

        criteria.add(Restrictions.like("someValue", "%Book1%"));
        List<StorageEntry> storageEntries = criteria.list();

        if (storageEntries.isEmpty()) {
            targetEntityManager.merge(storedEntry);
            // No flush then nodata is saved in the different database
            targetHibernateSession.flush();
            System.out.println("Stored the new object to target database.");
        } else {
            System.out.println("Object already existed in target database.");
        }


    }
}
一个application.properties文件,该文件被修改为在Postgresql中创建2个数据库,而不是在Postgresql中创建1个数据库和在Mysql中创建1个数据库

server.port=8082
# -----------------------
# POSTGRESQL DATABASE CONFIGURATION
# -----------------------
    spring.postgresql.datasource.url=jdbc:postgresql://localhost:5432/test_book_db
spring.postgresql.datasource.username=petauser
spring.postgresql.datasource.password=petapasswd
spring.postgresql.datasource.driver-class-name=org.postgresql.Driver

# ------------------------------
# POSTGRESQL 1 DATABASE CONFIGURATION
# ------------------------------

   spring.mysql.datasource.url=jdbc:postgresql://localhost:5432/test_author_db
spring.mysql.datasource.username=petauser
spring.mysql.datasource.password=petapasswd
spring.mysql.datasource.driver-class-name=org.postgresql.Driver
包:com.roufid.tutorial.configuration 类APostgresqlConfiguration

package com.roufid.tutorial.configuration;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
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.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.roufid.tutorial.entity.postgresql.Book;

/**
 * Spring configuration of the "PostgreSQL" database.
 *
 * @author Radouane ROUFID.
 *
 */
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "postgresqlEntityManager",
        transactionManagerRef = "postgresqlTransactionManager",
        basePackages = "com.roufid.tutorial.dao.postgresql"
)
public class APostgresqlConfiguration {

    /**
     * PostgreSQL datasource definition.
     *
     * @return datasource.
     */
    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.postgresql.datasource")
    public DataSource postgresqlDataSource() {
        return DataSourceBuilder
                .create()
                .build();
    }

    /**
     * Entity manager definition.
     *
     * @param builder an EntityManagerFactoryBuilder.
     * @return LocalContainerEntityManagerFactoryBean.
     */
    @Primary
    @Bean(name = "postgresqlEntityManager")
    public LocalContainerEntityManagerFactoryBean postgresqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(postgresqlDataSource())
                .properties(hibernateProperties())
                .packages(Book.class)
                .persistenceUnit("postgresqlPU")
                .build();
    }

    @Primary
    @Bean(name = "postgresqlTransactionManager")
    public PlatformTransactionManager postgresqlTransactionManager(@Qualifier("postgresqlEntityManager") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }

    private Map<String, Object> hibernateProperties() {

        Resource resource = new ClassPathResource("hibernate.properties");

        try {
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            return properties.entrySet().stream()
                    .collect(Collectors.toMap(
                            e -> e.getKey().toString(),
                            e -> e.getValue())
                    );
        } catch (IOException e) {
            return new HashMap<String, Object>();
        }
    }
}
package com.roufid.tutorial.configuration;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.roufid.tutorial.entity.mysql.Author;
import com.roufid.tutorial.entity.postgresql.Book;

/**
 * Spring configuration of the "mysql" database.
 *
 * @author Radouane ROUFID.
 *
 */
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "mysqlEntityManager",
        transactionManagerRef = "mysqlTransactionManager",
        basePackages = "com.roufid.tutorial.dao.postgresql"
)
public class MysqlConfiguration {

    /**
     * MySQL datasource definition.
     *
     * @return datasource.
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.mysql.datasource")
    public DataSource mysqlDataSource() {
        return DataSourceBuilder
                .create()
                .build();
    }

    /**
     * Entity manager definition.
     *
     * @param builder an EntityManagerFactoryBuilder.
     * @return LocalContainerEntityManagerFactoryBean.
     */
    @Bean(name = "mysqlEntityManager")
    public LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(mysqlDataSource())
                .properties(hibernateProperties())
                .packages(Book.class)
                .persistenceUnit("mysqlPU")
                .build();
    }

    /**
     * @param entityManagerFactory
     * @return
     */
    @Bean(name = "mysqlTransactionManager")
    public PlatformTransactionManager mysqlTransactionManager(@Qualifier("mysqlEntityManager") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }

    private Map<String, Object> hibernateProperties() {

        Resource resource = new ClassPathResource("hibernate.properties");
    }
}    try {
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            return properties.entrySet().stream()
                    .collect(Collectors.toMap(
                            e -> e.getKey().toString(),
                            e -> e.getValue())
                    );
        } catch (IOException e) {
            return new HashMap<String, Object>();
        }
    }
}
和一个测试类来注入BookRepository,它将只使用MysqlConfiguration类(第二个数据源)


}看起来您需要多租户支持

有一个基于Spring的解决方案

您需要实现CurrentTenantIdentifierResolver接口

public String resolveCurrentTenantIdentifier()
延伸

AbstractDataSourceBasedMultiTenantConnectionProviderImpl
为租户返回数据源


请参阅更多内容,因此我想我自己得到了答案(我只想使用Spring JPA和Hibernate)。这就是我所做的,灵感来自

最重要的类是config类,用于手动创建2个数据源(Postgresql中的2个数据库)

因为,我想将存储的实体从源数据库复制到目标数据库。所以我使用SpringJPA从源数据库读取对象

public interface StorageEntryRepository extends     CrudRepository<StorageEntry, Long> {

}
公共接口StorageEntryRepository扩展了Crudepository{
}
我创建了一个服务类来检查目标数据库中是否存在按值存在的实体(someValue包含子字符串“Book”),然后通过Hibernate将其持久化到目标数据库中(这里的StorageEntry是上面示例链接中的域类)

@服务
@事务性(rollboor=Exception.class)
公共类StorageEntryService{
@自动连线
私有StorageEntryRepository StorageEntryRepository;
@PersistenceContext(unitName=“target”)
私人实体管理者目标实体管理者;
public void save(StorageEntry-StorageEntry)引发异常{
//this.storageEntryRepository.save(storageEntry);
//从源数据库加载存储的条目
StorageEntry storedEntry=this.storageEntryRepository.findOne(12L);
//this.storageEntryRepository.save(storageEntry);
//还保存到其他数据库
最终会话targetHibernateSession=targetEntityManager.unwrap(Session.class);
Criteria=targetHibernateSession.createCriteria(StorageEntry.class);
添加(限制,如“someValue”,“Book1%”);
List-storageEntries=criteria.List();
if(storageEntries.isEmpty()){
targetEntityManager.merge(storedEntry);
//无刷新,则节点数据保存在不同的数据库中
targetHibernateSession.flush();
System.out.println(“将新对象存储到目标数据库”);
}否则{
System.out.println(“对象已存在于目标数据库中。”);
}
}
}

最后,我可以使用当前工作应用程序中的JPA,只需创建另一个具有配置类和服务类的应用程序,即可将现有对象迁移到新数据库

是的,似乎我有必要这样做,我找到了一个关于多租户和独立数据库的好教程。另外,我真正的问题也比较简单,它将从现有数据库中读取一个对象,并将该对象持久化到一个新的数据库中。我刚刚快速浏览了教程链接,所以不确定它是否支持此案例。请检查springbatch,您描述的案例是典型的读者/处理器/作者。有很多spring阅读器和编写器,例如基于JDBC的Hanks,我检查了您发布的链接,它似乎将数据写入文件并插入数据库,如果可能的话,使用一些我不熟悉的语法。我更喜欢通过JPA复制整个对象,因为这两个数据库共享由Hibernate创建的相同模式。这里有一个例子,我读得很好,在我的情况下,这是可能的。您只需要基于DB的读写器。它可以是基于JDBC的,也可以实现自己的。只需检查读写器界面,如果我们不想使用实体管理器进行查询并使用存储库中的所有命名查询,该怎么办?我不是Hibernate的专业人士,因此您可能需要问一个单独的问题。您应该接受自己的答案,因为它对您有效!
AbstractDataSourceBasedMultiTenantConnectionProviderImpl
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "sourceEntityManagerFactory",
        basePackages = "application"
)
public class PersistenceConfig {

    @Autowired
    private JpaVendorAdapter jpaVendorAdapter;

    private String databaseUrl = "jdbc:postgresql://localhost:5432/test_book_db";

    private String targetDatabaseUrl = "jdbc:postgresql://localhost:5432/test_author_db";

    private String username = "petauser";

    private String password = "petapasswd";

    private String driverClassName = "org.postgresql.Driver";

    private String dialect = "org.hibernate.dialect.PostgreSQLDialect";

    private String ddlAuto = "update";

    @Bean
    public EntityManager sourceEntityManager() {
        return sourceEntityManagerFactory().createEntityManager();
    }

    @Bean
    public EntityManager targetEntityManager() {
        return targetEntityManagerFactory().createEntityManager();
    }

    @Bean
    @Primary
    public EntityManagerFactory sourceEntityManagerFactory() {
        return createEntityManagerFactory("source", databaseUrl);
    }

    @Bean
    public EntityManagerFactory targetEntityManagerFactory() {
        return createEntityManagerFactory("target", targetDatabaseUrl);
    }

    @Bean(name = "transactionManager")
    @Primary
    public PlatformTransactionManager sourceTransactionManager() {
        return new JpaTransactionManager(sourceEntityManagerFactory());
    }

    @Bean
    public PlatformTransactionManager targetTransactionManager() {
        return new JpaTransactionManager(targetEntityManagerFactory());
    }

    private EntityManagerFactory createEntityManagerFactory(final String persistenceUnitName,
            final String databaseUrl) {
        final LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();

        final DriverManagerDataSource dataSource = new DriverManagerDataSource(databaseUrl, username, password);
        dataSource.setDriverClassName(driverClassName);
        entityManagerFactory.setDataSource(dataSource);

        entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);
        entityManagerFactory.setPackagesToScan("application.domain");
        entityManagerFactory.setPersistenceUnitName(persistenceUnitName);

        final Properties properties = new Properties();
        properties.setProperty("hibernate.dialect", dialect);
        properties.setProperty("hibernate.hbm2ddl.auto", ddlAuto);
        entityManagerFactory.setJpaProperties(properties);

        entityManagerFactory.afterPropertiesSet();
        return entityManagerFactory.getObject();
    }

}
public interface StorageEntryRepository extends     CrudRepository<StorageEntry, Long> {

}
@Service
@Transactional(rollbackFor = Exception.class)
public class StorageEntryService {

    @Autowired
    private StorageEntryRepository storageEntryRepository;

    @PersistenceContext(unitName = "target")
    private EntityManager targetEntityManager;

    public void save(StorageEntry storageEntry) throws Exception {

        // this.storageEntryRepository.save(storageEntry);

        // Load an stored entry from the source database
        StorageEntry storedEntry = this.storageEntryRepository.findOne(12L);                
        //this.storageEntryRepository.save(storageEntry);
        // Save also to a different database
        final Session targetHibernateSession = targetEntityManager.unwrap(Session.class);
        Criteria criteria = targetHibernateSession.createCriteria(StorageEntry.class);

        criteria.add(Restrictions.like("someValue", "%Book1%"));
        List<StorageEntry> storageEntries = criteria.list();

        if (storageEntries.isEmpty()) {
            targetEntityManager.merge(storedEntry);
            // No flush then nodata is saved in the different database
            targetHibernateSession.flush();
            System.out.println("Stored the new object to target database.");
        } else {
            System.out.println("Object already existed in target database.");
        }


    }
}