90-MyList프로젝트2 / 15 페이지
Persistence
데이터 유지와 관련된 일
Framework
프로그램의 실행 흐름을 미리 만들어 놓은 것
실제 작업을 수행할 컴포넌트를 개발자가 작성
라이브러리 → 도구
프레임워크는 도구를 엮어서 특정 목적을 달성할 수 있도록 도구를 엮어 놓은 것
🔹 SQL Mapper
DAO와 SQL을 연결 ---> DBMS 작업 수행
DAO --- 연결 --- SQL 파일 ---> DBMS
개발자의 몫
🔹 OR Mapper
DAO와 API를 엮고 API는 SQL과 연결됨
SQL 자동생성
DAO --- 연결 --- API + 전용 QL --- 연결 --- SQL ---> DBMS
개발자의 몫 자동생성
SQL 자동생성
=> 개발자가 특정 DBMS를 고민할 필요 없다!
90-MyList프로젝트2 / 16 페이지
https://www.egovframe.go.kr/home/main.do
Persistent layer
① 기존 DB 프로그래밍 방식
DAO 안에 JDBC 코드와 SQL문이 들어 있다.
DAO = JDBC 코드 + SQL 코드
유사한 코드가 반복되고 있음
② 개선된 방식
JDBC 코드와 SQL 코드를 분리시킨다
DAO
--- call ---> Mybatis
--- 읽고 실행 ---> SQL
DAO - JDBC 코드 → MyBatis
DAO - SQL 코드 → SQL 파일
🔹 MyBatis : 반복적으로 작성하는 JDBC 코드를 쓰기 쉽게 캡슐화 (클래스와 메서드로 포장)
SQL 코드를 작성하기 쉽게
읽고 이해하기 쉽게
별도의 파일(XML)로 분리한다
DAO
---call---> Mybatis
---읽고 실행---> SQL
DAO가 Mybatis에 있는 걸 호출한다
Mybatis가 SQL을 읽어서 실행한다
MyBatis를 사용한다는 것은
개발자가 DAO, SQL을 작성한다.
이 구조로 바꿔보자!
실무에 가면 당연히 이렇게 쓴다
https://search.maven.org/artifact/org.mybatis/mybatis/3.5.9/jar
https://mybatis.org/mybatis-3/
https://github.com/mybatis/spring
① App
---call---> MyBatis 라이브러리
---call---> JDBC Driver
---> DBMS
내부적으로 JDBC Driver call
MyBatis 라이브러리를 통해서 간접적으로 call
MyBatis 라이브러리 ← JDBC Driver 사용하는 코드를 캡슐화 시킨 거
MyBatis 라이브러리: JDBC 프로그래밍 코드를 캡슐화 (클래스와 메서드로 포장)
② App + Spring 프레임워크
---call---> MyBatis 라이브러리
Spring 프레임워크에서 MyBatis 라이브러리에 있는 객체를 관리할 수 있도록 어댑터가 있어야 되는데
MyBatis-Spring 어댑터
adapt (새로운 용도·상황에) 맞추다[조정하다]
Spring 프레임워크는 MyBatis를 직접 관리할 수 없음
Spring 규칙에 따라서 만든 게 아님
둘 사이를 연결시켜주는 클래스가 MyBatis-Spring 어댑터
Spring 프레임워크
---call---> MyBatis-Spring 어댑터
---call---> MyBatis 라이브러리
물론 실제 DBMS에 접속하는 건 JDBC Driver 라는 건 변함없음
iBatis, MyBatis, Hibernate, Spring Data JPA 모두 JDBC 프로그래밍 코드를 캡슐화한 거
디자인 패턴에도 adapter 패턴 있음
하나의 메서드로 포장해버림
MyBatis만 있으면 다이렉트로 DBMS에 붙는다고 착각하는 경우가 있음
관리가 가능하도록 중간에 어댑터 클래스가 필요
③ Spring Boot 안에 App이 존재
Spring Boot가 App을 포함한다는 느낌이 강함
MyBatis 라이브러리를 관리하기 위해서 중간에 mybatis-spring-boot-starter
Spring Boot
---설정---> mybatis-spring-boot-starter
https://github.com/mybatis/spring-boot-starter
spring-boot-starter
http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/
https://search.maven.org/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter/2.2.2/jar
https://search.maven.org/artifact/org.mybatis/mybatis-spring/2.0.7/jar
build.gradle 파일에 추가
// Mybatis 프레임워크 (직접 구성)
// implementation 'org.mybatis:mybatis:3.5.9'
// implementation 'org.mybatis:mybatis-spring:2.0.7'
// Mybatis 프레임워크 (스프링 스타터로 간접 구성)
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
gradle cleanEclipse
gradle eclipse
리프레시 하고 Referenced Libraries 확인하기
Mybatis 설정 추가하기
com.eomcs.mylist.dao.mariadb.BoardDaoImpl 클래스 변경
SQL 코드를 뜯어내어 XML 파일로 옮긴다.
xml 검색
BoardDao.xml 만들기
https://mybatis.org/mybatis-3/getting-started.html
<mapper namespace="BoardDao">
BoardDao에서 뜯어낸 SQL임을 알려준다
BoardDaoImpl에서 findAll()에 있는 SQL문을 뜯어낸다.
select 해서 가져온 컬럼 값을 resultType="com.eomcs.mylist.domain.Board"
객체에 담아라
<?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="BoardDao">
<select id="sql1" resultType="com.eomcs.mylist.domain.Board">
select
board_no,
title,
created_date,
view_count
from
ml_board
order by
board_no desc
</select>
</mapper>
MyBatis를 호출하는 코드를 DAO에 작성해야 한다
BoardDaoImpl.java
먼저 의존 객체 SqlSessionFactory 주입받아야 됨
SqlSessionFactory ← SqlSession을 만들어주는 공장
SqlSession ← SQL을 실행시켜주는 도구
@Autowired
SqlSessionFactory sqlSessionFactory; // => Mybatis: SQL 실행 도구를 만들어 주는 객체
SqlSession sqlSession = sqlSessionFactory.openSession(); // SQL을 실행시켜주는 도구를 준비
sqlSession.selectList(namespace.sql_id)
sqlSession.selectList("BoardDao.sql1")
return selectList의 리턴값
@Override
public List<Board> findAll() {
try (SqlSession sqlSessio n = sqlSessionFactory.openSession();) { // SQL을 실행시켜주는 도구를 준비
return sqlSession.selectList("BoardDao.sql1");
}
}
findAll()에 JDBC 코드가 없음
진짜 없습니까?
selectList() 안에 들어 있음 (캡슐화)
우리가 직접 JDBC 코드를 짤 필요가 없으므로 편리하다
절대 '90-MyList프로젝트2 / 17 페이지' 그림을 잊어서는 안 됨
MyBatis 라이브러리에 있는 function을 호출한다고 해서 JDBC를 건너뛰고 가는 게 아님
MyBatis 라이브러리가 내부적으로 JDBC Driver에 있는 Connection, PreparedStatement, executeQuery() 이런 것들을 직접 호출한다
JDBC Driver가 사라지는 게 아님
JDBC Driver가 없으면 아예 동작이 안 됨
App.java
mylist-boot/app/src/main/java/com/eomcs/mylist/App.java
SqlSessionFactory ← 인터페이스
SqlSessionFactoryBean ← SqlSessionFactory 인터페이스를 구현한 구현체
mybatis setmapperlocations 검색
설정이 누락된 게 있으면 에러 뜰 거임
// Mybatis 객체 준비
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 1) SQL을 실행할 때 사용할 DB 커넥션풀을 주입한다.
sqlSessionFactoryBean.setDataSource(dataSource); // DB 커넥션 풀을 주입한다.
// 2) SQL 문이 들어 있는 파일의 위치를 설정한다.
PathMatchingResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resourceResolver.getResources("classpath:com/eomcs/mylist/dao/*.xml"));
return sqlSessionFactoryBean.getObject();
}
localhost:8080/board/list
No가 다 0으로 나옴
컬럼 이름과 필드 이름이 일치향
컬럼명과 같은 이름을 가진 필드에 값을 담는다
컬럼명과 필드 이름이 일치해야 한다
왜? Mybatis에서 컬럼값을 자바객체에 담을 때
컬럼명과 같은 이름을 가진 필드를 찾는다.
리턴 받은 게 title 밖에 없어서 그런 거
레코드 컬럼과 객체 필드 이름이 일치해야 꽂아준다
SQL에서 select 한 컬럼에 대해 객체 필드에 담는다
<?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="BoardDao">
<select id="sql1" resultType="com.eomcs.mylist.domain.Board">
select
board_no as no,
title,
created_date as createDate,
view_count as viewCount
from
ml_board
order by
board_no desc
</select>
</mapper>
<mapper namespace="BoardDao">
<select id="sql2" resultType="com.eomcs.mylist.domain.Board" parameterType="int">
select
board_no as no,
title,
content,
created_date as createdDate,
view_count as viewCount
from
ml_board
where
board_no=#{no}
</select>
</mapper>
sqlSession.selectOne(namespace.id, parameter 값)
sqlSession.selectOne("BoardDao.sql2", 2)
파라미터 값을 가리키는 이름
파라미터 타입이 primitive type, String일 경우
아무 이름을 적어도 된다
@Override
public Board findByNo(int no) {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
return sqlSession.selectOne("BoardDao.sql2", no);
}
}
@Override
public Board findByNo(int no) {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
System.out.println(sqlSession.getClass().getName());
return sqlSession.selectOne("BoardDao.sql2", no);
}
}
org.apache.ibatis.session.defaults.DefaultSqlSession
디버그 모드로 추적...
결과를 한 개를 리턴하는 건 selectOne()
@Override
public int insert(Board board) {
try (SqlSession sqlSession = sqlSessionFactory.openSession();) { // SQL을 실행시켜주는 도구를 준비
return sqlSession.insert("BoardDao.sql3", board);
}
}
몇 개 insert 했는지 개수를 리턴
정확하게 패키지명까지 다 적어주기
parameterType="com.eomcs.mylist.domain.Board"
<insert id="sql3" parameterType="com.eomcs.mylist.domain.Board">
insert into ml_board(title,content)
values(?,?)
</insert>
sqlSession.insert("BoardDao.sql3", board)
Board 클래스
no
title
content
viewCount
createdDate
insert into ml_board(title,content) ← 컬럼명
values(#{title},#{content}) ← 객체 필드명
<insert id="sql3" parameterType="com.eomcs.mylist.domain.Board">
insert into ml_board(title,content)
values(#{title},#{content})
</insert>
http://localhost:8080/board/add?title=aaa3&content=bbb3
BoardDao.xml ← sqlmapper 파일
<insert id="sql4" parameterType="com.eomcs.mylist.domain.Board">
update ml_board set
title=#{title},
content=#{content}
where
board_no=#{no}
</insert>
insert 태그 안에 update문이 있다고 당황하지 말기
가독성 측면에서 update 태그로 써주기
@Override
public int update(Board board) {
try (SqlSession sqlSession = sqlSessionFactory.openSession();) { // SQL을 실행시켜주는 도구를 준비
return sqlSession.insert("BoardDao.sql4", board);
}
}
insert 태그 안에 update문이 있다고 당황하지 말기
그래도 가독성 측면에서 update 태그로 써주기
<update id="sql4" parameterType="com.eomcs.mylist.domain.Board">
update ml_board set
title=#{title},
content=#{content}
where
board_no=#{no}
</update>
@Override
public int update(Board board) {
try (SqlSession sqlSession = sqlSessionFactory.openSession();) { // SQL을 실행시켜주는 도구를 준비
return sqlSession.update("BoardDao.sql4", board);
}
}
<select id="sql5" resultType="int">
select count(*) from ml_board
</select>
<delete id="sql6" parameterType="int">
delete from ml_board
where board_no=#{no}
</delete>
<update id="sql7" parameterType="int">
update ml_board set
view_count=view_count + 1
where board_no=#{no}
</update>
자바 소스 코드에 SQL문이 있으면 관리하기 힘들다
DAO 구현체를 자동으로 생성한다.
Mybatis에서 DAO 구현체를 자동으로 생성한다.
DAO 구현체가 사용할 SQL Mapper 파일의 위치는 인터페이스의 패키지 경로와 일치해야 한다.
예) com/eomcs/mylist/dao/BoardDao.xml
인터페이스의 메서드가 호출될 때 사용할 SQL은 ID는 메서드 이름과 일치해야 한다.
예) <select id="countAll">...</select>
resultType이 없다
BoardDao(인터페이스), BoardDao.xmal(SQL mapper 파일) 만 만들면 된다
http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/
mybatis.type-aliases-package=com.example.domain.model
Book 데이터를 저장할 테이블을 생성한다.
create table ml_book(
book_no int not null,
title varchar(255) not null,
author varchar(100) not null,
press varchar(100) not null,
feed text not null,
read_date date,
page int,
price int
);
alter table ml_book
add constraint primary key (book_no),
modify column book_no int not null auto_increment;
PK나 유니크 컬럼에만 auto_increment 설정할 수 있다.
com.eomcs.mylist.domain.Book 클래스 변경
primary key를 저장할 no 추가한다
여기서 오타 있는지 확인하기
parameterType="Book" // "book"도 상관없음
대소문자 상관없음
update ml_book set
title='ohora',
author='heo',
press='nono',
feed='good',
read_date='2022-2-1',
page=200,
price=20000
where
book_no=1;
delete from ml_book
where book_no=1;