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는 간단하게 코드만 보고 넘어가겠습니다.

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

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를 최상위 애플리케이션 클래스에서 실행하지 않고 서비스 클래스를 별도로 만들어 분리하고 실행합니다.

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 어노테이션 등도 존재합니다. 추후에 이것들도 포스팅할 기회가 있으면 해볼게요. :)

 

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

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
35 [EMF] EMF Tutorial EMF 튜터리얼 file 졸리운_곰 2023.08.23 283
34 Eclipse RAP Tutorial for Beginners - Workbench Application (OLD) file 졸리운_곰 2021.01.30 323
33 Learn Eclipse GMF in 15 minutes file 졸리운_곰 2019.11.27 253
32 [Eclipse] GEF entry series (10, an implementation of the form) file 졸리운_곰 2019.11.25 212
31 GEF Programmer Guide 번역 졸리운_곰 2019.11.25 227
30 Learn Eclipse GMF in 15 minutes file 졸리운_곰 2019.11.20 224
29 RCP 에디터 정리 졸리운_곰 2019.11.20 333
28 다른 그림과 관련하여 GEF 편집기 레이아웃에서 그림의 위치 제한 조건을 동적으로 계산 Dynamically calculating the position constraints for a figure in a GEF editor layout in relation to another figure file 졸리운_곰 2019.11.20 277
27 RCP 등에서 .mf 파일로 다른 프로젝트 익스포트 포함시 라이브러리(메소드)를 찾지 못할 때, Eclipse RCP - cant resolve importing libraries in final build file 졸리운_곰 2019.10.15 410
26 ESE2006-EclipseModelingSymposium15_GMF.pdf file 졸리운_곰 2019.09.21 302
25 GMF_Creation_Review.pdf file 졸리운_곰 2019.09.21 366
24 Eclipse EMF and GMF Tutorial file 졸리운_곰 2019.09.21 222
23 GMF Tutorial/ko file 졸리운_곰 2019.09.20 347
22 Model Driven Architecture approach to domain of graphical editors file 졸리운_곰 2019.09.20 268
21 Single_Sourcing_RAP_RCP_en.pdf file 졸리운_곰 2019.05.15 311
20 Rich client platform 설명 및 배우기 참고 졸리운_곰 2019.05.15 309
19 Rich Ajax Platform, Part 1: 소개 file 졸리운_곰 2019.05.15 320
18 또 하나의 크로스 플랫폼: Eclipse RAP file 졸리운_곰 2019.05.15 319
17 Eclipse 4 RCP 튜토리얼(완료) file 졸리운_곰 2019.05.14 977
16 Updating UI in Eclipse RCP 졸리운_곰 2015.11.07 408
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED