본문 바로가기
카테고리 없음

[Spring] Spring JPA

by GGShin 2022. 7. 3.

잘 알면 편리한데 깊이 들어가면 어렵다는 JPA의 기본적인 내용들을 학습해보았습니다. ☺️..

아직 깊이 들어가지도 않았는데 살짝 떨립니다. 잠 못자는 날이 올까봐 ㅎㅎ

 

일단 Spring JPA를 어떻게 사용하는지,  Spring JDBC와의 어떤 차이가 있는지 위주로 살펴보려고 합니다.

Spring JDBC의 경우는 개발자가 Get, Post, Patch, Delete등의 method에 직접 query문을 작성해야했습니다. 이 방식은 Table의 수가 늘어날 수록 상당히 관리가 어렵다고 합니다. 그래서 대안으로 나온 방식이 ORM이라는 것인데, ORM은 Object Relational Mapping의 약자로 객체(Object)와 데이터 베이스(Table)를 자동으로 Mapping 하는 기술입니다. 자동으로 mapping 해 주고 개발자가 query 문을 명시하지 않아도 되게끔 해줍니다. 이 ORM 기술을 이용한 것이 바로 Spring JPA입니다. (그리고 Hibernate이 Spring JPA의 구현체입니다.)

 

그렇다면 어떻게 Spring JPA를 사용하는지에 대해 기본적인 CRUD 위주로 알아보겠습니다.

 

1. 먼저 dependency가 필요하기 때문에 추가해줍니다.

Spring project를 https://start.spring.io/ 에서 만들때 부터 dependency를 추가하여 프로젝트를 생성해도 됩니다. 

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

 

2. Spring JDBC에서는 JdbcTemplate이 DB와 연결하는 역할을 해주었는데, 

Spring JPA에서는 EntityManager가 그 역할을 수행합니다. EntityManager를 사용하면 Entity를 persistence context라는 곳에서 저장&관리하게 됩니다. @PersistenceContext를 EntityManager 위에 붙여주어 Persistence Context에서 Entity를 관리하도록 해줍니다.

@Repository //Repository로의 역할을 명시주는 anno
@Transactional //Transaction 기능을 위한 anno
public class PersonJpaRepository {

    /* All the oprations you are performing in a specific session are all stored in the EntityManager
      when you add @PersistenceContext anno on it.
     */
    @PersistenceContext
    EntityManager entityManager; //EntityManager는 SpringJDBC에서의 JDBCTemplate과 같이 database와 connect 해주는 역할을 한다.
    
    //...
    
    }

 

PersonJpaRepository 위에는 @Transactional이 붙어있는데, 이는 Transaction의 특성(ACID)을 갖게 하도록 만드는 애노테이션입니다. (@Transactional을 붙이지 않으면 TransactionRequiredException이 발생하더라구요. merge call을 안전하게 수행할 수 없다는 이야기가 나옵니다.)

 

 

한 가지 유의할 것은 JPA는 schema update이라는 기능이 있어서 schema.sql 파일에 table을 따로 Create해 둘 필요가 없습니다. Schema update은 spring boot auto configuration 이라는 Hibernate의 기능 중에 하나인데, 자동으로 entity의 table을 만들어 냅니다. 그래서 오히려 schema.sql과 같은 파일에 table을 정의해 두면 중복된 테이블이라는 에러가 발생하게 됩니다.

 

이미 테이블이 존재한다는 에러메세지

대신 테스팅 등의 이유로 몇가지 더미 데이터들을 넣어 두는 것은 지장이 없습니다. 

이런 식으로요! Spring JDBC에서 처럼 CREATE TABLE은 하면 안되지만 dummy data는 넣어주어도 되므로 INSERT INTO 쿼리문은 그대로 사용해도 됩니다.

 

3. Entity(DB Table)로 관리할 class에는 @Entity 를 붙여서 명시해주어야 합니다. 

 

만약에 table명을 class이름과 다르게 설정하는 경우에는 @Table(name=" ")으로 이름을 설정해주면 됩니다. (꼭 다르게 설정하지 않는 경우라도 해당 애노테이션으로 이름을 명시해 주는 것이 좋다고 합니다. 명시하지 않는 경우라면 DB table명도 camel case로 생성이 되는데, database에서는 보통 underscore를 사용하는 방식을 따르기 때문입니다. 예를 들어 entity명이 PersonInformation이라면 table 명은 person_information이 되는 것이 좋으므로 따로 명시해주어야 한다는 말입니다. 명시해주지 않으면 그대로 PersonInformation이라는 table이 생성되게 되니까요.)

@Entity //Entity 명시해주는 anno
/* @Table(name="person") 을 사용하면 해당 entity가 person이라는 table과 mapping된다는 것을
   명시해준다. Table명과 Entity명이 불일치할 때 사용하면 된다.
 */
public class Person {
    @Id //Primary key임을 알려주는 anno
    @GeneratedValue //Hibernate이 자동으로 값을 생성해주도록 하는 anno
    private int id;
    /* @Column(name="name")을 사용하면 mapping할 column 명을 명시해 줄 수 있다.
    @Table(name = " ")과 마찬가지로 field명과 column명이 불일치할 때 사용하면 된다.
     */
    private String name;
    private String location;
    private Date birthDate;

    //항상 No argument constructor가 있어야 한다!
    public Person(){}

    //Hibernate이 Id를 자동으로 생성해 줄 것이므로 id는 파라미터로 받지 않는 constructor를 만들어 둔다.
    public Person(String name, String location, Date birthDate) {
        this.name = name;
        this.location = location;
        this.birthDate = birthDate;
    }
    
    //...
  }

 

그리고 primary key 역할을 할 field에 @Id 를 붙여서 명시해줍니다. 

데이터가 생성될 때마다 자동으로 값이 할당되게 하려면 @GeneratedValue를 사용합니다. (SQL의 AUTO_INCREMENT와 동일한 기능인 것 같습니다.)

@Column은 DB table의 column이 된다는 의미입니다. 따로 붙여주지 않아도 되지만, @Table 처럼 역시나 네이밍 측면에서 고려해야 하는 경우에 붙여주면 됩니다. 

 

그리고 한 가지 유의해야 할 것은 반드시 No argument constructor를 따로 정의해 두어야 한다는 점입니다.

 

그렇다면 한 번 CRUD는 어떻게 구현하는지 알아보겠습니다.

 

1. CREATE

 

row를 추가하기 위해서는 merge method를 사용합니다.

 

public Person insert(Person person) {
      return entityManager.merge(person);
}

상당히 심플하죠? Spring JDBC에서는 이렇게 써주었었는데 말입니다.

 

 public int insert(Person person) {
        return jdbcTemplate.update("insert into person (id, name, location, birth_date) values(?, ?, ?, ?)",
                person.getId(),
                person.getName(),
                person.getLocation(),
                new Timestamp(person.getBirthDate().getTime()));
}

query문도 써주고 parameter별로 값을 하나씩 get method로 지정해주다보니 코드가 길어졌습니다.

Spring JPA에서는 EntityManager가 대신 java object(entity)를 table의 row로 바꾸어주는 것 같습니다. Merge라는 method 명도 entity를 table의 row로 merge한다는 의미에서 나온 이름이 아닐까 추측해 봅니다. 

 

2. READ

Table의 데이터를 읽는 것은 find method를 사용합니다.

 

public Person findById(int id) {
  return entityManager.find(Person.class, id);
}

예를 들어서 id를 기준으로 데이터를 찾는 메서드를 정의하겠다고 한다면, 위와 같이 작성하면 됩니다.

find 메서드 안의 parameter로는 어떤 entity에서 찾을 것인지, 그리고 어떤 column을 기준으로 찾을 것인지 이 두 가지를 알려주면 됩니다.

 

3. Update

Table의 record를 수정하는 것은 Create와 동일한 merge method를 사용합니다.

public Person update(Person person) {
        return entityManager.merge(person);
}

 

person object를 parameter로 전달했을 때, 만약 존재하는 data라면 수정하고, 존재하지 않는 data라면 새로 생성해줍니다.

물론 update을 할 때는 person object를 parameter로 받기 보다는 id와 같은 특정 column의 값을 넘겨받을 수도 있을 것 같은데요, 

그럴 때는 이런식으로 코드를 짜볼 수 있을 것 같습니다.

 

public Person updateLocation(int id, String location) {
	Person foundPerson = entityManager.find(Person.class, id);
	foundPerson.setLocation(location);
	return entityManager.merge(foundPerson);
}

 

location을 변경하는 메서드를 정의해보았는데, id로 어떤 레코드를 변경할 것인지 알려주고, 바꿀 location 정보를 같이 넘겨주어서 변경해주는 것입니다. 이렇게도 가능하긴 하겠지만, 이렇게되면 column 별로 method를 다 만들어야 할테니 아무래도 번거로울 것 같습니다. 하나의 method로 여러 column을 바꿀 수 있고, column 중 몇 가지 정보가 비더라도 에러 없이 update가 가능해지는 method는 어떻게 만들면 좋을 지 한 번 알아보고 추후에 포스팅에 내용 추가 해보겠습니다!

 

4. Delete

record를 삭제할 때는 remove method를 사용하면 됩니다.

Read할 때 사용한 findById method로 존재하는 row를 찾아 Person type의 변수로 받아주고,

해당 변수를 remove의 parameter로 넣어주어 해당 데이터를 삭제합니다.

public void deleteById(int id) {
        Person person = findById(id);
        entityManager.remove(person);
}

 

이렇게 Spring JPA를 활용한 기본적인 CRUD 방법을 알아보았습니다.

다음번에는 Spring Data JPA와 Spring Data JDBC도 알아보도록 하겠습니다. 

감사합니다 :)

 

 

 


참고자료

 

https://www.icatpark.com/entry/JPA-%EA%B8%B0%EB%B3%B8-Annotation-%EC%A0%95%EB%A6%AC

 

JPA 기본 Annotation 정리

@Entity @Entity 어노테이션은 데이타베이스의 테이블과 일대일로 매칭되는 객체 단위이며 Entity 객체의 인스턴스 하나가 테이블에서 하나의 레코드 값을 의미합니다. 그래서 객체의 인스턴스를

www.icatpark.com

 

반응형