Spring Data JPA (Java Persistence API)
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로 반대쪽 엔티티의 어떤 속성에 해당하는지 명시해주어야 한다.
'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 |