[Spring JPA #4] 관계 맵핑


| @ManyToOne

package org.kyhslam.jpa;

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

@Entity
public class Study {

    @Id @GeneratedValue
    private Long id;

    private String name;


    // 하나의 계정으로 여러 study를 만들 수 있기 때문에 MaytoOne이다.
    // (뒤에께 해당 컬럼에 해당)
    // 햇갈릴 경우에는 앞쪽이 해당클래스(Study)이고 뒤쪽이 해당 컬럼(Account)
    @ManyToOne
    private Account owner;


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Account getOwner() {
        return owner;
    }

    public void setOwner(Account owner) {
        this.owner = owner;
    }
}
  • @ManyToOne은 현재 Study 엔티티가 Account엔티티와 N:1 관계를 맺는다는 의미이다. 따라서 study 테이블에 account테이블의 컬럼을 참조하는 외래키를 생성하게 된다.
  • 햇갈릴 경우 앞쪽(Many)이 해당클래스(Study)이고 뒤쪽(One)이 해당 컬럼(Account) 이라고 생각하면 된다.
package org.kyhslam.jpa;


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

@Entity
public class Account {


    @Id @GeneratedValue
    private long id;

    private String username;

    private String password;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
spring.datasource.url=jdbc:postgresql://localhost:5432/springboot
spring.datasource.username=kyhslam
spring.datasource.password=pass

spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
package org.kyhslam.jpa;

import org.hibernate.Session;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;


@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

    @PersistenceContext
    EntityManager entityManager;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Account account = new Account();

        account.setUsername("kyhslam");
        account.setPassword("jpa");

        Study study = new Study();
        study.setName("Spring Data JPA");
        study.setOwner(account);

        Session session = entityManager.unwrap(Session.class);
        session.save(account);
        session.save(study);

        // 이거도 가능
        //entityManager.persist(account);
        //entityManager.persist(study);
    }
}

결과

Hibernate: 

    alter table if exists study 
       drop constraint if exists FK210g5r7wftvloq2ics531e6e4
Hibernate: 

    drop table if exists account cascade
Hibernate: 

    drop table if exists study cascade
Hibernate: 

    drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start 1 increment 1
Hibernate: 

    create table account (
       id int8 not null,
        password varchar(255),
        username varchar(255),
        primary key (id)
    )
Hibernate: 

    create table study (
       id int8 not null,
        name varchar(255),
        owner_id int8,
        primary key (id)
    )
Hibernate: 

    alter table if exists study 
       add constraint FK210g5r7wftvloq2ics531e6e4 
       foreign key (owner_id) 
       references account
springboot=# \dt
         List of relations
 Schema |  Name   | Type  |  Owner
--------+---------+-------+---------
 public | account | table | kyhslam
 public | study   | table | kyhslam
(2 rows)

springboot=# select * from study;
 id |      name       | owner_id
----+-----------------+----------
  2 | Spring Data JPA |        1
(1 row)

springboot=# select * from account;
 id | password | username
----+----------+----------
  1 | jpa      | kyhslam
(1 row)

 


| @OneToMany

소스

package org.kyhslam.jpa;


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.HashSet;
import java.util.Set;

@Entity
public class Account {


    @Id @GeneratedValue
    private long id;

    private String username;

    private String password;

    @OneToMany
    private Set<Study> studies = new HashSet<>();


    public Set<Study> getStudies() {
        return studies;
    }

    public void setStudies(Set<Study> studies) {
        this.studies = studies;
    }

 생략..
}
package org.kyhslam.jpa;

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

@Entity
public class Study {

    @Id @GeneratedValue
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

}

ㅅ스

package org.kyhslam.jpa;

import org.hibernate.Session;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

    @PersistenceContext
    EntityManager entityManager;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Account account = new Account();

        account.setUsername("kyhslam");
        account.setPassword("jpa");

        Study study = new Study();
        study.setName("Spring Data JPA");

        account.getStudies().add(study);


        //entityManager.persist(account);
        Session session = entityManager.unwrap(Session.class);
        session.save(account);
        session.save(study);

        //entityManager.persist(account);
       // entityManager.persist(study);
    }
}

 


| 양방향 맵핑

  • FK 가지고 있는 쪽이 오너이다. 따라서 기본값은 @ManyToOne 가지고 있는 쪽이 주인이다.
  • 주인이 아닌쪽(@OneToMany)에서 mappedBy 사용해서 관계를 맺고 있는 필드를 설정해야 한다.
  • @ManyToOne(이쪽이 주인)
  • @OneToMany(mappedBy)
  • 주인한테 관계를 설정해야 DB에 반영이 된다.
  • 관계가 주인인쪽에 관계를 맵핑해야 한다.

Account

package org.kyhslam.jpa;


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.HashSet;
import java.util.Set;

@Entity
public class Account {


    @Id @GeneratedValue
    private long id;

    private String username;

    private String password;

    @OneToMany(mappedBy = "owner")
    private Set<Study> studies = new HashSet<>();


    public Set<Study> getStudies() {
        return studies;
    }

    public void setStudies(Set<Study> studies) {
        this.studies = studies;
    }
    
    
    // 이상적인 추가 메소드
    public void addStudy(Study study) {
        this.studies.add(study);
        study.setOwner(this);
    }
    
    
	// 삭제
    public void removeStudy(Study study) {
        this.studies.remove(study);
        study.setOwner(null);
    }

}

study

package org.kyhslam.jpa;

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

@Entity
public class Study {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne
    private Account owner;

    public Account getOwner() {
        return owner;
    }

    public void setOwner(Account owner) {
        this.owner = owner;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

}
  • Study 엔티티에 @ManyToOne을 추가해서 양방향 매핑에 대한 정보를 설정해야 합니다. Account 엔티티에 어떤 컬럼과 매핑할 것인지에 대한 정보가 설정되어 있으므로 그와 관련된 정보는 여기서 부가하지 않았습니다.
package org.kyhslam.jpa;

import org.hibernate.Session;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;


@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

    @PersistenceContext
    EntityManager entityManager;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Account account = new Account();

        account.setUsername("kyhslam");
        account.setPassword("jpa");

        Study study = new Study();
        study.setName("Spring Data JPA");

        account.getStudies().add(study);
        study.setOwner(account); // 이 부분을 빼먹으면 데이터가 저장되지 않는다


        //entityManager.persist(account);
        Session session = entityManager.unwrap(Session.class);
        session.save(account);
        session.save(study);

        //entityManager.persist(account);
       // entityManager.persist(study);
    }
}

결과 화면

springboot=# select * from study;
 id |      name       | owner_id
----+-----------------+----------
  2 | Spring Data JPA |
(1 row)

springboot=# select * from study;
 id |      name       | owner_id
----+-----------------+----------
  2 | Spring Data JPA |        1
(1 row)

springboot=# select * from account;
 id | password | username
----+----------+----------
  1 | jpa      | kyhslam
(1 row)

댓글

Designed by JB FACTORY