-
2. 예제 도메인 개발스프링/spring data jpa 2023. 2. 15. 22:22
예제 도메인 엔티티 ERD
엔티티 코드
Member.java
package study.datajpa.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; @Entity @Getter @Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) // jpa 는 내부적으로 리플렉션을 사용하기 때문에 기본생성자가 필요하다. @ToString(of = {"id", "username", "age"}) // team 쓰면 무한 루프가 일어날 수도 있어서 가급적이면 toString에 안쓰는게 좋다. public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "member_id") // @Column name 속성 : 데이터베이스 테이블과 매핑되는 이름으로 맞춰준다. private Long id; private String username; private int age; // 연관관계매핑 : 자신 엔티티 To 상대 엔티티, 연관관계 주인, fetch 전략 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "team_id") // fk 명 연결 private Team team; public Member(String username) { this.username = username; } public Member(String username, int age, Team team) { this.username = username; this.age = age; this.team = team; } public void changeTeam(Team team) { this.team = team; team.getMembers().add(this); // 객체 연관관계이기 때문에 team 에도 member 를 추가해주었다. } }
- NoArgsConstructor(access = AccessLevel.PROTECTED)
- JPA는 내부적으로 리플렉션을 사용하기 때문에 기본생성자가 필요함.
- 기본생성자는 리플렉션 용도 이외에 외부에서 사용될 일이 없으므로, 잘못 호출되는 것을 방지하기 위해 접근제한자를 protected(자신 + 상속한 클래스) 로 설정.
- ToString은 연관관계 매핑이 있는 엔티티는 가급적 사용하면 안된다. A가 B를 호출하고, B가 A를 호출하는 무한루프에 빠질 수 있다.
- @Column 의 name 속성은 데이터베이스 테이블 컬럼명을 엔티티 클래스 필드에 매핑하기 위해 사용한다.
Team.java
package study.datajpa.entity; import java.util.ArrayList; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; @Getter @Setter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @ToString(of = {"id", "name"}) public class Team { @Id @GeneratedValue @Column(name = "team_id") private Long id; private String name; @OneToMany(mappedBy = "team") // foreign key 가 없는 쪽에 mappedBy를 걸어야한다. private List<Member> members = new ArrayList<>(); public Team(String name) { this.name = name; } }
테스트 코드
package study.datajpa.entity; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Rollback; import org.springframework.transaction.annotation.Transactional; @SpringBootTest @Transactional @Rollback(false) class MemberTest { @PersistenceContext EntityManager em; @Test public void testEntity() { Team teamA = new Team("teamA"); Team teamB = new Team("teamB"); em.persist(teamA); em.persist(teamB); Member member1 = new Member("member1", 10, teamA); Member member2 = new Member("member2", 20, teamA); Member member3 = new Member("member3", 30, teamB); Member member4 = new Member("member4", 40, teamB); em.persist(member1); em.persist(member2); em.persist(member3); em.persist(member4); // 영속성 컨텍스트에 변경사항을 쿼리로 반영하고, 영속성 컨텍스트를 초기화 em.flush(); em.clear(); // 확인 List<Member> members = em.createQuery("select m from Member m", Member.class) .getResultList(); for (Member member : members) { System.out.println("member = " + member); System.out.println("-> member.team = " + member.getTeam()); // 지연로딩으로 적용된다. } } }
'스프링 > spring data jpa' 카테고리의 다른 글
1. 프로젝트 설정 및 테스트 (0) 2023.02.15 - NoArgsConstructor(access = AccessLevel.PROTECTED)