Как подключиться к двум базам данных с помощью приложения Spring Boot?

Мне нужно подключиться к двум базам данных оракула из весеннего загрузочного приложения. Ниже то, что я сделал до сих пор:

приложение.свойства

 #############################################
## Database Configuration
#############################################
# HikariCP settings
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.maximumPoolSize=20
spring.datasource.hikari.idleTimeout=30000
spring.datasource.hikari.maxLifetime=2000000
spring.datasource.hikari.connectionTimeout=30000
spring.datasource.hikari.poolName=HikariPoolOrcl
# JPA settings
spring.jpa.database=default
spring.datasource.dialect=org.hibernate.dialect.OracleDialect
spring.jpa.hibernate.use-new-id-generator-mappings=false
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver.class=oracle.jdbc.driver.OracleDriver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.OracleDialect
spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
############################################
# OracleDB connection settings
###########################################
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.jdbc-url=jdbc:oracle:thin:@localhost:1521:orcl
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:orcl
spring.datasource.username=userdev
spring.datasource.password=pass123
spring.datasource.pool-size=30
############################################
# OracleDB connection settings FOR IL DB
###########################################
spring.il.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.il.datasource.jdbc-url=jdbc:oracle:thin:@//192.126.98.77:1521/apimdbuat
spring.il.datasource.username=userdev
spring.il.datasource.password=Ahjhj20
spring.il.datasource.pool-size=30 

модель пользователя в первой базе данных

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "User")
@Table(name = "users")
public class User {
    @Id
    @SequenceGenerator(name = "user_sequence", sequenceName = "user_sequence", allocationSize = 1

    )

    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence"

    )
    @Column(nullable = false, updatable = false)
    private Long id;

    @Column(nullable = false, length = 64)
    @NotBlank(message = "Firstname is required")
    private String firstname;
    @NotBlank(message = "Lastname is required")
    @Column(nullable = false, length = 64)
    private String lastname;

    @NotBlank(message = "Username is required")
    @Column(nullable = false, length = 64, unique = true)
    private String username;

    @Column(nullable = false, length = 64, unique = true)
    @Email
    @NotEmpty(message = "Email is required")
    private String email;

    @NotBlank(message = "Password is required")
    @Column(nullable = false, length = 64)
    @JsonIgnore
    private String password;

    private String profileImgUrl;
    private Date lastLoginDate;
    
    private Date joinDate;
    @JsonProperty("isActive")
    private boolean isActive;
    @JsonProperty("isNotLocked")
    private boolean isNotLocked;


    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")

    )
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Set<Role> roles = new HashSet<>();

а вот модель для второй базы данных

@AllArgsConstructor
@NoArgsConstructor

@Entity
@Table(
        name = "app_config"
)
public class AppConfig {

    @Id
    @Column(
            name = "ID"
    )
    @GeneratedValue(
            strategy = GenerationType.AUTO
    )
    private Long id;
    private String appCode;

    private String appName;
    private String version;

}

Репозитории:

public interface UserRepository extends JpaRepository<User, Long> {

    boolean existsByEmail(String email);

    boolean existsByUsername(String username);


} 
public interface AppConfigRepository extends JpaRepository<AppConfig, Long> {
}

и, наконец, классы конфигурации

@Configuration(proxyBeanMethods = false)
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "primaryEntityManagerFactory",
        transactionManagerRef = "primaryTransactionManager",
        basePackages = {"com.app.models",
                "com.app.repositories"}
)
public class PrimaryDatabaseConfig {
    @Bean(name = "primaryDataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public HikariDataSource dataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Primary
    @Bean(name = "primaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder,
                                                                              @Qualifier("primaryDataSource") DataSource primaryDataSource) {
        return builder
                .dataSource(primaryDataSource)
                .packages("com.app.repositories", "com.app.models")
                .build();
    }

    @Bean(name = "primaryTransactionManager")
    public PlatformTransactionManager primaryTransactionManager(
            @Qualifier("primaryEntityManagerFactory") EntityManagerFactory primaryEntityManagerFactory) {
        return new JpaTransactionManager(primaryEntityManagerFactory);
    }
}

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "secondaryEntityManagerFactory",
        transactionManagerRef = "secondaryTransactionManager",
        basePackages = {"com.app.il_models",
                "com.app.il_repositories"}
)
public class SecondaryDatabaseConfig {
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.il.datasource")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder,
                                                                                @Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
        return builder
                .dataSource(secondaryDataSource)
                .packages("com.app.il_models",
                        "com.app.il_repositories")
                .build();
    }

    @Bean(name = "secondaryTransactionManager")
    public PlatformTransactionManager secondaryTransactionManager(
            @Qualifier("secondaryEntityManagerFactory") EntityManagerFactory secondaryEntityManagerFactory) {
        return new JpaTransactionManager(secondaryEntityManagerFactory);
    }
}


В первый раз я получил исключения, связанные с URL-адресом jdbc, и после его исправления приложение запустилось нормально, но когда я вызываю API входа в систему, который принимает имя пользователя/пароль, я получаю следующую ошибку.

SQL Error: 904, SQLState: 42000
ORA-00904: "USER0_"."PROFILEIMGURL": invalid identifier
Unauthorized error: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet


{
    "code": "401",
    "message": "could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet"
}

PS: Все работало нормально, когда это была одна база данных.

Спасибо.

Проверьте свои файлы lgo, так как в них будет stactrace с дополнительной информацией. Слишком мало, чтобы продолжать только с фрагментом полной трассировки стека.

M. Deinum 10.01.2023 14:23
profileImgUrl кажется, не существует в таблице. Но да, полная трассировка стека была бы полезна
XtremeBaumer 10.01.2023 14:25

@M.Deinum, XtremeBaumer, profileImgUrl существует, и он отлично работает, когда я держу только одну базу данных. В логах нет ошибок, кроме упомянутых в посте

Mohamde amine Karek 10.01.2023 15:14

Я сомневаюсь, что больше нет, в вашем файле журнала должна быть полная трассировка стека. Ваш файл журнала - это не то, что вы видите в браузере, а вывод Spring Boot либо в консоли, либо в файле журнала.

M. Deinum 10.01.2023 19:38
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
4
80
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам нужно добавить два разных источника данных в файл application.properties. так :

 spring.datasource.url= jdbc:mysql://<host1>:<port>/<database1>
 spring.datasource.username= <username1>
 spring.datasource.password= <password1>
 
 spring.second-datasource.url= jdbc:mysql://<host2>:<port>/<database2>
 spring.second-datasource.username= <username2>
 spring.second-datasource.password= <password2>

Затем создайте два отдельных EntityManagerFactoryBean, как показано ниже.

    @Primary
    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityScanPackage entitiesScanPackage,
            DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource);
        emf.setPackagesToScan(entitiesScanPackage.getPackageName());
        emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        emf.setJpaProperties(jpaProperties());
        return emf;
    }
    @Bean(name = "secondEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean secondEntityManagerFactory(
            EntitiesScanPackage entitiesScanPackage,
            @Qualifier("secondDataSource") DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource);
        emf.setPackagesToScan(entitiesScanPackage.getPackageName());
        emf.setJpaVendorAdapter(new HibernateJpaVendor

Спасибо.

Спасибо. Это примерно то же самое, что я сделал, и все еще не работает!

Mohamde amine Karek 15.01.2023 09:30
Ответ принят как подходящий

По некоторым причинам после одновременного подключения к двум базам данных имена атрибутов моделей весенней загрузки не совпадают с именами столбцов в базе данных, хотя они отлично работают, когда это была только одна база данных. Чтобы решить эту проблему, мне пришлось аннотировать все атрибуты с помощью @Column и указать имена полей так, как они есть в базе данных, я также принудительно использовал имена таблиц с помощью @Table.

@Column(name = "JOIN_DATE")
    private Date joinDate;

@Table(name = "USERS")

Другие вопросы по теме