반응형

[Spring JPA #4] JPA 관계 매핑

반응형

| @ManyToOne


프로젝트 구조

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │   └── tutorial
│   │   │   └── springbootjpa
│   │   │   ├── Account.java
│   │   │   ├── Address.java
│   │   │   ├── JpaRunner.java
│   │   │   ├── SpringBootJpaApplication.java
│   │   │   └── Study.java
│   │   └── resources
│   │   ├── application.properties
│   │   ├── static
│   │   └── templates


의존성 관리

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


application.properties

spring.datasource.url=jdbc:postgresql://localhost:5432/springboot
spring.datasource.username=saelobi
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


소스 코드

@SpringBootApplication
public class SpringBootJpaApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootJpaApplication.class, args);
}

}
@Entity(name = "myAccount")
@Table(name = "Account")
public class Account {

@Id
@GeneratedValue
private Long id;

@Column(nullable=false, unique=true)
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;
}
}
@Embeddable
public class Address {

private String street;

private String city;

private String state;

private String zipCode;
}
@Entity
public class Study {

@Id
@GeneratedValue
private Long id;

private String name;

@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 테이블의 컬럼을 참조하는 외래키를 생성하게 됩니다.


@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

@PersistenceContext
EntityManager entityManager;

@Override
public void run(ApplicationArguments args) throws Exception {
Account account = new Account();
account.setUsername("saelobi");
account.setPassword("jpa");

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

entityManager.persist(account);
entityManager.persist(study);
}
}
  • Account 객체를 생성한 후 이 Account 객체를 Study 객체의 owner 컬럼의 레퍼런스로서 설정하게 되면 다음과 같이 RDB의 테이블 상에서 이 정보가 account의 한 id로 변경되며 해당 row를 참조하게 됩니다.


결과화면

Hibernate:

create table account (
id int8 not null,
password varchar(255),
username varchar(255) not null,
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 account
add constraint UK_gex1lmaqpg0ir5g1f5eftyaa1 unique (username)
Hibernate:

alter table if exists study
add constraint FK210g5r7wftvloq2ics531e6e4
foreign key (owner_id)
references account
Hibernate:
select
nextval ('hibernate_sequence')
Hibernate:
select
nextval ('hibernate_sequence')
Hibernate:
insert
into
account
(password, username, id)
values
(?, ?, ?)
Hibernate:
insert
into
study
(name, owner_id, id)
values
(?, ?, ?)
springboot=# select * from study;
id | name | owner_id
----+-----------------+----------
2 | Spring Data JPA | 1
springboot=# select * from account;
id | password | username
----+----------+----------
1 | jpa | saelobi


| @OneToMany


소스 코드

@SpringBootApplication
public class SpringBootJpaApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootJpaApplication.class, args);
}

}

@Entity(name = "myAccount")
@Table(name = "Account")
public class Account {

@Id
@GeneratedValue
private Long id;

@Column(nullable=false, unique=true)
private String username;

private String password;

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

public Set<Study> getStudy() {
return study;
}

public void setStudy(Set<Study> study) {
this.study = study;
}

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;
}
}
  • Account 엔티티에 위와 같이 Account : Study = 1: N 의 관계를 맞추기 위해 @OneToMany 어노테이션과 다수의 Study 엔티티에 대한 정보를 저장하기 위한 Set 자료구조를 도입하였습니다. 이때 JPA에서는 @OneToMany에서의 관계를 보고 account와 study 테이블의 관계를 정의한 account_study 테이블을 생성하여 관계 정보를 저장합니다.


@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;
}
}


결과화면

Hibernate:

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

create table account_study (
my_account_id int8 not null,
study_id int8 not null,
primary key (my_account_id, study_id)
)
Hibernate:

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

alter table if exists account
add constraint UK_gex1lmaqpg0ir5g1f5eftyaa1 unique (username)
Hibernate:

alter table if exists account_study
add constraint UK_2wmnv2rfmyl3w24rwy6qo47ss unique (study_id)
Hibernate:

alter table if exists account_study
add constraint FKaj8d7ald8auk6it7koyokkkkj
foreign key (study_id)
references study
Hibernate:

alter table if exists account_study
add constraint FKntr6qqv6r6tb5wng0tuc0w54c
foreign key (my_account_id)
references account
Hibernate:
select
nextval ('hibernate_sequence')
Hibernate:
select
nextval ('hibernate_sequence')
Hibernate:
insert
into
account
(password, username, id)
values
(?, ?, ?)
Hibernate:
insert
into
study
(name, id)
values
(?, ?)
Hibernate:
insert
into
account_study
(my_account_id, study_id)
values
(?, ?)
springboot=# select * from study;
id | name
----+-----------------
2 | Spring Data JPA
(1 row)

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

springboot=# select * from account_study;
my_account_id | study_id
---------------+----------
1 | 2
(1 row)


| 양방향 매핑


소스 코드

@Entity(name = "myAccount")
@Table(name = "Account")
public class Account {

@Id
@GeneratedValue
private Long id;

@Column(nullable=false, unique=true)
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 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;
}

public void addStudy(Study study) {
this.studies.add(study);
study.setOwner(this);
}

public void removeStudy(Study study) {
this.studies.remove(study);
study.setOwner(null);
}
}
  • 양방향 매핑을 이루기 위해 @OneToMany 어노테이션에 mappedBy = "owner" 라는 인수를 추가했습니다. 이 인수의 목적은 Study 엔티티의 어떤 값과 매핑되어 양방향 매핑이 될 것인지에 대한 정보를 추가하는 용도입니다.
  • addStudy와 removeStudy는 Account 엔티티에서 Study 엔티티 목록을 추가하거나 삭제했을 때 어떻게 양방향 매핑정보를 처리하는 지 알아볼 수 있습니다.

@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 엔티티에 어떤 컬럼과 매핑할 것인지에 대한 정보가 설정되어 있으므로 그와 관련된 정보는 여기서 부가하지 않았습니다.

@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

@PersistenceContext
EntityManager entityManager;

@Override
public void run(ApplicationArguments args) throws Exception {
Account account = new Account();
account.setUsername("saelobi");
account.setPassword("jpa");

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

account.addStudy(study);

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


결과 화면

Hibernate:

create table account (
id int8 not null,
password varchar(255),
username varchar(255) not null,
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 account
add constraint UK_gex1lmaqpg0ir5g1f5eftyaa1 unique (username)
Hibernate:

alter table if exists study
add constraint FK210g5r7wftvloq2ics531e6e4
foreign key (owner_id)
references account
Hibernate:
select
nextval ('hibernate_sequence')
Hibernate:
select
nextval ('hibernate_sequence')
Hibernate:
insert
into
account
(password, username, id)
values
(?, ?, ?)
Hibernate:
insert
into
study
(name, owner_id, id)
values
(?, ?, ?)
springboot=# select * from account;
id | password | username
----+----------+----------
1 | jpa | saelobi
(1 row)

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


https://www.inflearn.com/course/스프링-데이터-jpa


반응형

이 글을 공유하기

댓글

Designed by JB FACTORY