인프런/스프링 입문

11) JPA

backend dev 2022. 11. 28.

jdbc -> jdbcTemplate -> JPA

이런순으로 더 간결하고 쉽게 코드를 짤 수있게끔 발전하는것 같다.

 

jdbc 에서 jdbcTemplate으로 교체했을때는 반복적인 코드가 확 줄었지만 sql문은 개발자가 직접 작성해야했다.

그런데

jpa라는 기술을 사용하면 그런 sql쿼리도 jpa가 자동으로 처리해준다. 그럼으로써 개발 생산성을 크게 증가 시킬수있다.

jpa를 사용하면 sql과 데이터보다는 객체중심으로 생각할 수 있다. 

 

jpa 라이브러리 추가

기존에 쓰던 jdbc는 jpa안에 들어있으므로 없어도된다.

    implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // jpa를 사용하기 위해 추가

// implementation 'org.springframework.boot:spring-boot-starter-jdbc'//jdbc를 사용하려고  -> jpa쓸때는 jap안에 들어있으므로 없어도됨

 

스프링부트에 jpa 설정 추가

resources/application.properties


#h2 데이터베이스 관련 정보들
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

#jpa가 생성하는 SQL를 출력하는 기능킨다.
spring.jpa.show-sql=true

#jpa는 테이블을 자동으로 생성하는 기능을 제공하는데 none으로 해줘서 해당 기능을 끈다.
#create를 사용하면 엔티티 정보를 바탕으로 테이블도 직접 생성해준다.
spring.jpa.hibernate.ddl-auto=none

jpa는 인터페이스이고 구현체로 hibernate가 있다.

우리는 hibernate를 사용하면된다.

 

jpa는

ORM( Object-Relational Mapping의 약자로 객체(Object)와 관계형 데이터베이스(Relational Database)의 데이터를 매핑(Mapping)해주는 것을 의미한다.)

 

객체와 데이터베이스의 데이터를 어떻게 매핑하는가? -> 어노테이션을 이용한다.

 

@Entity

https://gmlwjd9405.github.io/2019/08/11/entity-mapping.html

 

[JPA] 엔티티 매핑 방법 (Entity Mapping) - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

 

매핑하기위해 수정된 Member.java

@Entity //jpa가 관리하는 엔티티라는 뜻

//아래는 lombok관련 어노테이션
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)//@Id로 pk를 매핑해준다, db에 멤버이름만 넣어도 키값이 자동으로 생성되는것을 identity 전략이라고한다.
    //멤버에는 그런 전략을 사용했으므로 @GeneratedValue로 해당 전략에 대해 적어준다.
    private Long id;

//    @Column(name = "username") // 만약에 db상에서 컬럼이름이 username이라면 @Column 어노테이션을 이용해서 매핑해준다, 컬렴명과 변수명이 같다면 필요없다.
    private String name;

}

 

 

jpaMemberRepository.java

 

 

EntityManager

private final EntityManager em; //jpa는 엔티티매니저라는것으로 모든게 동작을 한다.
//jpa관련 라이브러리를 받으면 spring boot가 자동으로 현재 데이터베이스와 연결까지 시켜줘서 엔티티매니저를 생성해준다.
//(application.properties에 적은 데이터베이스정보를 이용하여)
//그래서 스프링부트가 만들어 놓은것을 injection(주입) 받기만 하면된다.

public jpaMemberRepository(EntityManager em) {
    this.em = em;
}

Save 구현

@Override
public Member save(Member member) {
    em.persist(member); //persist가 고집하다 지속하다도 있지만 영구저장하다는 느낌도 있다고한다.
    return member;
}

이렇게만해도 jpa가 insert query를 만들어서 알아서 집어넣고, 데이터 넣을시 자동 생성되는 id까지

멤버에 넣어서 리턴해준다.

 

 

findById 구현

@Override
public Optional<Member> findById(Long id) {
    Member member = em.find(Member.class, id);//조회할 타입과 식별자를 넘겨주면 조회가 된다.
    return Optional.ofNullable(member);
}

id가 pk(primaryKey)라서 find로 간단하게 처리가 가능

pk기반이 아닌 나머지들은 jpql을 작성해서 처리해줘야한다.

findAll 구현

@Override
public List<Member> findAll() {
    //jpql이라는 쿼리언어를 사용한다.
    //보통 테이블을 대상으로 쿼리를 날리는데, 여기서는 객체를 대상으로 쿼리를 날린다.(그게 sql로 번역이됨)
    //Member엔티티를 대상으로 쿼리를 날리는데 멤버 m 엔티티 전체를 select한다 (sql문의 *과 같은거)
    //jdbc와 달리 jpa에서는 매핑없고 알아서 매핑도 됨
    return em.createQuery("select m from Member m", Member.class)
        .getResultList();
}

findByName 구현

@Override
public Optional<Member> findByName(String name) {
    List<Member> result = em.createQuery("select m from Member m where m.name= :name",
            Member.class) //findByname은 이런식으로.
        .setParameter("name", name) //파라미터 전달은 여기서
        .getResultList();

    return result.stream().findAny();
}

 

jpa 쓸때 주의해야할점 

항상 트랜잭션이 있어야한다.

서비스 계층에 @Transactional 처리를 해준다.

데이터를 저장하거나 변경할때는 항상 트랜잭션 처리가 되어있어야한다.

클래스네임위에 해서 전체를 트랜잭션 처리를 하던지

클래스네임위에다 하지않고 메소드마다 트랜잭션처리를 하던지 고르면 될듯하다. (전체를 해놓는게 편할듯)

 

 

Test

application.properties에서

#jpa가 생성하는 SQL를 출력하는 기능킨다.
spring.jpa.show-sql=true

이걸 true로 했기에

hibernate가 어떤 쿼리를 날렸는지 확인할 수 있다.

첫번째 select는 중복회원체크(validation 처리) 때문에 날라간거고

두번쨰의 insert에서 저장시키는걸 확인할 수 있다. 

 

댓글