일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 패스트캠퍼스
- 생성자 주입
- 쇼트유알엘
- 데이터베이스
- @jsonproperty
- emqx
- WEB SOCKET
- 스프링의 정석
- 남궁성과 끝까지 간다
- 스웨거
- DB
- 프로그래머스
- 카프카
- docker
- Spring Security
- JavaScript
- Kafka
- 개인프로젝트
- visualvm
- 시큐리티
- MYSQL
- 항해99
- 스파르타코딩클럽
- AWS
- JWT
- java
- EC2
- Spring
- 웹개발
- CentOS
- Today
- Total
Nellie's Blog
[TIL-221205월] 항해99 22일차 (정규화/ Entity관련 어노테이션/ 연관관계 매핑) 본문
배운점
효율적인 모델링 작업하기 (정규화)
도서 Entity에 관해서 말하자면, 도서관의 같은 책(책 이름이 똑같은)이 있다는 가정하에 모델링을 했음에도 불구하고, 이것을 효율적으로 못한 모델링을 한 표본이 되고 말았다.
왜냐하면 도서번호로 같은 책이 있더라도 번호를 달리해 구분은 가능할지 몰라도,
똑같은 자료를 중복하게되는 구조여서 결과적으로 데이터공간을 낭비하는 결과를 초래하기 때문이다.
다시 말하면 도서관의 실제도서를 나타내는 Entity와 실제도서의 속성을 정의하는 Entity를 구분해야된다는 말이다.
왜냐하면, 도서번호만 다른 똑같은 책인 속성 정보가 완전히 같기 때문에 아래 사진에서 볼수 있듯이 같은 도서의 속성들의 데이터가 중복으로 저장됨을 표로 볼 수 있다.
따라서 이 속성정보를 중복해서 한 테이블에 저장하는 것보다, 테이블을 따로 분리해서 속성을 따로 저장하는게 데이터공간을 절약할 수 있게 된다.
Entity 관련 어노테이션
- @Entity 어노테이션으로 도메인 표시
- 자주 쓰는 엔티티 어노테이션
- @Id : PK 설정
- @GeneratedValud : Auto Increment 설정
- @Column : 객체 변수 이름과 실제 컬럼명과 다를 경우 실제 컬럼명과 매핑 해줌
- Column(name="컬럼명")
- name외에 nullable, unique등등 여러 옵션 있음
- 중복되면 안되는 값의 경우 unique 옵션 권장
- 동시성 때문에 겹칠수도 있기 때문에 권장
- @OneToOne, ManyToOne 등등 관계 설정
- 양방향의 경우 주인 표시 필요
- ex : OneToMany(mappedBy="객체명")
- 관계가 있는 반대 테이블의 해당 객체를 주인으로 사용하고 해당 어노테이션이 있는 객체는 읽기전용으로 되어 변경 불가
- @JoinColumn : 참조하는 테이블의 FK 설정
- JoinColumn(name="컬럼명")
- @Inheritance : 상속관계 전략 지정
- SINGLE_TABLE : 한 테이블로 구성, default
- TABLE_PER_CLASS : 각 구현체별로 테이블 구성
- JOINED : 부모 객체의 PK를 FK로 구성
- @Inheritance(strategy = InheritanceType.전략)
- @DiscriminatorColumn
- 상속 관계 시에 자식 테이블 구분을 위한 필드
- 부모 테이블에 추가함
- @DiscriminatorColumn(name="컬럼명")
- @DiscriminatorValue
- 자식 테이블의 구분자
- 부모 테이블의 DiscriminatorColumn 필드에 해당 값이 들어감
- 해당 어노테이션 사용하지 않으면 클래스 명으로 들어감
- DiscriminatorValue("구분자")
- @Enumerated : enum 객체 사용
- @Enumerated(EnumType.타입)
- EnumType.STRING : enum 그대로 사용
- EnumType.ORDINAL : 정의 순서에 따라 값 배정
- ORDINAL 사용 시 중간에 값이 새로 들어오면 값이 다시 배정되므로 가급적 STRING로 사용하자.
Entity 설계 시 주의점
- Setter는 가급적 사용하지 말자
- 데이터가 어디서 변경됐는지 찾기가 어려움
- 모든 연관관계는 지연로딩으로 설정
- 즉시로딩(EAGER)일 시 연관 매핑되어 있는 테이블 조회 쿼리문도 다 실행이 되어 자원낭비가 된다.
- 연관관계(fetch = FetchType.타입)
- EAGER,LAZY
- ex : ManyToOne(fetch = LAZY)
- XToOne는 기본 즉시로딩
- XToMany는 기본 지연로딩
- 양방향 연관관계인 경우 양쪽 모두 set하는 method 필요
- 양쪽 테이블에 데이터가 바뀌어야한다.
- 좀더 역할이 큰 쪽에 method 추가
- 생성자에 의한 객체 생성보다는 정적 메소드를 통해서만 생성되도록 하는게 유지보수 측면에서 더 좋다
- 생성자 권한을 protected로 줘서 생성자에 의한 객체 생성을 방지
- @NoArgsConstructor 데코레이터로도 가능
- @NoArgsConstructor(access = AccessLevel.PROTECTED)
Repository 관련 어노테이션
- @Repository 어노테이션으로 스프링 빈에 등록
- Repository 생성하여 사용시 생성자를 통해 인젝션 하는게 좋으며 이때 객체는 final로 변경할 수 없게 하는게 좋다.
- @AllArgsConstructor
- rombok method로 생성자 알아서 만들어줌
- @RequirArgsConstructor
- final 붙은 녀석만 생성자 만들어 줌
- @PersistanceContext
- EntityManager에 해당 repository 주입
- Autowired로도 주입 가능
- Spring Data Jpa가 지원해줌
- Autowired로 주입 가능하기 때문에 생성자로도 주입이 가능하고 결국 rombok로 대체 가능
- @RequirArgsConstructor 으로 EntityManager를 주입하는 생성자를 생성하므로써 일관성 있는 코드 관리 가능
- Autowired로도 주입 가능
- EntityManager에 해당 repository 주입
EntityManager Method
- persist
- 영속 컨텍스트에 저장
- 정보 저장
- persist(객체)
- merge
- 정보 업데이트
- merge(객체)
- 한번이라도 persist한 객체는 영속 컨텍스트로 남아 있고 merge 시 ID로 영속 컨텍스트에서 찾아서 db에 업데이트하고 객체 복사본을 다시 영속 컨텍스트에 업데이트 한다.
- find
- 정보 하나 검색
- 기본 PK로 검색함
- find(객체타입, 키명)
- 전체 조회나 다른 값으로 조회시 쿼리 생성해야함
- Spring Data Jpa로 쉽게 생성 가능하다
- createQuery
- JPQL로 쿼리문 생성 해줌
- ex 테이블의 전체 내용 조회
- createQuery(select m from Member m)
- m은 Member 클래스 객체
- ex 이름으로 데이터 조회
- createQuery(select m from Member m where m.name = :name).setParameter("name",name)
- 콜론 문자로 표시하여 setParameter로 바인딩해줌
- ex 테이블의 전체 내용 조회
- JPQL로 쿼리문 생성 해줌
Serivce 관련 어노테이션
- @Service 어노테이션으로 스프링 빈에 등록
- @Transactional
- 자동으로 commit,rollback 등으로 해줌
- 없다면 데이터 조작 후 반드시 commit 나 rollback 해야 DB에 반영됨
- readOnly
- 읽기 전용으로 사용, 좀더 효율적
- 데이터 읽기의 경우 가급적 옵션값 true로 넣어줌
- 데이터 수정, 저장인 경우 해당 옵션 없어야 정상 저장
양방향 연관관계 매핑문제
(백기선님 유투브 참고)
엔티티 2개를 매핑한다. Book, BookStore
아래 코드로 매핑하니 데이터베이스에 저장이 안되고 null값이 들어간다. 이유는 무엇일까?
Book클래스에서는 @ManyToOne으로 BookStore과 매핑을 해주었다. 단방향 연관관계이다.
BookStore에서는 @OneToMany(mappedBy = "BookStore") 를 지정해서,
역시 Book과 매핑을 해주었고, mappedBy = "BookStore" 라고 선언하여, BookStore는 외래키가 있는 주인이 아닌 클래스이며, Book이 관계의 주인임을 알려주었다.
관계의 주인인 Book클래스 쪽에서 관계가 설정이 되어야 한다. 그래야지만 데이터베이스에 반영이 된다.
위의 코드에서는, 관계의 주인이 Book인데, Book에는 설정을 하지않고,
BookStore에서 자기 자신한테만 설정을 했기 때문에
Book클래스와 BookStore클래스는 따로 놀고 있었던 것.
아래와 같이 주인인 book객체에 BookStore를 세팅해주어야 DB에 반영이 되는 것이다.
느낀점
ERD설계, 엔티티 어노테이션, 단/양방향 연관관계에 대해 공부를 했다.
과제 하나 하는게 정말 많은 지식을 필요로 하는 것 같다. 공부할게 정말 많다는걸 새삼 느꼈다. 그래도 뿌듯했다.
https://www.youtube.com/watch?v=hsSc5epPXDs&t=436s
'회고록' 카테고리의 다른 글
[TIL-221207수] 항해99 24일차(유효성검사) (0) | 2022.12.07 |
---|---|
[TIL-221206화] 항해99 23일차 (DataIntegrityViolationException, IllegalArgumentException) (0) | 2022.12.06 |
[WIL] 항해99 3주차 회고 (DI / IOC개념정리) (0) | 2022.12.04 |
[TIL-221204일] 항해99 21일차 (0) | 2022.12.04 |
[TIL-221203토] 항해99 20일차 (0) | 2022.12.03 |