생성자 주입은 외부에서 생성자를 통해 의존성을 주입하는 방식으로, 객체가 생성될 때 생성자를 통해 필드를 초기화한다 초기화된 필드는 게터를 통해 속성값을 읽을 수 있고 반면에, 세터 주입은 세터 메서드를 통해 객체가 생성된 후 의존성을 수정하는 방식이다
1. Car 클래스는 @Entity 어노테이션을 사용하여 JPA 엔티티로 정의되어 있으며, 이 클래스는 데이터베이스의 테이블에 매핑, 클래스 생성자 필드들은 데이터베이스 열(columns)에 대응되어 JPA가 자동으로 관리
1-1.외부 클래스인 Owner 객체는 생성자 주입(Constructor Injection)을 통해 Car 객체에 주입, 이렇게 주입된 Owner는 @ManyToOne 어노테이션을 사용하여 다대일(N:1) 관계로 매핑되며, 외래 키는 데이터베이스에서 owner_id 열로 저장
1-2. 또한, fetch = FetchType.LAZY를 설정하여 Owner 엔티티가 실제로 필요할 때만 로딩되도록 지연 로딩을 적용해 성능을 최적화
Car.class
package com.example.demo.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.JoinColumn;
@Entity
public class Car {
// 외래키, 다대일
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="owner")
private Owner owner;
// 게터
public Owner getOwner() {
return owner;
}
// 세터 - 생성자를 매개변수에 넣음
public void setOwner(Owner owner) {
this.owner = owner;
}
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String brand,model,color,registraionNumber;
private int modelYear,price;
public Car() {
}
// 데이터 베이스 열에 매핑
public Car(String brand,String model,String color,String registraionNumber, int modelYear,int price, Owner owner) {
super();
this.brand = brand;
this.model = model;
this.color = color;
this.registraionNumber = registraionNumber;
this.modelYear = modelYear;
this.price = price;
this.owner = owner;
}
// 게터 세터
public Long getId() {
return id;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel (String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getRegistrationNumber() {
return registraionNumber;
}
public void setRegistrationNumber(String registraionNumber) {
this.registraionNumber = registraionNumber;
}
public int getModelYear() {
return modelYear;
}
public void setModelYear(int modelYear) {
this.modelYear = modelYear;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
2. 일대다 관계인 Owner.class
package com.example.demo.domain;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer","handler"})
public class Owner {
// 자동차 여러개라 List<Car> 타입
// 일대다,연쇄 업데이트,삭제
@JsonIgnore
@OneToMany(cascade=CascadeType.ALL,mappedBy="owner")
private List<Car> cars;
public List<Car> getCars(){
return cars;
}
public void setCars(List<Car> cars) {
this.cars= cars;
}
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long ownerid;
private String firstname,lastname;
public Owner() {}
public Owner(String firstname,String lastname) {
super();
this.firstname = firstname;
this.lastname = lastname;
}
// 게터 - 클래스 필드의 값을 읽는 역할(재사용성 용이)
public Long getOwnerid() {
return ownerid;
}
public String getFistName() {
return firstname;
}
public String getLastName() {
return lastname;
}
// 세터 - 클래스 필드의 값을 수정하는 역할(재사용성 용이,여기성는 첫번째,마지막이름 수정)
public void setFirstName(String firstname) {
this.firstname = firstname;
}
public void setLastName(String lastname) {
this.lastname = lastname;
}
}
3. Car, Owner클래스에 JPA에 crud기능을 제공하는 스프링 CrudeRepository을 상속받아 각각의 레포지토리를 생성
package com.example.demo.domain;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface CarRepository extends CrudRepository<Car,Long>{
// 브랜드조회
List<Car> findByBrand(String brand);
// 컬러조회
List<Car> findByColor(String color);
// 모델연도 조회
List<Car> findByModelYear(int modelYear);
}
package com.example.demo.domain;
import org.springframework.data.repository.CrudRepository;
public interface OwnerRepository extends CrudRepository<Owner,Long>{
}
3-1. Restful서비스를 만들려면 따로 web 패키지를 만든 후 컨트롤러 클래스생성 그리고 @RestController,@GetMapping어노테이션 사용, 브랜드를 구체적으로 조회하고 싶으면 아래와 같이 @RequestParam을 사용한다.
package com.example.demo.web;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.demo.domain.Car;
import com.example.demo.domain.CarRepository;
@RestController
public class CarController {
private final CarRepository repository;
public CarController(CarRepository repository){
this.repository=repository;
}
@GetMapping("/cars")
public Iterable<Car> getCars(){
return repository.findAll();
}
// 브랜드로 자동차 조회
@GetMapping("/cars/brand")
public Iterable<Car> getCarsByBrand(@RequestParam("brand") String brand) {
return repository.findByBrand(brand);
}
}


4. 마지막으로 객체를 생성 후 레포지토리 saveAll()메서드로 데이터베이스에 저장한다. 로거에는 브랜드,모델을 표시한다.
package com.example.demo;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.example.demo.domain.Car;
import com.example.demo.domain.CarRepository;
import com.example.demo.domain.Owner;
import com.example.demo.domain.OwnerRepository;
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
private static final Logger logger =LoggerFactory.getLogger(
DemoApplication.class);
private final CarRepository repository;
private final OwnerRepository ownerrepository;
// 의존성 주입(생성자 매개변수)
public DemoApplication(CarRepository repository,OwnerRepository ownerrepository) {
this.repository = repository;
this.ownerrepository = ownerrepository;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
Owner owner1 = new Owner("Hong", "Sun");
Owner owner3 = new Owner("Luke", "Skywalker");
ownerrepository.saveAll(Arrays.asList(owner1,owner3));
repository.save(new Car("Ford", "Mustang", "Red", "ADF-1121",2023,590000, owner1));
repository.save(new Car("Nissan", "Leaf", "White", "SSJ-3002",2020,29000,owner3));
repository.save(new Car("Toyota", "Prius", "Silver", "KK0-0212",2022,390000,owner1));
for(Car car:repository.findAll()) {
logger.info("brand:{}, model:{}", car.getBrand(), car.getModel());
}
}
}
5. Maria 데이터베이스

6. 세터주입으로 값을 수정할 수 있다(브랜드 Ford -> Kia로 변경)


참고한 책입니다
https://www.yes24.com/product/goods/139860417
실전! 스프링 부트 3 & 리액트로 시작하는 모던 웹 애플리케이션 개발 - 예스24
스프링 부트와 리액트의 풍부하고 다양한 도구들을 활용해 최신 풀스택 애플리케이션 개발 비법을 배워보자!『실전! 스프링 부트 3 & 리액트로 시작하는 모던 웹 애플리케이션 개발』은 풀스택
www.yes24.com