1. JPA
JPA 는 Java Persistence API 의 줄임말로 Java 표준 ORM 인터페이스이다. ORM 은 Object Relational Mapping 의 줄임말로 객체와 RDB 를 매핑하는 기술이다. 객체와 DB 를 매핑하면서 SQL 쿼리 중심이 아닌 객체 중심의 코딩을 할 수 있도록 한다.
JPA 는 Java 표준 ORM 인터페이스로 JPA 의 기능을 사용하면 기존의 반복적인 코드는 물론이고 기본적인 SQL 쿼리를 자동으로 생성해주어 코드를 간결하게 유지할 수 있다. 또한 JPA 를 사용하여 데이터를 객체와 매핑하게 되면, SQL 과 데이터 중심의 설계에서 Java 언어와 객체 중심의 설계로 전환할 수 있다.
2. 환경 설정
JPA 를 사용하기 위해서는 application 의 라이브러리 의존성과 스프링 설정 등을 진행해야 한다.
- build.gradle dependencies
build.gradle 의 dependencies 에 JPA 관련 라이브러리를 추가하여 JPA를 사용할 수 있다. 아래와 같이 spring-boot-starter-data-jpa 라이브러리를 추가한다. 이때 jdbc 를 사용하기 위해서 spring-boot-starter-jdbc 가 있다면 해당 라이브러리는 JPA라이브러리에 포함되기 때문에 제거해도 된다.
// implementation 'org.springframework.boot:spring-boot-starter-jdbc' // spring-boot-starter-data-jpa 에서 jdbc 를 포함하기 때문에 제거
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
- application.properties
application.properties 에서 spring.datasource.url, spring.datasource.driver-class-name, spring.datasource.username 등은 앞서 DB 연동을 위해 설정한 값과 동일하게 간다. 대신 JPA 와 관련된 설정을 추가해서 jpa 를 사용할 때 설정을 저장할 수 있다.
# jpa 가 만든 쿼리문을 확인할 수 있도록 한다.
spring.jpa.show-sql=true
# JPA 가 객체에 따라 테이블을 자동으로 생성하지 않도록 한다.
spring.jpa.hibernate.ddl-auto=none
3. 구현
- 엔티티 매핑
Jpa 에서는 annotation 을 통해서 엔티티와 매핑에 필요한 기능들을 제공해준다.
@Entity
@Table(name = "USERS")
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String uid;
private String name;
@Override
public String toString() {
return "User [id=" + id + ", uid=" + uid + ", name=" + name + "]";
}
// JPA Entity 에는 default constructor 가 필요하다.
public User() {}
// ...
}
위의 예제는 Entity 클래스에 annotation 을 사용하여 table 과 entity 의 매핑을 적용한 예제이다.
어노테이션 이름 | 어노테이션 설명 |
@Entity | 해당 class 를 entity class 로 지정한다. |
@Table | 해당 class 와 매핑될 table 을 지정해준다. 만약 따로 table 이름을 지정해주지 않으면 entity 의 이름을 기본값을 매핑한다. |
@Id | 해당 column 을 PK 로 사용한다. |
@GeneratedValue | PK 의 값을 DB 가 설정된 전략에 따라서 자동으로 생성하고 관리해준다. |
@Column | 변수와 매핑할 테이블의 컬럼을 지정한다. |
Entity 클래스를 정의할 때 주의할 점은 public 또는 protected 기본 생성자를 만들어주어야 한다는 것이다.
JPA 에서 DB 의 값을 객체 필드에 주입할 때 Reflection API 를 사용하여 Entity 클래스를 조회한다. 이때 Reflection API 는 클래스의 기본 클래스가 없으면 객체를 생성할 수 없다. 그 이유는 Reflection API 는 클래스의 생성자의 인자정보를 가져올 수 없기 때문에 기본 생성자를 사용한 후에 클래스의 각 필드들을 초기화 해주는 방식으로 객체를 생성한다. 그렇기 때문에 JPA 를 사용하기 위해서는 인자가 없는 기본 클래스가 필요하다. 이에 대해서는 추후에 다시 한번 Reflection API 등과 함께 정리할 예정이다.
- Jpa Repository
위에서 구현한 User Entity 를 가지고 다음과 같은 Repository 를 구현할 수 있다.
@Repository("JpaUserEmRepository")
public class JpaUserEmRepository implements UserRepository {
@Autowired
private EntityManager em;
@Override
public void save(User user) {
em.persist(user);
}
@Override
public List<User> findAll() {
String query = String.format("SELECT u FROM %s as u", ENTITY);
return em.createQuery(query, User.class).getResultList();
}
public User findById(Long id) {
User result = em.find(User.class, id);
return result;
}
@Override
public User findByUid(String uid) {
String query = String.format("SELECT u FROM %s as u WHERE UID = :id", ENTITY);
List<User> result = em.createQuery(query, User.class).setParameter("id", uid).getResultList();
return result.size() == 0 ? null : result.get(0);
}
@Override
public void update(User user) {
String query = String.format("UPDATE %s as u SET NAME=:name WHERE ID = :id", ENTITY);
em.createQuery(query).setParameter("name", user.getName()).setParameter("id", user.getId());
}
@Override
public void delete(User user) {
em.remove(user);
}
@Override
public void deleteById(Long id) {
User user = em.find(User.class, id);
em.remove(user);
}
@Override
public void deleteByUid(String uid) {
User user = this.findByUid(uid);
em.remove(user);
}
}
위의 예제는 JPA 의 entity manager 를 명시적으로 사용하여 Repository 를 구현한 내용이다.
Entity manager 는 JPA 의 영속성 컨텍스트를 관리하는 객체이다. 영속성 컨텍스트는 엔티티를 저장하는 공간으로 애플리케이션과 DB 사이에서 엔티티 데이터를 저장하고 있는다. JPA 는 영속성 컨텍스트에 엔티티를 저장하다가 DB connection 을 통해서 DB 에 접근하여 데이터 작업을 수행한다.
영속성과 관련된 내용은 JPA 와 관련된 다음 게시물에서 확인할 수 있다.
https://jammdev.tistory.com/172
EntityManager 는 EntityManagerFactory 에서 실제 transaction 단위로 수행할 때마다 생성되는데, 위의 예제에서와 같이 Autowired 어노테이션을 통해서 injection 받을수도 있다.
[Reference]
- https://velog.io/@woply/스프링을-이용한-주요-DB-사용-방법-5가지#jpa를-이용한-db-접근-특징
- https://hyeonic.tistory.com/191
- https://velog.io/@yyy96/JPA-%EA%B8%B0%EB%B3%B8%EC%83%9D%EC%84%B1%EC%9E%90
'Tech > Spring | SpringBoot' 카테고리의 다른 글
[SpringBoot] JUnit 을 이용한 테스트 코드 작성 (0) | 2022.11.28 |
---|---|
[SpringBoot] SpringBoot 에 lombok 적용하기 (0) | 2022.11.11 |
[SpringBoot] Logging - 2 (Logback) (0) | 2022.10.05 |
[SpringBoot] Logging - 1 (JCL, Slf4j, logback, log4j, log4j2) (0) | 2022.10.01 |
[SpringBoot] Database 연동 - 3 (JdbcTemplate) (1) | 2022.09.21 |