• groupld: com,springboot
• artifactid: relationship
• name: relationship
• Developer Tools: Lombok, Spring Configuration Processor
• Web: Spring Web
• SQL: Spring Data JPA. MariaDB Driver
이전에 사용한 소스코드를 다음과 같이 그대 로 가져와 사용합니다.
(이에 따라 queryDSL의 의존 성과 플러그인 설정을 Don. xml 파일에 그대로 추가해야함)
@Entity
@Table(name = "product_detail")
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ProductDetail extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String description;
// 주목
@OneToOne
@JoinColumn(name = "product_number")
private Product product;
}
// data/repository/ProductDetailRepository.java
public interface ProductDetailRepository extends JpaRepository<ProductDetail, Long> {
}
// test/com.springboot.relationship/data/repository/ProductDetailRepositoryTest.java
package com.springboot.relationship.data.repository;
import com.springboot.relationship.data.entity.Product;
import com.springboot.relationship.data.entity.ProductDetail;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class ProductDetailRepositoryTest {
// 상품과 상품 정보에 매핑된 리포지토리 의존성 주입
@Autowired
ProductDetailRepository productDetailRepository;
@Autowired
ProductRepository productRepository;
@Test
public void saveAndReadTest1(){
Product product = new Product();
product.setName("스프링부트 JPA");
product.setPrice(5000);
product.setStock(500);
productRepository.save(product);
ProductDetail productDetail = new ProductDetail();
productDetail.setProduct(product);
productDetail.setDescription("스프링 부트와 JPA를 함께 볼 수 있는 책");
productDetailRepository.save(productDetail);
// 생성한 데이터 조회
System.out.println("savedProduct: " + productDetailRepository.findById(
productDetail.getId()).get().getProduct());
System.out.println("savedProductDetail: " + productDetailRepository.findById(
productDetail.getId()).get());
}
}
public @interface OneToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default FetchType.EAGER;
boolean optional() default true;
String mappedBy() default "";
boolean orphanRemoval() default false;
}
@Entity
// 생략..
public class Product extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long number;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer price;
@Column(nullable = false)
private Integer stock;
// 추가
@OneToOne
private ProductDetail productDetail;
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name="product")
public class Product extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long number;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer price;
@Column(nullable = false)
private Integer stock;
// 주목
@OneToOne(mappedBy = "product")
private ProductDetail productDetail;
}
// data/entity/Product.java
@OneToOne(mappedBy = "product")
@ToString.Exclude
private ProductDetail productDetail;
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name="provider")
public class Provider extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
package com.springboot.relationship.data.entity;
// 생략..
public class Product extends BaseEntity {
// 생략..
@ManyToOne
@JoinColumn(name = "provider_id")
@ToString.Exclude
private Provider provider;
}
public interface ProviderRepository extends JpaRepository<Provider, Long> {
}
package com.springboot.relationship.data.repository;
import com.springboot.relationship.data.entity.Product;
import com.springboot.relationship.data.entity.Provider;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class ProviderRepositoryTest {
// 의존성 주입
@Autowired
ProviderRepository providerRepository;
@Autowired
ProductRepository productRepository;
@Test
void relationshipTest1(){
// 테스트 데이터 생산
Provider provider = new Provider();
provider.setName("Corner");
providerRepository.save(provider);
Product product = new Product();
product.setName("가위");
product.setPrice(5000);
product.setStock(500);
productRepository.save(product);
// 테스트
System.out.println("product: " + productRepository.findById(1L)
.orElseThrow(RuntimeException::new));
System.out.println("provider: " + productRepository.findById(1L)
.orElseThrow(RuntimeException::new).getProvider());
}
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name="provider")
public class Provider extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// @OneToMny의 기본 fetch 전략이 Lazy이기 때문에 즉시 로딩으로 조정
@OneToMany(mappedBy = "provider", fetch = FetchType.EAGER)
@ToString.Exclude
private List<Product> productList = new ArrayList<>();
}
@SpringBootTest
public class ProviderRepositoryTest {
@Autowired
ProviderRepository providerRepository;
@Autowired
ProductRepository productRepository;
@Test
void relationshipTest(){
// 테스트 데이터 생성
Provider provider = new Provider();
provider.setName("Corner");
providerRepository.save(provider);
Product product1 = new Product();
product1.setName("펜");
product1.setPrice(1000);
product1.setStock(1000);
product1.setProvider(provider);
Product product2 = new Product();
product2.setName("가방");
product2.setPrice(5000);
product2.setStock(300);
product2.setProvider(provider);
Product product3 = new Product();
product3.setName("모자");
product3.setPrice(500);
product3.setStock(50);
product3.setProvider(provider);
productRepository.save(product1);
productRepository.save(product2);
productRepository.save(product3);
List<Product> productList = providerRepository.findById(provider.getId()).get().getProductList();
for(Product product: productList){
System.out.println(product);
}
}
}
먼저 상품 분류 엔티티와 레포지토리를 생성합니다.
// data/entity/Category.java
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode
@Table(name="category")
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String code;
private String name;
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "category_id")
private List<Product> products = new ArrayList<>();
}
// data/repository/CategoryRepository.java
public interface CategoryRepository extends JpaRepository<Category, Long> {
}
// test/com.springboot.relationship/data/repository/CategoryRepositoryTest.java
@SpringBootTest
public class CategoryRepositoryTest {
@Autowired
ProductRepository productRepository;
@Autowired
CategoryRepository categoryRepository;
@Test
void relationshipTest(){
// 테스트 데이터 생성
Product product = new Product();
product.setName("펜");
product.setPrice(2000);
product.setStock(100);
productRepository.save(product);
Category category = new Category();
category.setCode("S1");
category.setName("도서");
category.getProducts().add(product);
categoryRepository.save(category);
// 테스트 코드
List<Product> productList = categoryRepository.findById(1L).get().getProducts();
for(Product foundProduct:productList){
System.out.println(product);
}
}
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name="producer")
public class Producer extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private String name;
// 주목
@ManyToMany
@ToString.Exclude
private List<Product> productList = new ArrayList<>();
public void addProduct(Product product){
productList.add(product);
}
}
public interface ProducerRepository extends JpaRepository<Producer, Long> {
}
public class ProducerRepositoryTest {
@Autowired
ProducerRepository producerRepository;
@Autowired
ProductRepository productRepository;
@Test
@Transactional
void relationshipTest(){
Product product1 = saveProduct("동글펜", 400, 1000);
Product product2 = saveProduct("네모 공책", 200, 200);
Product product3 = saveProduct("지우개", 300, 1400);
Producer producer1 = saveProducer("flature");
Producer producer2 = saveProducer("wikibooks");
producer1.addProduct(product1);
producer1.addProduct(product2);
producer2.addProduct(product2);
producer2.addProduct(product3);
producerRepository.saveAll(Lists.newArrayList(producer1,producer2));
System.out.println(producerRepository.findById(1L).get().getProductList());
}
private Product saveProduct(String name, Integer price, Integer stock){
Product product = new Product();
product.setName(name);
product.setPrice(price);
product.setStock(stock);
return productRepository.save(product);
}
private Producer saveProducer(String name){
Producer producer = new Producer();
producer.setName(name);
return productRepository.save(producer);
}
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name="product")
public class Product extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long number;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer price;
@Column(nullable = false)
private Integer stock;
@OneToOne(mappedBy = "product")
@ToString.Exclude
private ProductDetail productDetail;
@ManyToOne
@JoinColumn(name = "provider_id")
@ToString.Exclude
private Provider provider;
// 생산업체에 대한 다대다 연관관계 설정
@ManyToMany
@ToString.Exclude
private List<Producer> producers = new ArrayList<>();
public void addProducer(Producer producer){
this.producers.add(producer);
}
}
Public class ProducerRepositoryTest {
// 생략..
@Test
@Transactional
void relationshipTest2(){
Product product1 = saveProduct("동글펜", 400, 1000);
Product product2 = saveProduct("네모 공책", 200, 200);
Product product3 = saveProduct("지우개", 300, 1400);
// 생략..
// 양방향 연관관계 설정
product1.addProducer(producer1);
product2.addProducer(producer2);
product2.addProducer(producer1);
product3.addProducer(producer2);
producerRepository.saveAll(Lists.newArrayList(producer1,producer2));
productRepository.saveAll(Lists.newArrayList(product1, product2, product3));
System.out.println(producerRepository.findById(1L).get().getProductList());
System.out.println(productRepository.findById(2L).get().getProducers());
}
// 생략..
}
종류 | 설명 |
ALL | 모든 영속 상태 변경에 대해 영속성 전이를 적용 |
PERSIST | 엔티티가 영속화할 때 연관된 엔티티도 함께 영속화 |
MERGE | 엔티티를 영속성 컨텍스트에 병합할 때 연관된 엔티티도 병합 |
REMOVE | 엔티티를 제거할 때 연관된 엔티티도 제거 |
REFRESH | 엔티티를 새로고침할 때 연관된 엔티티도 새로고침 |
DETACH | 엔티티를 영속성 컨텍스트에서 제외하면 연관된 엔티티도 제외 |
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name="provider")
public class Provider extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 영속성 전이를 위한 @OneToMany 어노테이션
@OneToMany(mappedBy = "provider", cascade = CascadeType.PERSIST)
@ToString.Exclude
private List<Product> productList = new ArrayList<>();
}
@SpringBootTest
public class ProviderRepositoryTest {
@Autowired
ProviderRepository providerRepository;
@Autowired
ProducerRepositoryTest productRepository;
@Test
void cascadeTest(){
Provider provider = saveProvider("new provider");
Product product1 = saveProduct("동글펜", 400, 1000);
Product product2 = saveProduct("네모 공책", 200, 200);
Product product3 = saveProduct("지우개", 300, 1400);
// 연관관계 설정
product1.setProvider(provider);
product2.setProvider(provider);
product3.setProvider(provider);
provider.getProductList().addAll(Lists.newArrayList(product1, product2, product3));
// 영속성 전이 수행
providerRepository.save(provider);
}
private Product saveProduct(String name, Integer price, Integer stock){
Product product = new Product();
product.setName(name);
product.setPrice(price);
product.setStock(stock);
return product;
}
private Provider saveProvider(String name){
Provider provider = new Provider();
provider.setName(name);
return provider;
}
}
providerRepository.save(provider);
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name="provider")
public class Provider extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 'orphanRemoval = true'은 고아 객체를 제거하는 기능
@OneToMany(mappedBy = "provider", cascade = CascadeType.PERSIST, orphanRemoval = true)
@ToString.Exclude
private List<Product> productList = new ArrayList<>();
}
@SpringBootTest
public class ProviderRepositoryTest {
// 생략...
product1.setProvider(provider);
product2.setProvider(provider);
product3.setProvider(provider);
provider.getProductList().addAll(Lists.newArrayList(product1, product2, product3));
providerRepository.saveAndFlush(provider);
// 엔티티 출력
providerRepository.findAll().forEach(System.out::println);
productRepository.findAll().forEach(System.out::println);
// 생략...
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name="provider")
public class Provider extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 영속성 전이 코드 작성
}
9. 상품 엔티티에 공급 업체 번호를 받기 위한 엔티티 필드를 추가하는 코드를 작성하시오.
@Entity
// 생략..
public class Product extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long number;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer price;
@Column(nullable = false)
private Integer stock;
@OneToOne(mappedBy = "product")
@ToString.Exclude
private ProductDetail productDetail;
// 코드 추가
}
8.
@OneToMany(mappedBy = "provider", cascade = CascadeType.PERSIST)
@ToString.Exclude
private List<Product> productList = new ArrayList<>();
9.
@ManyToOne
@JoinColumn(name = "provider_id")
@ToString.Exclude
private Provider provider;
[출처] 장정우, 『스프링 부트 핵심 가이드』, 위키북스(2022), p.159-207.
ⓒ 다라
[스프링 3팀] 12. 서버 간 통신 & 섹션 0. 시큐리티 (0) | 2023.12.29 |
---|---|
[스프링 3팀] 10장. 유효성 검사와 예외 처리 (2) | 2023.12.22 |
[스프링3] 8장. Spring Data JPA 활용 (0) | 2023.11.24 |
[스프링 3] 07 테스트 코드 작성하기 (0) | 2023.11.17 |
[스프링3] 6장. 데이터베이스 연동 - 2 (0) | 2023.11.10 |