4. SQL 맵퍼 파일 작성
SqlSession 객체에서 SQL을 실행할 때 참조하는 SQL 맵퍼 파일을 작성한다. spms.dao 패키지에 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" resultMap="projectResultMap">
SELECT PNO, PNAME, STA_DATE, END_DATE, STATE
FROM PROJECTS
ORDER BY PNO DESC
</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>
<update id="update" parameterType="project">
UPDATE PROJECTS
SET PNAME = #{title}
,CONTENT = #{content}
,STA_DATE = #{startDate}
,END_DATE = #{endDate}
,STATE = #{state}
,TAGS = #{tags}
WHERE PNO = #{no}
</update>
<delete id="delete" parameterType="int">
DELETE FROM PROJECTS
WHERE PNO = #{value}
</delete>
</mapper>
SQL 맵퍼 파일에서 <mapper> 태그의 namespace 값과 <select>, <insert> 태그 등 SQL 태그의 id 값은 SqlSession 객체가 SQL문을 찾을 때 사용한다. SQL 맵퍼 파일을 작성하는 자세한 방법은 다음 강의에 계속되니 지금은 'mybatis를 사용하니까 편하더라'에 집중한다.
5. ApplicationContext 변경
mybatis를 도입함에 따라 ApplicationContext 클래스를 변경해야 한다. 기존에는 프로퍼티 파일과 어노테이션을 통해 객체를 생성하였다. 그러나 SqlSessionFactory 객체는 이런 방식으로 생성할 수 없다. 별도로 SqlSessionFactory 객체를 준비하여 ApplicationContext 클래스에 등록해야 한다. 외부에서 생성한 객체를 등록할 수 있게 ApplicationContext 클래스를 변경한다.
spms.context.ApplicationContext 클래스를 열고 다음과 같이 편집한다.
package spms.context;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Set;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.reflections.Reflections;
import spms.annotation.Component;
//mybatis 적용에 필요한 변경
public class ApplicationContext {
//객체 저장할 보관소
Hashtable<String,Object> objTable = new Hashtable<String,Object>();
//객체 꺼낼 메서드(getter)
public Object getBean(String key) {
return objTable.get(key);
}
/**
* 외부에서 생성한 객체 등록
* @param name : 객체 이름
* @param obj : 객체 주소
*/
public void addBean(String name, Object obj) {
objTable.put(name, obj);
}
/**
* 프로퍼티 파일의 내용 로딩 후 객체 준비 메서드
* @param propertiesPath : 프로퍼티 파일의 경로
* @throws Exception
*/
public void prepareObjectsByProperties(String propertiesPath) throws Exception {
//'이름=값'형태로 된 파일 다루는 Properties 클래스
Properties props = new Properties();
//load() : FileReader를 통해 읽어드린 프로퍼티 내용 키-값 형태로 내부 맵에 보관
props.load(new FileReader(propertiesPath));
//JNDI 객체 찾을 때 사용할 InitialContext 준비
Context ctx = new InitialContext();
String key = null;
String value = null;
for(Object item : props.keySet()) {
key = (String)item;
value = props.getProperty(key);
if(key.startsWith("jndi.")) {
//lookup() : JNDI 인터페이스 통해 톰캣 서버에 등록된 객체 찾음
objTable.put(key, ctx.lookup(value));
}else {//사용안함
//Class.forName() 호출하여 클래스 로딩하고, newInstance() 사용하여 인스턴스 생성
objTable.put(key, Class.forName(value).newInstance());
}
}
}
/**
* 어노테이션이 붙은 클래스를 찾아 객체 준비 메서드
* @param basePackage : 어노테이션을 검색할 패키지 이름
* @throws Exception
*/
public void prepareObjectsByAnnotation(String basePackage) throws Exception {
Reflections reflector = new Reflections(basePackage);
Set<Class<?>> list = reflector.getTypesAnnotatedWith(Component.class);
String key = null;
for(Class<?> clazz : list) {
key = clazz.getAnnotation(Component.class).value();
objTable.put(key, clazz.newInstance());
}
}
/**
* 각 객체가 필요로 하는 의존 객체 주입 메서드
* @throws Exception
*/
public void injectDependency() throws Exception {
for(String key : objTable.keySet()) {
if(!key.startsWith("jndi.")) {
callSetter(objTable.get(key));
}
}
}
//매개변수로 주어진 객체에 대해 셋터 메서드 찾아서 호출
private void callSetter(Object obj) throws Exception {
Object dependency = null;
for(Method m : obj.getClass().getMethods()) {
if(m.getName().startsWith("set")) {
dependency = findObjectByType(m.getParameterTypes()[0]);
//의존 객체 찾았다면 셋터 메서드 호출
if(dependency != null) {
m.invoke(obj, dependency);
}
}
}
}
//셋터 메서드의 매개변수 타입이 일치하는 객체를 objTable에서 찾음 - 셋터 메서드 호출할 때 넘겨줄 의존 객체 찾음
private Object findObjectByType(Class<?> type) {
for(Object obj : objTable.values()) {
//타입이 일치하면 객체의 주소 리턴
if(type.isInstance(obj)) {//isInstance() : 주어진 객체가 해당 클래스 또는 인터페이스의 인스턴스인지 검사
return obj;
}
}
return null;
}
}
기존 소스에서 인스턴스 목록을 저장하는 objTable 변수와 인스턴스를 찾아주는 getBean() 메서드는 그대로 둔다.
- addBean() 추가
외부에서 생성한 SqlSessionFactory 객체를 등록할 수 있게 addBean() 메서드를 추가했다. 매개변수로 객체 이름과 객체 주소를 받아서 objTable에 보관한다.
/**
* 외부에서 생성한 객체 등록
* @param name : 객체 이름
* @param obj : 객체 주소
*/
public void addBean(String name, Object obj) {
objTable.put(name, obj);
}
- 생성자 제거
기존 코드에서는 객체를 준비하고 의존 객체를 주입하는 일을 생성자에서 일괄처리하였다.
//ApplicationContext 클래스의 생성자
public ApplicationContext(String propertiesPath) throws Exception {
//'이름=값'형태로 된 파일 다루는 Properties 클래스
Properties props = new Properties();
//load() : FileReader를 통해 읽어드린 프로퍼티 내용 키-값 형태로 내부 맵에 보관
props.load(new FileReader(propertiesPath));
prepareObjects(props);
prepareAnnotationObjects();
injectDependency();
}
이제는 외부에서 객체를 주입하는 경우도 고려해야 하기 때문에 일괄처리 방식을 개별처리 방식으로 변경해야 한다. 따라서 기존 생성자를 제거하였다.
- prepareObjectsByAnnotation() 메서드
기존의 prepare AnnotationObjects() 메서드의 이름을 좀 더 이해하기 쉽게 변경하였다. 또한, 외부에서 호출해야 하기 때문에 접근 제한자를 public으로 변경하였다. 기존 메서드는 매개변수가 없었지만, 변경한 메서드는 어노테이션을 검색할 패키지 이름을 매개변수로 받는다.
//private void prepareAnnotationObjects() throws Exception {
public void prepareObjectsByAnnotation(String basePackage) throws Exception {
Reflections reflector = new Reflections(basePackage);
...
}
- prepareObjectsByProperties() 메서드
기존 prepareObjects() 메서드의 이름을 앞의 메서드 이름에 맞추어 변경하였다. 이 메서드 또한 외부에서 호출해야 하기 때문에 접근 제한자를 public으로 변경하였다. 매개변수는 Properties 객체를 직접 받는 대신, 프로퍼티 파일의 경로를 받아서 내부에서 Properties 객체를 생성하도록 변경하였다.
//private void prepareObjects(Properties props) throws Exception {
public void prepareObjectsByProperties(String propertiesPath) throws Exception {
//'이름=값'형태로 된 파일 다루는 Properties 클래스
Properties props = new Properties();
//load() : FileReader를 통해 읽어드린 프로퍼티 내용 키-값 형태로 내부 맵에 보관
props.load(new FileReader(propertiesPath));
//JNDI 객체 찾을 때 사용할 InitialContext 준비
Context ctx = new InitialContext();
...
}
- injectDependency() 메서드
의존 객체를 주입하는 injectDependency() 메서드도 외부에서 호출할 수 있게 접근 제한을 private에서 public으로 변경하였다.
//private void injectDependency() throws Exception {
public void injectDependency() throws Exception {
...
}
ApplicationContext 클래스의 나머지 메서드 (callSetter(), findObjectByType())는 기존과 같다.
참고도서 : https://freelec.co.kr/book/1674/
[열혈강의] 자바 웹 개발 워크북
[열혈강의] 자바 웹 개발 워크북
freelec.co.kr
'교재 실습 > 자바 웹 개발 워크북' 카테고리의 다른 글
107. SQL 맵퍼 파일 (1) (0) | 2022.09.01 |
---|---|
106. mybatis 적용 (4) (0) | 2022.08.31 |
104. mybatis 적용 (2) (0) | 2022.08.29 |
103. mybatis 적용 (1) (0) | 2022.08.28 |
102. mybatis 소개 (2) | 2022.08.27 |
댓글