Zookeeper - http://zookeeper.apache.org
분산 환경에서 서버간의 상호 조정이 필요한 다양한 서비스를 제공하는 시스템으로 크게 다음과 같은 네가지 역할을 수행한다. 첫째, 하나의 서버에만 서비스가 집중되지 않게 서비스를 알맞게 분산해 동시에 처리하게 해준다. 둘째 하나의 서버에서 처리한 결과를 다른 서버와도 동기화해서 데이터의 안정성을 보장한다. 셋째 운영서버에 문제가 발생해서 서비스를 제공할 수 없을 경우, 다른 대기 중인 서버를 운영 서버로 바꿔서 서비스가 중지 없이 제공되게 한다. 넷째 분산환경을 구성하는 서버의 환경설정을 통합적으로 관리한다.
YARN - http://hadoop.apache.org
얀(YARN)은 데이터 처리 작업을 실행하기 위한 클러스터 자원(CPU,메모리,디스크 등)과 스케줄링을 위한 프레임워크이다. 기존 하둡의 데이터 처리 프레임워크인 맵리듀스의 단점을 극복하기 위해 시작된 프로젝트이며, 하둡2.0부터 사용할 수 있다. 맵리듀스, 하이브, 임팔라, 타조, 스파크 등 다양한 어플리케이션들은 얀에서 리소스를 할당받아서 작업을 실행하게 된다.
Mesos - http://mesos.apache.org
메소스(Mesos)는 클라우드 인프라스트럭처 및 컴퓨팅 엔진의 다양한 자원(CPU, 메모리, 디스크)을 통합적으로 고나리할 수 있도록 만든 자원 관리 프로젝트이다. 메소스는 클러스터링 환경에서 동적으로 자원을 할당하고 격리하는 메커니즘을 제공하며, 이를 통해 분산 환경에서 작업 실행을 최적화할 수 있다. 1만대 이상의 노드에도 대응 가능하며, 웹 기반 UI, 자바, C++, 파이썬API를 제공한다. 하둡, 스파크, 스톰, 엘라스틱 서치(Elastic Search), 카산드라(Cassandra), 젠킨스(Jenkins) 등 다양한 어플리케이션을 메소스에서 실행 할 수 있다.
HBase - http://hbase.apache.org
H베이스는 HDFS기반의 칼럼 기반 데이터베이스 이다. 구글의 빅테이블 논문을 기반으로 개발 되었다. 실시간 랜덤 조회 및 업데이트가 가능하며, 각 프로세스는 개인의 데이터를 비동기적으로 업데이트 할 수 있다. 단 맵리듀스는 일괄 처리 방식으로 수행된다. 트위터, 야후, 어도비 같은 해외 업체에서 사용하고 있으며, 국내에서는 2012년 네이버가 모바일 메신저인 라인에 H베이스를 적용한 시스템 아키텍처를 발표했다.
Kudu - http://getkudu.io
쿠두는 컬럼 기반의 스토리지로서, 특정 컬럼에 대한 데이터 읽기를 고속화할 수 있다. 물론 기존에도 HDFS dptjeh 파케이(Parquet), RC, ORC와 같은 파일 포맷을 사용하면 컬럼 기반으로 데이터를 저장할 수 있지만 HDFS 자체가 온라인 데이터 처리에 적합하지 않다는 약점이 있다. 그리고 HDFS기반으로 온라인 처리가 가능한 H베이스의 경우 데이터 분석 처리가 느라다는 단점이 잇다. 쿠두는 이러한 문제점을 보완해서 개발한 컬럼 기반 스토리지이며, 데이터의 발생부터 분석까지의 시간을 단축할 수 있다.
Chukwa - http://chukwa.apache.org
척와는 분산 환경에서 생성되는 데이터를 HDFS에 안정적으로 자장하는 플랫폼이다. 분산된 각 서버에서 에이전트를 실행하고, 콜렉터가 에이전트로부터 데이터를 받아 HDFS에 저장합니다. 콜렉터는 100개의 에이전트당 하나씩 구동되며, 데이터 중복 제거 등의 작업은 맵리듀스로 처리한다.
Flume - http://flume.apache.org
플럼은 척와처럼 분산된 서버에 에이전트가 설치되고, 에이전트로부터 데이터를 전달받는 콜랙터로 구성된다. 차이점은 전체 데이터의 흐름을 관리하는 마스터 서버가 있어서 데이터를 어디서 수집하고, 어떤 방식으로 전송하고, 어디에 저장할지를 동적으로 변경할 수 있다.
Scribe - http://github.com/facebook/scribe
페이스북에서 개발한 데이터 수집 플랫폼이며, Chukwa와는 다르게 데이터를 중앙 집중 서버로 전송하는 방식이다. 최종 데이터는 HDFS 외에 다양한 저장소를 활용할 수 있으며, 설치와 구성이 쉽게 다양한 프로그램 언어를 지원한다. HDFS에 저장할려면 JNI(Java Native Interface)를 이용해야한다.
Sqoop - http://sqoop.apache.org
스쿱은 대용량 데이터 전송 솔루션이며, Sqoop은 HDFS, RDBMS, DW, NoSQL 등 다양한 저장소에 대용량 데이터를 신속하게 전송하는 방법을 제공한다. 오라클, MS-SQL, DB2 등과 같은 상용 RDBMS와 MySQL, PostgreSQL과 같은 오픈 소스 RDBMS등을 지원한다.
Kafak - http://kafka.apache.org
카프카는 데이터 스트림을 실시간으로 관리하기 위한 분산 메세징 시스템이다. 2011년 링크드인에서 자사의 대용량 이벤트처리를 위해 개발됐다. 발행구독모델로 구성되어 있으며, 데이터 손실을 막기 위하여 디스크에 데이터를 저장한다. 파티셔닝을 지원하기 때문에 다수의 카프카 서버에서 메시지를 분산 처리할 수 있으며, 시스템 안전성을 위하여 로드밸런싱과 내고장성(Fault Tolerant)를 보장한다. 다수의 글로벌 기업들이 카프카를 사용하고 있으며, 그중 링크드인은 하루에 1조1천억건 이상의 메시지를 카프카에서 처리하고 있다.
Pig - http://pig.apache.org
복잡한 맵리듀스 프로그래밍을 대체할 피그라틴(Pig Latin)이라는 자체 언어를 제공한다. 맵리듀스API를 매우 단순화한 형태이고 SQL과 유사한 형태로 설계되엇다. SQL과 유사하기만 할 뿐, 기존SQL 지식을 활용하기가 어려운 편이다.
Mahout - http://mahout.apache.org
머하웃은 하둡기반으로 데이터 마이닝 알고리즘을 구현한 오픈소스프로젝트이다. 현재 분류(classification), 클러스터링(clustering), 추천 및 협업 필터링(Recommenders/collaborative filtering), Pattern Mining, 회귀분석(Regression), 차원 리덕션(Dimension reduction), 진화 알고리즘(Evolutionary Algorithms) 등 주요 알고리즘을 지원한다. 머하웃을 그대로 사용할 수도 잇지만 각 비즈니스 환경에 맞게 최적화해서 사용하는 경우가 많다.
Spark - http://spark.apache.org
스파크는 인메모리 기반의 범용 데이터 처리 플랫폼이다. 배치 처리, 머신러닝, SQL, 질의 처리, 스트리밍 데이터 처리, 그래프 라이브러리 처리와 같은 다양한 작업을 수용할 수 있도록 설계되었다.
Impala - http://impala.io
임팔라는 클라우데라에서 개발한 하둡기반의 분산 쿼리 엔진이다. 맵리듀스를 사용하지 않고, C++로 개발한 인메모리 엔진을 사용해 빠른 성능을 보여준다. 임팔라는 데이터 조회를 위한 인터페이스로 HiveQL을 사용하며, 수초 내에 SQL 질의 결과를 확인할 수 있다.
Presto - https://prestodb.io
프로스토는 페이스북이 개발한 대화형 질의를 처리하기 위한 분산 쿼리 엔진이다. 메모리 기반으로 데이터를 처리하며, 다양한 데이터 저장소에 데이터를 SQL로 처리할 수 있다. 특정 질의의 경우 하이브 대비 10배 정도 빠른 성능을 보여준다.
Hive - http://hive.apache.org
하이브는 하둡기반의 데이터웨어하우징용 솔루션이다. 페이스북에서 개발했으며, 오픈소스로 공개되며 주목박은 기술이다. SQL과 매우 유사한 HiveQL이라는 쿼리 언어를 제공한다. 그래서 자바를 모르는 데이터 분석가들도 쉡게 하둡 데이터를 분석할 수 있게 도와준다. HiveQL은 내부적으로 맵리듀스가 잡으로 변환되어 실행된다.
Tajo - http://tajo.apache.org
타조는 고려대학교 박사과정 학생들이 주도해서 개발한 하둡 기반의 데이터 웨어하우스 시스템이다. 맵리듀스 엔진이 아닌 자체 분산 처리 엔진을 사용하며, HiveQL을 사용하는 다른 시스템과는 다르게 표준 SQL을 지원하는 것이 특징이다. HDFS, AWS, S3, H베이스, DBMS 등에 저장된 데이터 표준 SQL로 조회할 수 있고, 이기종 저장소 간의 데이터 조인 처리도 가능하다. 질의 유형에 따라 하이브나 스파크보다 1.5배 ~ 10배 빠른 성능을 보여준다.
Oozie - http://oozie.apache.org
우지는 하둡 작업을 관리하는 워크플로우 및 코디네이터 시스템 이다. 자바 서블릿 컨테이너에서 실행되는 자바 웹 어플리케이션 서버이며, 맵리듀스 작업이나 피그 작업 같은 특화된 액션으로 구성된 워크플로우를 제어한다.
Airflow - http://nerds.airbnb.com/airflow
에어플로우는 에어비앤비에서 개발한 워크플로우 플랫폼이다. 데이터 흐름의 시각화, 스케줄링, 모니터링이 가능하며, 하이브, 프레스토, DBMS 엔진과 결합해서 사용할 수 있다.
Azkaban - https://azkaban.github.io
아즈카반은 링크드인에서 개발한 워크플로우 플랫폼이다. 링크드인은 자사의 복잡한 데이터 파이프라인을 관리하기 위해 아즈카반을 개발했으며, 이를 오픈소스로 공개했다. 아즈카반은 워크플로우 스케줄러, 시각화된 절차, 인증 및 권한 관리, 작업 모니터링 및 알람 등 다양한 기능은 웹UI로 제공한다.
Nifi -https://nifi.apache.org
니이파이는 테이터 흐름을 모니터링하기 위한 프레임워크이다. 여러 네트워크를 통과하는 데이터 흐름을 웹UI에서 그래프로 표현하며, 프로토콜과 데이터 형식이 다르더라도 분석이 가능하다. 또한 데이터를 흘려보낼 때 우선순위를 제어할 수 있다.
Zeppelin - https://zeppelin.incubator.apache.org
제플린은 빅데이터 분석가를 위한 웹 기반 분석도구이며, 분석결과를 즉시 표, 그래프로 표현하는 시각화까지 지원한다. 아이파이썬(iPython)의 노트북(Notebook)과 유사한 노트북 기능을 제공하며, 분석가는 이를 통해 손쉽게 데이터를 추출, 정제, 분석, 공규할 수 있다. 또한 스파크, 하이브, 타조, 플링크, 엘라스틱 서치, 카산드라, DBMS 등 다양한 분석 플랫폼과 연동할 수 있다.
Avro - http://avro.apache.org
RPC(Remote Procedure Call)와 데이터 직렬화를 지원하는 프레임워크이다. JSON을 이용해 데이터 형식과 프로토콜을 정의하며, 작고 빠른 바이너리 포맷으로 데이터를 직렬화한다. 경쟁 솔루션으로는 아파치 쓰리프트(Thrift), 구글 프로토콜 버퍼(Protocol Buffer)등이 있다.
Thrift - http://thrift.apache.org
쓰리프트는 서로 다른 언어로 개발된 모듈들의 통합을 지원하는 RPC 프레임워크이다. 예를 들어, 서비스모듈은 자바로 개발하고, 서버 모듈은 C++로 개발됐을 때 쓰리프트로 두 모듈이 통신하는 코드를 생성할 수 있다. 쓰리프트는 개발자가 데이터 타입과 서비스 인터페이스를 선언하면 RPC 형태의 클라이언트와 서버 코드를 자동으로 생성한다. 자바, C++, C#, 펄, PHP, Python, 뎅파이, 얼랭, Go, Node.js 등 과 같이 다양한 언어를 지원한다.
객체 지향 설계 기법 중에서 팩토리 메서드(factory method) 패턴과 빌더(Builder)패턴이 있다.
인스턴스를 생성하는 일반적인 방법은 new 명령을 사용하는 것인데, new 명령을 통해 클래스와 생성자를 지정하면 해당 클래스의 인스턴스가 생성되고 초기화 된다. 문제는 인스턴스 생성작업이 복잡한 경우이다. 매번 인스턴스를 생성할 때마다 복잡한 과정을 거쳐야 한다면 코딩할 때 부담이 된다. 이를 해결하기 위해 나온 것이 팩토리 메서드 패던과 빌더 패턴이다.
팩토리 클래스는 인스턴스 생성과정을 캡슐화 한다. 즉 인스턴스를 생성하고 초기화하는 복잡한 과정을 감추기 때문에 개발자입장에서는 인스턴스를 얻는 과정이 간결해진다.
팩토리 메서드를 만들 때는 두가지 방법이 있다. 스태틱으로 선언하여 클래스 메서드로 만들거나 아니면 인스턴스 메서드로 만드는 것이다.
먼저 스태틱으로 선언된 팩토리 메서드를 설정하는 방법을 보자
package exam.test11;
import java.text.SimpleDateFormat;
import java.util.Properties;
public class TireFactory {
public static Tire createTire(String maker) {
if (maker.equals("Hankook")) {
return createHankookTire();
} else {
return createKumhoTire();
}
}
private static Tire createHankookTire() {
Tire tire = new Tire();
tire.setMaker("Hankook");
Properties specProp = new Properties();
specProp.setProperty("width", "205");
specProp.setProperty("ratio", "65");
specProp.setProperty("rim.diameter", "14");
tire.setSpec(specProp);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
tire.setCreatedDate(dateFormat.parse("2014-5-5"));
} catch (Exception e) {}
return tire;
}
private static Tire createKumhoTire() {
Tire tire = new Tire();
tire.setMaker("Kumho");
Properties specProp = new Properties();
specProp.setProperty("width", "185");
specProp.setProperty("ratio", "75");
specProp.setProperty("rim.diameter", "16");
tire.setSpec(specProp);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
tire.setCreatedDate(dateFormat.parse("2014-3-1"));
} catch (Exception e) {}
return tire;
}
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hankookTire" class="exam.test11.TireFactory"
factory-method="createTire">
<constructor-arg value="Hankook" />
</bean>
//자바코드 -> Tire hankookTire = TireFactory.createTire("Hankook");
<bean id="kumhoTire" class="exam.test11.TireFactory"
factory-method="createTire">
<constructor-arg value="Kumho" />
</bean>
//자바코드 -> Tire kumhoTire = TireFactory.createTire("kumho");
</beans>
여기서 중요한 것은 factory-method
속성의 값이다. 이 값은 Tire 인스턴스를 생성할 메서드 이름이다. factory-method
속성에는 반드시 static 메서드 이름을 지정해야 한다. 팩토리 메서드에 넘겨 줄 매개변수 값은 <constructor-arg>
태그로 설정한다.
**자바객체(Java object) vs 인스턴스(instance) vs 빈(bean)
new 명령어나 팩토리 메서드로 생성된
인스턴스
를 일반적으로자바 객체
라 부른다. 스프링에서는 자바 객체를빈
이라고 부른다. 일상 생활에서도 하나의 사물을 다양하게 표현하듯이, 예를 드렁 물을 음료라고 부리기도 하고 음료수, 마실것 등으로 부르는 것처럼, 프로그래밍에서도 하나의 개념을 다양한 말로 표현한다.
다음으로 인스턴스 팩토리 메서드를 정의하고, 사용하는 예이다.
package exam.test12;
import java.text.SimpleDateFormat;
import java.util.Properties;
public class TireFactory {
public Tire createTire(String maker) {
if (maker.equals("Hankook")) {
return createHankookTire();
} else {
return createKumhoTire();
}
}
private Tire createHankookTire() {
Tire tire = new Tire();
tire.setMaker("Hankook");
Properties specProp = new Properties();
specProp.setProperty("width", "205");
specProp.setProperty("ratio", "65");
specProp.setProperty("rim.diameter", "14");
tire.setSpec(specProp);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
tire.setCreatedDate(dateFormat.parse("2014-5-5"));
} catch (Exception e) {}
return tire;
}
private Tire createKumhoTire() {
Tire tire = new Tire();
tire.setMaker("Kumho");
Properties specProp = new Properties();
specProp.setProperty("width", "185");
specProp.setProperty("ratio", "75");
specProp.setProperty("rim.diameter", "16");
tire.setSpec(specProp);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
tire.setCreatedDate(dateFormat.parse("2014-3-1"));
} catch (Exception e) {}
return tire;
}
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="tireFactory" class="exam.test12.TireFactory"/>
//자바코드 -> TireFactory tireFactory = new TireFactory
<bean id="hankookTire" factory-bean="tireFactory" factory-method="createTire">
<constructor-arg value="Hankook" />
</bean>
//자바코드 -> Tire hankookTire = tireFactory.createTire("Hankook");
<bean id="kumhoTire" factory-bean="tireFactory" factory-method="createTire">
<constructor-arg value="Kumho" />
</bean>
//자바코드 -> Tire hankookTire = tireFactory.createTire("Kumho");
</beans>
factory-bean
속성에 팩토리 객체(tireFactory)를 지정한다. class 속성은 설정하지 않는다. factory-method
속성에는 인스턴스 팩토리 메서드 이름을 지정한다. 스태틱 메서드는 안된다.!! 팩토리 메서드의 매개변수 값은 이전과 마찬가지로 <constructor-arg>
태그로 지정한다.
스프링에서는 팩토리 빈이 갖추어야 할 규칙을 org.springframework.beans.factoryFactoryBean
인터페이스에 지정했다. 팩토리 클래스를 만들 때 이 규칙에 따라 메서드를 구현하면 된다.
FactoryBean인터페이스 구현
- [Interface] FactoryBean
- getObject()
- getObjectType()
- isSingleton()
보통 FactoryBean 인터페이스를 직접 구현하기보다는 스프링에서 제공하는 추상클래스를 상속해서 만든다. org.springframework.beans.factory.config.AbstractFactoryBean
은 FactoryBean인터페이스를 미리 구현했다.
- [interface] FactoryBean
- [abstract] AbstractFactoryBean - createInstance(), getObjectType()
- [class] TireFactoryBean
AbstractFactoryBean에는 createInstance()라는 추상 메서드가 잇다. 빈을 생성할 때 팩토리메서드로서 getObject()가 호출 되는데, getObject()는 내부적으로 바로 이 메서드를 호출한다. 즉 실제 빈을 생성하는 것은 createInstacne() 이다. 따라서 AbstractFactoryBean 클래스를 상속받을 때는 반드시 이 메서드를 구현해야 하고, 이 메서드에 빈 생성 코드를 넣는다.
그리고 FactoryBean 인터페이스로부터 받은 getObjectType() 추상 메서드가 구현되지 않은 채로 남아 있다. 이 메서드는 팩토리 메서드(getObject())가 생성하는 객체의 타입을 알려 준다. 이 메서드는 반드시 구현해야한다.
package exam.test13;
import java.text.SimpleDateFormat;
import java.util.Properties;
import org.springframework.beans.factory.config.AbstractFactoryBean;
public class TireFactoryBean extends AbstractFactoryBean<Tire> {
String maker;
public void setMaker(String maker) {
this.maker = maker;
}
@Override
public Class<?> getObjectType() {
return exam.test13.Tire.class;
}
protected Tire createInstance() {
if (maker.equals("Hankook")) {
return createHankookTire();
} else {
return createKumhoTire();
}
}
private Tire createHankookTire() {
Tire tire = new Tire();
tire.setMaker("Hankook");
Properties specProp = new Properties();
specProp.setProperty("width", "205");
specProp.setProperty("ratio", "65");
specProp.setProperty("rim.diameter", "14");
tire.setSpec(specProp);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
tire.setCreatedDate(dateFormat.parse("2014-5-5"));
} catch (Exception e) {}
return tire;
}
private Tire createKumhoTire() {
Tire tire = new Tire();
tire.setMaker("Kumho");
Properties specProp = new Properties();
specProp.setProperty("width", "185");
specProp.setProperty("ratio", "75");
specProp.setProperty("rim.diameter", "16");
tire.setSpec(specProp);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
tire.setCreatedDate(dateFormat.parse("2014-3-1"));
} catch (Exception e) {}
return tire;
}
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hankookTire" class="exam.test13.TireFactoryBean">
<property name="maker" value="Hankook" />
</bean>
//자바코드 -> FactoryBean tempFactoryBean = new TireFactoryBean();
// tempFactoryBean.setMaker("Hankook");
// Tire tireFactory = tempFactoryBean.getObject();
</beans>
스프링 IoC컨테이너는 빈을 생성할 때 기본으로 한개만 생성한다. getBean()을 호출하면 계속 동일한 객체를 반환한다. 그러나 설정을 통해 이런 빈의 생성 방식을 조정할 수 있다. 다음은 스프링에서 설정 가능한 범위를 정리한 표이다.
범위 | 설명 |
---|---|
singleton | 오직 하나의 빈만 생성한다.(기본설정) |
prototype | getBean()을 호출할 때마다 빈을 생성한다. |
request | HTTP요청이 발생할 때마다 생성되며, 웹 어플리케이션에서만 이 범위를 설정할 수 있다. |
session | HTTP세션이 생성될 때마다 빈이 생성되며, 웹 어플리케이션에서만 이 범위를 설정할 수 있다. |
globalsession | 전역 세션이 준비될 때 빈이 생성된다. 웹 어플리케이션에서만 이 범위를 지정할 수 있으며, 보통 포틀릿 컨텍스트에서 사용한다. |
싱글톤과 프로토타입
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hyundaiEngine" class="exam.test14.Engine"
p:maker="Hyundai" p:cc="1997"/>
<bean id="kiaEngine" class="exam.test14.Engine"
p:maker="Kia" p:cc="3000"
scope="prototype"/>
</beans>
hyundaiEngine은 범위를 설정하지 않아서 기본 범위인 singletone
으로 설정되었다.
kiaEngine은 scope속성의 값을 prototype
으로 설정했다. 이 경우 빈 컨테이너에게 kiaEngine을 요청할 때마다 매번 새 Engine인스턴스를 만들어 반환할 것이다.
package exam.test14;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("exam/test14/beans.xml");
System.out.println("[singleton 방식(기본)]-------------------------");
Engine e1 = (Engine) ctx.getBean("hyundaiEngine");
Engine e2 = (Engine) ctx.getBean("hyundaiEngine");
System.out.println("e1-->" + e1.toString());
System.out.println("e2-->" + e2.toString());
if (e1 == e2) {
System.out.println("e1 == e2");
}
System.out.println("[prototype 방식]-------------------------");
Engine e3 = (Engine) ctx.getBean("kiaEngine");
Engine e4 = (Engine) ctx.getBean("kiaEngine");
System.out.println("e3-->" + e3.toString());
System.out.println("e4-->" + e4.toString());
if (e3 != e4) {
System.out.println("e3 != e4");
}
}
}
/*
[singleton 방식(기본)]
e1-->[Engine:Hyundai,1997]
e2-->[Engine:Hyundai,1997]
e1 == e2
[prototype 방식]
e3-->[Engine:Kia,3000]
e4-->[Engine:Kia,3000]
e3 != e4
*/
kiaEngine의 범위는 프로토타입(prototype)으로 설정되었다. 따라서 getBean()을 호출할 때마다 새로운 빈을 만들어서 반환한다. 그래서 e3와 e4가 다르다고 출력된다.
스프링에서는 자바 애노테이션을 이용해 간단히 의존 객체를 주입할 방법을 제공해 준다. 빈의 셋터 메서드에 @Autowired
를 선언하면 빈 컨테이너가 셋터의 매개변수 타입과 일치하는 빈을 찾아 자동으로 설정해 준다. 이 기능을 이용하려면 빈 설정 파일에 다음 객체를 선언해야 한다.
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
AutowiredAnnotationBeanPostProcessor
클래스는 빈의 후 처리기(post processor)로서 빈을 생성한 후 즉시 @Autowired
로 선언된 셋터를 찾아서 호출하는 역할을 수행한다. 즉 @Autowired
가 붙은 프로퍼티에 대해 의존 객체(프로퍼티 타입과 일치하는 빈)를 찾아 주입(프로퍼티 할당)하는 일을 한다.
@Autowired
적용
public class Car {
String model; // 모델명
Engine engine; // 엔진
Tire[] tires; // 타이어
Map<String,Object> options; //선택사항
public Car() {}
public Car(String model, Engine engine) {
this.model = model;
this.engine = engine;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public Engine getEngine() {
return engine;
}
@Autowired
public void setEngine(Engine engine) {
this.engine = engine;
}
....
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
//자바코드-> AutowiredAnnotationBeanPostProcessor tempAutowiredPostProcessor=
new AutowiredAnnotationBeanPostProcessor();
<bean id="hyundaiEngine" class="exam.test17.Engine">
<constructor-arg value="Hyundai"/>
</bean>
//자바코드-> Engine hyundaiEngine = new Engine("Hyundai");
<bean id="car1" class="exam.test17.Car">
<property name="model" value="Sonata"/>
</bean>
<bean id="car2" class="exam.test17.Car">
<property name="model" value="Grandeur"/>
</bean>
//자바코드 -> Car car1 = new Car();
// Car car2 = new Car();
// car1.setModel("Sonata");
// car2.setModel("Grandeur");
// car1.setEngine(hyundaiEngine);
// car2.setEngine(hyundaiEngine);
</beans>
@Autowired
의 required
속성*
프로퍼티(셋터메서드)에 대해 @Autowired
를 선언하면 해당 프로퍼티는 기본으로 필수 입력 항목이 된다. 만약 프로퍼티에 주입할 의존 객체를 찾을 수 없으면 예외가 발생한다. 이때 @Autowired
의 required
속성을 이용하면 필수 입력 여부를 조정할 수 있다. required
속성을 false로 설정하면 프로퍼티에 주입할 의존객체를 찾지 못하더라도 예외가 발 생하지 않는다.
@Autowired(required=false)
public void setEngine(Engine engine) {
this.engine = engine;
}
@Qualifier
로 주입할 객체를 지정하기
@Autowired
는 프로퍼티에 주입할 수 있는 의존객체가 여러개 있을 경우 오류를 발생시키다. 즉 같은 타입의 객체가 여러개 있으면 그 중에서 어떤 객체를 주입해야 할지 알 수 없기 때문이다. 이 경우 애노테이션 @Qualifier
를 사용하면 손쉽게 해결 가능하다. @Qualifier
는 빈의 이름(또는 아이디)으로 의존 객체를 지정할 수 있다.
이 애노테이션을 사용하려면 빈의 후 처리기(bean post processor)가 필요하다. 이런식으로 하다보니 애노테이션을 사용할 때마다 그 애노테이션을 처리할 객체를 등록해야 하는 불편함이 발생한다. 이런 불편함을 해소하기 위해 스프링에서는 애노테이션 처리와 관련된 빈(bean post processor)을 한꺼번에 등록할 수 있는 방법을 제공한다. 다음과 같이 간단히 태그를 선언하면 된다.
<context:annotation-config/>
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<bean id="hyundaiEngine" class="exam.test19.Engine">
<constructor-arg value="Hyundai"/>
</bean>
<bean id="kiaEngine" class="exam.test19.Engine">
<constructor-arg value="Kia"/>
</bean>
<bean id="car1" class="exam.test19.Car">
<property name="model" value="Sonata"/>
</bean>
</beans>
public class Car {
...
@Autowired(required=false)
@Qualifier("kiaEngine")
public void setEngine( Engine engine) {
this.engine = engine;
}
...
}
engine 프로퍼티(setEngine()) 앞에 @Qualifier
선언을 했고, 이는 bean.xml에 선언된 엔진 객체 중에서 kiaEngine를 의존 객체로 주입한다.
@Autowired
+ @Qualifier
= @Resource
JSR-250 명세에 정의된 @Resource
를 사용하면 더 간단히 특히 의존객체를 지정할 수 있다. 이름으로 의존 객체를 지정할 경우 이 애노테이션을 사용할 것을 권장한다. 단, @Autowired
는 required
속성을 사용하여 프로퍼티 값의 필수 여부를 지정할 수 있지만 @Resource
에는 그런 기능이 없으므로 @Resource
는 무조건 필수 입력입니다. 해당 프로퍼티에 주입할 의존 객체가 없으면 오류가 발생하므로 의존객체 주입을 선택 사항으로 만들고 싶다면 @Autowired
를 사용하면된다.
public class Car {
...
@Resource(name="kiaEngine")
public void setEngine( Engine engine) {
this.engine = engine;
}
...
}
@Resource
를 선언하고 name 속성에 주입하고자 하는 의존 객체의 이름을 지정한다.
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<bean id="hyundaiEngine" class="exam.test20.Engine">
<constructor-arg value="Hyundai"/>
</bean>
<bean id="kiaEngine" class="exam.test20.Engine">
<constructor-arg value="Kia"/>
</bean>
<bean id="car1" class="exam.test20.Car">
<property name="model" value="Sonata"/>
</bean>
</beans>
@Resource
에 설정한 대로 해당 의존객체(kiaEngine)가 주입된다.
컴포넌트 스캔과 애노테이션을 이용해 빈을 자동 등록하는 방법은 다음과 같다.
@Component
애노테이션으로 표시한다.@Component
가 붙은 클래스를 찾아 빈을 생성한다.스프링에서는 @Component
외에 클래스를 관리하고 이해하는데 도움을 주기 위해 클래스의 역할에 따라 붙일 수 있는 애노테이션을 추가로 제공한다. 다음 표는 빈 생성 대상 클래스임을 표시할 때 붙일 수 있는 애노테이션이다.
애노테이션 | 설명 |
---|---|
@Component |
빈 생성 대상이 되는 모든 클래스에 대해 붙일 수 있다. |
@Repository |
DAO와 같은 퍼시스턴스(persistence) 역할을 수행하는 클래스에 붙인다. |
@Service |
서비스 역할을 수행하는 클래스에 붙인다. |
@Controller |
MVC구조에서 Controller 역할을 수행하는 클래스에 붙인다. |
@Component
예>
@Component("car")
public class Car {
...
@Autowired
public void setEngine(Engine engine) {
this.engine = engine;
}
...
}
@Component
에 들어가는 속성값은 빈의 이름이다. getBean()을 호출할 때 사용한다.
@Component("engine")
public class Engine {
...
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="exam.test21"/>
</beans>
<context:component-scan/>
태그를 사용하여 @Component, @Repository
등 빈 생성 표시자(애노테이션)가 붙은 클래스를 검색하라고 선언한다.
앞의 코드의 경우 exam.test21 패키지 및 그 하위 패키지를 모두 검색할 것이다.
<context:component-scan/>
태그를 선언하면 <context:annotation-config/>
태그의 기능이 자동으로 활성화된다.
package exam.test21;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("exam/test21/beans.xml");
Car car = (Car) ctx.getBean("car");
Engine engine = (Engine) ctx.getBean("engine");
engine.setMaker("Hyundai");
engine.setCc(1997);
if (car != null) {
System.out.println("car != null");
}
if (engine != null) {
System.out.println("engine != null");
}
System.out.println(car);
System.out.println(car.toString());
}
}
/*
car != null
engine != null
[car:null
[Engine:hyundai,1997]
]
*/
이렇게 컴포넌트 자동 탐색(<context:component-scan/>
)과 빈 자동생성 표시자(@Component
등)를 이용하면 빈 설정 파일(bean.xml)에 일일이 빈 정보를 선언할 필요가 없다.
컴포넌트 탐색의 포함 조건과 제외 조건
<context:component-scan/>
을 사용할 때 다음과 같이 탐색 조건을 추가할 수 있다.<context:component-scan base-package="exam.test21"> <context:include-filter type="" expression=""/> <context:exclude-filter type="" expression=""/> </context:component-scan>
context:include-filter
태그는 빈 탐색에 포함할 대상자를 지정할 때사용한다.
context:exclude-filter
태그는 빈 탐색에 제외할 대상자를 지정할 때 사용한다.
스프링에서는 자바 객체를 빈(Bean)
이라고 한다. 그래서 객체관리 컨테이너를 빈컨테이너
라고 한다.
(스프링 IoC컨테이너 == 빈컨테이너)
ApplicationContext의 계층도
- ApplicationContext (interface)
- ClasspathXmlApplicationContext (class)
- FileSystemXmlApplicationContext (class)
- WebApplicationContext (interface)
스프링에서 빈 정보는 XML 파일에 저장해 두고 ClassPathXmlApplicationContext 클래스나 FileSystemXmlApplicaionContext 클래스를 사용하여 빈을 자동 생성한다. ClassPathXmlApplicationContext는 자바 클래스 경로에서 XML로 된 빈 설정 파일을 찾는다. FileSystemXmlApplicationContext는 파일 시스템 경로에서 빈 설정 파일을 찾는다. WebApplicationContext는 웹 어플리케이션을 위한 IoC 컨테이너로서 web.xml파일에 설정된 정보에 따라 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="score" class="exam.test01.Score"/>
</beans>
bean태그는 <beans xmlns="http://www.springframework.org/schema/beans"/>
네임스페이스에 소속되어 있기 때문에 XML파서가 이해할 수 있다. 이는 자바 클래스에서 import하는 것과 비슷하다.
위의 소스를 자바소스로 표현하면 아래와 같다.
new exam.test01.Score();
스프링 IoC 컨테이너가 생성할 자바 빈에 대한 정보는 <bean>
태그로 선언한다. class속성에는 클래스 이름을 정한다. 주의 할점은 반드시 패키지 이름을 포함한 클래스 이름(fully qulified name)이어야 한다. id속성은 객체의 식별자이다. 빈컨테이너에서 객체를 꺼낼 때 이 식별자를 사용한다.
스프링 IoC 컨테이너 사용
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("exam/test01/beans.xml");
Score score = (Score) ctx.getBean("score");
System.out.println("합계:" + score.sum());
System.out.println("평균:" + score.average());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="exam.test03.Score"/>
<bean class="exam.test03.Score"/>
</beans>
package exam.test03;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("exam/test03/beans.xml");
System.out.println("[컨테이너에 보관된 객체의 이름 출력]");
for (String name : ctx.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("[exam.test03.Score#0의 별명 출력]");
for (String alias : ctx.getAliases("exam.test03.Score#0")) {
System.out.println(alias);
}
System.out.println("[익명 빈 꺼내기]");
Score score1 = (Score) ctx.getBean("exam.test03.Score");
Score score2 = (Score) ctx.getBean("exam.test03.Score#0");
if (score1 == score2) System.out.println("score == score#0");
Score score3 = (Score) ctx.getBean("exam.test03.Score#1");
if (score1 != score3) System.out.println("score != score#1");
System.out.println("[클래스 타입으로 빈 꺼내기]");
Score score4 = (Score) ctx.getBean(exam.test03.Score.class);
}
}
/*
[컨테이너에 보관된 객체의 이름 출력]
exam.test03.Score#0
exam.test03.Score#1
[exam.test03.Score#0의 별명 출력]
exam.test03.Score
[익명 빈 꺼내기]
score == score#0
score != score#1
[클래스 타입으로 빈 꺼내기]
Error
클래스 타입으로도 객체를 꺼낼 수 있다. 하지만 getBean()을 호출할 때 Score 클래스에 대한 정보를 담은 java.lang.Class객체를 넘겨서 빈컨테이너에서 이 클래스의 인스턴스를 찾는다. 이때 같은 타입의 객체가 여러 개 있을 경우 예외가 발생한다.
<constructor-arg>
엘러먼트를 이용하면 호출될 생성자를 지정할 수 있다.
생성자를 지정하는 방법은 아래와 같다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="score1" class="exam.test04.Score">
<constructor-arg><value type="java.lang.String">홍길동</value></constructor-arg>
<constructor-arg><value type="float">91</value></constructor-arg>
<constructor-arg><value type="float">92</value></constructor-arg>
<constructor-arg><value type="float">93</value></constructor-arg>
</bean>
//자바코드 -> new Score("임꺽정",91,91,91);
<bean id="score2" class="exam.test04.Score">
<constructor-arg><value>임꺽정</value></constructor-arg>
<constructor-arg><value>81</value></constructor-arg>
<constructor-arg><value>82</value></constructor-arg>
<constructor-arg><value>83</value></constructor-arg>
</bean>
//자바코드 -> new Score("임꺽정",81,82,83);
<bean id="score3" class="exam.test04.Score">
<constructor-arg type="java.lang.String" value="장보고"/>
<constructor-arg type="float" value="71"/>
<constructor-arg type="float" value="72"/>
<constructor-arg type="float" value="73"/>
</bean>
//자바코드 -> new Score("장보고",71,72,73);
<bean id="score4" class="exam.test04.Score">
<constructor-arg value="이순신"/>
<constructor-arg value="100"/>
<constructor-arg value="98"/>
<constructor-arg value="99"/>
</bean>
//자바코드 -> new Score("이순신",100,98,99);
<bean id="score5" class="exam.test04.Score">
<constructor-arg value="70" index="3"/>
<constructor-arg value="50" index="1"/>
<constructor-arg value="강감찬" index="0"/>
<constructor-arg value="60" index="2"/>
</bean>
//자바코드 -> new Score("강감찬",50,60,70;
</beans>
프로퍼티값 설정은 다음과 같다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="score1" class="exam.test05.Score">
<property name="name"><value>홍길동</value></property>
<property name="kor"><value>100</value></property>
<property name="eng"><value>95</value></property>
<property name="math"><value>90</value></property>
</bean>
//자바코드 -> Score score1 = new Score();
// score1.setName('홍길동');
// score1.setKor(100);
// score1.setEng(59);
// score1.setMath(90);
<bean id="score2" class="exam.test05.Score">
<property name="name" value="임꺽정"/>
<property name="kor" value="85"/>
<property name="eng" value="99"/>
<property name="math" value="100"/>
</bean>
//자바코드 -> Score score2 = new Score();
// score2.setName('임꺽정');
// score2.setKor(85);
// score2.setEng(99);
// score2.setMath(100);
</beans>
<bean>
의 속성을 이용하여 생성자 및 프로퍼티 설정하기<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="score1" class="exam.test06.Score"
p:name="홍길동" p:kor="100" p:eng="95" p:math="90"/>
//자바코드 -> Score score1 = new Score();
// score1.setName('홍길동);
// score1.setKor(100);
// score1.setEng(95);
// score1.setMath(90);
<bean id="score2" class="exam.test06.Score"
c:name="임꺽정" c:kor="80" c:eng="90" c:math="100"/>
//자바코드 -> Score score2 = new Score('임꺽정',80,90,100);
</beans>
xmlns:p="http://www.springframework.org/schema/p"
, xmlns:c="http://www.springframework.org/schema/c"
의 네임스페이스를 통해서 <bean>
속성을 통해 프로퍼티와 생성자도 설정이 가능하다.
어떤 객체가 작업을 수행하기 위해 다른 객체를 지속적으로 사용한다면 그 사용되는 객체를 의존 객체(depencencies)라 한다. 보통 지속적으로 사용할 객체는 프로퍼티에 보관한다.
예제로 Car객체를 만들때 tires객체와 engine객체를 사용한다.
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="engine1" class="exam.test07.Engine"
c:maker="Hyundai" p:cc="1998"/>
//자바코드 -> Engine engine1 = new Engine("Hyundai");
// engine1.setCc(1998);
<bean id="car1" class="exam.test07.Car">
<property name="model"><value>Avante</value></property>
<property name="engine"><ref bean="engine1"/></property>
</bean>
//자바코드 -> Car car1 = new Car();
// car1.setModel("Avante");
// car1.setEngine(engine1);
<bean id="car2" class="exam.test07.Car"
c:model="Equus" c:engine-ref="engine1"/>
//자바코드 -> Car car2 = new Car("Equus",engine1);
</beans>
<ref>
태그를 사용하여 Engine의 참조하였다.
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="car1" class="exam.test08.Car">
<constructor-arg value="Avante" />
<constructor-arg>
<bean class="exam.test08.Engine" p:maker="Hyundai" p:cc="1495" />
</constructor-arg>
</bean>
//자바코드 -> Engine temp = new Engine();
// temp.setMaker("Hyundai");
// temp.setCc(1495);
// Car Car1 = new Car("Avante", temp);
<bean id="car2" class="exam.test08.Car">
<property name="model" value="Sonata" />
<property name="engine">
<bean class="exam.test08.Engine" p:maker="Hyundai" p:cc="1997" />
</property>
</bean>
//자바코드 -> Car car2 new Car();
// car2.setModel("Sonata");
// Engine temp = new Engine();
// temp.setMaker("Hyundai");
// temp.setCc(1997);
// car2.setEngine(temp);
</beans>
배열 프로퍼티의 값 주입
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="car1" class="exam.test09.Car">
<constructor-arg value="Avante" />
<constructor-arg>
<bean class="exam.test09.Engine" p:maker="Hyundai" p:cc="1495" />
</constructor-arg>
<property name="tires">
<list>
<bean class="exam.test09.Tire" p:maker="Kumho" p:spec="P185" />
<bean class="exam.test09.Tire" p:maker="Kumho" p:spec="P185" />
</list>
</property>
</bean>
//자바코드 -> Engine temp = new Engine();
// temp.setMaker("Hyundai");
// temp.setCc(1495);
// Car car1 = new Car("Avante",temp);
// Tire[] temp2 = new Tire[]{new Tire(), new Tire()};
// temp2[0].setMaker("Kumho");
// temp2[0].setSpeck("P185");
// temp2[1].setMaker("Kumho");
// temp2[1].setSpeck("P185");
// car1.setTire(temp2);
</beans>
Map과 Properties값 주입
Map과 Preoperties의 용도
java.util.Map 타입의 클래스는 식별자(key)나 값(value)으로 문자열뿐만 아니라 다른 타입의 객체도 사용할 수 있습니다. java.util.Properties 클래스도 Map의 일종(Map 인터페이스를 구현했음)이지만 주로 문자열로 된 식별자와 값을 다룰 때 사용한다.
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="spareTire" class="exam.test10.Tire">
<property name="maker" value="Hyundai" />
<property name="spec">
<props>
<prop key="width">205</prop>
<prop key="ratio">65</prop>
<prop key="rim.diameter">14</prop>
</props>
</property>
</bean>
//자바 코드 -> Tire spareTire = new Tire();
// spareTire.setMaker("Hyundai");
// Properties temp = new Properties();
// temp.setProperty("width", "205");
// temp.setProperty("ratio","65");
// temp.setProperty("rim.diameter","14");
// spareTire.setSpec(temp);
<bean id="car1" class="exam.test10.Car">
<constructor-arg value="Avante" />
<constructor-arg>
<bean class="exam.test10.Engine" p:maker="Hyundai" p:cc="1495" />
</constructor-arg>
<property name="options">
<map>
<entry>
<key>
<value>sunroof</value>
</key>
<value>yes</value>
</entry>
<entry key="airbag" value="dual" />
<entry key="sparetire">
<ref bean="spareTire" />
</entry>
</map>
</property>
</bean>
//자바 코드 -> Engine temp = new Engine();
// temp.setMaker("Hyundai")
// temp.setCc(1496);
// Car car1 = new Car("Avante",temp);
// Map<Object,Object> tempMap = new HashMap<Object,Object>();
// car1.setOptions(tempMap);
// tempMap.put("sunroof","yes");
// tempMap.put("airbag","dual");
// tempMap.put("spaertire",spareTire);
</beans>
mybatis는 동적 SQL을 위한 엘리먼트를 제공한다.
동적 SQL을 작성할 때 사용하는 엘리먼트
엘리먼트 예 | 설명 |
---|---|
<if test=”조건”>SQL문</if> | <if> 태그는 어떤 값을 상태를 검사하여 참일 경우에만 SQL문을 포함하고 싶을 때 사용한다. test속성에 지정된 조건이 참이면 <if> 태그의 내용을 반환한다. |
<choose> <when test=”조건1”>SQL문</when> <when test=”조건2”>SQL문</when> <otherwise>SQL문 </otherwise> </choose> |
<choose> 태그는 검사할 조건이 여러 개일 경우에 사용한다. test속성에 지정된 조건이 참이면 <when> 태그의 내용을 반환한다. 일치하는 조건이 없으면 <otherwise> 의 내용을 반환한다. |
<where> <if test =”조건1>SQL문</when> <if test=”조건2>SQL문</when> </where> |
<where> 태그는 WHERE절을 반환한다. <where> 안의 하위 태그를 실행하고 나서 반환값이 있으면 WHERE절을 만들어 반환하고, 없으면 WHERE절을 반환하지 않는다. |
<trim prefix=”단어” prefixOverrides=”문자열|문자열”> <if test=”조건1”>SQL문</when> <if test=”조건2”>SQL문</when> </trim> |
<trim> 태그는 특정 단어로 시작하는 SQL문을 반환하고 싶을 때 사용한다. prefix는 반환값 앞에 붙일 접두어를 지정한다. prefixOverrides는 반환할 값에서 제거해야 하는 접두어를 지정한다. 다시 말하면 <trim> 의 반환값이 있다면, 그 값의 앞부분이 prefixOverride에 지정된 문자열과 일치할 경우 그 문자열을 제거한다. 그리고 그 값의 앞부분에 prefix로 지정한 접두어를 붙여 반환한다. |
<set> <if test=”조건1”>SQL문</when> <if test=”조건2”>SQL문</when> <set> |
<set> 태그는 UPDATE문의 SET절을 만들 때 사용한다. 조건이 참인 <if> 의 내용은 SET절에 포함된다. SET 절의 항목이 여러개 일 경우 자동으로 콤마(,)를 붙인다. |
<foreach item=”항목” index=”인덱스” collection=”목록” open=”시작문자열” close=”종료문자열” separator=”구분자”> </foreach> |
<foreach> 태그는 목록의 값을 가지고 SQL문을 만들 때 사용한다. 특히 IN(값, 값…)조건을 만들 때 좋다. item속성에는 항목을 가리킬 때 사용할 변수의 이름을 지정한다. index속성에는 항목의 인덱스 값을 꺼낼 때 사용할 변수 이름을 지정한다. collection속성에는 java.util.List 구현체나 배열 객체가 온다. open속성에는 최종 반환값의 접두어를 지정한다. close는 최종 반환값의 접미어를 지정한다. separatory 속성은 반복으로 생ㅇ성하는 값을 구분하기 위해 붙이는 문자열을 지정한다. |
<bind name=”변수명” value=”값”/> | <bind> 태그는 변수를 생성할 때 사용한다. |
사용예
//MySqlProjectDao.xml
<select id="selectList" resultMap="projectResultMap">
select PNO, PNAME, STA_DATE, END_DATE, STATE
from PROJECTS
order by
<choose>
<when test="orderCond == 'TITLE_ASC'">PNAME asc</when>
<when test="orderCond == 'TITLE_DESC'">PNAME desc</when>
<when test="orderCond == 'STARTDATE_ASC'">STA_DATE asc</when>
<when test="orderCond == 'STARTDATE_DESC'">STA_DATE desc</when>
<when test="orderCond == 'ENDDATE_ASC'">END_DATE asc</when>
<when test="orderCond == 'ENDDATE_DESC'">END_DATE desc</when>
<when test="orderCond == 'STATE_ASC'">STATE asc</when>
<when test="orderCond == 'STATE_DESC'">STATE desc</when>
<when test="orderCond == 'PNO_ASC'">PNO asc</when>
<otherwise>PNO desc</otherwise>
</choose>
</select>
<update id="update" parameterType="map">
update PROJECTS
<set>
<if test="title != null">PNAME=#{title},</if>
<if test="content != null">CONTENT=#{content},</if>
<if test="startDate != null">STA_DATE=#{startDate},</if>
<if test="endDate != null">END_DATE=#{endDate},</if>
<if test="state != null">STATE=#{state},</if>
<if test="tags != null">TAGS=#{tags}</if>
</set>
where PNO=#{no}
</update>
데이터베이스 입출력을 로그로 볼 수 있는 기능이 mybatis에 있다. mybatis설정 파일에 다음과 같이 편집을 한다.
퍼시스턴스라는 용어는 개발 분야에서 많이 사용하는 용어이다. 데이터의 지속성을 의마하는 용어로 어플리케이션을 종료하고 다시 실행하더라도 이전에 저장한 데이터를 다시 불러올 수 있는 기술이다.
퍼시스턴스 프레임워크는 데이터의 저장, 조회, 변경, 삭제를 다루는 클래스 및 설정 파일들의 집합니다. 이를 사용하면 JDBC 프로그래밍의 복잡함이나 번거로움 없이 간단한 작업 만으로 데이터베이스와 연동되는 시스템을 빠르고 안전적으로 구동되게 개발할 수 있다.
퍼시스턴스 프레임워크에는 SQL문장으로 직접 DB데이터를 다루는 SQL맵퍼(mapper)
와 자바객체를 통해 간접적으로 DB데이터를 다루는 객체 관계 맵퍼(Object-Relational mapper)
가 있다.
SQL맵퍼에 대표적으로 `mybatis가 있고, 객체 관계 맵퍼에는 Hibernate와 TopLink가 있다.
객체관계맵퍼는 프레임워크에서 제공하는 API와 전용 객체 질의어를 사용하여 데이터를 다룬다. 그래서 이를 사용할려면 데이터베이스의 정규화가 잘돼 있어야 한다. 그래야만 테이블을 객체와 연결하기 쉽고, 객체를 통해 테이블의 데이터를 다루기가 쉽다. 근데, 유지보수 단계에 들어가면 잦은 기는 변경과 추가로 데이터베이스구조가 흐트러지게 되어 실무에서는 잘 사용하지 않는다.
mybatis의 핵심은 개발과 유지보수가 쉽도록 소스코드에 박혀있는 SQL을 별도의 파일로 분리하는 것이다. 또 단순하고 반복적인 JDBC코드를 캡슐화하여 데이터베이스 프로그래밍을 간결하게 하는것이다.
라이브러리 파일은 http://github.com/mybatis에서 mybatis-3
로 가서 다운 받는다.
컴포넌트 | 설명 |
---|---|
SqlSession | 실제 SQL을 실행하는 객체. 이 객체는 SQL을 처리하기 위해 JDBC 드라이버를 사용함 |
SqlSessionFactory | SqlSession 객체를 생성함 |
SqlSessionFactoryBuilder | mysql설정 파일의 내용을 토대로 SqlSessionFactory를 생성함. |
mybatis설정파일 | 데이터베이스 연결정보, 트갠잭션정보, mybatis제어 정보 등의 설정 내용을 포함하고 있음. SqlSessionFactory를 만들 때 사용됨. |
SQL 맵퍼 파일 | SQL문을 담고 있는 파일. SqlSession객체가 참조함. |
메서드 | 설명 |
---|---|
selectList() | SELECT문을 실행. 값객체(Value Object) 목록을 반환함 |
selectOne() | SELECT문을 실행. 하나의 값 객체를 반환함 |
insert() | INSERT문을 실행. 반환값은 입력 데이터 개수. |
update() | UPDATE문을 실행. 반환값은 변경한 데이터 개수. |
delete() | DELETE문을 실행. 반환값은 삭제한 데이터 개수. |
예>
@Component("projectDao")
public class MySqlProjectDao implements ProjectDao {
SqlSessionFactory sqlSessionFactory;
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
public List<Project> selectList(HashMap<String,Object> paramMap)
throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
return sqlSession.selectList("spms.dao.ProjectDao.selectList", paramMap);
} finally {
sqlSession.close();
}
}
public int insert(Project project) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
int count = sqlSession.insert("spms.dao.ProjectDao.insert", project);
sqlSession.commit();
return count;
} finally {
sqlSession.close();
}
}
public Project selectOne(int no) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
return sqlSession.selectOne("spms.dao.ProjectDao.selectOne", no);
} finally {
sqlSession.close();
}
}
....
//MySqlProjectDao.xml -> 맵퍼파일
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="spms.dao.ProjectDao">
<resultMap type="project" id="projectResultMap">
<id column="PNO" property="no"/>
<result column="PNAME" property="title"/>
<result column="CONTENT" property="content"/>
<result column="STA_DATE" property="startDate" javaType="java.sql.Date"/>
<result column="END_DATE" property="endDate" javaType="java.sql.Date"/>
<result column="STATE" property="state"/>
<result column="CRE_DATE" property="createdDate" javaType="java.sql.Date"/>
<result column="TAGS" property="tags"/>
</resultMap>
<select id="selectList" parameterType="map" resultMap="projectResultMap">
select PNO, PNAME, STA_DATE, END_DATE, STATE
from PROJECTS
order by
<choose>
<when test="orderCond == 'TITLE_ASC'">PNAME asc</when>
<when test="orderCond == 'TITLE_DESC'">PNAME desc</when>
<when test="orderCond == 'STARTDATE_ASC'">STA_DATE asc</when>
<when test="orderCond == 'STARTDATE_DESC'">STA_DATE desc</when>
<when test="orderCond == 'ENDDATE_ASC'">END_DATE asc</when>
<when test="orderCond == 'ENDDATE_DESC'">END_DATE desc</when>
<when test="orderCond == 'STATE_ASC'">STATE asc</when>
<when test="orderCond == 'STATE_DESC'">STATE desc</when>
<when test="orderCond == 'PNO_ASC'">PNO asc</when>
<otherwise>PNO desc</otherwise>
</choose>
</select>
<insert id="insert" parameterType="project">
insert into PROJECTS(PNAME,CONTENT,STA_DATE,END_DATE,STATE,CRE_DATE,TAGS)
values (#{title},#{content},#{startDate},#{endDate},0,now(),#{tags})
</insert>
<select id="selectOne" parameterType="int" resultMap="projectResultMap">
select PNO, PNAME, CONTENT, STA_DATE, END_DATE, STATE, CRE_DATE, TAGS
from PROJECTS
where PNO=#{value}
</select>
...
package spms.listeners;
// SqlSessionFactory 객체 준비
@WebListener
public class ContextLoaderListener implements ServletContextListener {
static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void contextInitialized(ServletContextEvent event) {
try {
applicationContext = new ApplicationContext();
String resource = "spms/dao/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//SqlSessionFactory객체를 mybatis설정파일(설계도)를 통해 빌드 시킴
//이런식의 객체 생성 패턴을 빌더 패턴(Builder Pattern)이라고 한다.
applicationContext.addBean("sqlSessionFactory", sqlSessionFactory);
ServletContext sc = event.getServletContext();
String propertiesPath = sc.getRealPath(
sc.getInitParameter("contextConfigLocation"));
applicationContext.prepareObjectsByProperties(propertiesPath);
applicationContext.prepareObjectsByAnnotation("");
applicationContext.injectDependency();
} catch(Throwable e) {
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent event) {}
}
//mybatis 설정파일
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<typeAlias type="spms.vo.Project" alias="project"/>
<typeAlias type="spms.vo.Member" alias="member"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/studydb"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="spms/dao/MySqlProjectDao.xml"/>
<mapper resource="spms/dao/MySqlMemberDao.xml"/>
</mappers>
</configuration>
DBMS는 INSERT, UPDATE, DELETE문을 실행하면 그 작업 결과를 임시 데이터베이스에 보관한다. 클라이언트 요청이 있어야만 임시 데이터베이스의 작업물을 운영 데이터베이스에 반영한다.
commit()
은 임시 데이터베이스에 보관된 작업 결과를 운영 데이터베이스에 적용하라고 요청할 때 사용하는 메서드이다.
rollback()
은 임시 데이터베이스의 작업 결과를 운영 데이터베이스에 반영하지 않고 취소할 때 호출한다.
DTD선언
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>
루트 엘리먼트SQL 맵퍼 파일은 루트 엘리먼트 <mapper>
를 작성하는 것으로 시작한다. namespace
속성은 자바의 패키지처럼 SQL문을 묶는 용도로 사용한다.
<mapper namespace ="spms.dao.ProjectDao">
...
</mapper>
<select>
, <insert>
, <update>
, <delete>
엘리먼트SQL문을 작성할 때 명령어에 맞는 태그를 사용한다.
id속성 -> SQL문을 작성할때 각각의 SQL문을 구분하기 위해 id 속성을 사용한다.
resultType속성 -> select문을 실행하면 결과가 생성되는데, 이 결과를 담을 객체를 지정하는 속성이 resultType속성이다. 값으로는 클래스 이름(패키지 이름 포함)이 온다.
<select id="selectList" resultType="spms.vo.Project">
만약 mybatis 설정 파일에 다음과 같이 spms.vo.Project에 대한 별명이 정의 되여 있다면 resultType의 값으로 그 별명을 사용할 수 있다.
//mybatis설정 파일의 일부분
<typeAliases>
<typeAlias type="spms.vo.Project" alias="project"/>
<typeAlias type="spms.vo.Member" alias="member"/>
</typeAliases>
<select id="selectList" resultType="project">
위 방법에 약간의 번거러움이 있다. mybatis는 SELECT 결과를 저장하고자 resultTpye에 선언된 클래스의 인스턴스를 생성한다. 그리고 각 칼럼에 대응하는 값객체의 셋터 메서드를 찾아서 호출한다. 이때 셋터메서드는 대소문자 구분없이 set을 뺀 메서드의 이름이과 칼럼의 이름이 같아야지만 인스턴스가 생성된다.
만약 칼럼의 이름과 일치하는 셋터가 없다면, 해당 칼럼의 값은 인스턴스에 저장되지 않는다. 이런 점이 매우 번거럽다. 이를 해결하기 위해 다음 엘리먼트를 사용한다.
<resultMap>
엘리먼트다음과 같이 <resultMap>
태그에 칼럼과 연결될 값객체의 셋터 메서드를 정의한다.
<resultMap type="project" id="projectResultMap">
<id column="PNO" property="no"/>
<result column="PNAME" property="title"/>
<result column="CONTENT" property="content"/>
<result column="STA_DATE" property="startDate" javaType="java.sql.Date"/>
<result column="END_DATE" property="endDate" javaType="java.sql.Date"/>
<result column="STATE" property="state"/>
<result column="CRE_DATE" property="createdDate" javaType="java.sql.Date"/>
<result column="TAGS" property="tags"/>
</resultMap>
`
<result>
엘리먼트
<resultMap>
태그의 자식 태그로서 칼럼과 셋터 메서드의 연결을 정의한다. column 속성에는 칼럼 이름을 지정하고, property 속성에는 객체의 프로퍼티 이름을 지정한다.<result column="PNAME" property="title"/>
<result>
태그를 사용하여 PNAME 칼럼 값을 setTitle()메서드와 연결시킨것을 보았다.
javaType속성
<result>
에서 javaType을 사용하면, 칼럼의 값을 특정 자바 객체로 변환할 수 있다. 다음과 같이 STA_DATE 칼럼에 대해 javaType을 java.sql.Date으로 설정하면, 칼럼 값을 꺼낼 때 그 객체로 변환된다.
<id>
엘리먼트
<id>
태그는<result>
태그와 작성법이 같다. 다만<id>
태그에서 지정한 프로퍼티는 객체 식별자로 사용된다. SELECT문을 실행하면 레코드 값을 저장하기 위해 결과 객체가 생성되는데, SELECT문을 실행할 때마다 매번 결과 객체를 생성한다면 실행 성능이 나빠진다. 이를 해결하기위해 SELECT를 통해 생성된 결과 객체들은 별도의 보관소에 저장(캐싱, chaching)해두고 재사용한다. 이때 보관소에 저장된 객체를 구분하는 값으로<id>
에서 지정한 프로퍼티를 사용한다.
mybatis에서는 입력 매개벼수를 #{프로퍼티명}
으로 표시한다.
<insert id="insert" parameterType="project">
insert into PROJECTS(PNAME,CONTENT,STA_DATE,END_DATE,STATE,CRE_DATE,TAGS)
values (#{title},#{content},#{startDate},#{endDate},0,now(),#{tags})
</insert>
#{프로퍼티명}
이 가리키는 값은 <insert>
의 paramerType에 지정한 객체의 프로퍼티 값(겟터 메서드의 반환값)이다. 즉 #{title} 자리에는 Project객체의 getTitle() 반환값이 놓인다.
public int insert(Project project) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
int count = sqlSession.insert("spms.dao.ProjectDao.insert", project);
sqlSession.commit();
return count;
} finally {
sqlSession.close();
}
}
sqlSession.insert()
를 호출하면 SQL 맵퍼 파일에서 spms.dao.ProjectDao.insert
아이디를가진 SQL문을 찾아 실행한다. 물론 spms.dao.ProjectDao
는 SQL 맵퍼 파일의 네임스페이스 이름을 가리키고 insert는 SQL 아이드를 가리킨다. project는 INSERT문을 실행할 때 입력 매개변수에 값을 공급할 객체이다.
만약 다음과 같이 공급하는 객체가 기본 타입 객체(랩퍼클래스,wrapper class)인 경우, 자바는 해당 타입에 대응하는 랩퍼 객체를 생성하여 자동포장 한다.
public int delete(int no) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
int count = sqlSession.delete("spms.dao.ProjectDao.delete", no);
sqlSession.commit();
return count;
} finally {
sqlSession.close();
}
}
즉 컴파일 할때 다음과 같이 변한다.
int count = sqlSEssion.delete("spms.dao.ProjectDao.delete",new Integer(no));
DTD선언
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
루트 엘리먼트configuration의 주요 자식 엘리먼트
엘리먼트 | 용도 |
---|---|
properties | 프로퍼티 파일이 있는 경로 설정. |
settings | 프레임워크의 실행 환경을 설정 |
typeAliases | 자바 클래스 이름(패키지 이름 포함)에 대한 별명 설저 |
typeHandler | 칼럼의 값을 자바 객체로, 자바 객체를 칼럼의 값으로 변환해 주는 클래스를 설정 |
environments | 프레임워크에서 사용할 데이터베이스 정보(트랜젝션 관리자, 데이터 소스)를 설정. |
mappers | SQL맵퍼 파일들이 있는 경로 설정 |
mybatis는 기본 데이터 형(byte, short, int, long,float, double, boolean, char)이나 랩퍼클래스에 대해 미리 별명을 정의했다.
<environments>
엘리먼트<environments>
태그는 데이터베이스 환경 정보를 설정할 때 사용하는 태그이다. 이 태그를 이용하면 여러개의 데이터베이스 접속 정보를 설정할 수 있다. 설정된 DB정보 중에서 하나를 선택할 때는 default속성을 사용한다.
<environments defaul="development">
<environment id="development">...</environment>
<environment id="test">...</environment>
<environment id="real">...</environment>
</environments>
각각의 데이터베이스 접속 정보는 <environment>
태그를 이용하여 정의 한다. id속성은 <environment>
를 구분할 때 사용하는 식별자이다.
<environment>
엘리먼트<environment>
는 트랜잭션 관리 및 데이터 소스를 설정하는 태그이다.
트랜잭션 관리 방식 설정
트랜잭션(Transaction)이란, 여러개의 데이터 변경작업을 하나의 작업으로 묶은 것이다.
트랜잭션 관리 유형 | 설명 |
---|---|
JDBC | 직접 JDBC의 커밋, 롤백기능을 사용하여 mybatis 자체에서 트랜잭션을 관리 |
MANAGED | 서버의 트랜잭션 관리 기능을 이용. 즉 Java EE애플리케이션 서버(JBoss, WebLogic, WebSphere등)나 서블릿 컨테이너(톰캣서버 등)에서 트랜잭션을 관리 |
<transactionManager type="JDBC">
데이터소스설정
mybatis는 JDBC 표준 인터페이스인 javax.sql.DataSource구현체를 이용하여 DB 커넥션을 다룬다.
데이터소스 유형 | 설명 |
---|---|
UNPOOLED | DB커넥션을 요청할 때마다 매번 커넥션 객체를 생성한다. 높은 성능을 요구하지 않는 단순한 어플리케이션에 적합하다. |
POOLED | 미리 DB커넥션 객체를 생성해두고, 요청하면 즉시 반환한다. 데이터베이스에 연결하는 과정 즉 연결을 초기화하고 사용자를 인증하는 과정이 없기 때문에 속도가 빠르다. |
JNDI | Java EE 어플리케이션 서버나 서블릿 컨테이너에서 제공하는 데이터 소스를 사용한다. |
//<properties>태그에서 설정된 파일을 참조해서 사용함
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/studydb"/>
</dataSource>
<mappers>
엘리먼트<mappers>
태그는 SQL 맵퍼 파일들의 정보를 설정할 때 사용한다. 각각의 SQL 맵퍼 파일의 정보는 <mapper>
태그로 정의한다.
SQL맵퍼 파일의 경로를 설정할 때, 두가지 방법이 있다. 자바 클래스 경로를 사용하는 방법과 운영체제의 파일 시스템 경로를 사용하는 방법이 있다.
//클래스 경로를 사용할 경우
<mappers>
<mapper resource="spms/dao/MySqlProjectDao.xml"/>
<mapper resource="spms/dao/MySqlMemberDao.xml"/>
</mappers>
//파일 시스템 경로를 사용할 경우
<mappers>
<mapper url="file:///c:/dao/MySqlProjectDao.xml"/>
</mappers>