SpringBoot JPA 예제(1:N, 양방향)

 
양방향 1:N JPA 관계를 맺어봅시다.

1. 준비

이 포스트는 다음과 같은 포스트에서 파생 되었습니다.

2. 양방향(bidirectional) 1:N 관계

1:N 양방향 관계는 이전에 봤던 @OneToMany, @ManyToOne 어노테이션을 사용합니다. 이번에는 살짝 코드를 분리시켰기 때문에 코드량이 많아졌습니다.

2.1. 양방향 1:N 예제

양방향 1:N 예제를 진행합니다.

2.1.1. Table

테이블은 아래처럼 구성 되어있습니다. MariaDB입니다.

CREATE TABLE `member` (
	`seq` INT(10) NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(50) NULL DEFAULT NULL,
	PRIMARY KEY (`seq`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;
CREATE TABLE `phone` (
	`seq` INT(10) NOT NULL AUTO_INCREMENT,
	`member_id` INT(10) NULL DEFAULT NULL,
	`no` VARCHAR(50) NULL DEFAULT NULL,
	PRIMARY KEY (`seq`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;

2.1.2. Entity

회원 Entity 및 핸드폰 Entity를 만듭니다.

2.1.2.1. Member Entity
package jpa3;

import java.util.ArrayList;
import java.util.Collection;

import javax.persistence.CascadeType;
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.OneToMany;
import javax.transaction.Transactional;

@Entity
public class Member {

	@Id
	@Column(name="seq")
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int seq;

	@Column(name="name")
	private String name;

	@OneToMany(cascade=CascadeType.ALL, mappedBy="member")
	private Collection<Phone> phone;

	public Member(){}

	public Member(String name){
		this.name = name;
	}

	public int getSeq() {
		return seq;
	}

	public void setSeq(int id) {
		this.seq = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Collection<Phone> getPhone() {
		if( phone == null ){
			phone = new ArrayList<Phone>();
		}
		return phone;
	}

	public void addPhone(Phone p){
		Collection<Phone> phone = getPhone();
		phone.add(p);
	}

	public void setPhone(Collection<Phone> phone) {
		this.phone = phone;
	}

	@Override
	public String toString() {
		String result = "[member_"+seq+"] " + name;
		return result;
	}
}

회원 Entity에 @OneToMany 어노테이션이 사용되었습니다. 그리고 mappedBy 라는 속성이 사용 되었습니다. 여기서 mappedBy의 값으로 사용된 "member"는 Phone Entity에서 Member 클래스 변수명입니다. 기본적으로 @OneToMany는 fetch 타입이 LAZY입니다.

2.1.2.2. Phone Entity
package jpa3;

import javax.persistence.CascadeType;
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;

@Entity
public class Phone {

	@Id
	@Column(name="seq")
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int seq;

	@Column(name="no")
	private String no;

	@ManyToOne(optional=false)
	@JoinColumn(name="member_id")
	private Member member;

	public Phone() {}
	public Phone(Member member, String no){
		this.no = no;
		this.member = member;
	}

	public int getSeq() {
		return seq;
	}

	public void setSeq(int seq) {
		this.seq = seq;
	}

	public String getNo() {
		return no;
	}

	public void setNo(String no) {
		this.no = no;
	}

	public Member getMember() {
		return member;
	}

	public void setMember(Member member) {
		this.member = member;
	}

	@Override
	public String toString() {
		String result = "[phone_"+seq+"] " + no;
		return result;
	}
}

Phone Entity에서는 @ManyToOne 어노테이션이 사용되었습니다. 여기에서 Member 클래스를 가진 멤버 변수 이름이 "member" 입니다. 아까 Member Entity에서 봤던 mappedBy에서 "member"의 값을 뜻합니다. 그리고 @ManyToOne 어노테이션의 기본 fetch 전략은 EAGER입니다.

2.1.3. Repository

Repository는 간단하게 코드만 보고 넘어가겠습니다.

package jpa3;

import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Integer>{}
package jpa3;

import org.springframework.data.jpa.repository.JpaRepository;

public interface PhoneRepository extends JpaRepository<Phone, Integer> {}

2.2. Application

실제로 위의 코드를 돌려보는 프로그램을 작성합니다. 이번 포스트에서는 바로 Repository를 최상위 애플리케이션 클래스에서 실행하지 않고 서비스 클래스를 별도로 만들어 분리하고 실행합니다.

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

2.2.1. RespositoryService

서비스 메소드를 정의할 인터페이스를 만듭니다.

package jpa3;

public interface RepositoryService {
	public void saveMember();
	public void print();
	public void lazyPrint();
	public void lazyPrint2();
	public void deletAll();
}

2.2.2. RespositoryServiceImpl

실제 서비스할 비즈니스 로직을 구현합니다. 자세한 내용은 주석으로 처리했습니다.

package jpa3;

import java.util.List;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RepositoryServiceImpl implements RepositoryService {

	@Autowired
	private MemberRepository mr;

	@Autowired
	private PhoneRepository pr;

	public void saveMember(){
		Member first = new Member("Jung");
		Member second = new Member("Dong");
		Member third = new Member("Min");

		Phone p = new Phone(first, "010-XXXX-YYYY");

		mr.save(first);
		mr.save(second);
		mr.save(third);

		pr.save(p);
	}

	public void print(){
		// @ManyToOne의 fetch 기본전략은 EAGER이다.
		// 따라서 @Transactional 어노테이션이 없더라도
		// 기본적으로 전부 데이터를 적재한다.
		List<Phone> phone = pr.findAll();
		for( Phone p : phone ){
			System.out.println(p.toString()+ " " + p.getMember().toString());
		}
	}

	@Transactional
	public void lazyPrint(){
		// @OneToMany의 fetch 기본전략은 LAZY이다.
		// 따라서 Member Entity 내부의 Phone 콜렉션은
		// LAZY 전략이기 때문에 @Transactional 어노테이션이 있어야 한다.
		List<Member> member = mr.findAll();
		for( Member m : member ) {
			System.out.println(m.toString());
			for( Phone e : m.getPhone() ){
				System.out.println(e.toString());
			}
		}
	}

	public void lazyPrint2(){
		// Entity가 LAZY 전략일지라도
		// LAZY 전략을 쓰는 객체를 사용하지 않는다면
		// @Transactional 어노테이션이 없어도 된다.
		List<Member> member = mr.findAll();
		for( Member m : member ) {
			System.out.println(m.toString());
		}
	}

	public void deletAll() {
		mr.deleteAll();
		pr.deleteAll();
	}
}

2.2.3. Application

RepositoryService의 메소드들을 실행합니다. 트랜잭션이 걸린것도 있고 아닌것도 있지만 콘솔 화면에 출력은 문제가 없습니다.

package jpa3;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Jpa3Application implements CommandLineRunner{

	@Autowired
	private RepositoryService rs;

    public static void main(String[] args) {
        SpringApplication.run(Jpa3Application.class, args);
    }
	@Override
	public void run(String... args) throws Exception {
		rs.deletAll();
		rs.saveMember();
		rs.print();
		rs.lazyPrint();
		rs.lazyPrint2();
	}
}
[phone_161] 010-XXXX-YYYY [member_336] Jung
[member_336] Jung
[phone_161] 010-XXXX-YYYY
[member_337] Dong
[member_338] Min
[member_336] Jung
[member_337] Dong
[member_338] Min

3. 마무리

1:N 양방향 예제에 대해 알아봤습니다. JPA 잘 쓰면 정말 빠르고 유연한 어플리케이션 개발이 될것 같네요!

이 외에도 @OneToOne, @ManyToMany 어노테이션 등도 존재합니다. 추후에 이것들도 포스팅할 기회가 있으면 해볼게요. :)

 

[출처] http://jdm.kr/blog/143

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
224 JPA 다대다 관계 모델 @OneToMany로 구현하여 Column 추가하기(@IdClass 사용) file 졸리운_곰 2018.05.18 94
223 Spring Data JPA 연관관계 매핑하는 방법 졸리운_곰 2018.05.18 21
222 공유된 FK(Foreign Key) JPA 연관 관계 매핑 하기 file 졸리운_곰 2018.05.18 676
221 JPA - 05. 연관관계 매핑 기초 졸리운_곰 2018.05.18 31
» SpringBoot JPA 예제(1:N, 양방향) 졸리운_곰 2018.05.18 28
219 SpringBoot JPA 예제(@OneToMany, 단방향) 졸리운_곰 2018.05.18 27
218 JPA / Hibernate One to Many Mapping Example with Spring Boot file 졸리운_곰 2018.05.18 88
217 The best way to map a @OneToMany relationship with JPA and Hibernate file 졸리운_곰 2018.05.18 177
216 (JPA) Embedded Type file 졸리운_곰 2018.05.18 33
215 스프링 데이터 JPA 레퍼런스 번역 file 졸리운_곰 2018.05.14 341
214 UML: 클래스 다이어그램과 소스코드 매핑 file 졸리운_곰 2018.04.30 170
213 lombok에 대해서 알아보자 file 졸리운_곰 2018.04.24 59
212 lombok을 잘 써보자! (2) 졸리운_곰 2018.04.24 151
211 lombok을 잘 써보자! (1) 졸리운_곰 2018.04.24 77
210 Maven 기초 사용법 졸리운_곰 2018.04.15 103
209 [JAVA] Java 와 Mysql 연동 및 DB 사용 졸리운_곰 2018.02.14 88
208 json을 파싱해보자 졸리운_곰 2018.02.12 58
207 [JAVA] json형식의 문자열을 json객체로 parsing하기 졸리운_곰 2018.02.12 90
206 [Java] Quartz (쿼츠)를 사용하여 자바 스케줄링(scheduling) 하기 졸리운_곰 2018.02.12 246
205 스프링(Spring) 프레임워크 기본 개념 강좌 (7) - Patterns 졸리운_곰 2017.10.02 92
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED