일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Token
- static
- 객체지향 언어
- 클래스
- 변수의 다양성
- 내일배움캠프
- KPT
- Diary 해우소
- Github_token
- 감사기록
- JVM
- #스파르타내일배움캠프TIL
- #스파르타내일배움캠프
- 스파르타내일배움캠프
- 인스턴스
- 해우소
- TiL_1st_0419
- 스파르타내일배움캠프TIL
- 메서드
- Java
- Git
- 생성자
- 성장기록
- diary
- Java의 이점
- 스레드
- 회고록
- GitHub
- #내일배움캠프
- 포맷은 최후의 보루
- Today
- Total
몬그로이
ORM 본문
ORM 은 DAO 또는 Mapper 를 통해서 조작하는것이 아니라
테이블을 아예 하나의 객체(Object)와 대응시켜 버립니다.
객체지향(Object) 을 관계형 데이터베이스(Relation) 에 매핑(Mapping) 한다는건
정말 많은 난관이 있습니다. 어떻게 해결했는지 알아볼까요?
상속의 문제
- 객체 : 객체간에 멤버변수나 상속관계를 맺을 수 있다.
- RDB : 테이블들은 상속관계가 없고 모두 독립적으로 존재한다.
해결방법 : 매핑정보에 상속정보를 넣어준다. (@OneToMany, @ManyToOne)
관계 문제
- 객체 : 참조를 통해 관계를 가지며 방향을 가진다. (다대다 관계도 있음)
- RDB : 외래키(FK)를 설정하여 Join 으로 조회시에만 참조가 가능하다. (즉, 다대다는 매핑 테이블 필요)
해결방법 : 매핑정보에 방향정보를 넣어준다. (@JoinColumn, @MappedBy)
탐색 문제
- 객체 : 참조를 통해 다른 객체로 순차적 탐색이 가능하며 콜렉션도 순회한다.
- RDB : 탐색시 참조하는 만큼 추가 쿼리나, Join 이 발생하여 비효율적이다.
해결방법 : 매핑/조회 정보로 참조탐색 시점을 관리한다.(@FetchType, fetchJoin())
밀도 문제
- 객체 : 멤버 객체크기가 매우 클 수 있다.
- RDB : 기본 데이터 타입만 존재한다.
해결방법 : 크기가 큰 멤버 객체는 테이블을 분리하여 상속으로 처리한다. (@embedded)
식별성 문제
- 객체 : 객체의 hashCode 또는 정의한 equals() 메소드를 통해 식별
- RDB : PK 로만 식별
해결방법 : PK 를 객체 Id로 설정하고 EntityManager는 해당 값으로 객체를 식별하여 관리 한다.(@Id,@GeneratedValue )
영속성 4가지 상태( Entity Manager가 관)
① 비영속(new/transient)
엔티티 객체가 만들어져서 아직 저장되지 않은 상태로, 영속성컨텍스트와 전혀 관계가 없는 상태
② 영속(managed)
엔티티가 영속성 컨텍스트에 저장되어, 영속성 컨텍스트가 관리할 수 있는 상태
③ 준영속(detached)
엔티티가 영속성 컨텍스트에 저장되어 있다가 분리된 상태로, 영속성컨텍스트가 더 이상 관리하지 않는 상태
④ 삭제(removed)
엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제하겠다고 표시한 상태
new > (비영속상태) > persist(),merge() > (영속성 컨텍스트에 저장된 상태)
> flush() > (DB에 쿼리가 전송된 상태) > commit() > (DB에 쿼리가 반영된 상태)
Item item = new Item(); // 1
item.setItemNm("테스트 상품");
EntityManager em = entityManagerFactory.createEntityManager(); // 2
EntityTransaction transaction = em.getTransaction(); // 3
transaction.begin();
em.persist(item); // 4-1
em.flush(item). // 4-2 (DB에 SQL 보내기/commit시 자동수행되어 생략 가능함)
transaction.commit(); // 5
em.close(); // 6
1️⃣ 영속성 컨텍스트에 담을 상품 엔티티 생성
2️⃣ 엔티티 매니저 팩토리로부터 엔티티 매니저를 생성
3️⃣ 데이터 변경 시 무결성을 위해 트랜잭션 시작
4️⃣ 영속성 컨텍스트에 저장된 상태, 아직 DB에 INSERT SQL 보내기 전
5️⃣ 트랜잭션을 DB에 반영, 이 때 실제로 INSERT SQL 커밋 수행
6️⃣ 엔티티 매니저와 엔티티 매니저 팩토리 자원을 close() 호출로 반환
쓰기지연 예제
Team teamA = new Team();
teamA.setName("TeamA");
em.persist(teamA);
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
Member member_A = new Member();
member_A.setName("memberA");
member_A.setTeam(teamA);
em.persist(member_A);
// em.flush(); //여기서 flush()를 하느냐 마느냐로 결과가 나뉨
Member findMember = em.find(Member.class, member_A.getId());
Team findTeam= findMember.getTeam();
System.out.println(findTeam.getName());
flush 한 경우(쓰기지연X) 일어나는 일
create member
create team //member 와 team 이 생성된 후
insert team // flush로 인해 쓰기지연이 발생하지 않음 - 바로 DB로 저장됨
insert member // flush로 인해 쓰기지연이 발생하지 않음 - 바로 DB로 저장됨
print "TeamA" (memberA.getTeam()) // DB에서 TeamA를 조회해 옴
*create 는 설정한 팀과 멤버가 생성됨을 의미하는 것일 뿐 실제 작성하거나 한 건 아님
flush 하지 않은 경우(쓰기지연O) 일어나는 일
create member
create team //member 와 team 이 생성된 후
print "TeamA"
(memberA
.getTeam
()) //쓰기 지연이 발생하더라도
" 영속성 컨텍스트에서TeamA를 조회 "해옴
- 이후 flush() 를 따로 진행
insert team // 쓰기 지연이 발생한 부분
insert member // 쓰기 지연이 발생한 부분
*print : 영속성 컨텍스트에서 관리되는 엔티티의 데이터를 불러오는 기능
JpaRepository
사용방법
JpaRepository<Entity,ID> 인터페이스를 인터페이스에 extends 붙인다.
적용되는 내용
1. @NotRepositoryBean 가 달린 상위 인터페이스들의 기능을 포함한 구현체가 프로그래밍된다.
@NotRepositoryBean
해당 인터페이스의 빈생성 막음(빈으로 아직 등록하지 않는다는 의미)
→ 이후 상속받으면 빈이 생성되어 사용가능해짐
즉, JpaRepository 는 데이터 액세스를 위한 핵심 기능의 종합적인 기능을 제공한다
*원래 인터페이스의 Bean 을 생성되지는 않지만 만약을 위해 명시해 두는 것
그리고 JpaRepository 는 @NotRepositoryBean 을 가진 인터페이스들을 상속받고 있기 때문에
각 인터페이스가 가진 특정 데이터 액세스 방법이나 전문적인 기술등을 사용할 수 있다
2. SpringDataJpa 에 의해 엔티티의 CRUD, 페이징, 정렬 기능 메소드들을 가진 빈이 등록된다. (상위 인터페이스들의 기능)
비교
- 기존 Repository 사용할 때
// UserRepository.java
@Repository
public class UserRepository {
@PersistenceContext
EntityManager entityManager;
public User insertUser(User user) {
entityManager.persist(user);
return user;
}
public User selectUser(Long id) {
return entityManager.find(User.class, id);
}
}
- JpaRepository 적용
// UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
// 기본 메서드는 자동으로 만들어짐
}
Raw JPA 테이블 매핑 기능
@Entity
JPQL, QuerDsl 에서 사용됨
@Table
Entity 이름과 다르게 Table 명을 지정하고 싶을때
JDBC, SQL Mapper 에서 사용됨
@Id
엔티티의 주키를 맵핑할 때 사용 ( *복합키를 만드는 맵핑하는 방법도 있음)
자바의 모든 primitive 타입과 그 랩퍼 타입을 사용할 수 있음( Date랑 BigDecimal, BigInteger도 사용 가능)
@GeneratedValue
주키의 생성 방법을 맵핑
생성 전략과 생성기를 설정( 기본 전략인 AUTO 와 TABLE, SEQUENCE, IDENTITY 가 있음)
@Column
unique, nullable, length, columnDefinitioin, ...등
@Temporal
JPA 2.1기준 Date 와 Calendar 만 지원
@Transient
컬럼으로 맵핑하고 싶지 않은 멤버 변수에 사용
Raw JPA 필드 타입 매핑 기능
1. Value 타입
@Column
String, Date, Boolean, 과 같은 타입들에 공통으로 사이즈 제한, 필드명 지정과 같이 옵션을 설정할 때 사용
* 클래스에 @Entity 가 붙으면 필드들에는 자동으로 @Column 이 붙기 때문
@Enumerated
Enum 매핑용으로 @Enumerated(EnumType.STRING) 으로 사용 권장
*Default 타입은 ORIGINAL (0, 1, 2 , ... 으로 들어가기 때문에 추후 순서가 바뀔 가능성 있음)
2. Composite Value 타입
@Embeddable
복합값 객체로 사용할 클래스 지정
@Embedded
필드에 복합 값 객체를 지정할 때
@AttributeOverrides
복합 값 객체 여러 개 지정
@AttributeOverride
복합 값 객체 필드명 선언
@Embeddable
public class Address {
private String street;
private String city;
private String state;
private String zipcode;
// Getters and setters
}
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Embedded
private Address address;
// Getters and setters
}
3. Collection Value 타입
* 컬럼의 값 크기제한이 있기 때문에 현업에서는 Collection Value 보다는 일대다 연관관계를 통한 Collection 타입의 변수를 주로 사용
- 기본 타입의 콜렉션
일반적인 Java 컬렉션 클래스(예: List, Set, Map)를 사용하여 구현 가능
컬렉션을 엔티티의 필드로 선언하고, @ElementCollection어노테이션을 사용하여 매핑
- Composite 타입의 콜렉션
@ElementCollection 어노테이션을 사용하여 콜렉션 매핑 가능
Raw JPA 연관관계 매핑 기능
@OneToOne
1:1 관계로 구성 한다는 것은
결국 하나의 목적에 부합되는 공통된 데이타를 관리한다고 볼 수 있으며
이것은 하나의 테이블에서 관리 할 수 있는 데이타일 가능성이 높다는 의미이므로 잘 생각해 볼 것
@OneToMany
속도를 위해 기본적으로 FetchType 설정이 LAZY 로 설정되어 있음
- mappedBy : 연관관계의 주인 필드를 선택한다.
- fetch : 글로벌 페치 전략 설정
- cascade : 영속성 전이 기능을 사용한다.
- targetEntity : 연관된 엔티티의 타입 정보를 설정한다.
일대다 단방향 (*일대다 양방향 관계는 없음)
//Parent 클래스
@OneToMany
@JoinColumn(name = "parent_id")
private List<Child> childList;
//Child 클래스
@Column(name = "parent_id")
private Long parentId;
일대다 단방향으로 사용될 때 발생할 수 있는 문제
성능문제
- 추가적 조인 : 컬렉션이 있는 엔티티를 조회할 때마다 추가적 조인 발생
- N+1 문제 : DB에 불필요한 부하 발생
엔티티를 조회할 때 연관된 엔티티들을 추가로 조회하기 위해 N개의 쿼리가 실행될 수 있음
데이터 무결성 문제
- 고아 객체: 부모 엔티티 삭제/변경 시, 자식 엔티티가 제대로 관리되지 않을 가능성
- 삭제 전파 : 부모 엔티티 삭제시, 연관된 자식 엔티티를 함께 삭제하도록 하는 것이 쉽지 않음
매핑 복잡성
설계의 복잡성: @OneToMany 는 직관적이지 않으며, 양방향 매핑에 비해 더 복잡하게 느껴질 수 있으므로 유지보수와 코드의 이해에 어려움을 줄 수 있음
@ManyToOne
- optional (default true) : false로 설정하면 연관된 엔티티가 반드시 있어야 함.
- fetch : 글로벌 패치 전략 설정 (기본이 EGEAR 로 설정되어있으나, 실무에서는 기본 LAZY로 설정 추천!)
- cascade : 영속성 전이 기능 사용
- targetEntity : 연관된 엔티티의 타입 정보 설정 (targetEntity = Member.class 식으로 사용)
// 다대일 양방향 관계
@Entity(name = "parent")
public class Parent {
@OneToMany(mappedBy="parent")
private List<Child> childList;
}
@Entity(name = "child")
public class Child {
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
}
@JoinColumn
- 외래 키 매핑 시 사용 (Join 을 요청하기 위한 매핑정보로 쓰인다.)
- @ManyToOne 어노테이션과 주로 함께 쓰인다. (조인대상 컬럼 지정기능을 안쓸거면 생략해도 됨)
- name 속성은 매핑할 외래키의 이름
- 어노테이션을 생략해도 외래 키가 생성됨 (생략 시 외래키의 이름이 기본 전략을 활용하여 생성된다)
- name : 매핑할 외래 키의 이름
- referencedColumnName : 외래 키가 참조하는 대상 테이블의 컬럼명
- foreignKey : 외래 키 제약조건 지정 (테이블 생성 시에만 적용됨)
- unique/nullable/insertable/updateable/columnDefinition/table : @Column의 속성과 같음
@ManyToMany
- 다대다 관계를 나타내는 매핑 정보 (N:M)
- 다대다 설정을 하게되면 중간 매핑테이블(JoinTable)이 자동으로 생성된다.
- 중간 매핑 테이블은 JPA상에서 숨겨져서(Entity 정의 없이) 관리된다.
- 매핑 테이블 관리가 불가능하여서 실무에서는 잘 사용하지 않는 기능
@Entity
public class Parent {
@ManyToMany(mappedBy = "parents")
private List<Child> childs;
}
@Entity
public class Child {
@ManyToMany
@JoinTable(
name = "parent_child",
joinColumns = @JoinColumn(name = "parent_id"),
inverseJoinColumns = @JoinColumn(name = "child_id")
)
private List<Parent> parents;
}
@ManyToMany 보다
TableA(@OneToMany) >> MappingTable(@ManyToOne, @ManyToOne) >> TableB(@OneToMany) 사용
@Entity
public class Parent {
@OneToMany(mappedBy = "parent")
private List<ParentChild> parentChilds;
}
@Entity
public class ParentChild {
@ManyToOne
@JoinColumn("parent_id")
private Parent parent;
@ManyToOne
@JoinColumn("child_id")
private Child child;
}
@Entity
public class Child {
@OneToMany(mappedBy = "child")
private List<ParentChild> parentChilds;
}
https://teamsparta.notion.site/JPA-0085a4d21ae84dc48e8eda40b621e5a0#6a4ee915e59248c79744d051e35e2094
'Organizing Docs > Java Docs' 카테고리의 다른 글
SubQuery, JPAExpressions (0) | 2024.07.01 |
---|---|
JPQL - 복잡한 쿼리를 수동으로 작성하고 실행하는 방법 (0) | 2024.06.30 |
Spring Data JPA (0) | 2024.06.28 |
페이징 (0) | 2024.06.28 |
MyBatis (0) | 2024.06.27 |