관리 메뉴

개발 노트

10/28 JPA 연관관계, oBootJpa02 프로젝트 본문

프로젝트 기반 JAVA 응용 SW개발 : 22.07.19~23.01.20/Spring

10/28 JPA 연관관계, oBootJpa02 프로젝트

hayoung.dev 2022. 11. 16. 18:27

JPA 연관관계

단 웬만하면 다대다는 쓰지 않는다.

일대다도 쓰지 않는다.

(다로 끝나는 건 안쓴다고 생각하면 된다.)

다대일 또는 일대일을 쓴다.

 

즉시 로딩, 지연 로딩은 jpa 성능을 좋게 하는데에 중요함.

 

[oBootJpa02]

 

java > com.oracle.oBootJpa02(package)에 controller, domain, repository, service (package) 생성

resource > static > index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <a href="/members/new">Member 신규 생성</a><p>
    <a href="/members">Member List 조회</a>
</body>
</html>

controller > MemberController.java (class)

package com.oracle.oBootJpa02.controller;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.oracle.oBootJpa02.domain.Member;
import com.oracle.oBootJpa02.service.MemberService;

@Controller
public class MemberController {
	private static final Logger logger = LoggerFactory.getLogger(MemberController.class);

	private final MemberService memberService;
	@Autowired
	public MemberController(MemberService memberService) {
		this.memberService = memberService;
	}
	
	@GetMapping(value="/members/new")
	public String createForm() {
		System.out.println("MemberController /members/new start.. ");
		return "members/createMemberForm";
	}

	@PostMapping(value = "/memberSave")
	public String memberSave(Member member) {
		System.out.println("MemberController create start.. ");
		System.out.println("member.getTeamname()->"+member.getTeam());
		System.out.println("member.getName()->"+member.getName());
		memberService.memberSave(member);
		return "redirect:/";
	}


	@GetMapping(value = "/members")
	public String listMember(Model model) {
		List<Member> memberList = memberService.getListAllMember();
		System.out.println("memberList.get(0).getName()->"+memberList.get(0).getName());
		System.out.println("memberList.get(0).getTeam().getName()->"+memberList.get(0).getTeam().getName());
//		System.out.println("memberList.get(1).getName()->"+memberList.get(1).getName());
//		System.out.println("memberList.get(1).getTeam().getName()->"+memberList.get(1).getTeam().getName());
		model.addAttribute("memberList", memberList);
		return "members/memberList";
	}
	
	@GetMapping(value = "/memberModifyForm")
	public String memberModify(Long id, Model model) {
		System.out.println("MemberController memberModify id->"+id);
		Member member = memberService.findByMember(id);
			
		System.out.println("member.get().getId()->"+member.getId());
		System.out.println("member.get().getName()->"+member.getName());
		System.out.println("member.get().getTeam().getName()->"+member.getTeam().getName());
		model.addAttribute("member", member);
		
		return "members/memberModify";

	}

	/* post방식 */
	@PostMapping(value = "/members/memberUpdate")
	public String memberUpdate(Member member, Model model) {
		
		System.out.println("MemberController memberUpdate id->"+member.getId());
		System.out.println("MemberController memberUpdate member.getName->"+member.getName());
		System.out.println("MemberController memberUpdate member.getTeamname->"+member.getTeamname());
		memberService.memberUpdate(member);
		return "redirect:/members";
	}	

}

service > MemberService.java (class)

package com.oracle.oBootJpa02.service;

import java.util.List;

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

import com.oracle.oBootJpa02.domain.Member;
import com.oracle.oBootJpa02.repository.MemberRepository;

@Service
@Transactional
public class MemberService {
	private final MemberRepository memberRepository;
	@Autowired
	public MemberService(MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
	}
	public Member memberSave(Member member) {
		System.out.println("MemberService join member.getName()->"+member.getName());
		memberRepository.memberSave(member);
		return member;
	}

	// 전체회원 조회
	public List<Member> getListAllMember() {
		List<Member> listMember  = memberRepository.findAll();
		System.out.println("MemberService getListAllMember listMember.size()->"+listMember.size());
		return listMember;
	}
	public Member findByMember(Long memberId) {
		Member member1 = memberRepository.findByMember(memberId);
		System.out.println("MemberService findByMember member1.getId()->"+member1.getId());
		System.out.println("MemberService findByMember member1.getName()->"+member1.getName());
		System.out.println("MemberService findByMember member1.getTeam().getName()->"
		                               +member1.getTeam().getName());
		return member1;
	}
	public void memberUpdate(Member member) {
		System.out.println("MemberService memberUpdate member.getName()->"+member.getName());
		System.out.println("MemberService memberUpdate member.getTeamname()->"+member.getTeamname());
		memberRepository.updateByMember(member);
		return;
	}

}

repository > MemberRepository.java (interface)

package com.oracle.oBootJpa02.repository;

import java.util.List;

import com.oracle.oBootJpa02.domain.Member;

public interface MemberRepository {

	Member        memberSave(Member member);

	List<Member>  findAll();

	Member        findByMember(Long memberId);

	int           updateByMember(Member member);

}

repository > JpaMemberRepository (class, MemberRepository 인터페이스 상속)

package com.oracle.oBootJpa02.repository;

import java.util.List;

import javax.persistence.EntityManager;

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

import com.oracle.oBootJpa02.domain.Member;
import com.oracle.oBootJpa02.domain.Team;

@Repository
public class JpaMemberRepository implements MemberRepository {
	private final EntityManager em;
	
    @Autowired
    public JpaMemberRepository(EntityManager em) {
    	this.em = em;
    }

	@Override
	public Member memberSave(Member member) {
		// 팀 (DB에) 저장
		Team team = new Team();
		team.setName(member.getTeamname());
		em.persist(team);
		
		//회원 저장
		member.setTeam(team);
		em.persist(member); /* DB에 저장 */
		
		return member;
	}

	@Override
	public List<Member> findAll() {
		List<Member> memberList = em.createQuery("select m from Member m", Member.class)
				.getResultList();

		return memberList;
    }

	@Override
	public Member findByMember(Long memberId) {
		//                       Entity       PK
		Member member = em.find(Member.class, memberId);
		return member;
	}

	@Override
	public int updateByMember(Member member) {
		int result = 0;
		System.out.println("JpaMemberRepository updateByMember member.getId()->"+member.getId());
	    Member member3 = em.find(Member.class, member.getId());
	    if( member3 != null) {
	    	// 팀 저장
	   		 System.out.println("JpaMemberRepository updateByMember member.getTeamid()->"
                     +member.getTeamid());
	    	 Team team = em.find(Team.class, member.getTeamid());
	    	 if (team != null) {
		 	   	 System.out.println("JpaMemberRepository updateByMember member.getTeamname()->"+
	                       member.getTeamname());
			 	 team.setName(member.getTeamname());
			 	 em.persist(team);
	    	 }
		  	 //회원 저장
	 	   	 System.out.println("JpaMemberRepository updateByMember member.getName()->"+member.getName());
			 member3.setTeam(team);             // 단방향 연관관계 설정, 참조 저장
			 member3.setName(member.getName()); // 단방향 연관관계 설정, 참조 저장
	  		 em.persist(member3);	
			 result = 1; /* 성공하면 결과값을 1로 보내준다. */
	    } else {
	    	 result = 0; /* 성공하면 결과값을 0로 보내준다. */
	  		 System.out.println("JpaMemberRepository updateByMember No Exist..");
	    }
		return result;
	}


}

domain > Member.java

package com.oracle.oBootJpa02.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;

import lombok.Data;

//Entity
//table->member2
//Long   id (PK)
//String name
//Long  sal
//Getter/Setter/ToString

@Entity
@Data
@SequenceGenerator(name = "member_seq_gen",               // 객체  SEQ
                   sequenceName = "member_seq_generator", // DB SEQ
                   initialValue = 1,			//보통 1로 설정
                   allocationSize = 1			//보통 1로 설정
		           )
@Table(name = "member2")
public class Member {
	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE,
	                generator = "member_seq_gen"
			       )
	@Column(name = "member_id" , precision = 10)
	private Long id; /* pk이거나 시퀸스 사용 시에는 long 타입을 권장 */
	@Column(name = "username" , length = 50)
	private String name	; 
	private Long   sal;
	
	// 관계 설정 
	@ManyToOne
	@JoinColumn(name = "team_id")
	private Team team; 
	
	// 실제Column X --> Buffer용도
	@Transient
	private Long   teamid;
	
	@Transient
	private String teamname;
	

}

domain > Team.java

package com.oracle.oBootJpa02.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;

import lombok.Data;

//Entity
//table->Team
//Long   team_id (PK)
//String name(column-->teamname) 
//Getter/Setter/ToString
// 1) 객체  SEQ  team_seq_gen(PK에 할당)
// 2) DB  SEQ  team_seq_generator

@Entity
@Data
@SequenceGenerator(
		name = "team_seq_gen",
		sequenceName = "team_seq_generator", //매핑할 DB 시퀀스 이름
		initialValue = 1,
		allocationSize = 1		
		)
public class Team {
	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE,
	                generator = "team_seq_gen")
	private Long team_id;
    @Column(name = "teamname") 
	private String name;

}

resources > application.properties

server.port=8384
# Oracle Connect
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=scottjpa
spring.datasource.password=tiger

# Jpa Setting
spring.jpa.show-sql=true
# *** Operation --> create / none
spring.jpa.hibernate.ddl-auto=create

resources > createMemberForm.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>JPA 회원 등록2</h1>
<div class="container">
    <form action="/memberSave" method="post">
     <!--   ID : <input type="text" id="id"     name="id"       required="required"> -->
            회원이름  : <input type="text" id="name" name="name"     placeholder="회원이름을 입력하세요"><p>
            팀   이름  : <input type="text" id="name" name="teamname" placeholder="팀이름을 입력하세요">
         <button type="submit">등록</button>
    </form>
</div> <!-- /container -->
</body>
</html>

resources > memberList.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
    <div>
        <table border="1">
            <thead>
            <tr>
                <th>No</th>
                <th>회원이름</th>
                <th>팀  이름</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="member : ${memberList}">
                <td th:text="${member.id}"></td>
                <!-- html > 검사로 확인해보면 예를들어 첫번째 값은 /memberModifyForm?id=1 이다. -->
                <td><a th:href="@{/memberModifyForm(id=${member.id})}" th:text="${member.name}"></a></td>
                <td th:text="${member.team.name}"></td>
               
            </tr>
            </tbody>
        </table> 
    </div>
</div> <!-- /container -->
</body>
</html>

resources > memberModify.html

<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>JPA2 회원 수정</h1>
<div class="container">
	<!-- 읽을때는 get, 저장할 땐 post 방식을 사용한다. -->
    <form  action="/members/memberUpdate" method="post">
        <!--   ID : <input type="text" id="id"     name="id"  required="required"> -->
        <input type="hidden"  name="id"       th:value="${member.id}">
        <input type="hidden"  name="teamid"   th:value="${member.team.team_id}"> 
                회원이름  : <input type="text" id="name" name="name"      th:value="${member.name}"   ><p>
                팀   이름  : <input type="text" id="name" name="teamname"  th:value="${member.team.name}"><p>
                <button type="submit">회원 수정 처리</button>
    </form>
</div> <!-- /container -->
</body>
</html>

 

출력 결과

객체를 선언해주면 거기에 맵핑할 테이블의 컬럼 명을 자동으로 설정해준다. (team_id가 자동으로 생겼다.)

 

 

fk로 연관돼있다.

 

member에 관한 모든 정보를 가져와서 돌리기 때문에 성능이 좋아진다.

 

반응형