반응형

이 포스트는 Spring boot + Mybatis 를 전제하고 있습니다.


스프링부트를 이용하면서 다른 종류의 DB를 동시에 사용해야하는 경우가 종종 있습니다.


그런데 이번엔 JNDI 환경까지 겹치면서 1번 DB는 Tomcat (jdbc pool) Datasource 를 사용하고 2번 DB는 JNDI를 이용해야 하는 이런 상황이 되었습니다.


여기에 멈추지 않고 Local 개발환경에서 JNDI 를 사용하기는 또 부담스럽고 귀찮으니 Tomcat embedded JNDI 도 사용해봤습니다.


보너스로 @Profile 을 이용해서 마음대로 각 DB에서 설정에 따라 자유롭게 Datasource 를 골라 쓸 수 있게 해보았습니다.



 
import javax.naming.NamingException;
import javax.sql.DataSource;
 
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
 
@Configuration
@MapperScan(value = Constants.PACKAGE_BASE + ".dao", sqlSessionFactoryRef = "coreSqlSessionFactory")
public class CoreRepositoryConfig {
 
  @Bean
  @ConfigurationProperties(prefix = "spring.datasource.primary")
  public JndiPropertyHolder getJndiPropertyHolder() {
    return new JndiPropertyHolder();
  }
 
  @Primary
  @Bean("coreDatasource")
  @Profile("jndi")
  public DataSource jndiDatasource() throws IllegalArgumentException, NamingException {
    String jndiName = getJndiPropertyHolder().getJndiName();
    return (DataSource) new JndiDataSourceLookup().getDataSource(jndiName);
  }
 
  @Primary
  @Bean("coreDatasource")
  @Profile("!jndi")
  @ConfigurationProperties(prefix = "primary.spring.datasource")
  public org.apache.tomcat.jdbc.pool.DataSource coreDatasource() {
    return (org.apache.tomcat.jdbc.pool.DataSource) DataSourceBuilder.create()
        .type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
  }
 
  @Bean(name = "coreSqlSessionFactory")
  @Primary
  public SqlSessionFactory coreSqlSessionFactory(@Qualifier("coreDatasource") DataSource coreDatasource,
      ApplicationContext applicationContext) throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(coreDatasource);
    sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:mybatis/config.xml"));
    return sqlSessionFactoryBean.getObject();
  }
 
  @Bean(name = "coreSqlSessionTemplate")
  @Primary
  public SqlSessionTemplate coreSqlSessionTemplate(SqlSessionFactory coreSqlSessionFactory) throws Exception {
    return new SqlSessionTemplate(coreSqlSessionFactory);
  }
 
  class JndiPropertyHolder {
    private String jndiName;
 
    public String getJndiName() {
      return jndiName;
    }
 
    public void setJndiName(String jndiName) {
      this.jndiName = jndiName;
    }
  }
}


<CoreRepositoryConfig.java>


1번 DB의 Java Config 파일이라고 보시면 되겠습니다.


import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan(value = Constants.PACKAGE_BASE + ".secondary.dao", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryRepositoryConfig {
  @Bean("secondaryDatasource")
  @ConfigurationProperties(prefix = "secondary.spring.datasource")
  public DataSource secondaryDatasource() {
    return (DataSource) DataSourceBuilder.create().type(DataSource.class).build();
  }
  @Bean(name = "secondarySqlSessionFactory")
  public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDatasource") DataSource secondaryDatasource,
      ApplicationContext applicationContext) throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(secondaryDatasource);
    sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:secondary/mybatis-config.xml"));
    return sqlSessionFactoryBean.getObject();
  }
  @Bean(name = "secondarySqlSessionTemplate")
  public SqlSessionTemplate secondarySqlSessionTemplate(SqlSessionFactory secondarySqlSessionFactory) throws Exception {
    return new SqlSessionTemplate(secondarySqlSessionFactory);
  }
}


<SecondaryRepositoryConfig.java>


SecondaryRepositoryConfig.java 는 2번 DB의 Java Config 입니다.


spring.profiles.active=local,jndi
 
spring.datasource.primary.jndi-name=MYJNDI
 
secondary.spring.datasource.driverClassName=oracle.jdbc.driver.OracleDriver
secondary.spring.datasource.url=jdbc:oracle:thin:@IP:PORT:DB
secondary.spring.datasource.username=아뒤
secondary.spring.datasource.password=패쓰워드
secondary.spring.datasource.max-active=1
secondary.spring.datasource.max-idle=1
secondary.spring.datasource.min-idle=1
secondary.spring.datasource.max-wait=-1
secondary.spring.datasource.initial-size=1
secondary.spring.datasource.test-on-borrow=true
secondary.spring.datasource.test-while-idle=true
secondary.spring.datasource.validation-query=select 'SECONDARY' from dual
secondary.spring.datasource.time-between-eviction-runs-millis=300000


<application.properties>


이 녀석은 Spring Boot 의 설정파일인 application.properties 입니다.



CoreRepositoryConfig.java 에서만 JNDI 혹은 Tomcat Datasouce를 쓸건지 선택할 수 있도록 되어있는데 이부분은 application.properties 의 설정중에


spring.profiles.active 에 jndi가 포함되어 있느냐 없느냐를 @Profile("jndi") or @Profile("!jndi") 로 구분해서 선택적으로 Datasource를 제공하고 있습니다.


JNDI 의 이름은 @ConfigurationProperties(prefix = "spring.datasource.primary") 를 이용해  


application.properties 의 spring.datasource.primary.jndi-name=MYJNDI 에서 획득하고 있습니다.


만약 1번 DB에서 JNDI 를 사용하지 않으려면 spring.profiles.active에서 jndi 를 지우고 2번 DB 설정처럼 primary.spring.datasource 설정들을 지정하면 됩니다.


그리고 2번 DB의 사용은 @ConfigurationProperties(prefix = "secondary.spring.datasource") 을 이용해 application.properties 에서 db설정 정보를 가져와 


Tomcat datasource를 사용하고 있습니다.


마지막으로 Local 환경에서도 JNDI 로 테스트하기 위해 Embedded Tomcat JNDI를 이용해 보겠습니다.



 
import javax.sql.DataSource;
 
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.descriptor.web.ContextResource;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
 
@Configuration
@Profile({ "local""dev" })
public class TomcatEmbdded {
  @Bean
  public TomcatEmbeddedServletContainerFactory tomcatFactory() {
    return new TomcatEmbeddedServletContainerFactory() {
 
      @Override
      protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
        tomcat.enableNaming();
        return super.getTomcatEmbeddedServletContainer(tomcat);
      }
 
      @Override
      protected void postProcessContext(Context context) {
        ContextResource resource = new ContextResource();
        resource.setName("JNDI이름");
        resource.setType(DataSource.class.getName());
        resource.setProperty("driverClassName""oracle.jdbc.driver.OracleDriver");
        resource.setProperty("url""jdbc:oracle:thin:@IP:PORT:DB");
        resource.setProperty("username""아뒤");
        resource.setProperty("password""패쓔워드");
        resource.setProperty("factory""org.apache.tomcat.jdbc.pool.DataSourceFactory");
        resource.setProperty("type""javax.sql.DataSource");
 
        context.getNamingResources().addResource(resource);
      }
    };
  }
}


<TomcatEmbdded.java - 오타가 ..>


@Profile({ "local", "dev" }) 를 통해서 spring.profiles.active 에 local 이나 dev 가 포함되어 있다면 Embedded Tomcat JNDI 를 사용할 수 있게 됩니다.





maven dependency

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>


@ConfigurationProperties 를 사용하기 위해 필요합니다.


org.apache.tomcat.jdbc.pool.DataSourceFactory 를 사용하기 위해선 tomcat-jdbc 가 필요한데 

저같은 경우 mybatis-spring-boot-starter (mybatis-spring-boot-starter > spring-boot-starter-jdbc > tomcat-jdbc ) 에 포함된 걸 이용하고 있습니다.

반응형

+ Recent posts