2017-07-17 27 views
4

我必须做一个Spring Boot版本1.5的应用程序,它可以这样做:它创建一个对象,并尝试持久保留两个数据源(例如:2个名为test_book_1和test_book_2的数据库在Postgresql中)。使用相同的存储库和模型类的多个数据源的Spring Boot?

我发现了一个可以用于2个不同对象(作者:A,Book:B)的例子,它可以存储在不同的数据库中(A转到test_book_1,B转到test_book_2)。这是一个很好的例子,但它不是我想要的。 Store separate objects to different data sources

我知道我需要定义2个自定义JPA DatabaseConfigurations并需要配置它们来管理相同的存储库和域类。但是,Spring只使用第二个类作为限定符来注入JPA存储库(我明白,当两个配置指向同一个类,然后第二个可以覆盖)。

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

以下是上述示例链接中的修改代码。

一个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>(); 
     } 
    } 
} 

包:com.roufid.tutorial.configuration 类MysqlConfiguration

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>(); 
     } 
    } 
} 

包com.roufid。 tutorial.dao.postgresql class BookRepository

package com.roufid.tutorial.dao.postgresql; 

import org.springframework.data.repository.CrudRepository; 

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

/** 
* Book repository. 
* 
* @author Radouane ROUFID. 
* 
*/ 
public interface BookRepository extends CrudRepository<Book, Long> { 

} 

包com.roufid.tutorial.entity.postgresql 类Book

package com.roufid.tutorial.entity.postgresql; 

import java.io.Serializable; 

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.Id; 
import javax.persistence.Table; 

@Entity 
@Table(name = "BOOK") 
public class Book implements Serializable { 

    private static final long serialVersionUID = -9019470250770543773L; 

    @Id 
    private Long id; 

    @Column 
    private String name; 

    @Column 
    private Long authorId; 

    ... 
    // Setters, Getters 

} 

和测试类注入,这将仅使用MysqlConfiguration类(第二数据源)的BookRepository。

@RunWith(SpringRunner.class) 
@SpringBootTest 
public class ApplicationTest { 
@Autowired 
private BookRepository bookRepository; 
@Before 
public void init() { 
    Book book = new Book(); 
    book.setId(bookId); 
    book.setName("Spring Boot Book"); 

    // How can it persist to the first datasource? 
    bookRepository.save(book); 
} 

}

回答

1

看起来你需要多租户支持。

有这个

您需要实现CurrentTenantIdentifierResolver接口

public String resolveCurrentTenantIdentifier() 

,并延长

AbstractDataSourceBasedMultiTenantConnectionProviderImpl 

返回数据源的租户

查看更多Spring的基础的解决方案here

+0

是的,看来我需要做到这一点,我发现一个很好的教程与多租户与单独的数据库http://anakiou.blogspot.de/2015/08/multi-tenant-application-with-spring。 HTML。 此外,我真正的问题也更简单,它会从现有数据库中读取对象并将此对象保存到新数据库中。我刚刚在教程链接中快速浏览过,因此不确定它可以支持这种情况。 –

+0

检查弹簧代替http://www.mkyong.com/tutorials/spring-batch-tutorial/您描述的情况是典型的读者/处理器/作家。春天的读者和作家有很多 - 例如基于JDBC的 – StanislavL

+0

谢谢,我检查了你发布的链接,它似乎写入数据到文件并插入到数据库,如果可能的话,为我一些陌生的语法。我宁愿通过JPA复制整个对象,因为两个数据库都共享由Hibernate创建的相同模式。在这里有一个例子,我阅读相当不错,在我的情况下https://stackoverflow.com/a/42960998/2028440 –

0

所以我想我自己得到了一个答案(我想坚持使用Spring JPA和Hibernate)。因此,这里是我做什么,从Spring Booth with 2 different data sources

灵感最重要的类是配置类手动创建2个数据源(2个数据库PostgreSQL中)

@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(); 
    } 

} 

因为我的要复制存储实体从源数据库到目标数据库。所以我用春天JPA读取从源数据库

public interface StorageEntryRepository extends  CrudRepository<StorageEntry, Long> { 

} 

对象,我做了一个服务类,以检查是否是由价值存在的实体(someValue中包含一个子“书”)在目标数据库中坚持前在目标数据库中通过Hibernate(这里的StorageEntry是来自上面示例链接的域类)。

@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."); 
     } 


    } 
} 

所以它结束了,我可以从当前工作的应用程序同时使用JPA,只是需要做一个配置类和服务类其他应用程序做现有对象的这种迁移到新的数据库。

+0

如果我们不想使用实体管理器进行查询并使用存储库中的所有命名查询,该怎么办? – Righto

+0

我不是Hibernate的专家,所以也许你需要问一个单独的问题。 –

相关问题