TIL

[TIL-54/240408] MyBatis

prao 2024. 4. 9. 08:43
반응형

MyBatis

특징

  • SQL 매핑 프레임워크
  • SQL문과 저장 프로시저(Stored Procedure)등의 매핑을 지원하는 퍼시스턴스 프레임워크(persistence framework)
  • JDBC로 처리하는 상당 부분의 코드와 파라미터 설정 및 결과 처리를 대신해줌
  • Map 인터페이스 그리고 자바 POJO를 설정 데이터베이스와 매핑해서 사용 가능
  • XML과 Annotation 설정을 통해 사용할 수 있음

mybatis 처리 과정

 

MyBatis 동작 구조

MyBatis는 SQL을 처리하고 Java 객체와 DB간의 매핑을 처리한다.

동작 구조

 

MyBatis 구성 요소

MyBatis는 SqlSession 인터페이스를 통해 DB와 상호작용 한다.

흐름

  • SqlSessionFactory
    • SqlSession 객체를 생성하는 팩토리 클래스
    • 런타임 도중 CRUD 처리 요청에 따라 SqlSession 객체를 생성
    • openSession() 메소드를 통해 객체를 생성
  • SqlSession
    • SQL 실행이나 트랜잭션 관리를 위한 메소드를 가진 인터페이스
    • SELECT, INSERT, UPDATE, DELETE 구문을 수행하기 위해 사용
    • SqlSession을 반드시 닫도록 한다(try with resources로 사용 권장).

 

MyBatis - 시작하기

  • mybatis-x.x.x.jar 파일을 프로젝트에 추가
  • maven 프로젝트를 사용한다면 pom.xml에 의존성 추가
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>Spring_06_MyBatis</groupId>
	<artifactId>Spring_06_MyBatis</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<build>
		<sourceDirectory>src</sourceDirectory>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<release>17</release>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.15</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<version>8.0.33</version>
		</dependency>
	
	</dependencies>
</project>

 

MyBatis - 설정하기( XML 설정파일)

  • 설정파일 작성(resource 폴더) - mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<typeAliases>
		<typeAlias type="com.ssafy.board.model.dto.Board" alias="Board"/>
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/ssafy_board?serverTimezone=UTC" />
				<property name="username" value="ssafy" />
				<property name="password" value="ssafy" />
			</dataSource>
		</environment>
	</environments>
 	<mappers>
		<mapper resource="mappers/boardMapper.xml" />
	</mappers>
</configuration>

 

  • 작성 순서가 중요함
    • configuration
      1. properties
      2. settings
      3. typeAliases
      4. typeHandlers
      5. objectFactory
      6. plugins
      7. environments
        1. environment
        2. transactionManager
        3. dataSource
      8. dataBaseIdProvider
      9. mappers

 

TypeAliases

  • 타입 별칭은 자바 타입에 대한 짧은 이름, XML 설정에서만 사용
  • 클래스 단위 or 패키지 단위로 등록 가능
  • 이름을 지정하지 않으면 소문자로 바뀐 형태의 값이 지정
  • 공통 자바 타입을 위한 여러 내장 타입 별칭이 존재(대소문자 구별 x)
  • 오버로딩된 이름 때문에 원시형 타입은 특별 취급
<typeAliases>
    <typeAlias type="com.ssafy.board.model.dto.Board" alias="Board"/>
</typeAliases>

 

environments

  • 여러 개의 환경으로 설정할 수 있음(개발, 테스트, 실제 환경, 여러 개의 DBMS 등)
  • DB별로 하나의 SqlSessionFactory 사용
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC" />
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://localhost:3306/ssafy_board?serverTimezone=UTC" />
            <property name="username" value="ssafy" />
            <property name="password" value="ssafy" />
        </dataSource>
    </environment>
</environments>

 

mappers

  • 매핑된 SQL 구문 설정파일의 위치 작성
  • class path의 상대경로의 리소스 사용
  • 절대경로의 url 사용
  • mapper 인터페이스 사용
  • 매퍼 패키지 내의 모든 인터페이스를 등록
<mappers>
    <mapper resource="mappers/boardMapper.xml" />
</mappers>
  • cache → 해당 네임스페이스를 위한 캐시 설정
  • cache-ref → 다른 네임스페이스의 캐시 설정에 대한 참조
  • resultMap → DB 결과 데이터를 객체에 로드하는 방법을 정의
  • parameterMap → 비권장됨(현재 사용 x)
  • sql → 다른 구문에서 재사용하기 위한 SQL 조각
  • insert → 매핑된 INSERT 구문
  • update  → 매핑된 UPDATE 구문
  • delete → 매핑된 DELETE 구문
  • select → 매핑된 SELECT 구문

 

예시 - 게시판 만들기

DAO

public interface BoardDao {
	// 전체 게시글을 조회
	public abstract List<Board> selectAll();

	// ID에 해당하는 게시글 하나 가져오기
	public abstract Board selectOne(int id);

	// 게시글 등록
	public void insertBoard(Board board);

	// 게시글 삭제
	public void deleteBoard(int id);

	// 게시글 수정
	public void updateBoard(Board board);

	// 조회수 증가
	public void updateViewCnt(int id);

}

DTO

public class Board {
	private int id;
	private String title;
	private String writer;
	private String content;
	private String regDate;
	private int viewCnt;

	public Board() {
	}

	public Board(String title, String writer, String content) {
		super();
		this.title = title;
		this.writer = writer;
		this.content = content;
	}

	public Board(int id, String title, String writer, String content, String regDate, int viewCnt) {
		this.id = id;
		this.title = title;
		this.writer = writer;
		this.content = content;
		this.regDate = regDate;
		this.viewCnt = viewCnt;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getWriter() {
		return writer;
	}

	public void setWriter(String writer) {
		this.writer = writer;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getRegDate() {
		return regDate;
	}

	public void setRegDate(String regDate) {
		this.regDate = regDate;
	}

	public int getViewCnt() {
		return viewCnt;
	}

	public void setViewCnt(int viewCnt) {
		this.viewCnt = viewCnt;
	}

	@Override
	public String toString() {
		return "Board [id=" + id + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regDate="
				+ regDate + ", viewCnt=" + viewCnt + "]";
	}

}

MyAppSqlConfig.xml

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyAppSqlConfig {
	//공장을 세워서 계속 불러다 쓸 계획
	private static SqlSessionFactory factory;
	
	static {
		//공장을 세워야 하는데 설정 파일(자원) 위치
		String resource = "config/mybatis-config.xml";
		try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
			factory = new SqlSessionFactoryBuilder().build(inputStream);
			System.out.println("공장 건설 완료");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("공장 건설 실패");
		}
	}
	
	public static SqlSessionFactory getFactory() {
		return factory;
	}
}

boardMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 네임스페이스에 DAO 풀패키지명을 작성 -->
<mapper namespace="com.ssafy.board.model.dao.BoardDao">
	<resultMap type="Board" id="boardMap">
		<!-- <result column="id" property="id"/> <result column="writer" property="writer"/> 
			<result column="title" property="title"/> <result column="content" property="content"/> -->
		<result column="view_cnt" property="viewCnt" />
		<result column="reg_date" property="regDate" />
	</resultMap>

	<!-- 전체 게시글 조회 -->
	<select id="selectAll" resultMap="boardMap">
		SELECT id, content, writer,
		title, view_cnt AS viewCnt, date_format(reg_date, '%Y-%M-%D') AS
		reg_date FROM board;
	</select>

	<!-- 상세 게시글 조회 -->
	<select id="selectOne" resultMap="boardMap" parameterType="int">
		SELECT * FROM board
		WHERE id = #{id};
	</select>
	
	<!-- 게시글 등록 -->
	<insert id="insertBoard" parameterType="Board">
		INSERT INTO board (title, writer, content)
		VALUES (#{title}, #{writer}, #{content});
	</insert>
	
	<!-- 게시글 삭제 -->
	<delete id="deleteBoard" parameterType="int">
		DELETE FROM board
		WHERE id = #{id};
	</delete>
	
	<!-- 조회수 증가 -->
	<update id="updateViewCnt" parameterType="int">
		UPDATE board
		SET view_cnt = view_cnt + 1
		WHERE id = #{id};
	</update>
	
	<!-- 게시글 수정 -->
	<!-- 만약 날짜가 수정 날짜였다면 같이 수정해줘야 함 -->
	<update id="updateBoard" parameterType="Board">
		UPDATE board
		SET title = #{title}, content = #{content}, reg_date = now()
		WHERE id = #{id};
	</update>
</mapper>

Test

package com.ssafy.board.test;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.ssafy.board.config.MyAppSqlConfig;
import com.ssafy.board.model.dao.BoardDao;
import com.ssafy.board.model.dto.Board;

public class Test {
	public static void main(String[] args) {
		try (SqlSession session = MyAppSqlConfig.getFactory().openSession(true)) {
			List<Board> boards = session.selectList("com.ssafy.board.model.dao.BoardDao.selectAll");

			for (Board board : boards) {
				System.out.println(board);
			}
		}
		
//		try(SqlSession session = MyAppSqlConfig.getFactory().openSession(true)) {
//			BoardDao dao = session.getMapper(BoardDao.class);
//			Board board = dao.selectOne(1);
//			System.out.println(board);
//		}
		
//		try(SqlSession session = MyAppSqlConfig.getFactory().openSession(true)) {
//			BoardDao dao = session.getMapper(BoardDao.class);
//			Board board = new Board("test","test_writer","test_content");
//			dao.insertBoard(board);
//			System.out.println(board);
//		}

//		try(SqlSession session = MyAppSqlConfig.getFactory().openSession(true)) {
//			BoardDao dao = session.getMapper(BoardDao.class);
//			dao.deleteBoard(0);
//		}
		
		try(SqlSession session = MyAppSqlConfig.getFactory().openSession(true)) {
			BoardDao dao = session.getMapper(BoardDao.class);
			dao.updateViewCnt(3);
			Board board = dao.selectOne(3);
			System.out.println(board);
		}
		
		try(SqlSession session = MyAppSqlConfig.getFactory().openSession(true)) {
			BoardDao dao = session.getMapper(BoardDao.class);
			
			Board board = dao.selectOne(1);
			board.setContent("update content");
			dao.updateBoard(board);
			System.out.println(board);
		}
		
	}
}

 

반응형