Spring MyBatis (1)

강서진·2024년 1월 11일
0

Spring

목록 보기
12/18
post-custom-banner

2번째 필수강의 ch4 1, 2강 요약

MyBatis

  • SQL 맵핑 프레임워크
    자바코드와 SQL을 맵핑해준다는 뜻으로, SQL을 별개의 xml로 구분하여 관리하고, 자바 코드에서 이를 간단한 코드로 사용할 수 있게 해준다.
    JPA는 객체지향 프로그래밍뿐만 아니라, SQL과 DB모델링에도 능숙해야 하기 때문에 MyBatis보다 사용이 어렵다. 또, 실무에서는 SQL문이 엄청나게 복잡할 수도 있기 때문에 오히려 JPA가 더 힘들 수 있다. 따라서 JPA가 MyBatis보다 무조건 좋다 같은 식으로 상위호환이 되는 것이 아니라 상황에 맞게 사용하는 것이 옳다.
    매개변수 설정 및 쿼리 결과를 읽어오는 코드들을 제거해줌으로써, 코드를 간결하게 해주기 때문에 생산성이 향상되고 유지보수가 편리해진다.

프로젝트 세팅 중 오류

강의 자료를 따라 실습 프로젝트를 설정하였는데, 서버를 시작하면 자꾸 강의와는 다르게

origin 서버가 대상 리소스를 위한 현재의 representation을 찾지 못했거나, 그것이 존재하는지를 밝히려 하지 않습니다.
...
this web application instance has been stopped already. could not load []. the following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.

이런 오류가 발생했다. 그래서 Tomcat clean을 해보았는데, 그래도 별반 달라지는 게 없었다.

- IntelliJ에서 Tomcat clean하기
File-Invalidate Caches-Clear filesystem cache and Local History 체크하고 Invalidate and Restart

root-context.xml을 자세히 살펴보다가, MySQL 드라이버 패키지 경로가 변경되기 이전 버전으로 되어 있어서 제대로 바꿔주었더니 홈 화면이 제대로 나오는 것을 확인할 수 있었다.

<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

🔽

<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>

이 실습에서 사용할 새로운 테이블을 springbasic에 하나 만들어준다.

CREATE TABLE `springbasic`.`board` (
  `bno` INT NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(100) NOT NULL,
  `content` TEXT NOT NULL,
  `writer` VARCHAR(30) NOT NULL,
  `view_count` INT NULL DEFAULT 0,
  `comment_count` INT NULL DEFAULT 0,
  `reg_date` DATETIME NULL DEFAULT now(),
  `up_date` DATETIME NULL DEFAULT now(),
  PRIMARY KEY (`idboard`));

강의에서는 MyBatis 3.5.9, MyBatis Spring 2.0.7을 사용했다. (이미 강의자료 pom.xml에 포함되어 있다.)


SQL 명령을 수행하는데 필요한 메서드를 제공하는 것은 SqlSession인데, 이를 생성하려면 SqlSessionFactory가 필요하다. MyBatis가 제공하는 인터페이스로, 이 두 모듈을 구현한 것이 MyBatis-Spring의 SqlSessionTemplate, SqlSessionFactoryBean이다.
따라서 SqlSessionTemplate과 SqlSessionFactoryBean을 빈으로 등록하여 사용하면 된다.
다음의 root-context.xml이 두 빈을 등록한 부분이다.
Mapper.xml은 sql을 저장해둔 xml 문서이다(일단은 든 내용이 없어 서버를 시작할 때 오류때문에 주석처리 해두었다). 여러 Mapper 문서가 있기 때문에 앞에 *가 붙어 있다.

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="configLocation"  value="classpath:mybatis-config.xml"/>
		<!--property name="mapperLocations" value="classpath:mapper/*Mapper.xml"/-->
	</bean>

	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg ref="sqlSessionFactory"/>
	</bean>

SqlSessionTemplate는 thread-safe이다.
thread-safe라는 것은 멀티 스레드라는 말로, 여러 DAO가 하나의 SqlSessionTemplate을 공유 가능하다는 뜻이다.
즉, 여러 스레드가 동시에 접근해도 안전하다는 것이다.


SqlSession의 주요 메서드

메서드설명
int insert()insert문을 실행 후 insert된 행의 갯수 반환
int delete()delete문을 실행 후 delete된 행의 갯수 반환
int update()update문을 실행 후 update된 행의 갯수 반환
T selectOne()하나의 행을 반환하는 select에 사용 (parameter로 SQL로 binding될 값을 제공)
List<E> selectList()여러 행을 반환하는 select에 사용 (parameter로 SQL로 binding될 값을 제공)
Map<K,V> selectMap()여러 행을 반환하는 select에 사용 (keyCol에 Map의 Key로 사용할 칼럼 지정)

Mapper 문서들은
1. namespace:
사용할 SQL문들이 겹치지 않도록 구별해주는 이름
2. id:
호출할 SQL문 이름
3. parameterType:
받을 parameter 타입(BoardDto라면 "com.fastcampus.ch4.domain.BoardDto")
4. resultType
반환할 결과값의 타입(parameterType과 마찬가지로 작성)

등을 가진다. 이 때 패키지명을 전부 작성하는 것은 길고 번거롭기 때문에 typeAliases(=별명)를 사용해 이름을 짧게 바꿀 수 있다.
예를 들어 int 같은 경우에는 java.lang.Integer라고 써줘야 하지만 built-in으로 alias가 설정되어 있어 int라고만 적어도 문제가 없다.
커스텀으로 alias를 설정하려면, mybatis-config.xml 문서에 alias를 등록해주면 된다.

<typeAliases>
	<typeAlias alias="BoardDto" type="com.fastcampus.ch4.domain.BoardDto" />
</typeAliases>

다만 alias는 대소문자 구분을 하지 않는다.


DTO

각 계층마다 담당하는 기능을 분할하게 되면서 계층 사이에 데이터를 담아 이동시킬 수 있는 객체가 필요하게 되었다. 이를 DTO라고 하며, 이 DTO는 엄연히 말하면 VO와는 다르다. Value Object는 값이 담긴 객체로, Immutable 한 특성을 가지고 있다. 즉, 변경이 불가하다는 것이다. 하지만 DTO는 데이터를 담고, 꺼내고, 변경할 수 있어야 하기 때문에 같다고 볼 수 없다.

예외처리

commit과 rollback은 try-catch로 처리하게 되는데, repository layer인 DAO에서 예외를 처리하게 될 경우 business layer의 트랜잭션은 예외가 발생했는지 안했는지를 알 수 없다. 따라서 DAO에서는 예외처리를 하지 않고 무조건 예외를 던진다.
예외를 받은 Service에게는 예외를 처리할지, 보고할지(=던질지) 선택지가 있다. 여기서는 처리할 수 있는 예외는 처리하고, 감당할 수 없는 예외는 Controller에게로 넘긴다. 혹은 예외를 처리하는 동시에 예외를 되던지기도 한다.
이 3가지 선택지를 두고 예외처리를 하면 되는데, 잘 모르겠으면 컨트롤러로 넘겨서 처리하게 두면 된다고 한다.

BoardDao

먼저 mapper 패키지에 boardMapper.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.fastcampus.ch4.dao.BoardMapper">
    <select id="select" parameterType="int" resultType="BoardDto">
        select bno,
            title,
            content,
            writer,
            view_count,
            comment_count,
            reg_date,
            up_date
        from springbasic.board
        where bno = ${bno}
    </select>
</mapper>

다음으로 이 select를 사용해 BoardDao를 작성한다.

public class BoardDao {
    @Autowired
    SqlSession session;
    String namespace = "com.fastcampus.ch4.dao.BoardMapper.";

    BoardDto select(int bno) throws Exception{
        return session.selectOne(namespace+"select", bno);
    }
}

session을 생성하여 사용하며, Mapper에 id로 설정한 이름으로 부르더라도 앞에 namespace가 붙어야 하므로 문자열로 선언을 해준다.
사용될 다른 메서드들도 생성한 후, BoardDao에서 인터페이스를 추출하면 코드의 재사용성을 높일 수 있다.

  • #{}과 ${}의 차이
    - #{}는 입력되는 값의 타입을 자동으로 감지하고 따옴표를 붙이거나 붙이지 않으며, PreparedStatement로 사용된다. PreparedStatement는 SQL Injection을 막을 수 있어 안정적이다.
    - ${}는 해당 값이 문자열로 들어가며, 따옴표를 알아서 추가해주어야 한다. Statement로 사용되며, 값만 받는 게 아니라 테이블 이름 명에도 넣을 수 있어 훨씬 더 사용이 유연하다. SQL Injection은 막지 못하지만, 동적으로 SQL을 사용해야 할 때는 필요하기 때문에 외부에서 입력받은 값을 사용하는 것이 아니라 내부적으로 사용해야 한다.
  • xml 내의 특수 문자 처리
    특수문자 중 <, >, & 등은 태그로 오인될 수 있기 때문에 변환하여 입력해야 한다. <는 <, >는 > 으로 변환해서 입력해주어야 하며, 특수문자가 많이 포함된 쿼리일 경우에는 <![CDATA[와 ]]>로 감싸 표현할 수 있다.

Test

작성한 BoardDao를 BoardDaoImpl로 이름을 바꾸고, BoardDao 인터페이스를 추출한다. 이 BoardDaoImpl을 테스트 할 때는 root-context.xml에서 주석처리했던 mapperLocations를 다시 살려야 한다.

post-custom-banner

0개의 댓글