본문 바로가기
Spring/JPA

1대N 연관관계

by step 1 2021. 8. 6.
반응형

@OneToMany(fetch = FetchType.EAGER): 1대N 연관관계를 즉시 로딩할 수 있도록 선언한다.

@JoinColumn(name = "user_id", insertable = false, updatable = false): 선언된 테이블에서는 선언한 컬럼의 해당하는 테이블에 데이터가 수정되거나 생성되지 않도록 선언한다.

@Column(name = "user_id"): 컬럼명을 연관관계에 있는 테이블에 컬럼과 맞춰준다.

 

예제 코드

User 클래스

package com.example.bookmanager.domain;

import com.example.bookmanager.domain.listener.UserEntityListener;
import lombok.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
@Data
@Builder
@Entity
//@EntityListeners(value = {MyEntityListener.class, UserEntityListener.class})
//공통 Entity 사용
@EntityListeners(value = {AuditingEntityListener.class, UserEntityListener.class})
//@Table(name = "user", indexes = {@Index(columnList = "name")}, uniqueConstraints = {@UniqueConstraint(columnNames = {"email"})})
public class User extends BaseEntity{
    @Id
    // 테이블마다 ID값이 증가하도록 설정
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NonNull
    private String name;

    @NonNull
    private String email;

    // update 할 경우 값을 제외하도록 설정
    @Column(updatable = false)
    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    @Enumerated(value = EnumType.STRING)
    private Gender gender;
//    @Transient
//    private String testData;

    public User(String name, String email, LocalDateTime creDate, LocalDateTime upDate) {
        this.name = name;
        this.email = email;
        this.createdAt = creDate;
        this.updatedAt = upDate;
    }

    //@OneToMany(fetch = FetchType.EAGER)
    //private List<Address> address;

    // Entity Listener 이벤트 정의 및 동작 시점 확인
    @PrePersist
    public void prePersist(){
        System.out.println(">>> prePersist");
        // 사용자가 입력을 안해주어도 자동으로 현재 날짜를 입력하도록 설정
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }

    @PostPersist
    public void postPersist(){
        System.out.println(">>> postPersist");
    }

    @PreUpdate
    public void preUpdate(){
        System.out.println(">>> preUpdate");
        // Update를 진행할 때 자동으로 update를 진행한 날짜를 입력하도록 설정
        this.updatedAt = LocalDateTime.now();
    }

    @PostUpdate
    public void postUpdate(){
        System.out.println(">>> postUpdate");
    }

    @PreRemove
    public void preRemove(){
        System.out.println(">>> preRemove");
    }

    @PostRemove
    public void postRemove(){
        System.out.println(">>> postRemove");
    }

    @PostLoad
    public void postLoad(){
        System.out.println(">>> postLoad");
    }

    // 1대N 관계
    // null 예외처리를 방지하기 위해서 arrayList 생성자를 선언해준다.
    @OneToMany(fetch = FetchType.EAGER)
    // UserHistory 테이블에 컬럼과 맞춰준다.
    // User 테이블에서는 History 테이블 컬럼에 데이터를 삽입하거나 수정하지 못하도록 한다.
    @JoinColumn(name = "user_id", insertable = false, updatable = false)
    private List<UserHistory> userHistories = new ArrayList<>();
}

 

UserHistroy 클래스 예제

package com.example.bookmanager.domain;

import com.example.bookmanager.domain.listener.Auditable;
import lombok.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.time.LocalDateTime;

@Data
// 상속받은 BaseEntity를 사용하기 위해서 ToString, EqualsAndHashCode 재정의
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)

@Entity
@NoArgsConstructor
// 자동으로 생성날짜, 수정날짜를 입력해주기 위해서 선언
//@EntityListeners(value = MyEntityListener.class)
// 공통 클래스 사용
//@EntityListeners(value = AuditingEntityListener.class)
// User객체에 History를 저장할 객체 생성
// BaseEntity를 상속받아 그 안에 있는 설정들을 사용할 수 있다.
public class UserHistory extends BaseEntity{
    @Id
    // 테이블마다 ID값이 증가하도록 설정
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "user_id")
    private Long userId;

    @NonNull
    private String name;

    @NonNull
    private String email;

    // update 할 경우 값을 제외하도록 설정
//    @Column(updatable = false)
    // 자동으로 해당값을 넣어준다.
//    @CreatedDate
//    private LocalDateTime createdAt;

    // 자동으로 해당값을 넣어준다.
//    @LastModifiedDate
//    private LocalDateTime updatedAt;
}

 

UserEntityListener 클래스

package com.example.bookmanager.domain.listener;

import com.example.bookmanager.domain.User;
import com.example.bookmanager.domain.UserHistory;
import com.example.bookmanager.repository.UserHistoryRepository;
import com.example.bookmanager.support.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.PostPersist;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;

// Autowired를 사용할 경우 spring bean 등록을 진행해야 하기 때문에 Component 어노테이션을 사용한다.
//@Component
public class UserEntityListener {
//    @Autowired
//    private UserHistoryRepository userHistoryRepository;

    // 업데이트, 생성를 진행할 경우 자동으로 history에 저장되도록 설정
//    @PreUpdate
//    @PrePersist
    // 저장한 이후에 동작하도록 재지정
    @PostPersist
    @PostUpdate
    public void prePersistAndPreUpdate(Object o) {
        // UserHistoryRepository의 bean을 가져온다.
        UserHistoryRepository userHistoryRepository = BeanUtils.getBean(UserHistoryRepository.class);
        User user = (User) o;

        UserHistory userHistory = new UserHistory();
        userHistory.setUserId(user.getId());
        userHistory.setName(user.getName());
        userHistory.setEmail(user.getEmail());

        userHistoryRepository.save(userHistory);
    }
}

 

UserHistoryRepository 클래스

package com.example.bookmanager.repository;

import com.example.bookmanager.domain.UserHistory;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface UserHistoryRepository extends JpaRepository<UserHistory, Long> {
    // userId 값으로 데이터를 찾을수 있도록 선언
    List<UserHistory> findByUserId(Long userId);
}

 

UserRepositoryTest 테스트 코드 추가

    // 1 대 N 테스트
    @Test
    void userRelationTest() {
        Dto();
        User user = new User();
        user.setName("david");
        user.setEmail("david@naver.com");
        user.setGender(Gender.MALE);
        userRepository.save(user);

        user.setName("daniel");
        userRepository.save(user);

        user.setEmail("daniel@naver.com");
        userRepository.save(user);

        // 자동으로 userHistory 테이블에 저장되는것을 확인할 수 있다.
//        userHistoryRepository.findAll().forEach(System.out::println);

        // userId 값으로 데이터 확인(특정 이메일의 Id를 가져와서 조회)
        List<UserHistory> result = userHistoryRepository.findByUserId(
                userRepository.findByEmail("daniel@naver.com").getId());
        //result.forEach(System.out::println);

        List<UserHistory> result2 = userRepository.findByEmail("daniel@naver.com").getUserHistories();

        result2.forEach(System.out::println);
        
    }

 

콘솔 확인

반응형

'Spring > JPA' 카테고리의 다른 글

N대N 연관관계 설정 2 (중간테이블을 만들어서 사용하는 방법)  (0) 2021.08.29
N 대 N 연관관계 설정  (0) 2021.08.29
1 대 1 관계 설정하는 방법  (0) 2021.07.24
연관 관계 설정 -1  (0) 2021.07.24
연관관계, ERD  (0) 2021.07.24