Spring

5. 스프링 공부 / 2021-07-15 (단방향 연관 관계)

공대키메라 2021. 7. 15. 21:48

 

오늘 공부의 목표란다!

 

 

연관관계가 필요한 이유

=> 객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다. 

 

예제 시나리오

- 회원과 팀이 있다

- 회원은 하나의 팀에만 소속될 수 있다.

- 회원과 팀은 다대일 관계다.

 

 

//==============Member.class
package jpabook.jpashop.domain;

import javax.persistence.*;

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME" , nullable = false)
    private String username;

    @Column(name = "TEAM_ID")
    private long teamId;
}

//==============Team.class
package jpabook.jpashop.domain;

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

@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String name;
}


//============JpaMain.class
package jpabook.jpashop;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try{

            tx.commit();

        }catch(Exception e){
            tx.rollback();
        }finally{
            //code
            em.close();
        }

        emf.close();
    }
}

이렇게 파일을 세팅하고 실행해 보면...

 

밑에 처럼 콘솔창에 쿼리가 날아간것을 확인할 수 있다.

Hibernate: 
    
    create table Member (
       MEMBER_ID bigint not null,
        TEAM_ID bigint,
        USERNAME varchar(255) not null,
        primary key (MEMBER_ID)
    )
Hibernate: 
    
    create table Team (
       TEAM_ID bigint not null,
        name varchar(255),
        primary key (TEAM_ID)
    )

위처럼 한 것이 테이블에 딱 맞춰서 한 설계이다

근데 이렇게하면 생기는 문제점이 있다!

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");
member.setTeamId(team.getId());
em.persist(member);

Member findMember = em.find(Member.class, member.getId());

Long findTeamId = findMember.getTeamId();
Team findTeam = em.find(Team.class, findTeamId);

평소에 Member 테이블과 Team 테이블이 서로 연관관계에 있으면


select * from member m 
   join team t on m.team_id = t.team_id;

 

그냥 이렇게 ANSI조인으로 꺼내면 간단한데 위에서는 member 세팅 해주고 team id 찾아서

또 그걸로 team name을 찾아야 한다 => 뭔가 엄청 번거로워 ㅠㅠ

 

 

그럼 객체지향 스럽게 테이블을 짠다면?

 

그리고 Member.class 에 teamId를 사용하지 않고 그냥 Team class를 변수로 선언해버린다.

 

package jpabook.jpashop.domain;

import javax.persistence.*;

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

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

   /* @Column(name = "TEAM_ID")
    private long teamId;*/
    @ManyToOne
    @JoinColumn(name = "TEAM_ID") // 뭘 조인해야 하는지도 알려줌!
    private Team team;
}

그냥 team을 변수로 선언하면 굳이 여러번 왓다갓다 할 필요가 없다!

 

근데 그러면 DB에 둘의 관계를 알려줘야함. 

 

일대다인지, 다대일인지, 다대다인지 말이다!

 

그래서 Team 위에 ManyToOne이라고 붙여야 한다. 

그리고 조인할 컬럼이 무엇인지 알려줘야 한다.

 

최종적으로는 다음과 같이 그림을 그릴 수 있다.

 

 

그럼 이제 이렇게 사용할 수 있다.

package jpabook.jpashop;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Team;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try{

            Team team = new Team();
            team.setName("TeamA");
            em.persist(team);

            Member member = new Member();
            member.setUsername("member1");
            member.setTeam(team); // 알아서 PK값 꺼내서 FK값으로 넣어줌
            em.persist(member);

            em.flush();
            em.clear();

            Member findMember = em.find(Member.class, member.getId());

            Team findTeam = findMember.getTeam();
            System.out.println("findTema : " + findTeam.getName());
            tx.commit();

        }catch(Exception e){
            tx.rollback();
        }finally{
            //code
            em.close();
        }

        emf.close();
    }
}

콘솔창 보면 결과를 확인할 수 있다.

 

Hibernate: 
    
    create table Member (
       MEMBER_ID bigint not null,
        USERNAME varchar(255),
        TEAM_ID bigint,
        primary key (MEMBER_ID)
    )
Hibernate: 
    
    create table Team (
       TEAM_ID bigint not null,
        name varchar(255),
        primary key (TEAM_ID)
    )
Hibernate: 
    
    alter table Member 
       add constraint FKl7wsny760hjy6x19kqnduasbm 
       foreign key (TEAM_ID) 
       references Team
7월 15, 2021 9:38:37 오후 org.hibernate.tool.schema.internal.SchemaCreatorImpl applyImportSources
INFO: HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@2a492f2a'
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    /* insert jpabook.jpashop.domain.Team
        */ insert 
        into
            Team
            (name, TEAM_ID) 
        values
            (?, ?)
Hibernate: 
    /* insert jpabook.jpashop.domain.Member
        */ insert 
        into
            Member
            (TEAM_ID, USERNAME, MEMBER_ID) 
        values
            (?, ?, ?)
Hibernate: 
    select
        member0_.MEMBER_ID as MEMBER_I1_0_0_,
        member0_.TEAM_ID as TEAM_ID3_0_0_,
        member0_.USERNAME as USERNAME2_0_0_,
        team1_.TEAM_ID as TEAM_ID1_1_1_,
        team1_.name as name2_1_1_ 
    from
        Member member0_ 
    left outer join
        Team team1_ 
            on member0_.TEAM_ID=team1_.TEAM_ID 
    where
        member0_.MEMBER_ID=?
findTema : TeamA