2020-11-10 TIL
mybatis
프로젝트에 마이바티스 적용하기
- 보드매퍼.xml에 sql문을 옮겨넣는다.
오토박싱
public class BoardDaoImpl implements com.eomcs.pms.dao.BoardDao{
.
.
.
@Override
public Board findByNo(int no) throws Exception {
// 김밥공장 sqlSessionFactory =
// new 김밥공장건설사().build(설계도);
// try (김밥 stmt = 김밥공장.깁밥만들어()) {
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("com/eomcs/pms/conf/mybatis-config.xml"));
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
// sqlSession.selectOne("BoardDao.findByNo", Integer.valueOf(no));
Board board = sqlSession.selectOne("BoardDao.findByNo", no);
sqlSession.update("BoardDao.updateViewCount", no); // no가 오토박싱된다.
return board;
}
}
.
.
.
}
앱에서 사용하는 객체를 앱이니리스너에서 준비한다.
- findByNo를 보드매퍼로 옮긴다.
<select id="findByNo"
parameterType="java.lang.Integer"
resultType="com.eomcs.pms.domain.Board">
select
b.no,
b.title,
b.content,
b.cdt,
b.vw_cnt,
m.no writer_no,
m.name
from
pms_board b inner join pms_member m on b.writer=m.no
where b.no = ?
</select>
- 보드 타입을 리턴
- 컬럼 명이 같아야 넣을 수 있다.
- 컬럼 명과 같도록 뒤에 이름을 알려주거나 맨 위에 보드맵으로 알려준다.
<resultMap type="com.eomcs.pms.domain.Board" id="BoardMap">
<!-- 자바 코드
Board b = new Board();
b.setNo(rs.getInt("no"));
b.setTitle(rs.getString("title"));
b.setRegisteredDate(rs.getDate("cdt"));
b.setViewCount(rs.getInt("vw_cnt"));
-->
<id column="no" property="no"/>
<result column="title" property="title"/>
<result column="cdt" property="registeredDate"/>
<result column="vw_cnt" property="viewCount"/>
- 이 경우, resultType으로 하지말고 resultMap으로 바꿔준다.
- 프리머티브 타입과 그 매핑 클래스, 스트링 클래스에 대해서는 값을 지정할 때 파라미터 이름을 마음대로 줄 수 있다.
<select id="findByNo"
parameterType="java.lang.Integer"
resultMap="BoardMap">
select
b.no,
b.title,
b.content,
b.cdt,
b.vw_cnt,
m.no writer_no,
m.name
from
pms_board b inner join pms_member m on b.writer=m.no
where b.no = #{ohora} // 아무거나 적어도 된다.
// 하지만 프라퍼티 이름은 의미가 통하도록 준다.
</select>
- 업데이트는 리절트 타입이 필요 없다.
- 뷰카운트는 업데이트 하는 것이다.(인서트나 셀렉트가 아님)
<update id="updateViewCount" parameterType="java.lang.Integer"> update pms_board set vw_cnt = vw_cnt + 1 where no = #{no} </update> - 업데이트는 리절트 타입이 필요 없다.
- 프라퍼티는 게터, 세터를 호출하는 것이다.
<update id="update" parameterType="com.eomcs.pms.domain.Board">
update pms_board set
title = #{title},
content = #{content}
where no = #{no}
</update>
- delete
<delete id="delete" parameterType="java.lang.Integer">
delete from pms_board
where no=#{no}
</delete>
public class BoardDaoImpl implements com.eomcs.pms.dao.BoardDao{
.
.
.
@Override
public int delete(int no) throws Exception {
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(
"com/eomcs/pms/conf/mybatis-config.xml"));
// 커밋과 관련된 것이기 때문에 공유하면 안된다.
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
return sqlSession.delete("BoardDao.delete", no);
}
}
- App에서 사용하는 객체를 준비한다.
- sqlsessionFactory 객체를 생성하여 context에 담는다.
- BoardDaoImpl(BoardDao구현체) 객체를 준비한다.
- Command 구현체 생성 코드도 이동시켜 준비한다.
public class AppInitListener implements ApplicationContextListener {
@Override
public void contextInitialized(Map<String,Object> context) {
System.out.println("프로젝트 관리 시스템(PMS)에 오신 걸 환영합니다!");
try {
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/studydb?user=study&password=1111");
//context.put("con", con); // 앱에서 사용하려고 담아둔 것이기 때문에 필요없다.
//MyBatis 구현체 생성
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(
"com/eomcs/pms/conf/mybatis-config.xml"));
context.put("sqlSessionFactory", sqlSessionFactory);
//Dao 구현체 생성
BoardDao boardDao = new BoardDaoImpl(sqlSessionFactory);
MemberDao memberDao = new MemberDaoImpl(con);
ProjectDao projectDao = new ProjectDaoImpl(con);
TaskDao taskDao = new TaskDaoImpl(con);
//Command 구현체 생성 및 CommandMap 객체 준비
Map<String,Command> commandMap = new HashMap<>();
commandMap.put("/board/add", new BoardAddCommand(boardDao, memberDao));
commandMap.put("/board/list", new BoardListCommand(boardDao));
commandMap.put("/board/detail", new BoardDetailCommand(boardDao));
commandMap.put("/board/update", new BoardUpdateCommand(boardDao));
commandMap.put("/board/delete", new BoardDeleteCommand(boardDao));
commandMap.put("/member/add", new MemberAddCommand(memberDao));
commandMap.put("/member/list", new MemberListCommand(memberDao));
commandMap.put("/member/detail", new MemberDetailCommand(memberDao));
commandMap.put("/member/update", new MemberUpdateCommand(memberDao));
commandMap.put("/member/delete", new MemberDeleteCommand(memberDao));
commandMap.put("/project/add", new ProjectAddCommand(projectDao, memberDao));
commandMap.put("/project/list", new ProjectListCommand(projectDao));
commandMap.put("/project/detail", new ProjectDetailCommand(projectDao));
commandMap.put("/project/update", new ProjectUpdateCommand(projectDao, memberDao));
commandMap.put("/project/delete", new ProjectDeleteCommand(projectDao));
commandMap.put("/task/add", new TaskAddCommand(taskDao, projectDao, memberDao));
commandMap.put("/task/list", new TaskListCommand(taskDao));
commandMap.put("/task/detail", new TaskDetailCommand(taskDao));
commandMap.put("/task/update", new TaskUpdateCommand(taskDao, projectDao, memberDao));
commandMap.put("/task/delete", new TaskDeleteCommand(taskDao));
commandMap.put("/hello", new HelloCommand());
commandMap.put("/login", new LoginCommand(memberDao));
commandMap.put("/whoami", new WhoamiCommand());
commandMap.put("/logout", new LogoutCommand());
context.put("Commandmap", commandMap);
} catch (Exception e) {
System.out.println("시스템이 사용할 객체를 준비하는 중에 오류 발생");
e.printStackTrace();
}
}
- BoardDaoImpl 수정
// 마이바티스 적용
// sqlSessionFactory를 자체적으로 생성하지 않고
// 생성자를 통해 외부에서 주입 받는다(DI)
public class BoardDaoImpl implements com.eomcs.pms.dao.BoardDao{
// Connection con;
SqlSessionFactory sqlSessionFactory;
public BoardDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public int insert(Board board) throws Exception {
// 커밋과 관련된 것이기 때문에 공유하면 안된다.
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
return sqlSession.insert("BoardDao.insert", board);
}
}
- App의 service()에서 필요없는 것들을 삭제한다.
public void service() throws Exception {
notifyApplicationContextListenerOnServiceStarted();
// 필터 관리자 준비
CommandFilterManager filterManager = new CommandFilterManager();
// 필터를 등록한다.
filterManager.add(new LogCommandFilter(new File("command.log")));
//filterManager.add(new AuthCommandFilter());
filterManager.add(new DefaultCommandFilter());
filterManager.init(context);
멤버에도 마이바티스를 적용한다.
- MemberMapper.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="MemberDao">
<resultMap type="com.eomcs.pms.domain.Member" id="MemberMap">
<!-- 자바 코드
Member member = new Member();
member.setNo(rs.getInt("no"));
member.setName(rs.getString("name"));
member.setEmail(rs.getString("email"));
member.setPhoto(rs.getString("photo"));
member.setTel(rs.getString("tel"));
member.setRegisteredDate(rs.getDate("cdt"));
-->
<id column="no" property="no"/>
<result column="name" property="name"/>
<result column="email" property="email"/>
<result column="photo" property="photo"/>
<result column="tel" property="tel"/>
<result column="cdt" property="registeredDate"/>
</resultMap>
<select id="findAll" resultMap="MemberMap">
select
no,
name,
email,
tel,
cdt
from
pms_member
order by
no desc
</select>
<insert id="insert" parameterType="com.eomcs.pms.domain.Member">
insert into pms_member(name,email,password,photo,tel)
values(#{name}, #{email}, #{password}, #{photo}, #{tel})
</insert>
<select id="findByNo"
parameterType="java.lang.Integer"
resultMap="MemberMap">
select
no,
name,
email,
photo,
tel,
cdt
from
pms_member
where
no =#{no}
</select>
<select id="findByName"
parameterType="java.lang.String"
resultMap="MemberMap">
select
no,
name,
email,
photo,
tel,
cdt
from
pms_member
where
name =#{name}
</select>
<update id="update" parameterType="com.eomcs.pms.domain.Board">
update pms_member set
name =#{name},
email =#{email},
password =password(#{password}),
photo =#{photo},
tel =#{tel}
where no =#{no}
</update>
<delete id="delete" parameterType="java.lang.Integer">
delete from pms_member
where no=#{no}
</delete>
<select id="findByProjectNo" parameterType="java.lang.Integer" resultMap="MemberMap">
select
m.no,
m.name
from
pms_member_project mp inner join pms_member m on
mp.member_no=m.no
where
mp.project_no=#{No}
order by
m.name asc
</select>
<select id="findByEmailPassword" parameterType="java.lang.String" resultMap="MemberMap">
select
no,
name,
email,
photo,
tel,
cdt
from pms_member
where
email =#{email}
and password = password(#{password})
</select>
</mapper>
- mybatis-config 에서 SQL 문이 들어있는 파일을 추가한다.
```java
</configuration>
- MemberDaoImpl 를 수정한다.
```java
package com.eomcs.pms.dao.mariadb;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.eomcs.pms.domain.Member;
public class MemberDaoImpl implements com.eomcs.pms.dao.MemberDao {
SqlSessionFactory sqlSessionFactory;
public MemberDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public int insert(Member member) throws Exception {
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
return sqlSession.insert("MemberDao.insert", member);
}
}
@Override
public int delete(int no) throws Exception {
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
return sqlSession.delete("MemberDao.delete", no);
}
}
@Override
public Member findByNo(int no) throws Exception {
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
return (Member) sqlSession.selectOne("MemberDao.findByNo", no);
}
}
@Override
public Member findByName(String name) throws Exception {
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
// select의 결과가 하나가 아닐 경우에는 List를 쓴다.
List<Member> members = sqlSession.selectList("MemberDao.findByName", name);
if (members.size() > 0) {
return members.get(0);
} else {
return null;
}
}
}
@Override
public List<Member> findAll() throws Exception {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
// select의 결과가 하나가 아닐 경우에는 List를 쓴다.
return sqlSession.selectList("MemberDao.findAll");
}
}
@Override
public int update(Member member) throws Exception {
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
return sqlSession.update("MemberDao.update", member);
}
}
@Override
public List<Member> findByProjectNo(int projectNo) throws Exception {
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
return sqlSession.selectOne("MemberDao.findByProjectNo", projectNo);
}
}
@Override
public Member findByEmailPassword(String email, String password) throws Exception {
Map<String,String> map = new HashMap<>();
map.put("email", email);
map.put("password", password);
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
// 파라미터를 하나만 줄 수 있기 때문에 map에 여러개의 값을 담아서 map을 넘긴다.
return sqlSession.selectOne("MemberDao.findByEmailPassword", map);
}
}
}
- AppInitListener 에서 Dao 구현체를 수정한다.
MemberDao memberDao = new MemberDaoImpl(sqlSessionFactory);
프로젝트에 마이바티스 적용
-
proejctVO = project value object (도메인 객체)
-
findAll
// 위에 나온 조인 결과가 left에 존재 // 기준이 되는 데이터는 반드시 출력한다. left outer pms_member_project mp on p.no=mp.project_no order by p.no desc </select> -
프로젝트 맵퍼
// 멤버 객체를 setOwner 하라는 것
<result column="owner_no" property="no"/>
<result column="owner_name" property="name"/>
</association>
// 멤버 객체를 어레이리스트에 담아서 그 리스트를 members에 담으라는 것
- 프로젝트 오너, 프로젝트 리스트의 멤버 하나하나마다 객체가 된다.
- 메모리 낭비가 심하다.
-
같은 이름이어도 해시가 다 다르다.
- 매퍼에서 설정한다.
- 일반 컬럼은 result로 지정했을 때
프로젝트 관리 시스템(PMS)에 오신 걸 환영합니다! 로그 파일을 열었습니다. 명령> /project/list [프로젝트 목록] 번호, 프로젝트명, 시작일 ~ 종료일, 관리자, 팀원 11, test101, 2020-01-01 ~ 2020-02-02, x1(70807318), [] 10, test100, 2020-01-01 ~ 2020-02-02, x1(910091170), [] 9, test2, 2020-01-01 ~ 2020-02-02, x1(1183888521), [] 8, test1, 2020-01-01 ~ 2020-02-02, x1(2041605291), [] 7, p4, 2020-04-04 ~ 2020-05-05, f(1008925772), [c(1052245076),e(2136288211)] 6, p3, 2020-03-03 ~ 2020-04-04, c(293907205), [d(1175259735),e(1205406622)] 4, p1p1, 2020-01-02 ~ 2020-01-03, a(1794717576), [b(988800485),c(345902941),d(454325163),e(796667727)]
- 실무에 가면 primary key 값은 id 를 준다.
- 객체를 중복시키기 않기 위해서
- 메모리 낭비가 덜하다.
- mapper 에서 id를 사용하면 id 에서 같은 값을 가지는 객체들은 모두 하나로 묶여진다.
- 만약 title 을 id로 지정할 경우 타이틀이 같은 프로젝트는 전부 하나의 객체로 간주된다.(오너나 멤버가 다르더라도)
- 오너나 멤버가 다른 같은 타이틀의 프로젝트가 있다면 나머지 프로젝트는 버려진다.
- 이러한 일이 발생하지 않도록 id는 같은 값이 없는 no로 지정해야 한다.
- 객체를 중복시키기 않기 위해서
- 조인한 테이블의 프라이머리 키 값은 반드시 셀렉트해라
-
그리고 그 프라이머리 키는 id로 지정하라
-
insert
insert into pms_project(title,content,sdt,edt,owner) values(#{title},#{content},#{startDate},#{endDate},#{owner.no})) // getOwner.getNo <insert id=”insert” parameterType=”com.eomcs.pms.domain.Project” // 파라미터로 받은 프로젝트 객체에 setNo를 호출해서 insert 한 후에 그 no 값을 넣어줘 // //insert 후의 no //setNo useGeneratedKeys=”true” keyColumn=”no” keyProperty=”no”> insert into pms_project(title,content,sdt,edt,owner) values(#{title},#{content},#{startDate},#{endDate},#{owner.no})) </insert>
- 프로젝트를 삭제할 때 task가 키로 되어 있어서 삭제하려고 하면 오류가 발생한다.
- 이 경우, 비활성화 컬럼을 하나 추가해서 비활성화된 사람은 팀 목록에서 출력되지 않게 한다.
- 하지만 그 회원이 했던 task는 그대로 남아있게 된다.
- 프로젝트를 삭제할 때 task가 키로 되어 있어서 삭제하려고 하면 오류가 발생한다.
마이그레이션
- 기존의 것에 새 구조, 새 기술을 적용하는 것