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정보 추가
context.xml
<?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정보 추가
context.xml
<?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설정 부분만 변경해주면 됩니다.
참조
'IT와' 카테고리의 다른 글
온-프레미스 애플리케이션을 클라우드로 이전할 때 전략은 (0) | 2022.02.28 |
---|---|
[기본 DML 튜닝] 1. DML 성능에 영향을 미치는 요소 (0) | 2022.02.23 |
다중 서버 환경에서 세션을 관리하는 방법 (0) | 2021.12.16 |
erwin을 이용한 Forward Engineering 생성시 comment 추가하기 (0) | 2021.10.28 |
[ERWIN] 논리 다이어그램 Logical 한글명 으로 나타내기 (0) | 2021.10.28 |
댓글