Spring Data JPA (Java Persistence API)

2024. 1. 7. 18:05
728x90

1. JPA 시작

1) ORM (Object Relational Mapping)

: 객체지향적 관점에서 객체를 사용하여 관계형 데이터베이스를 사용하는 기술.

- 객체를 테이블 및 레코드에 매핑.

- 관계 자체를 객체의 형태로 활용하며 객체의 데이터를 SQL을 실행하는데 활용하는 형태가 아닌, 객체를 저장하는 행위를 데이터베이스에 반영하는 부분까지 포함한다. 

- 생산성이 증가하고, 데이터 변환 과정을 간소화해 코드 중복을 줄이며 데이터베이스 의존성을 줄이게 된다. 

- 실제 SQL을 작성하지 않고 위임하기 때문에 성능에 영향을 미치기도 한다. 

 

2) JPA와 Hibernate

- JPA : 데이터가 어떻게 테이블에 매핑되는지 명세하기 위함. 인터페이스와 어노테이션으로 구성! 

@Entity
public class Student {}  // 이 객체가 데이터베이스 엔티티임을 표시

- Hibernate : 이런 JPA의 명세를 바탕으로 데이터베이스를 활용할 수 있게 해주는 ORM 프레임워크.

~~> 어노테이션을 확인해서 데이터베이스에 필요한 형태로 적용시켜준다.

 

 

1) Spring Boot JPA 의존성 추가

 

2) build.gradle에 SQLite와 hibernate 의존성 추가 

  // sqlite
	runtimeOnly 'org.xerial:sqlite-jdbc:3.41.2.2'
    //hibernate
	runtimeOnly 'org.hibernate.orm:hibernate-community-dialects:6.2.4.Final'

 

3) application.yml 파일 설정

- JPA도 어떤 데이터베이스를 사용할지가 설정되어야 함! 

- jpa 관련 설정도 필요. (spring 하위에 위치)

  • spring.jpa.hibernate.ddl-auto : Hibernate가 Entity 객체를 적용하기 위한 DDL을 어떻게 실행할지에 대한 옵션.
  • spring.jpa.show-sql: Hibernate에서 실제로 실행한 SQL을 콘솔에서 확인하는 옵션
  • spring.jpa.database-platform : Hibernate에서 사용할 SQL Dialect를 지정하는 옵션
spring:
  datasource:
    url: jdbc:sqlite:db.sqlite
    driver-class-name: org.sqlite.JDBC
#    username: sa
#    password: password

  jpa:
    hibernate:
      ddl-auto: create
    show-sql: true
    database-platform: org.hibernate.community.dialect.SQLiteDialect

 

4) Entity 작성하기

- 자바 객체를 데이터베이스의 테이블, 또는 레코드로 정의할 수 있다. 

  • @Entity : 이 클래스가 데이터베이스 상의 어떤 테이블을 나타내는 클래스임을 알려주는 어노테이션
  • @Table / @Column: 클래스 이름과 실제 테이블이 다르거나 기타 설정들을 제공하기 위한 어노테이션
  • @Id : 이 필드가 테이블의 PK(Identity)임을 나타내는 어노테이션
  • @GeneratedValue(strategy = GenerationType.IDENTITY) : 이 속성의 컬럼 데이터는 데이터베이스에서 자동으로 부여하는 값 및 어떻게 부여할지를 정의하는 어노테이션 (== AUTOINCREMENT) 
package com.example.jpa.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

/*
CREATE TABLE article (
    id INTEGER PRIMARY KEY AUTOINCREMENT, --Long
    title TEXT, --String
    content TEXT --String
    );
 */
@Entity
@Table(name = "article")
public class ArticleEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private String content;
}

>> 실행하면 그 결과 테이블이 만들어진다.

 

 

2. JpaRepository

1) JPA와 Hibernate

- JPA는 EntityManager를 사용, 

- Spring Data JPA는 JpaRepository를 사용.

 

- JpaRepository를 상속받는 인터페이스 생성.

: <사용할 엔티티 클래스, 해당 클래스의 PK 타입>

: Bean 객체로 등록되기 때문에 의존성 주입이 자동으로 된다. 

import com.example.jpa.entity.ArticleEntity;
import org.springframework.data.jpa.repository.JpaRepository;

//JpaRepository를 상속받는 인터페이스
//JpaRepository의 기능을 사용해서 데이터베이스와 소통할 수 있다. 
public interface ArticleRepository extends JpaRepository<ArticleEntity, Long> {

}


//service class
@Service
public class ArticleService {
    private final ArticleRepository repository;
    // ...
}

 

2) Create 

: 새 객체 만들고 save()

//ArticleService class

@Service
@RequiredArgsConstructor
public class ArticleService {
    private final ArticleRepository repository;

    public void createArticle(String title, String content) {
        // 1. 새로운 ArticleEntity 인스턴스를 만든다.
        ArticleEntity article = new ArticleEntity();
        // 2. 인스턴스에 내가 저장하고 싶은 데이터를 넣어준다.
        article.setTitle(title);
        article.setContent(content);
        // 3. repository를 이용해서 저장한다.
        repository.save(article);
    }
}

 

3) Read

- 전체조회 : findAll()

- 단일조회 : findById() -> Optional이 반환된다. 

//ArticleService class
	public ArticleEntity readArticle(Long id) {
        return repository.findById(id).orElse(null);
    }
    //혹은 이렇게도 가능하다. 리턴값 형태를 Optional로 주기
    public Optional<ArticleEntity> readArticle(Long id) {
        return repository.findById(id);
    }
    
    
    public List<ArticleEntity> readArticleAll() {
        return repository.findAll();
    }

 

4) Update

: 해당 Entity를 조회한 뒤, Entity의 필드를 수정 후 create와 동일하게 save()를 진행.

// ArticleService class
    public void update(Long id, String title, String content) {
        ArticleEntity article = repository.findById(id).orElse(new ArticleEntity());
        article.setTitle(title);
        article.setContent(content);
        repository.save(article);
    }

 

5) Delete

: 삭제하고자 하는 Entity를 delete()에 전달하면 삭제 가능.

: 또는 PK를 기준으로 한다면 deleteById()도 가능.

// ArticleService class
    public void deleteArticle(Long id) {
        ArticleEntity article = repository.findById(id).orElse(new ArticleEntity());
        repository.delete(article);
        // or 
        repository.deleteById(id); //도 가능
    }

 

6) JpaRepository Query Methods

: JpaRepository에 메서드를 추가하면 다양한 방식의 조회가 가능해진다. 

public interface StudentRepository extends JpaRepository<Student, Long> {
    //     ** SELECT ORDER BY
    // SELECT * FROM student ORDER BY name;
    List<Student> findAllByOrderByName();

    // SELECT * FROM student ORDER BY age DESC;
    List<Student> findAllByOrderByAgeDesc();

    //    ** SELECT LIKE
    // SELECT * FROM student WHERE email LIKE '$?';
    List<Student> findAllByEmailEndingWith(String email);

    // SELECT * FROM student WHERE phone LIKE '?%';
    List<Student> findAllByPhoneStartingWith(String phone);

    //    ** SELECT WHERE
    // SELECT * FROM student WHERE age<=?;
    List<Student> findAllByAgeLessThanEqual(Integer age);

    //    ** SELECT IN
    // SELECT * FROM student WHERE age IN (10, 20, 30, 40, 50);
    List<Student> findAllByAgeIn(List<Integer> ages);

    //    ** SELECT BETWEEN
    // SELECT * FROM student WHERE age BETWEEN 30 AND 40;
    List<Student> findAllByAgeBetween(Integer over, Integer under);
}

 

3. ManyToOne & OneToMany

* 하나의 테이블에는 다른 테이블의 레코드를 넣지 못한다... > 그래서 테이블 데이터를 표현하기 위해 ORM이 등장. 

- > ORM을 사용하면 테이블간 관계를 Entity의 필드로 표현 가능! 

 

ex)

1) ManyToOne

: 일반적인 데이터베이스 관계에서 가장 흔하게 활용되는 관계

 

>> Instructor - Student : ManyToOne 관계

  • Foreign Key 컬럼의 이름을 바꾸고 싶다면 @JoinColumn 활용
@Data
@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Integer age;
    private String phone;
    private String email;

    @ManyToOne
    @JoinColumn(name = "advisor")
    private Instructor advisor;
}

>> 실제로 사용할 때 Instructor Entity 찾아서 할당 후 save()

@Service
@RequiredArgsConstructor
public class StudentService {
    private final StudentRepository studentRepository;
    private final InstructorRepository instructorRepository;

    public void create(
            String name,
            Integer age,
            String phone,
            String email,
            Long advisorId
    ) {
        Student newStudent = new Student();
        newStudent.setName(name);
        newStudent.setAge(age);
        newStudent.setPhone(phone);
        newStudent.setEmail(email);
        newStudent.setAdvisor(instructorRepository.findById(advisorId).orElse(null));
        studentRepository.save(newStudent);
    }

 

2) OneToMany

>> Student - Instructor : One To Many 관계

  • 이때 mappedBy로 반대쪽 엔티티의 어떤 속성에 해당하는지 명시해주어야 한다. 

 

728x90

'Programming > Spring, SpringBoot' 카테고리의 다른 글

Logging  (0) 2024.01.15
Spring Beans  (0) 2024.01.15
MyBatis  (1) 2024.01.04
Post/Redirect/Get : redirect를 사용해야하는 이유  (0) 2024.01.04
Spring MVC & Thymeleaf  (4) 2024.01.03

BELATED ARTICLES

more