MyBatis

cy8erpsycho·2023년 7월 23일
1

MyBatis

목록 보기
1/2
post-thumbnail

MyBatis


1. 기본 설정

MyBatis를 쓰는 이유

JDBC를 직접 사용하면 SQL 쿼리를 실행하기 위한 여러 단계를 직접 관리해야 합니다. 예를 들어, 데이터베이스 연결을 생성하고, 쿼리를 준비하고, 파라미터를 설정하고, 쿼리를 실행하고, 결과를 처리하고, 마지막으로 연결을 닫는 등의 작업을 수행해야 합니다. 이 과정에서 예외 처리와 자원 관리를 제대로 하지 않으면 문제가 발생할 수 있습니다.

반면, MyBatis를 사용하면 이러한 작업들이 대부분 자동화됩니다. SQL 쿼리는 XML 파일이나 어노테이션을 통해 명시하고, MyBatis가 이 쿼리를 실행하고 결과를 자동으로 Java 객체에 매핑해줍니다. 따라서 개발자는 복잡한 JDBC 코드를 작성하는 대신 비즈니스 로직에 집중할 수 있습니다.

  • 객체 지향 (클래스 재사용성을 높이자 = 재컴파일X)
  • 재배포(Redeploy)할 때 시간이 오래 걸린다.
  • 소스와 관련없는/자바와 관련없는 것들을 분리한다.
  • SQL구문을 떼어내서 따로 관리한다.(XML)


CustomerRepository.java

public class CustomerRepository {
	public Customer selectById(String id) 
			throws FindException{
		Connection conn = null;
		try {
			conn = MyConnection.getConnection();
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
			throw new FindException(e.getMessage());
		}
		String selectSQL = "SELECT *\r\n"
				+ "FROM customer\r\n"
				+ "WHERE id=?";
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			pstmt = conn.prepareStatement(selectSQL);
			pstmt.setString(1, id);
			rs = pstmt.executeQuery();
			if(rs.next()) {
				return new Customer(
						rs.getString("id"),
						rs.getString("pwd"),
						rs.getString("name")
						);
			}else {
				throw new FindException("고객이 없습니다");
			}
		} catch (SQLException e) {			
			e.printStackTrace();
			throw new FindException(e.getMessage());
		}
		
	}
  1. 위의 코드에서 JDBC용 connection 관련 부분을 제거한 후 아래와 같이 MyBatis를 쓰겠다는 코드를 작성한다.(JDBC의 커넥션 객체와 MyBatis의 세션객체가 같은 역할을 한다.)
public class CustomerRepository {
	private SqlSessionFactory sessionFactory;

	public CustomerRepository() {
		String resource = "/mybatisconfig/mybatis-config.xml";
		InputStream inputStream;
		try {
			inputStream = Resources.getResourceAsStream(resource);
			sessionFactory = 
					new SqlSessionFactoryBuilder().build(inputStream);

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public Customer selectById(String id) throws FindException {
		
		SqlSession session = null;
		try {
			session = sessionFactory.openSession();// Connection과 같은 뜻
			Customer c = session.selectOne("com.my.customer.mapper.CustomerMapper.selectById", id); // 한 행만 검색
			// session.selectList() 여러 행 검색
			if (c == null) {
				throw new FindException("고객이 없습니다");
			}
			System.out.println("c.id=" + c.getId() + ", c.pwd=" + c.getPwd() + ", c.name=" + c.getName());
			return c;
		} catch (Exception e) {
			e.printStackTrace();
			throw new FindException(e.getMessage());
		} finally {
			if (session != null) {
				session.close(); // DBCP에게 Connection돌려줌
			}
		}


  1. 그 후, xml파일을 생성(일반 파일 생성하고 확장자만 .xml로 하면 된다)한다. MyBatis 공식홈페이지에 있는 xml양식코드를 그대로 불어넣기 해준다.


2. 코드설명

Customer c = session.selectOne("com.my.customer.mapper.CustomerMapper.selectById", id);

이 코드는 MyBatis를 사용하여 com.my.customer.mapper.CustomerMapper.selectById에 정의된 SQL 쿼리를 실행하고, 그 결과를 Customer 타입의 객체에 매핑하여 반환하는 것을 의미합니다.

자세히 살펴보면:

  1. session.selectOne(...): MyBatis SqlSessionselectOne 메소드를 호출하여 SQL 쿼리를 실행합니다. 이 메소드는 주어진 파라미터로 SQL 쿼리를 실행하고, 결과를 하나의 객체로 반환합니다. 만약 쿼리의 결과가 여러 개의 레코드인 경우, 첫 번째 레코드만 반환하고 나머지는 무시합니다.

  2. "com.my.customer.mapper.CustomerMapper.selectById": MyBatis 설정 파일에서 정의된 SQL 쿼리의 식별자입니다. 이 식별자는 mapper 네임스페이스와 select 태그의 id 속성을 합친 형태로 이루어져 있습니다.

  3. id: selectOne 메소드의 두 번째 파라미터로, SQL 쿼리에 전달될 실제 파라미터 값입니다. 이 값은 쿼리 문자열 내의 #{id} 부분에 대입됩니다.

  4. Customer c = ...: selectOne 메소드가 반환하는 객체를 Customer 타입의 변수 c에 할당합니다. 이 객체는 resultType="com.my.customer.dto.Customer" 속성에 따라 Customer 클래스의 인스턴스로 생성되고, 쿼리의 결과 레코드가 이 객체의 필드에 매핑됩니다.

결과적으로, 이 코드는 id에 해당하는 고객 정보를 데이터베이스에서 조회하고, 그 정보를 Customer 객체로 변환하여 반환하는 작업을 수행합니다.

코드 정리

이 XML 파일은 MyBatis의 mapper 파일입니다. 이 파일은 데이터베이스 쿼리와 그에 대한 결과 처리 방식을 명시하며, 각 쿼리는 Java에서 호출될 수 있습니다.

  1. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

    • 이 줄은 이 XML 문서가 MyBatis 3.0 mapper DTD(Document Type Definition)에 따라 작성되었다는 것을 알립니다. DTD는 XML 문서의 구조를 정의하는 규칙입니다.
  2. <mapper namespace="com.my.customer.mapper.CustomerMapper">

    • <mapper> 태그는 이 XML 파일이 MyBatis의 mapper 파일임을 명시합니다.
    • namespace 속성은 이 mapper의 유일한 식별자입니다. Java 코드에서 이 mapper를 참조할 때 이 namespace를 사용합니다.
  3. <select id="selectById" resultType="com.my.customer.dto.Customer" parameterType="string">

    • <select> 태그는 SQL SELECT 쿼리를 정의합니다.
    • id 속성은 이 쿼리의 식별자입니다. Java 코드에서 이 쿼리를 호출할 때 이 id를 사용합니다.
    • resultType 속성은 이 쿼리의 결과가 매핑될 Java 클래스를 명시합니다. 여기서는 com.my.customer.dto.Customer 클래스로 매핑됩니다.
    • parameterType 속성은 이 쿼리가 받을 파라미터의 타입을 명시합니다. 여기서는 문자열(string) 타입의 파라미터를 받습니다.
  4. SELECT * FROM customer WHERE id=#{id}

    • 이 부분은 실제 SQL 쿼리입니다. #{id} 부분은 쿼리를 실행할 때 실제 파라미터 값으로 대체됩니다.
  5. <insert id="makeId" parameterType="map">

    • <insert> 태그는 SQL INSERT 쿼리를 정의합니다.
    • id 속성은 이 쿼리의 식별자입니다.
    • parameterType 속성은 이 쿼리가 받을 파라미터의 타입을 명시합니다. 여기서는 맵(map) 타입의 파라미터를 받습니다.
  6. INSERT INTO customer(id, pwd, name) VALUES (#{i},#{p},#{n})

    • 이 부분은 실제 SQL 쿼리입니다. #{i}, #{p}, #{n} 부분은 쿼리를 실행할 때 실제 파라미터 값으로 대체됩니다. 이 파라미터들은 맵의 키로 참조됩니다.
  7. </mapper>

    • </mapper> 태그는 <mapper> 태그를 닫는 부분입니다. 이 태그 이후에는 다른 쿼리를 정의할 수 없습니다.

이 XML 파일은 Java에서 다음과 같이 사용됩니다:

// 쿼리를 실행하고, 그 결과를 Customer 객체로 받습니다.
Customer c = session.selectOne("com.my.customer.mapper.CustomerMapper.selectById", id);

// map 파라미터를 만들고 쿼리를 실행합니다.
Map<String, Object> params = new HashMap<>();
params.put("i", id);
params.put("p", pwd);
params.put("n", name);
session.insert("com.my.customer.mapper.CustomerMapper.makeId", params);

id 값이 {aaa}에 자동대입된다. { } 중괄호 안에는 어떤 문자가 와도 상관이 없다. PreparedStament 에서의 ? 와 비슷하다.

실행된 결과 값은 Customer 타입의 객체 c로 반환된다.


3. 정리

MyBatis를 사용하면 JDBC를 직접 다루는 대신, MyBatis가 대신 JDBC 작업을 처리해주므로 코드가 더 간결해집니다.

먼저, 해당 기능을 위한 SQL 쿼리를 MyBatis mapper XML 파일에 정의해야 합니다:

CustomerMapper.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="com.my.customer.mapper.CustomerMapper">
    <insert id="makeId" parameterType="map">
        INSERT INTO customer(id, pwd, name) VALUES (#{i},#{p},#{n})
    </insert>
</mapper>

다음으로, 해당 메서드를 MyBatis를 사용하여 다음과 같이 수정할 수 있습니다:

public void makeId(String id, String pwd, String name) throws AddException {
    Map<String, Object> param = new HashMap<>();
    param.put("i", id);
    param.put("p", pwd);
    param.put("n", name);
    
    SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession();
    try {
        int rowcnt = session.insert("com.my.customer.mapper.CustomerMapper.makeId", param);
        if (rowcnt == 0) {
            throw new AddException("회원가입실패");
        }
        session.commit();
    } catch (Exception e) {
        session.rollback();
        throw new AddException(e.getMessage());
    } finally {
        session.close();
    }
}

session.commit();을 해야 DB에 값이 들어간다.

이렇게 변환하면, MyBatis가 대부분의 JDBC 작업을 처리하므로 코드가 더 간결해지고 가독성이 향상됩니다. 또한 SQL 쿼리는 XML 파일에 별도로 저장되므로, 쿼리 관리도 더 효율적으로 수행할 수 있습니다.

resultType이 없는 이유

<insert> 태그에서는 resultType 속성이 필요하지 않습니다. 왜냐하면 INSERT 쿼리는 행을 삽입하고, 삽입된 행의 수를 반환하지만 특정 결과 객체를 반환하지 않기 때문입니다.

resultType은 SQL 쿼리가 실행된 후 반환되는 결과를 어떤 타입으로 매핑할지를 명시하는 속성입니다. SELECT 쿼리와 같이 결과를 반환하는 쿼리에서는 resultType 속성을 사용하여 결과의 타입을 지정합니다.

반면에 INSERT, UPDATE, DELETE와 같은 쿼리는 쿼리 실행 후 특정 결과 객체를 반환하지 않습니다. 대신 이런 쿼리는 영향을 받은 행의 수를 반환합니다. 따라서 이런 쿼리를 정의하는 <insert>, <update>, <delete> 태그에서는 resultType 속성을 사용하지 않습니다.

<insert id="makeId" parameterType="map">에서 parameterType="map"은 이 INSERT 쿼리에 전달되는 파라미터의 타입이 Map임을 나타냅니다. 이 Map의 각 엔트리는 쿼리 내부의 #{키} 표현식을 통해 참조됩니다.

참고 https://mybatis.org/mybatis-3/getting-started.html

1개의 댓글

comment-user-thumbnail
2023년 7월 23일

정리가 잘 된 글이네요. 도움이 됐습니다.

답글 달기

관련 채용 정보