※ Keep in mind
본 내용은 웹개발과정의 강의 중 내용을 복습을 위해서 메모한 것에 불과한 것입니다. 이러한 연유로 강의내용을 오인한 나머지 오기재 및 불기재가 있을 수 있으니 '참고'만 해주시길 바랍니다. 저의 경우에도 본 내용을 단순하 읽은 것이 결코 저의 것이라고 생각하지 않습니다. 본 내용은 복습를 위한 초기 내지 중간 과정에 불과한 것이고, 이후에 내용을 보충 후 인출 및 설명하기 과정이 있어야 비로소 복습의 단추가 어느정도 마무리 되어간다고 볼 수 있습니다.
따라서 당초에 본 내용은 비공개였습니다. 그럼에도 불구하고 본 내용을 공개한 점은 함께 공부하는 동료들과 나눔을 바탕으로 배움과 성장의 공진화라는 소기의 목적을 달성에 어느정도 도움이 될수 있기 때문이라고 생각합니다.
I. Mybatis
mapper 어노테이션없이 메소드 오버로드 등에 가능하다
이경우 MBMenurepository로 인터페이스를 구현이 가능해!
1. @Mapper 를 사용하지않고 Repository 구현하기
(1)직접구현한 Repository객체를 JUnit으로 테스트하기 - 방법1: 메소드마다 구현방식
메소드 오버로딩을 해보자!!
1) 방법1: 메소드마다 구현방식
우선 @mapper을 주석처리하자! (사용하지않기 위해서)
import java.util.List;
//ibatis가 원래 이름이야!
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import kr.co.rland.web.entity.Menu;
//이것을 구현한 구현체를 알아서 repo에 보내주는거야!
//@Mapper
public interface MenuRepository {
//jdbc 와 mybatis로 구현한게 두개야!
List<Menu> findAll();
List<Menu> findAll (Integer offset,
Integer size);
// @Select("select * from menu")
List<Menu> findAll( Integer offset,
Integer size,
String query,
Integer categoryId,
Integer price,
String orderField,
String orderDir
);
오버로딩에 대한 interface파일에 설계도도 같이 넣자!
2)
그리고 구현한 MbMenuResponsitory.java 에다가 구현을 직접해주자
스프링의 bean객체와 mybatis의 bean객체는 별개이기때문에 이 경우 sqlSession을 이용한다.
그 이유는 sqlSession은 mybatis에서 bean객체를 꺼내기 위한것인데,
스프링에서 mybatis를 이용하기 위해서는, sqlSession bean객체를 만들어주고 IoC컨테이너에 담게끔해서 스프링이 관리할 수있게끔 해준다.
package kr.co.rland.web.repository.mybatis;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import kr.co.rland.web.entity.Menu;
import kr.co.rland.web.repository.MenuRepository;
@Repository
public class MbMenuResponsitory implements MenuRepository {
@Autowired
private SqlSession session;
@Override
public List<Menu> findAll() {
MenuRepository repository = session.getMapper (MenuRepository.class);
return repository.findAll(0, 10, null, null, null, null, null);
}
@Override
public List<Menu> findAll (Integer offset, Integer size){
MenuRepository repository = session.getMapper (MenuRepository.class);
return repository.findAll (offset, size, null, null, null, null, null);
}
@Override
public List<Menu> findAll(Integer offset, Integer size, String query, Integer categoryId, Integer price,
String orderField, String orderDir) {
MenuRepository repository = session.getMapper(MenuRepository.class);
return repository.findAll (null, null, null, null, null, null, null);
}
@Override
public List<Menu> findAllByIds(List<Long> ids) {
// TODO Auto-generated method stub
MenuRepository repository = session.getMapper(MenuRepository.class);
return repository.findAllByIds(ids);
}
@Override
public Menu findById(long id) {
// TODO Auto-generated method stub
return null;
}
@Override
public Menu insert(Menu menu) {
// TODO Auto-generated method stub
return null;
}
@Override
public int update(Menu menu) {
// TODO Auto-generated method stub
return 0;
}
@Override
public void delete(long id) {
// TODO Auto-generated method stub
}
}
findAll() 메소드는 오버로딩이 가능하다
2)
실행결과가 잘못된 구문이라고 하는데 조건문을 넣어서 mapper.xml을 정리하자!
<select id="findAll" resultMap="menuResultMap">
<!-- resultType이 무엇이냐 modle을 넣어주자 -->
select *
from menu
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="query != null">
name like '%${query}%'
</if>
<if test="price != null">
and price > #{price}
</if>
<if test="categoryId != null">
and categoryId=#{categoryId}
</if>
</trim>
<if test="orderField != null">
order by ${orderField} ${orderDir}
</if>
<if test="size != null">
limit #{size} offset #{offset}
</if>
</select>
3)
@AutoConfigureTestDatabase(replace = Replace.NONE)
//@MybatisTest
@AutoConfigureMybatis
@SpringBootTest
class MenuRepositoryTest {
//이거를 쓰려면 스프링의 IoC 컨테이너를 load해야해!
//컨테이너에 담긴적 없으면 본 repo는 null이야
@Autowired
private MenuRepository repository;
어노테이션 @MybatisTest 방식은 풀 DI기능(???)을 지원하지않아서(이 경우 다른 @controller, @service, @Repository 객체가 공자루(IoC컨테이너)에 담기지 않음) 테스트에 어려움이 있다. 다른 어노테이션을 써야한다
이러한 문제를 해결하기 위해서는 @springBootTest 어노테이션을 추가해야한다
이렇게 한다면 스프링Di 객체(@controller, @service, @Repository 객체)들을 IoC컨테이너에 담는 작업을 해주어야한다.
그럼에도 불구하고 오류가 발생하기 때문에 추가로 @AutoConfigureMybatis를 해주어야한다.
(2)직접구현한 Repository객체를 JUnit으로 테스트하기 - 방법2: 생성자 주입방식
1) 방법2: 생성자 주입방식
그런데 MenuResponsitory가 반복되는게 있는데 번거로워
이런경우 생성자 주입(constructior injaction) 이 있어! (이제 SqlSession 멤버가 아니라 생성자에다가 @autowired를 한다)
이렇게 하면 반복되게 매퍼 받을 필요가 없어서 좋아져
즉 초기화하는 코드가 필요한경우 생성자 인젝션하면 돼
@Repository
public class MbMenuResponsitory implements MenuRepository {
private SqlSession session;
private MenuRepository repository;
public MbMenuResponsitory() {
// TODO Auto-generated constructor stub
}
//여기에다가 주입한다!
@Autowired
public MbMenuResponsitory(SqlSession session) {
super();
this.session = session;
this.repository = session.getMapper(MenuRepository.class);
}
@Override
public List<Menu> findAll() {
// MenuRepository repository = session.getMapper (MenuRepository.class);
return repository.findAll(0, 10, null, null, null, null, null);
}
@Override
public List<Menu> findAll (Integer offset, Integer size){
// MenuRepository repository = session.getMapper (MenuRepository.class);
return repository.findAll (offset, size, null, null, null, null, null);
}
@Override
public List<Menu> findAll(Integer offset, Integer size, String query, Integer categoryId, Integer price,
String orderField, String orderDir) {
// MenuRepository repository = session.getMapper(MenuRepository.class);
return repository.findAll (null, null, null, null, null, null, null);
}
@Autowired 주입을 생성자에 우선해준다.
원래는 각 메소드마다 있었던 resposioty 를 클래스의 멤버로 repository를 추가한다
그리고 생성자에다가 Mapper로 구현한 것들을 respository 참조변수 에다가 대입을한다
그렇게하면 각 findAll()메소드마다 반복을 덜어서 할 수있다.
2) 또 다른 방법으로
기존에는 mapper로 반환할수있게 해줬는데, 옛날방식으로 하면 selectList방식으로 가능해!
@Override
public List<Menu> findAll() {
// MenuRepository repository = session.getMapper (MenuRepository.class);
//최근 mapper를 받아서 하는 방식
// return repository.findAll(0, 10, null, null, null, null, null);
//옛날방식 selectList로 쿼리문을 받아서 하는 방식
return session.selectList("kr.co.rland.web.repository.MenuRepository.findAll");
}
3)
수동옵션 사용해야하는 경우라면 Dao 안에서 트랜잭션 처리가 가능해!
트랜잭션으로 commit / rollback 이 있어!
그런데 기업용에서는 안써 기업은 Dao에서 안하고 service에서 해
우리가 하는 서비스는 서비스에서 해
서비스에서 트랜잭션 처리 기존에는 어려웠는데, 어노테이션 하나로 끝이야!
트랜잭션 처리할때 리플랙션 어노테이션 AOP방법론까지 사용해
사용자 인증권한 등에 Spring security 등이 있어
2. @mapper로 하기
다시 본론으로 돌아와서 직접 Mapper.xml을 구현해보자
여기에 요구되는 코드들은 반드시 워크벤치에서 먼저 실행한뒤에 해보자
여기서 반드시 명심할 것은 이제 Mapper.xml 에서 쿼리문을 만드는것 자체가 repository(Dao)구현을 하는 것이야!
(1) delete
<!-- 이렇게 이제 쿼리문 만드는게 repo 구현이야! -->
<delete id="delete">
delete from menu where id =${id}
</delete>
(2) count
본 함수로 집계를 하는 이유는 마지막 페이지를 구분하기 위해서야!
1)
map 모델 마련
2) query문
<select id="count" resultType="Integer">
select count(id) count from menu
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="query != null">
name like '%${query}%'
</if>
<if test="price != null">
and price > #{price}
</if>
<if test="categoryId != null">
and categoryId=#{categoryId}
</if>
</trim>
</select>
쿼리문을 작성한다.
각 조건을 만족할때 마다 검색되는 메뉴의 수가 몇갠지 알 수있다.
resultType을 Interger로 해준다 왜냐하면....나오는 타입은 정수가 나오기 때문이다.
우리는 이렇게 나온 집계 결과를 콘솔로 출력해준다.
3) junit 테스트
@Test
void testCount() {
int count = repository.count(null, null, null);
System.out.println(count);
}
이렇게 해서 3개의 입력값을 아무것도 주지않는 경우에 반환되는 정수값을 콘솔에 출력한다.
II. DataBase 모델링
1. 들어가며
(1) 들어가며
1)
테이블간에 관계가 있는경우에는 Join / 관계가 없는 경우에는 서브쿼리를 이용한다 다만 이 경우 성능이슈가 있는데 튜닝을 해주어야한다
DB모델링은 설계이다
구축하기 위해서는 설계를 해야해!
설계를한다는게 DB모델링해야해
-계정만 달리들어가면 프로젝트 계정으로 가능해
문제는 공간에다가 설계해서 넣어야해
모델링을 이야기 해야해
(2) DB 설계
1) DB 설계 구현 프로세스
개념설계 -> 논리설계 -> 물리설계 -> 구현
2) entity 와 relation
entity 와 relation
entity 란 주체와 대상을 의미한다 (key)
relation 이란 행위를 의미한다 주체(재준)와 대상(영화)간 연결고리(relation : 예약) 이다
모든 행위들은 동사인데 이 중에서 특히 저장되는 업무가 DB
ex:
재준, 슬램덩크, 예약 -> 예매 정보가 쌓여(저장)
주형, 드래곤볼, 예약 -> 쌓이면 DB
3)
key 가 되는 데이터 entity라 해 / 행위는 relation
이런것들을 그리면 ER diagram 이라고 해!
네모 : 엔티티 /
마름모 : 릴레이션 /
원 : 속성
2. 개념 설계
(1) 설계하기
1) 역할자
역할자? : 역할이라는 개념이 중요한데 얘가 주체가 돼
대상? : 상품, 강좌
역할자 찾으라 하면 잘 몰라,
역할자가 다 같으면, 다들 같은거만 보게 돼!
일이 다르면 & 역할이 달라!
즉 관리자 & 회원 역할이 달라!
회원의 일은 회원이 / 관리자의 일은 관리자가!
2)
공개하면 공개하는 행위로서 등록하는거야
등록을하고, 오픈을 했어!
이것으로 메뉴가 사용자에게 보여!
3)
회원이 메뉴를 가지고 메뉴를 ___ 하나? -> 주문?
이런게 다 행위야 그런데 업무적인거야?
주문 DB에 저장할거야? 그러면 업무야! / 조회 DB에 저장해? 아니면 업무가 아니야
조회가 저장한다면 DB에 넣는지 / 적재만 하는 data공간인지! 구분하자
DB는 수정, 삭제하기 위한 관리가 된 것이야. 가끔 수정삭제 할필요없는 data가 있어
수정,삭제보다는 기록용으로 필요할 때도 있어 BigData라고 볼 수있어 (정책적으로)
수정,삭제보다는 적재에 특화된 DB가 있어! -> 비정형화(비정규화) DB라고 해!
어차피 수정삭제하는 데이터 결함에 대해서 생각안해! DB는 무결성을 염두해두고, 수정하다가 발견된게 결함이있는지 노심초사하는거라면, 얘는 관계가 없어 컬럼은 필요한 만큼 다 넣어! 어차피 수정삭제 하는게 없어 결함 생길염려가 없어 이를 비정규화 비정형화 DB야 -> NoSQL (ex : mongoDB) |
(2) ERD 그리기
1)
- 저장되지않는 정보이면 조회를 제거하자
- 누가 무얼 주문했는지 모르자나! 그러면 행위자와 연결하자!
2)
여기서 '함께하면 좋은 메뉴'는 사용자가 아니라 관리자가 등록하는거야!
3)
메뉴에 대한 속성들을 타원형으로 하자
4)
릴레이션도 마찬가지로 속성이 있어
5)
사람이 행위를 하는 주체로서 주문환불취소결제 다있어
대상과 대상간의 관계도 있어 -> 포함관계
ex : 시험 문제 포함 / 강의장 책상 포함
여기서는 카테고리가 메뉴를 포함하고있어
이렇게 구성으로 엮어줄 수있어!
6)
강사는 수강생 페이지 다 쓸수있으면서, 강사페이지도 쓸수있어
이런경우 관리자가 수강생의 후기남기는 entity등을 또 만들필요없이 상속관계로 하면돼!
부모라는건 공통분모야!
화살표가 있는 부분이 부모야! (회원이 부모 / 관리자 자식)
[회원:부모 [ 관리자 : 자식 ] ]
7)
모든 행위는 관계를 가질 때 몇 대 몇인지 생각해야해!
<등록>
1 : N 의 경우
1명의 관리자가 메뉴의 여러개를 등록이 가능해!
만약 1: 1 이라면
1명의 관리자는 딱 한개씩의 음료만 등록이 가능해!
<추천>
각 메뉴여러개를 여러명의 관리자가 등록이 가능하다고 볼 수있어
왜나하면 하나의 메뉴에 대해서도 여러명의 관리자가 추천할 수있기 때문이야
만약 이 관계가 1:N 이라면
1명의 관리자만이 추천이 가능하고, 그 외 관리자들은 중복되고 추천을 할 수없어!
-대표적인 예
1 : 1
하나의 학생은 하나의 신체정보
1 : N
하나의 학생은 취미가 여러개
N : N
TV 는 여러회사(삼성,LG) 에서만들고, 삼성 LG 는 TV,세탁기,냉장고등을 만든다
1. 보충
(1)
2. 회고
'배움 __IL > TIL 1기' 카테고리의 다른 글
TIL : 67번째- 230313 [3-2-월] (0) | 2023.03.13 |
---|---|
TIL : 66번째- 230310 [3-1-금] (0) | 2023.03.10 |
TIL : 64번째- 230308 [3-1-수] (0) | 2023.03.08 |
TIL : 63번째- 230307 [3-1-화] (0) | 2023.03.07 |
TIL : 62번째- 230304 [3-1-월] (0) | 2023.03.06 |