지난 포스팅에서 Entity / Entity Manager / Persistence Context 의 역할과 관계에 대해 알아보았다.
이번 포스팅에서는 실제 Entity 클래스를 생성하는 방법과, Spring Data JPA 를 사용하기에, Entity Manager 를 사용하는 Repository 에 대해 알아보자.
** 포스팅 내, 샘플 소스는 롬복(Lombok) 을 사용하여 작성되어있으니 유의 바랍니다. **
롬복 관련해서는 별도의 포스팅을 작성할 예정입니다.
<목차>
1. Entity
2. Repository
3. Repository & Entity Manager
[Entity]
JPA에서 Entity 는 DB 테이블에 매핑된 클래스를 의미한다.
Entity 클래스에서는 어노테이션을 활용해 DB에 쓰일 테이블과 컬럼을 정의한다.
예제는, 참고 도서를 활용하겠다.
생성할 테이블의 구조는 다음과 같다.
이제 Product 라는 이름의 Entity 클래스를 생성해보자.
//Lombok
@Builder
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(of = "number")
//Entity 관련
@Entity
@Table(name="product")
public class Product {
@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;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
위 소스에서는, @Entity / @Table / @Id / @GeneratedValue / @Column 와 같은 것들이 Entity 클래스에서 사용하는 어노테이션이다.
작성된 어노테이션 외, Entity 클래스에서 사용하는 기본 어노테이션에서 알아보자.
- 기본 어노테이션
- @Entity
- 클래스가 JPA Entity 임을 나타내며, DB테이블과 매핑된다.
- 해당 클래스의 인스턴스는 매핑되는 테이블에서 하나의 Row를 의미한다.
- @Table
- 기본적으로 클래스명과 동일한 테이블이 생성되며, 별도로 name 속성을 활용해 테이블 명을 지정할 수 있다.
- Java에서 클래스 명의 첫 글자는 대문자로 시작하기에, "Product" 와 같은 형태로 생성되며, 이를 방지하기 위해 name 속성을 활용한다.
- EX) @Table(name="product")
- @Id
- 엔티티 클래스의 필드 중, PK로 사용할 필드를 지정하는 어노테이션
- 모든 엔티티에 필수로 있어야 함!
- @GeneratedValue
- 일반적으로 @Id 어노테이션과 함께 사용된다.
- 해당 필드의 값이 자동으로 생성되는 방법을 정의한다.
- 미사용 : 비즈니스 로직에서 자체적으로 고유한 기본 값을 생성하여 사용하는 경우
- AUTO : Default 설정으로, DB에 맞게 자동 생성한다.
- IDENTITY : DB에 기본값 생성을 위임하는 방식으로, AUTO_INCREMENT 를 사용해 생성된다.
- SEQUENCE : DB 시퀀스를 사용한다.
- TABLE : 별도의 테이블을 사용하여 기본값을 생성한다.
- @Column
- 엔티티 클래스의 필드는 자동으로 테이블 컬럼으로 매핑된다.
- 따라서, 별도의 설정을 할 내용이 없다면, 어노테이션을 명시하지 않아도 된다.
- 사용할 수 있는 속성은 아래와 같은 것들이 있다.
- name : 컬럼명을 지정하는 속성으로, 명시하지 않으면 필드명과 동일하게 생성된다.
- nullable : 해당 컬럼이 null 을 허용할 지 명시하는 속성 (true / false)
- length : 해당 컬럼의 길이를 지정한다.
- unique : 해당 컬럼을 Unique 로 설정한다.
- @ Transient
- 데이터베이스에 매핑되지 않는 필드를 지정할 때 사용하며, JPA는 이 필드를 데이터베이스에 저장하거나 로드하지 않는다.
- @Entity
엔티티 간의 관계에 대한 어노테이션은 추후 정리 예정.
[Repository]
Spring Data JPA 를 사용하면, Repository 를 사용하여 DB에 접근한다.
이제 실제 Repository 인터페이스를 생성하는 방법에 대해 알아보자.
Spring Data JPA 를 사용한다면, 매우 간단하다.
위 생성한 Product Entity 에 대한 Repository 를 생성해보자.
<PrudctRepository>
public interface ProductRepository extends JpaRepository<Product, Long> {
}
이처럼, JpaRepository 인터페이스를 상속받아 생성하지만, 해당 인터페이스에는 비교적 기본적인 CRUD 메서드만 존재한다.
<JpaRepository>
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAllById(Iterable<ID> ids);
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
/** @deprecated */
@Deprecated
default void deleteInBatch(Iterable<T> entities) {
this.deleteAllInBatch(entities);
}
void deleteAllInBatch(Iterable<T> entities);
void deleteAllByIdInBatch(Iterable<ID> ids);
void deleteAllInBatch();
/** @deprecated */
@Deprecated
T getOne(ID id);
/** @deprecated */
@Deprecated
T getById(ID id);
T getReferenceById(ID id);
<S extends T> List<S> findAll(Example<S> example);
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
이 기본 메서드 외 필요한 부분이 있다면 다음과 같은 규칙을 따라 메서드를 만들어 줄 수 있다.
Entity를 저장 / 갱신 / 삭제 시에는 별도의 규칙이 필요한 경우가 없기 때문에, 주로 Read 부분에 해당하는 Select 쿼리를 생성하는 메서드를 만들 수 있다.
- findBy
- SQL 문의 WHERE 절 역할을 수행하는 구문으로, findBy 뒤에 Entity의 필드명을 입력해서 사용한다.
- And / Or
- AND, OR 조건 여러 개 설정하기 위해 사용한다.
- Like / NotLike
- SQL 문의 LIKE / NOT LIKE 와 동일한 기능을 수행한다.
- StartWith / StartingWith
- 특정 키워드로 시작하는 문자열 조건을 설정한다.
- EndWith / EndingWith
- 특정 키워드로 끝나는 문자열 조건을 설정한다.
- IsNull / IsNotNull
- 레코드 값이 Null 이거나 Null 이 아닌 값을 검색한다.
- True / False
- Boolean 타입의 레코드를 검색할 때 사용한다.
- Before / After
- 시간을 기준으로 값을 검색한다.
- LessThan / GreaterThan
- 특정 값을 기준으로 대소 비교를 할 때 사용한다.
- Between
- 두 값 사이의 데이터를 조회한다.
- OrderBy
- SQL 문에서 ORDER BY 절과 같은 기능을 수행한다.
- Asc / Desc 를 붙여 오름차순 / 내림차순을 설정할 수 있다.
- CountBy
- SQL 문의 COUNT와 동일한 기능을 수행한다.
[Repository & EntityManager]
앞선 포스팅에서, Entity 는 Entity Manager에 의해 관리되며, Entity 와 DB 간의 상호작용을 처리한다 하였으니, Repository 의 실제 구현체인 SimpleJpaRepository 를 살펴보자.
@Repository
@Transactional(
readOnly = true
)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!";
private final JpaEntityInformation<T, ?> entityInformation;
private final EntityManager em;
private final PersistenceProvider provider;
@Nullable
private CrudMethodMetadata metadata;
private EscapeCharacter escapeCharacter;
public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
this.escapeCharacter = EscapeCharacter.DEFAULT;
Assert.notNull(entityInformation, "JpaEntityInformation must not be null!");
Assert.notNull(entityManager, "EntityManager must not be null!");
this.entityInformation = entityInformation;
this.em = entityManager; //!!엔티티 매니저!!
this.provider = PersistenceProvider.fromEntityManager(entityManager);
}
public SimpleJpaRepository(Class<T> domainClass, EntityManager em) {
this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em);
}
... 중략 ...
public Optional<T> findById(ID id) {
Assert.notNull(id, "The given id must not be null!");
Class<T> domainType = this.getDomainClass();
if (this.metadata == null) {
return Optional.ofNullable(this.em.find(domainType, id));
} else {
LockModeType type = this.metadata.getLockModeType();
Map<String, Object> hints = new HashMap();
this.getQueryHints().withFetchGraphs(this.em).forEach(hints::put);
return Optional.ofNullable(type == null ? this.em.find(domainType, id, hints) : this.em.find(domainType, id, type, hints));
}
}
... 이하 생략 ...
위와 같이, SimpleRepository 는 EntityManager 의존성을 주입받아 생성되며, 주입받은 EntityManager는 Entity 를 관리하는데 사용된다.
따라서, Spring Data JPA 에서 Repository 사용에 앞서 Extend / Implements 구조를 이해하고 있으면 도움이 된다.
JpaRepository
'Spring & Spring boot' 카테고리의 다른 글
[Springboot] springdoc 을 사용하여 Swagger 설정하기 - 1 (0) | 2024.08.04 |
---|---|
[Spring Boot] JPA - 4. CRUD Example (0) | 2024.08.04 |
[Spring Boot] JPA - 2. Entity (0) | 2024.07.25 |
[Spring Boot] JPA - 1. ORM 과 JPA (0) | 2024.07.25 |
[Spring Boot] Rest Controller - 6. HttpEntity (1) | 2024.07.24 |