Использование 2 источников данных для 2 БД с Spring - PullRequest
0 голосов
/ 28 июня 2018

Я безуспешно пытался решить проблему использования 2 БД с одинаковой схемой в Spring. Проблема, которую я пытаюсь решить, - это создание веб-страницы для ресторана, который базируется в 2 разных городах, поэтому я подумал, что использование отдельной базы данных для каждого города будет лучшим решением.

Я получаю результаты только из одной базы данных, другая по какой-то причине не используется. Базы данных называются BA и KE для города, и я использую перечисление City с теми же значениями.

BAConfig.java

@Configuration
@PropertySources(
    {@PropertySource("classpath:jpa.properties"),
            @PropertySource("classpath:jdbc.properties")})
@EnableTransactionManagement    // Enable use of the @Transactional annotation
@ComponentScan(basePackages = "dao")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class BAConfig {

@Autowired
private Environment environment;

@Bean(name="dataSourceBA")
public DataSource buildDataSource()
{
    HikariConfig hkcfg = new HikariConfig();
    hkcfg.setJdbcUrl(environment.getRequiredProperty("jdbc.urlBA"));
    hkcfg.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
    hkcfg.setUsername(environment.getRequiredProperty("jdbc.username"));
    hkcfg.setPassword(environment.getRequiredProperty("jdbc.password"));
    HikariDataSource ds = new HikariDataSource(hkcfg);
    return ds;
}

public static LocalContainerEntityManagerFactoryBean entityManagerFactoryBuilder(DataSource ds)
{
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setDataSource(ds);
    emf.setJpaVendorAdapter(new EclipseLinkJpaVendorAdapter());
    emf.setPackagesToScan("model"); // Look for entities in this package

    Properties props = new Properties();
    props.setProperty("databasePlatform", "org.eclipse.persistence.platform.database.PostgreSQLPlatform");
    props.setProperty("generateDdl", "true");
    props.setProperty("showSql", "true");
    props.setProperty("eclipselink.weaving", "false");
    props.setProperty("eclipselink.ddl-generation", "create-tables");
    emf.setJpaProperties(props);
    return emf;
}

@Bean(name="BAEM")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("dataSourceBA") DataSource ds) {
    return entityManagerFactoryBuilder(ds);
}


@Bean(name = "txManagerBA")
JpaTransactionManager transactionManager(@Qualifier("BAEM") EntityManagerFactory em) {
    JpaTransactionManager transactionManager = new JpaTransactionManager(em);
    return transactionManager;
}
}

KEConfig.java

@Configuration
@PropertySources(
    {@PropertySource("classpath:jpa.properties"),
            @PropertySource("classpath:jdbc.properties")})
@EnableTransactionManagement    // Enable use of the @Transactional annotation
@ComponentScan(basePackages = "dao")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class KEConfig {

@Autowired
private Environment environment;

@Bean("dataSourceKE")
public DataSource buildDataSource()
{
    HikariConfig hkcfg = new HikariConfig();
    hkcfg.setJdbcUrl(environment.getRequiredProperty("jdbc.urlKE"));
    hkcfg.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
    hkcfg.setUsername(environment.getRequiredProperty("jdbc.username"));
    hkcfg.setPassword(environment.getRequiredProperty("jdbc.password"));
    HikariDataSource ds = new HikariDataSource(hkcfg);
    return ds;
}

@Bean(name="KEEM")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("dataSourceKE")DataSource ds) {
    return BAConfig.entityManagerFactoryBuilder(ds);
}


@Bean(name = "txManagerKE")
JpaTransactionManager transactionManager(@Qualifier("KEEM") EntityManagerFactory em) {
    JpaTransactionManager transactionManager = new JpaTransactionManager(em);
    return transactionManager;
}
}

Они оба импортированы в класс MainConfig.java и используют следующий файл свойств.

jdbc.properties

jdbc.driverClassName=org.postgresql.Driver
jdbc.urlBA=jdbc:postgresql://localhost:5432/BambooBA
jdbc.urlKE=jdbc:postgresql://localhost:5432/BambooKE

Вот контроллер покоя для данной сущности.

ReservationsController.java

@RestController
@RequestMapping("/reservations")
public class ReservationsController {

@Autowired
private ReservationsService reservationsService;

@RequestMapping(value = "/getAll/{c}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Reservations>> getAll(@PathVariable City c) {
    try {
        List<Reservations> reservations = new ArrayList<Reservations>();
        switch(c)
        {
            case BA: reservations = reservationsService.findAllBA(); break;
            case KE: reservations = reservationsService.findAllKE(); break;
        }
        return new ResponseEntity<List<Reservations>>(reservations, HttpStatus.OK);
    } catch (NoSuchElementException e)
    {
        return new ResponseEntity<List<Reservations>>(HttpStatus.NOT_FOUND);
    }
}
}

Вот служба бронирования, где я пытался извлечь фиктивные данные (в обеих БД идентификатор равен 1).

ReservationsService.java

@Service
public class ReservationsService {

@Autowired
private ReservationsDao reservationsDao;

@Transactional("txManagerBA")
public List<Reservations> findAllBA() throws NoSuchElementException {
    reservationsDao.setEM(City.BA);
    List<Reservations> reservations = new ArrayList<Reservations>();
    reservations.add(reservationsDao.find(1));
    if(reservations.size() == 0)
    {
        throw new NoSuchElementException();
    }
    return reservations;
}

@Transactional("txManagerKE")
public List<Reservations> findAllKE() throws NoSuchElementException {
    reservationsDao.setEM(City.KE);
    List<Reservations> reservations = new ArrayList<Reservations>();
    reservations.add(reservationsDao.find(1));
    if(reservations.size() == 0)
    {
        throw new NoSuchElementException();
    }
    return reservations;
}
}

А вот суперкласс DAO (конкретный DAO наследуется от этого класса и содержит только супер-конструктор).

BaseDao.java

public abstract class BaseDao<T>{

@PersistenceContext(unitName = "BAEM")
EntityManager emBA;

@PersistenceContext(unitName = "KEEM")
EntityManager emKE;

EntityManager em;

protected final Class<T> type;

protected BaseDao(Class<T> type) {
    this.type = type;
}

public void setEM(City c)
{
    switch(c) {
        case BA: em = emBA; break;
        case KE: em = emKE; break;
    }
}

public T find(Integer id) {
    return em.find(type, id);
}

public List<T> findAll() {
    return em.createQuery("SELECT e FROM " + type.getSimpleName() + " e", type).getResultList();
}
}

Отладка (точка останова, установленная в BaseDAO в функции find ()) показывает, что для извлечения данных используется правильная единица персистентности (когда я полностью перехожу к persistenceUnitInfo.nonJtaDataSource.jdbcUrl, URL-адрес правильный).

Тем не менее, используется только одна из баз данных, независимо от запроса. Я также попытался использовать AbstractRoutingDataSource, но с той же проблемой - база данных будет установлена ​​при первом запросе, и с этого момента будет использоваться только та база данных, безразличная к запросу.

1 Ответ

0 голосов
/ 28 июня 2018

Вот конфигурация, которую мы используем в нашем приложении spring4 с Hikaripool Аннотация @Qualifier будет полезна для различения, когда у вас есть несколько источников данных базы данных, и @Primary для создания источника данных по умолчанию при использовании с @AutoWired, а при использовании другого источника данных используйте аннотацию @Qualifier вместе с @AutoWired

@Bean(destroyMethod = "close")
@Primary
@Qualifier("tspDataSource")
public DataSource dataSource() {

    HikariConfig config = new HikariConfig("/hikari-tsp.properties");
    config.addDataSourceProperty("cachePrepStmts", "true");
    config.addDataSourceProperty("prepStmtCacheSize", "250");
    config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
    final HikariDataSource ds = new HikariDataSource(config);
    return ds;
}

и второй

 @Bean(destroyMethod = "close")
    @Qualifier("fdxDataSource")
    public DataSource fdxDataSource() {
        HikariConfig config = new HikariConfig("/hikari-fdx.properties");
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        final HikariDataSource ds = new HikariDataSource(config);
        return ds;
    }
...