[Spring Boot] JavaConfig로 Datasource 설정하기

2020. 4. 5. 22:37Spring/Basic

300x250
반응형

Spring Boot에서는 기본적인 구성 세팅을 프로퍼티에서 설정할 수 있습니다.

datasource 나 jpa 설정도 프로퍼티 설정만으로 끝낼 수 있어, 프로젝트 설정을 매우 간소화 시킬 수 있습니다.

기존 Spring Boot Tutorial 과정에서 demo 프로젝트를 진행할 때에도 별도의 JavaConfig 또는 xml 설정 없이 db 설정을 했습니다.

프로퍼티 설정만으로 프로젝트 설정을 하기 위해서는, 미리 약속된 프로퍼티 키에 값을 설정해야 합니다.

  1. 프로퍼티 설정으로 dataSource 빈 자동 생성
  2. JavaConfig에 application.yml 설정값 주입하여 dataSource 빈 직접 생성
    2-1) 프로퍼티 수정
    2-2) JavaConfig 설정

1. 프로퍼티 설정으로 dataSource 빈 자동 생성

아래의 코드는 기존에 demo 웹 애플리케이션에서 설정했던 application.yml 설정 정보입니다.
JavaConfig 클래스로 dataSource 빈 생성을 하지 않아도 예약된 키에 프로퍼티 설정을 하면 자동으로 jpa 설정과 dataSource 빈을 생성해줍니다.

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: "jdbc:mysql://localhost/jiniworld_test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC&tinyInt1isBit=false"
    username: test
    password: test
    hikari:
      auto-commit: false
      connection-test-query: SELECT 1
      minimum-idle: 10
      maximum-pool-size: 50
      transaction-isolation: TRANSACTION_READ_UNCOMMITTED
      pool-name: pool-jiniworld
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    properties:
      hibernate:
        format_sql: true
        implicit_naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
        physical_naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
        hbm2ddl.auto: update
    open-in-view: false
    show-sql: true

DB 연결에 관한 예약된 key는 spring.datasource 이며,
driver-class-name, url, username, password 속성을 반드시 설정해야 합니다.

DBCP Pool은 tomcat, dbcp2, hikari가 있으며, 사용할 DBCP Pool 를 하나 설정해주면 됩니다.

jpa 설정에 관한 예약된 key는 spring.jpa 입니다.
jpa 설정은 설정하지 않을 시, 기본 값으로 대체되므로 필수적으로 설정할 필요는 없으나, 테이블명 생성 규칙, 컬럼명 생성규칙, ddl 생성 방식, sql beautify 등 여러 편의 설정을 지정할 수 있습니다.

이름 생성 규칙은 spring.jpa.hibernate.naming.implicit-strategy, spring.jpa.hibernate.naming.physical-strategy에 설정할 수도 있으나, @ConfigurationProperties를 통해 프로퍼티를 로딩할 때 불러올 수 없기 때문에 위와 같이 spring.jpa.properties.hibernate 내에 설정하는 것을 권장합니다.


2. JavaConfig에 application.yml 설정값 주입하여 dataSource 빈 직접 생성

프로퍼티의 예약된 key에 datasource 설정을 하고 웹 애플리케이션을 실행하면 dataSource 빈이 자동으로 생성됩니다.

우리는 이 dataSource빈을 Spring Boot에서 JPA와 MyBatis 동시에 사용하기 포스팅에서 @Qualifier 애너테이션을 이용하여 MyBatis에서도 이용할 수 있도록 JavaConfig 설정을 했었습니다.

웹 애플리케이션 상에서 이용할 database 정보가 하나라면 프로퍼티 자동 설정을 이용하는 것이 편리해서 좋습니다. 하지만 여러개의 database를 연결해야 할 경우에는 JavaConfig 또는 xml을 통한 datasource 설정이 필요할 것입니다.

2-1) 프로퍼티 수정

프로퍼티에 설정한 값을 JavaConfig로 읽어들이기 이전에, 프로퍼티 키의 일부분을 수정해야 합니다.

spring:
  datasource:
    hikari:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: "jdbc:mysql://localhost/jiniworld_test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC&tinyInt1isBit=false"
      username: test
      password: test
      auto-commit: false
      connection-test-query: SELECT 1
      minimum-idle: 10
      maximum-pool-size: 50
      transaction-isolation: TRANSACTION_READ_UNCOMMITTED
      pool-name: pool-jiniworld

JavaConfig에서 @ConfigurationProperties 로 dataSource 정보를 읽어들일 때, 트리형태로 datasource 하위 키인 hikari를 읽어들이지 않고, 같은 키레벨에 설정된 곳에서 database url, driver-class-name, username, password 및 기타 정보를 읽어들입니다.

따라서 spring.datasource에 설정되어있었던 driver-class-name, url, username, password를 hikari 내부로 옮깁니다.

hikariCP 는 database url을 jdbc-url이라는 예약된 키로 받기 때문에 url을 jdbc-url로 변경합니다.

Caused by: java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.
	at com.zaxxer.hikari.HikariConfig.validate(HikariConfig.java:951)
	at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:109)

만일 jdbc-url로 변경하지 않으면 위와 같은 에러를 출력합니다.

2-2) JavaConfig 설정

@Configuration
@EnableJpaRepositories(basePackages = "me.jiniworld.demo.repositories", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
@MapperScan(basePackages = {"me.jiniworld.demo.mapper"}, sqlSessionFactoryRef="sqlSessionFactory", sqlSessionTemplateRef="sqlSessionTemplate")
public class DatasourceConfig {

    @Primary
    @Bean(name="dataSource")
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "jpaProperties")
    @ConfigurationProperties(prefix = "spring.jpa")
    public JpaProperties jpaProperties() {
        return new JpaProperties();
    }

    @Primary
    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
            @Qualifier("dataSource") DataSource primaryDataSource,
            @Qualifier("jpaProperties") JpaProperties jpaProperties) {
        return builder
                .dataSource(primaryDataSource)
                .properties(jpaProperties.getProperties())
                .packages("me.jiniworld.demo.models.entities")
                .persistenceUnit("default")
                .build();
    }

    @Primary
    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager(
            @Qualifier("entityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager(entityManagerFactory.getObject());
        transactionManager.setNestedTransactionAllowed(true);
        return transactionManager;
    }

    @Bean(name="sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage("me.jiniworld.demo.models.entities");
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*-mapper.xml"));
        return sqlSessionFactoryBean.getObject();
    }

    @Bean(name="sqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

ln 2 : multiple datasource 설정 시, 연동시킬 repositories 패키지와 entityManagerFactoryRef, transactionManagerRef를 설정합니다.
ln 6~11 : spring.datasource.hikari에 설정한 구성 정보를 가져와 dataSource 빈을 구성합니다.
ln 13~18 : spring.jpa에 설정한 jpa 관련 설정을 읽어들여 jpa 설정을 합니다.
ln 20~31 : 위에 설정했던 dataSource, jpaProperties 설정을 가져와 엔티티매니저를 만드는 entityManagerFactory 빈을 구성합니다.
ln 33~40 : 위에 설정한 entityManagerFactory를 이용하여 트랜젝션 처리에 이용되는 transactionManager 빈을 만듭니다.


위에서 생성했던 dataSource, jpaProperties, entityManagerFactory, transactionManager 빈들에 @Primary 설정이 되어있습니다.
나중에 Multiple Datasource 설정을 했을 때, 빈 이름을 별도로 설정하지 않을 경우에는 @Primary 설정된 빈을 자동으로 autowired 합니다.

@Transactional
public User save(UserValue value) {
  User user = userRepository.save(User.builder()
      .type(value.getType())
      .email(value.getEmail())
      .birthDate(value.getBirthDate())
      .name(value.getName())
      .password(passwordEncoder.encode(value.getPassword()))
      .phoneNumber(value.getPhoneNumber())
      .sex(value.getSex()).build());
  return user;
}

예를 들어, 위와같은 코드에서는 @Primary 설정이 되어있었던 TransactionManager 인 @Bean(name = "transactionManager")빈을 이용하여 트랜젝션 처리를 합니다.

@Primary 설정된 빈은 위와 같이 name을 생략하고 사용 가능합니다.

연결해야할 datasource 가 여러개일 경우, 위의 과정을 따라 프로퍼티를 별도로 설정하고,
JavaConfig 파일을 설정하면 됩니다.


+++

  • Spring boot HikariCP 설정
  • Spring boot 2.0 HikariCP configuration
  • Spring boot HikariCP multiple DataSource
  • Spring boot HikariCP mybatis
300x250
반응형