안녕하세요. J4J입니다.
이번 포스팅은 JNDI를 이용한 Tomcat에 Datasource 구성에 대해 적어보는 시간을 가져보려고 합니다.
JNDI란?
JNDI는 Java Naming and Directory Interface의 약자로 디렉터리 서비스에서 제공하는 데이터 및 객체를 참고하여 사용할 수 있도록 도와주는 자바 API입니다.
JNDI를 사용하는 이유는 프로젝트 내부에 DB정보를 담아두는 것이 아니라 Tomcat과 같은 WAS 서버에 DB정보를 담아두고 사용하기 위해서입니다.
보통 개인들이 모여서 하는 프로젝트에서는 DB서버를 하나만 사용하지만 회사에서는 개발 목적, 운영 목적 등으로 구분되어 여러 서버를 사용하는 경우가 있습니다.
이런 경우 목적에 적합한 DB를 실행시키기 위해 매번 dataSource정보를 변경하여 적용하는 것은 매우 귀찮은 작업이고 혹여나 실수가 유발되면 큰 사고가 날 수도 있습니다.
이런 상황에서 JNDI를 이용하면 WAS 서버에 담아둔 DB정보를 사용하여 추가적인 코드 변경 없이 목적에 맞는 DB를 사용할 수 있도록 해줍니다. (DB 개수만큼 WAS서버를 생성)
방법 1: xml 설정
예를 들어 MyBatis와 JPA를 동시에 사용하는 dataSource를 설정하기 위해 다음과 같이 root-context.xml이 작성되어 있다고 해보겠습니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.11.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:component-scan base-package="com.spring.jndi.service"></context:component-scan> <!-- component scan -->
<mybatis-spring:scan base-package="com.spring.jndi.repository"/> <!-- mapper scan -->
<jpa:repositories base-package="com.spring.jndi.repository"></jpa:repositories> <!-- jpaRepository scan -->
<!-- 기존 방식 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/jndi?serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- jpa 설정 -->
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="persistenceUnitName" value="jpa-mysql"></property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"></property>
</bean>
<!-- mybatis 설정 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<!-- transactional 설정 -->
<bean id="myBatisTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<bean id="transactionManager" class="org.springframework.data.transaction.ChainedTransactionManager">
<constructor-arg>
<list> <!-- jpa, mybatis 순서 바뀌면 에러 -->
<ref bean="jpaTransactionManager"/>
<ref bean="myBatisTransactionManager"/>
</list>
</constructor-arg>
</bean>
</beans>
이렇게 설정되어 있을 경우 어떤 WAS서버를 사용하여 실행해도 동일하게 localhost에 있는 DB에만 접근을 하게 됩니다.
WAS 서버마다 다른 DB에 접근하기 위해 위의 코드를 JNDI를 이용하여 변경해보도록 하겠습니다.
1. Tomcat 서버를 생성하여 context.xml에 Resource정보 추가
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jndi/mysql"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/jndi?serverTimezone=UTC"
username="root"
password="root"
maxTotal="100"
maxIdle="30"
maxWaitMillis="10000"/>
...
</Context>
2. root-context.xml 파일 수정
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.11.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:component-scan base-package="com.spring.jndi.service"></context:component-scan> <!-- component scan -->
<mybatis-spring:scan base-package="com.spring.jndi.repository"/> <!-- mapper scan -->
<jpa:repositories base-package="com.spring.jndi.repository"></jpa:repositories> <!-- jpaRepository scan -->
<!-- jndi 방식 -->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jndi/mysql"></property> <!-- java:comp/env/ + Resource name -->
</bean>
<!-- jpa 설정 -->
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="persistenceUnitName" value="jpa-mysql"></property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"></property>
</bean>
<!-- mybatis 설정 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<!-- transactional 설정 -->
<bean id="myBatisTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<bean id="transactionManager" class="org.springframework.data.transaction.ChainedTransactionManager">
<constructor-arg>
<list> <!-- jpa, mybatis 순서 바뀌면 에러 -->
<ref bean="jpaTransactionManager"/>
<ref bean="myBatisTransactionManager"/>
</list>
</constructor-arg>
</bean>
</beans>
위와 같이 다른 것은 건드릴 필요 없이 dataSource에 해당하는 빈만 변경해주면 됩니다.
그리고 프로젝트를 DevelopmentServer로 실행하게 되면 Resource로 등록한 dataSource정보를 가지고 DB에 접근하게 됩니다.
방법 2: Java 설정
위에서 적용한 xml설정을 모두 Java설정으로 변경해보겠습니다.
프로젝트에 DB정보를 저장하는 경우는 다음과 같이 RootContext 클래스 파일이 생성되어 있습니다.
package com.spring.jndi.config;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@ComponentScan(basePackages = {"com.spring.jndi.service"})
@MapperScan(basePackages = {"com.spring.jndi.repository"})
@EnableJpaRepositories(basePackages = {"com.spring.jndi.repository"})
public class RootContext {
// 기존 방식
@Bean
public BasicDataSource dataSource() {
BasicDataSource datasource = new BasicDataSource();
datasource.setDriverClassName("com.mysql.cj.jdbc.Driver");
datasource.setUrl("jdbc:mysql://localhost:3306/jndi?serverTimezone=UTC");
datasource.setUsername("root");
datasource.setPassword("root");
return datasource;
}
// jpa 설정
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource());
entityManagerFactory.setPersistenceUnitName("jpa-mysql");
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
return entityManagerFactory;
}
// mybatis 설정
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource());
sqlSessionFactory.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml"));
return sqlSessionFactory;
}
@Bean
public SqlSessionTemplate sqlSession(SqlSessionFactoryBean sqlsessionFactory) throws Exception {
return new SqlSessionTemplate(sqlsessionFactory.getObject());
}
// transactional 설정
@Bean
public PlatformTransactionManager transactionManager() throws Exception {
DataSourceTransactionManager myBatisTransactionManager = new DataSourceTransactionManager();
myBatisTransactionManager.setDataSource(dataSource());
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
ChainedTransactionManager transactionManager = new ChainedTransactionManager(jpaTransactionManager, myBatisTransactionManager);
return transactionManager;
}
}
해당 코드도 JNDI를 이용하여 WAS 서버에 DB정보를 저장하기 위해서는 다음과 같이 설정하면 됩니다.
1. Tomcat 서버를 생성하여 context.xml에 Resource정보 추가
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jndi/mysql"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/jndi?serverTimezone=UTC"
username="root"
password="root"
maxTotal="100"
maxIdle="30"
maxWaitMillis="10000"/>
...
</Context>
2. RootContext 클래스 수정
package com.spring.jndi.config;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@ComponentScan(basePackages = {"com.spring.jndi.service"})
@MapperScan(basePackages = {"com.spring.jndi.repository"})
@EnableJpaRepositories(basePackages = {"com.spring.jndi.repository"})
public class RootContext {
// jndi 방식
@Bean
public DataSource dataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource("jndi/mysql");
}
// jpa 설정
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource());
entityManagerFactory.setPersistenceUnitName("jpa-mysql");
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
return entityManagerFactory;
}
// mybatis 설정
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource());
sqlSessionFactory.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml"));
return sqlSessionFactory;
}
@Bean
public SqlSessionTemplate sqlSession(SqlSessionFactoryBean sqlsessionFactory) throws Exception {
return new SqlSessionTemplate(sqlsessionFactory.getObject());
}
// transactional 설정
@Bean
public PlatformTransactionManager transactionManager() throws Exception {
DataSourceTransactionManager myBatisTransactionManager = new DataSourceTransactionManager();
myBatisTransactionManager.setDataSource(dataSource());
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
ChainedTransactionManager transactionManager = new ChainedTransactionManager(jpaTransactionManager, myBatisTransactionManager);
return transactionManager;
}
}
자바로 설정하는 경우에도 xml과 동일하게 dataSource설정 부분만 변경해주면 됩니다.
참조
Tomcat JNDI Datasource 설정하여 Spring 연동하기
JNDI를 사용하여 Spring Boot에서 다중 데이터 소스 구성
이상으로 JNDI를 이용한 Tomcat에 Datasource 구성에 대해 간단하게 알아보는 시간이었습니다.
읽어주셔서 감사합니다.
'Spring > Spring' 카테고리의 다른 글
[Spring] JWT란? (0) | 2021.04.07 |
---|---|
[Spring] JUnit Test에 JNDI 적용 (0) | 2021.04.05 |
[Spring] MyBatis ↔ Repository를 연결하는 다양한 방법들 (1) | 2021.03.16 |
[Spring] 파일 업로드 - MultipartRequest(With. React) (0) | 2021.03.05 |
[Spring] 파일 업로드 - MultipartFile(With. React) (0) | 2021.03.04 |
댓글