본문 바로가기

Tech/Spring | SpringBoot

[Spring] Database 연동 - 4 (JPA)

반응형

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

 

[JPA] 영속성 관리

JPA 는 영속성 컨텍스트를 통해서 객체와 DB 사이의 entity 를 관리한다. 영속성 컨텍스트는 entity manager 에 의해 관리되는데, 이를 통해서 애플리케이션과 데이터베이스 사이에서 

jammdev.tistory.com

 

 

EntityManager EntityManagerFactory 에서 실제 transaction 단위로 수행할 때마다 생성되는데, 위의 예제에서와 같이 Autowired 어노테이션을 통해서 injection 받을수도 다.

 

 

[Reference]

- https://velog.io/@woply/스프링을-이용한-주요-DB-사용-방법-5가지#jpa를-이용한-db-접근-특징

 

[spring] DB를 사용하는 주요 기술 5가지

Spring을 이용하여 DB를 사용하는 주요 접근 기술을 학습하고 정리한 내용입니다.

velog.io

- https://hyeonic.tistory.com/191

 

[JPA] 왜 JPA의 Entity는 기본 생성자를 가져야 하는가?

왜 JPA의 Entity는 기본 생성자를 가져야 하는가? 정확히 이야기하면 Entity는 반드시 파라미터가 없는 생성자가 있어야 하고, 이것은 public 또는 protected 이어야 한다. 이러한 궁금증을 가지게 된 이

hyeonic.tistory.com

- https://velog.io/@yyy96/JPA-%EA%B8%B0%EB%B3%B8%EC%83%9D%EC%84%B1%EC%9E%90

 

JPA Entity 생성자에 관하여

JPA 에서 기본 생성자가 필요한 이유와 private 으로 선언하면 안되는 이유에 관하여

velog.io

 

반응형