JPA entity

수박참외메론·2022년 11월 12일
0

스프링 데이터 JPA 쿼리 생성 기능

주요 메서드

  • save : 새로운 엔티티는 저장하고 이미 있는 엔티티는 수정
  • delete : 엔티티 하나를 삭제. 내부에서 EntityManager.remove() 를 호출
  • findOne : 엔티티 하나를 조회. 내부에서 EntityManager.find() 를 호출
  • getOne : 엔티티를 프록시로 조회. 내부에서 EntityManager.getReference() 를 호출
  • findAll : 모든 엔티티를 조회한다. 정렬이나 페이징 조건을 페러미터로 달 수 있음

쿼리 메소드 기능

쿼리 메소드 기능은 spring data JPA 가 제공하는 기능으로 메소드 이름만으로 JPQL 쿼리를 생성해서 실행한다.

메소드 이름으로 쿼리 생성

기본적인 문법

  • And / Or
  • Is, Equals / Between
  • LessThan / LessThanEqual / GreaterThan / GreaterThanEqual
  • IsNull / IsNotNull, NotNull
  • Like / NotLike / StartingWith / EndingWith
  • Containing
  • OrderBy
  • Not
  • In / NotIn
  • TRUE / FALSE
  • IgnoreCase
  • _ (언더바)

언더바 활용

List<PersonalSchedule> findAllByMember_Id(Long userId);
Hibernate: 
    select
        personalsc0_.id as id1_2_,
        personalsc0_.finish_time as finish_t2_2_,
        personalsc0_.group_schedule_id as group_sc3_2_,
        personalsc0_.member_id as member_i8_2_,
        personalsc0_.memo as memo4_2_,
        personalsc0_.name as name5_2_,
        personalsc0_.start_time as start_ti6_2_,
        personalsc0_.weekday as weekday7_2_ 
    from
        personal_schedule personalsc0_ 
    left outer join
        member member1_ 
            on personalsc0_.member_id=member1_.member_id 
    where
        member1_.member_id=?

참고블로그

JPA Named Query

apthem dlfmadmfh JPA Named 쿼리를 호출하는 기능으로 @NamedQuery 어노테이션 안에 name, query 속성을 정의하여 메소드에 쿼리를 매핑하는 기능

@Entity
@NamedQuery(
	name="findByUsername",
    query="select m from Member where m.username = :username"
    
)
public class Member {
...
}

스프링 데이터 JPA는 선언한 "도메인클래스"."메소드이름" 으로 named query를 먼저 찾고, 없으면 메소드 이름으로 쿼리생성 전략을 사용한다.

레포지토리 메소드에 쿼리 정의

@Query 어노테이션을 레포지토리 메소드에 직접 쿼리를 정의하는 방식이다.

public interface MemberRepository extends JpaRespository<Member, Long>{
	@Query("select m from Member where m.username = ?1")
    Member findByUsername(String username);
    
    //이름기반 파라미터 바인딩
    @Query("select m from Member where m.username = ?1")
    Member findByUsername(String username);
    
    
    @Query("select * from member where userinfo = ?0",
    	nativeQuery = true)
    Member findByUserInfo(String userinfo);
}

즉시로딩과 지연로딩

우리가 자바를 다룰때는 객체를 불러와 객체 그래프로 연관된 객체를 탐색하지만, JPA에서는 그 연관된 객체가 데이터베이스에 저장되어 있으므로, 성능상 이슈로 가벼운 마음으로 탐색을 막하고 다니기는 쉽지 않다.
이때, JPA는 프록시를 사용하여 연관된 객체를 처음부터 db에서 조회해 오는 것이 아니라 실제 사용 시점에 데이터베이스에서 조회해오도록 한다.

프록시

public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "member_id")
    private Long id;

    @Column(nullable = false, unique = true)
    private String username;

    ...

    @JsonIgnore
    @OneToMany(mappedBy = "member")
    private List<PersonalSchedule> personalScheduleList;
}
public class PersonalSchedule {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="member_id")
    private Member member;

    @Column(nullable = false)
    private String name;

    ...
}

이런 1:N 관계의 두 엔티티 memberpersonalSchedule 이 있다고 해보자.

이때 personalSchedule을 조회해 올 일이 있다고 할 때, member 객체까지 조회가 되려면 join 을 해서 member 객체를 조회해 오던지, 따로 쿼리를 날려서 조회해 오던지 해야한다.
하지만 실제로 personalSchedule을 사용할 때 연관된 member을 사용하는 경우는 사용하는 방식에 따라 흔할 수도 있고, 흔치 않을 수도 있다.
member 을 사용하는게 흔치 않으면, 매번 schedule을 조회할 때마다 member 도 같이 봐주는 것은 성능상 좋지 않다. 이를 위해 JPA는 db 조회를 지연하는 방법을 제공하는데 이를 지연 로딩 이라고 한다.
근데 지연로딩을 사용하려면 실제 entity 객체 대신에 데이터베이스 조회를 지연할 수 있는 가짜 객체가 필요한데, 이를 프록시 객체라고 한다.

Member member = em.find(Member.class, "member1");
Member member = em.getReference(Member.class, "member1");

프록시 객체의 초기화

Member member = em.getReference(Member.class, "member1");
member.getName();

프록시의 특징

  • 프록시 객체는 처음 사용할 때 한번만 초기화됨
    초기화한다고 프록시 객체가 실제 엔티티로 바뀌는 것이 아니고 프록시 객체를 통해 실제 엔티티에 접근하는 방식
  • 그래서 타입체크시 유의해야함
  • 영속성 컨텍스트 안에 찾는 엔티티가 이미 있으면 DB를 조회해 오지 않고 실제 엔티티 반환함

즉시로딩

Eager Loading을 사용하려면 @ManyToOne의 fetch 속성을 eager로 설정하면 된다.

public class PersonalSchedule {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.Eager)
    @JoinColumn(name="member_id")
    private Member member;

    @Column(nullable = false)
    private String name;

    ...
}
Hibernate: 
    select
        personalsc0_.id as id1_2_0_,
        personalsc0_.finish_time as finish_t2_2_0_,
        personalsc0_.group_schedule_id as group_sc3_2_0_,
        personalsc0_.member_id as member_i8_2_0_,
        personalsc0_.memo as memo4_2_0_,
        personalsc0_.name as name5_2_0_,
        personalsc0_.start_time as start_ti6_2_0_,
        personalsc0_.weekday as weekday7_2_0_,
        member1_.member_id as member_i1_1_1_,
        member1_.nickname as nickname2_1_1_,
        member1_.password as password3_1_1_,
        member1_.roles as roles4_1_1_,
        member1_.username as username5_1_1_ 
    from
        personal_schedule personalsc0_ 
    left outer join
        member member1_ 
            on personalsc0_.member_id=member1_.member_id 
    where
        personalsc0_.id=?

기본적으로 @ManyToOne, @OneToOne의 fetch 속성은 즉시로딩이다.

지연로딩

public class PersonalSchedule {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="member_id")
    private Member member;

    @Column(nullable = false)
    private String name;

    ...
}
Hibernate: 
    select
        personalsc0_.id as id1_2_0_,
        personalsc0_.finish_time as finish_t2_2_0_,
        personalsc0_.group_schedule_id as group_sc3_2_0_,
        personalsc0_.member_id as member_i8_2_0_,
        personalsc0_.memo as memo4_2_0_,
        personalsc0_.name as name5_2_0_,
        personalsc0_.start_time as start_ti6_2_0_,
        personalsc0_.weekday as weekday7_2_0_ 
    from
        personal_schedule personalsc0_ 
    where
        personalsc0_.id=?
Hibernate: 
    select
        member0_.member_id as member_i1_1_0_,
        member0_.nickname as nickname2_1_0_,
        member0_.password as password3_1_0_,
        member0_.roles as roles4_1_0_,
        member0_.username as username5_1_0_ 
    from
        member member0_ 
    where
        member0_.member_id=?

기본적으로 @OneToMany, @ManyToMany의 fetch 속성은 지연로딩이다.

join 관계에 대하여

List<PersonalSchedule> findAllByMember_Id(Long userId);
Hibernate: 
    select
        personalsc0_.id as id1_2_,
        personalsc0_.finish_time as finish_t2_2_,
        personalsc0_.group_schedule_id as group_sc3_2_,
        personalsc0_.member_id as member_i8_2_,
        personalsc0_.memo as memo4_2_,
        personalsc0_.name as name5_2_,
        personalsc0_.start_time as start_ti6_2_,
        personalsc0_.weekday as weekday7_2_ 
    from
        personal_schedule personalsc0_ 
    left outer join
        member member1_ 
            on personalsc0_.member_id=member1_.member_id 
    where
        member1_.member_id=?

@Transactional

두가지 import

연관관계

One-to-Many

Many-to-One

Many-to-Many

profile
하루하루는 성실하게 인생전체는 되는대로

0개의 댓글