BackEnd/WEB

ORM_DB프레임워크_국비_DAY72

Leo.K 2022. 6. 14. 16:46

ORM(Object Relationship Mapper) -> 데이터베이스 프레임워크를 통칭하는 말

  • DB와 객체를 연결시켜주는 프레임워크 (DB와 자바의 객체(Vo)와 자동으로 mapping 해주는 객체)
  • DB <=> Object처리하는 프레임워크

종류

  • Hibernate(ORM성격이 짙다. 객체의 속성명과 DB의 컬럼명을 일치시켜서 연결시켜놓는다.) : SQL 문장X 
    • 자바에서 지원하는 객체는 JPA가 있다.
  • Mybatis(SQL Mapper)
    • iBatis    : 2.x에 대한 명칭
    • Mybatis: 3.x에 대한 명칭

sql문장으로 구성된 id를 이용해서 호출을 하면, SqlSession이 해당 sql명령을 처리해준다.

 

[ MyBatis 작업 순서 ]

기존에 DB에 접근할때 사용했던 DBService를 사용하지 않고, MyBatisConnector를 사용할 것이다.

SqlSessionFactory은 sqlMapConfig.xml파일을 기반으로 만들어지기 때문에 파일 내부의 두 가지 정보를 알고 있다. 접근할 DB에 대한 DBCP정보, 어디로 맵핑할 것인지(sawon.xml)에 대한 환경설정 파일의 내용을 확인하여 작업 객체(SqlSession : 인터페이스)를 생성한다. 이제는 이 객체를 사용해서 DB에 접근하여 CRUD 명령을 수행할 수 있다.

 

위의 이미지를 실제로 코드로 구현하면서 흐름을 정리하겠다. 

1. [ 환경설정 ] ★중요★

기본적으로 DB에 접근하기 위한 라이브러리가 필요한데, 기존에 사용했던 jdbc드라이버와 dbcp처리를 위한 라이브러리에 mybatis라이브러리가 추가되었다. 혹시나 라이브러리가 없는 사람은 여기에 방문해서 라이브러리를 다운로드 받기 바란다. 필자도 처음인지라 나머지는 필자의 깃허브에서 다운로드 받으면 된다. 주소는 이곳이다.

필자가 정리한 내용 이외로 추가적인 공부를 하고싶다면 여기에 접속해서 공식 문서를 확인해보길 바란다.

준비사항으로 준비해야 하는 라이브러리들은 아래의 이미지와 같다. 추가적으로 dbcp에 대한 처리사항을 설정한 context.xml또한 반드시 가져와야 하며, 이는 필자가 제공한 파일을 그대로 사용하면 안돼고, 사용자가 사용하는 db의 계정정보를 반영하도록 수정을 거쳐야 한다.

 

2. [ service.MyBatisConnector ] 파일의 이름은 바꾸어도 무방하다.

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package service;
 
import java.io.IOException;
import java.io.Reader;
 
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 
 
public class MyBatisConnector {
    
    //mybatis 작업 객체를 만드는 공장. interface(사용설명서)이다.
    SqlSessionFactory  factory=null;
    
    //싱글톤 구조로 객체를 생성한다.
    private static MyBatisConnector connector; 
    
    //생성자로 커넥터와, 공장을 만든다.
    public MyBatisConnector()
    {
        try {
            //sqlMapConfig.xml읽어들인다.
            Reader reader = 
                Resources.getResourceAsReader("config/mybatis/sqlMapConfig.xml");
            factory = new SqlSessionFactoryBuilder().build(reader);            //읽어 들인 xml파일을 기반으로 공장을 만든다.
            reader.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    //single-ton
    public static MyBatisConnector getInstance(){
        if(connector==null)
            connector = new MyBatisConnector();
        return connector;
    }
    public SqlSessionFactory  getSqlSessionFactory()
    {
        return factory;
    }
}
 
cs
  • SqlSessionFactory 
    • Interface
    • 작업 객체(SqlSession)를 만드는 공장
    • SqlSession도 인터페이스이다. 
    • MyBatisConnector() 생성자가 호출될 때 build된다. 
    • sqlMapConfig.xml을 읽어들여서 그 안에 두 가지 설정정보를 기반으로 공장이 만들어진다.
    • <environments>태그 내부에서 우리가 사용하는 DB로부터 연결정보(Connection)를 가져오기 위해 JNDI기법으로 찾는 과정을 정의한다. 세부과정을 정리한 블로그는 여기에서 확인하길 바란다. 링크 내용을 보면 기존에 사용했던 DBService내부에는 JNDI기법을 사용해서 직접 찾는 방법이 설명되어 있는데, 이렇게 xml파일에 환경설정 정보를 담아서 과정을 단순화 시킬 수 있다. 당연히 두 방법 모두 context.xml파일은 존재해야 한다.
    • <mappers> 공장이 생성한 SqlSession이 작업하는 영역이 어디인지 Mapping해주는 설정이다. http://mybatis.org/dtd/mybatis-3-mapper.dtd이 사이트에 있는 DTD를 참조하여 태그를 사용하기 때문에 이 영역에서는 xml의 장점인 사용자가 원하는 태그를 만들어서 사용할 수 없다. DTD에 정의된 태그만을 사용해야 한다.
    • 이처럼 위의 두 설정 정보를 기반으로 공장(SqlSessionFactory)을 만들기 때문에 공장이 만들어 주는 작업객체(SqlSession) 또한 해당 설정 정보를 알고 있고, 실제 CRUD작업을 수행하는 객체이다.

3. [ config.mybatis.sqlMapConfig.xml ] 환경설정 정보

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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>
    <environments default="">
        <environment id="">        <!-- DB에 연결하는 작업 -->
            <transactionManager type="JDBC" />
            <dataSource type="JNDI">
                <property name="data_source" 
                          value="java:comp/env/jdbc/oracle_test" />            
                <!-- JNDI를 쓸것이라고 명시하고 value값만 정확하게 주면 된다. -->
            </dataSource>
        </environment>
    </environments>
    
    <!-- Mapping하는 작업 -->
    <mappers>
        <mapper resource="config/mybatis/mapper/sawon.xml" />
    </mappers>
</configuration>
cs

4. [ config.mybatis.mapper.sawon.xml ]

조회시에는 DTD에 정의된 <select>태그를 사용한다. 

  •  id              : mybatis에게 동일한 id값을 가진 태그를 찾아서 명령을 수행하고, 결과를 resultType으로 반환하도록 하기 위해서 사용하는 속성
  • resultType : 태그 내부에 정의된 SQL명령을 수행하고 그 결과를 포장할 타입을 지정한다. 여기서는 SawonVo로 포장을 하고 이 결과를 list로 반환한다. 리스트로 반환하는 코드는 Dao에서 작성하기 때문에 잘 연결해서 확인해야 한다. 
  • <mapper> 태그에는 namespace 속성이 있다. 여러 사람이 사용하다 보면 태그의 id값이 동일해서 충돌이 나는 에러가 발생할 수 있는데, 이를 방지하기 위해 각 태그의 영역을 지정해준다. 영역을 지정해줄때 사용하는 속성이 namespace이다.
  • Vo에서 DB의 컬럼명과 Vo의 속성명을 일치시키라고 강조를 했다. 그 이유는 여기서 발생하는데, 이름을 일치시켰다면, Mybatis가 자체적으로 resultType에 지정된 Vo에 접근하여 세터함수를 호출함으로써 포장을 한다. 하지만 이름을 일치시키지 않은 경우에는 아래와 같은 복잡한 과정으로 mapping을 해주어야 한다. 실제로 사용하는 코드와 비교해보면서 이름을 동일시키라는 의미를 확실하게 이해하고 넘어가야 한다.  
  • dao에서 접근을 할 때는 dot방식을 사용하여 다음과 같은 방법으로 접근한다. namespace.mapper_id 
  • 결국 mybatis를 사용하면 mapper만 오류없이 잘 만들면 된다!

[ 이름을 일치시키지 않은 경우 ]

1
2
3
4
5
6
7
8
9
<resultMap type="vo.SawonVo" id="sawonMap">
    
             <!-- DB컬럼명      Vo의 속성명 -->
    <result column="sabun" property="sabun1"/>
</resultMap>
    
<select id="sawon_list" resultMap="sawonMap">
    select * from sawon
</select>
cs

[ 이름을 일치시킨 경우 - 실제 사용 코드 ]

1
2
3
<select id="sawon_list" resultType="vo.SawonVo">
    select * from sawon
</select>
cs

[ 전체 코드 ]

1
2
3
4
5
6
7
8
9
10
11
<?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="sawon">
 
    <select id="sawon_list" resultType="vo.SawonVo">
        select * from sawon
    </select>
</mapper>
 
cs

 

5. [ vo.SawonVo ]

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package vo;
 
public class SawonVo {
    //DB의 컬럼명 == Vo의 속성명        MyBatis를 사용할때는 필수이다!!
    int     sabun;
    String  saname;
    String  sajob;
    String  sasex;
    String  sahire;
    int        deptno;
    int        samgr;
    int     sapay;
    
    
    public int getSabun() {
        return sabun;
    }
    public void setSabun(int sabun) {
        this.sabun = sabun;
    }
    public String getSaname() {
        return saname;
    }
    public void setSaname(String saname) {
        this.saname = saname;
    }
    public String getSajob() {
        return sajob;
    }
    public void setSajob(String sajob) {
        this.sajob = sajob;
    }
    public String getSasex() {
        return sasex;
    }
    public void setSasex(String sasex) {
        this.sasex = sasex;
    }
    public String getSahire() {
        return sahire;
    }
    public void setSahire(String sahire) {
        this.sahire = sahire;
    }
    public int getDeptno() {
        return deptno;
    }
    public void setDeptno(int deptno) {
        this.deptno = deptno;
    }
    public int getSamgr() {
        return samgr;
    }
    public void setSamgr(int samgr) {
        this.samgr = samgr;
    }
    public int getSapay() {
        return sapay;
    }
    public void setSapay(int sapay) {
        this.sapay = sapay;
    }
    
}
 
cs

 

6. [ action.SawonListAction ]

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package action;
 
import java.io.IOException;
import java.util.List;
 
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import dao.SawonDao;
import vo.SawonVo;
 
/**
 * Servlet implementation class SawonListAction
 */
@WebServlet("/sawon/list.do")
public class SawonListAction extends HttpServlet {
    private static final long serialVersionUID = 1L;
 
    /**
     * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
     */
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        
        List<SawonVo> list = SawonDao.getInstance().selectList();
        
        request.setAttribute("list", list);
 
        //forward
        String forward_page = "sawon_list.jsp";
        RequestDispatcher disp = request.getRequestDispatcher(forward_page);
        disp.forward(request, response);
 
    }
 
}
 
cs

 

7. [ sawon_list.jsp ]

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
사원목록...
 
<table border="1">
    <tr>
        <th>사번</th>
        <th>이름</th>
        <th>직위</th>
        <th>성별</th>
        <th>부서번호</th>
        <th>입사일자</th>
        <th>상사번호</th>
        <th>연봉</th>
    </tr>
    <c:forEach var="vo" items="${list }">
        <tr>
            <td>${vo.sabun }</td>
            <td>${vo.saname }</td>
            <td>${vo.sajob }</td>
            <td>${vo.sasex }</td>
            <td>${vo.deptno }</td>
            <td>${vo.sahire }</td>
            <td>${vo.samgr }</td>
            <td>${vo.sapay }</td>
        </tr>
    </c:forEach>
    
</table>
</body>
</html>
cs

 

8. [ dao.SawonDao ]

MybatisConnector에서 생성한 SqlSessionFactory를 여기에서 사용한다.

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package dao;
 
import java.util.List;
 
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
 
import service.MyBatisConnector;
import vo.SawonVo;
 
public class SawonDao {
    
    SqlSessionFactory factory;
    
    //single-ton : 객체 1개만 생성해서 사용하자
    static SawonDao single = null;
 
    public static SawonDao getInstance() {
 
        //객체가 생성되어 있지 않으면 만들어라.
        if (single == null) {
            single = new SawonDao();
        }
        //이전에 만들어 놨던 객체를 그대로 반환한다.
        return single;
    }
 
    //외부에서 생성하지 못하도록 접근제한. 객체는 getInstance메소드를 통해서만 생성가능.
    private SawonDao() {
        // TODO Auto-generated constructor stub
        //MyBatisConnector가 생성한 팩토리를 반환하는 코드
        factory = MyBatisConnector.getInstance().getSqlSessionFactory();
    }
    
    
    //사원목록 가져오기
    public List<SawonVo> selectList(){
        List<SawonVo> list = null;
        //1. SqlSession생성(작업자)
        //factory가 JNDI에 의해 만들어진 DBCP에 대한 정보를 알기 때문에 connection을 얻어온다. 
        //mapper정보를 사용할 수도 있다.
        SqlSession sqlSession = factory.openSession();//Connection 얻어온다. 단. 자원을 얻어왔으면 반드시 반납해야한다.!!
        
        //2.작업수행            형식: namespace.mapper_id
        list = sqlSession.selectList("sawon.sawon_list");
        
        //3. factory가 얻어와서 sqlSession에게 전해준 Connection을 닫는다.
        //닫아줘야 basicdatasource가 새로운 커넥션 객체를 maxsize에 맞게 새로 채워준다.
        sqlSession.close();
        
        return list;
    }
}
 
cs

 

[ 정리 ] 

  1. MybatisConnector안에서 생성자를 통해 SqlSessionFactory인터페이스를 생성한다.(build)
  2. 생성할 때는 sqlMapConfig.xml파일을 기반으로 생성한다. xml파일은 환경설정 정보로 DB연결을 위한 DBCP정보와 Mapping정보다.
    1. mapping정보를 만들기 위해 mappers태그에 mapper를 추가한다. -> sqlMapConfig.xml
    2. namespace와 id속성을 충돌이 나지않도록 설정한다.                     -> sawon.xml
    3. 자주 발생하는 에러 
      1. mapper id가 다른 경우 (mappert파일[ex-> sawon.xml]과 mapper파일에 접근하는 java파일[ex->Dao])
      2. Vo의 속성명과 DB의 컬럼명이 다른 경우 -> 위에서 정리한 대로 복잡한 과정을 거치면 가능하다.
      3. mapper파일의 namespace가 다른 경우 (예시는 1과 동일)
      4. config파일의 mappers태그에 mapper 파일을 등록하지 않은 경우 OR 스펠링 오타
      5. 같은 application안에서 namespace가 중복되는 경우
  3. 실제로 사용하는 Dao에서 작업객체인 sqlSession을 생성한다. 이때, 생성과 동시에 Connection객체를 factory가 가져와주는데 사용했으면 반드시 반납해야 한다.!!!!!

'BackEnd > WEB' 카테고리의 다른 글

mybatis_검색기능_국비Day74  (0) 2022.06.16
mybatis_국비Day73  (0) 2022.06.15
XML_Naver검색API_국비DAY71  (0) 2022.06.13
XML_국비_DAY70  (0) 2022.06.10
PhotoGallery[완결]_국비_DAY69  (0) 2022.06.09