MyBatis를 쓰는 이유
JDBC를 직접 사용하면 SQL 쿼리를 실행하기 위한 여러 단계를 직접 관리해야 합니다. 예를 들어, 데이터베이스 연결을 생성하고, 쿼리를 준비하고, 파라미터를 설정하고, 쿼리를 실행하고, 결과를 처리하고, 마지막으로 연결을 닫는 등의 작업을 수행해야 합니다. 이 과정에서 예외 처리와 자원 관리를 제대로 하지 않으면 문제가 발생할 수 있습니다.
반면, MyBatis를 사용하면 이러한 작업들이 대부분 자동화됩니다. SQL 쿼리는 XML 파일이나 어노테이션을 통해 명시하고, MyBatis가 이 쿼리를 실행하고 결과를 자동으로 Java 객체에 매핑해줍니다. 따라서 개발자는 복잡한 JDBC 코드를 작성하는 대신 비즈니스 로직에 집중할 수 있습니다.
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());
}
}
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돌려줌
}
}
Customer c = session.selectOne("com.my.customer.mapper.CustomerMapper.selectById", id);
이 코드는 MyBatis를 사용하여 com.my.customer.mapper.CustomerMapper.selectById
에 정의된 SQL 쿼리를 실행하고, 그 결과를 Customer
타입의 객체에 매핑하여 반환하는 것을 의미합니다.
자세히 살펴보면:
session.selectOne(...)
: MyBatis SqlSession
의 selectOne
메소드를 호출하여 SQL 쿼리를 실행합니다. 이 메소드는 주어진 파라미터로 SQL 쿼리를 실행하고, 결과를 하나의 객체로 반환합니다. 만약 쿼리의 결과가 여러 개의 레코드인 경우, 첫 번째 레코드만 반환하고 나머지는 무시합니다.
"com.my.customer.mapper.CustomerMapper.selectById"
: MyBatis 설정 파일에서 정의된 SQL 쿼리의 식별자입니다. 이 식별자는 mapper
네임스페이스와 select
태그의 id
속성을 합친 형태로 이루어져 있습니다.
id
: selectOne
메소드의 두 번째 파라미터로, SQL 쿼리에 전달될 실제 파라미터 값입니다. 이 값은 쿼리 문자열 내의 #{id}
부분에 대입됩니다.
Customer c = ...
: selectOne
메소드가 반환하는 객체를 Customer
타입의 변수 c
에 할당합니다. 이 객체는 resultType="com.my.customer.dto.Customer"
속성에 따라 Customer
클래스의 인스턴스로 생성되고, 쿼리의 결과 레코드가 이 객체의 필드에 매핑됩니다.
결과적으로, 이 코드는 id
에 해당하는 고객 정보를 데이터베이스에서 조회하고, 그 정보를 Customer
객체로 변환하여 반환하는 작업을 수행합니다.
코드 정리
이 XML 파일은 MyBatis의 mapper 파일입니다. 이 파일은 데이터베이스 쿼리와 그에 대한 결과 처리 방식을 명시하며, 각 쿼리는 Java에서 호출될 수 있습니다.
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.my.customer.mapper.CustomerMapper">
<mapper>
태그는 이 XML 파일이 MyBatis의 mapper 파일임을 명시합니다.namespace
속성은 이 mapper의 유일한 식별자입니다. Java 코드에서 이 mapper를 참조할 때 이 namespace를 사용합니다.<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
) 타입의 파라미터를 받습니다.SELECT * FROM customer WHERE id=#{id}
#{id}
부분은 쿼리를 실행할 때 실제 파라미터 값으로 대체됩니다.<insert id="makeId" parameterType="map">
<insert>
태그는 SQL INSERT 쿼리를 정의합니다.id
속성은 이 쿼리의 식별자입니다.parameterType
속성은 이 쿼리가 받을 파라미터의 타입을 명시합니다. 여기서는 맵(map
) 타입의 파라미터를 받습니다.INSERT INTO customer(id, pwd, name) VALUES (#{i},#{p},#{n})
#{i}
, #{p}
, #{n}
부분은 쿼리를 실행할 때 실제 파라미터 값으로 대체됩니다. 이 파라미터들은 맵의 키로 참조됩니다.</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로 반환된다.
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
의 각 엔트리는 쿼리 내부의 #{키}
표현식을 통해 참조됩니다.
정리가 잘 된 글이네요. 도움이 됐습니다.