JPA에서SQL Query를 통해사용자가사용자 정의한Repository 인터페이스의메서드작성하는 방법
。JpaRepository에서 기본 구현된method(save(),findById(), ... ) 외 다른 기능이 필요한 경우Repository 인터페이스내 직접SQL을 작성하여 다른기능을 지닌메서드를 정의
▶ 주로Query Method방식을 사용하며 복잡한SQL Query의 경우JPQL또는QueryDSL를 사용
。JpaRepository를확장하는Repository 인터페이스내메서드로 정의하여 사용
。사용자가 정의 및 생성한사용자 정의 메서드의 경우Test Class에서검증작업이 필요public interface MemberRepository extends JpaRepository<MemberEntity, UUID> { // Query Method 방식 Boolean existsByLoginId(String loginId); // JPQL 방식 @Query(""" SELECT EXISTS ( SELECT m FROM MemberEntity m WHERE m.loginId = ?1 ) """) Boolean existsByLoginIdByJPQL(String loginId); // Native SQL 방식 @Query(value = """ SELECT EXISTS (SELECT * FROM Member WHERE loginId = ?1); """,nativeQuery = true) Boolean existsByLoginIdByNativeSQL(String loginId); }
Native SQL
。개발자가DB Table을 대상으로하는SQL Query을 직접 작성 후 사용
。@Query(SQL문, nativeQuery = true)를 통해 활용
。매개변수를매핑시SQL내에는:변수명설정 및메서드 매개변수에는@Param("변수명")설정
。SQL에서return하는필드명과Entity의필드명이 동일해야함.@Query(value = """ WITH cte_buffer as ( SELECT ST_transform( ST_Difference( ST_Buffer(ST_transform(ST_SetSRID(ST_MakePoint(:longitude,:latitude), 4326),5174), :distance), ST_Buffer(ST_transform(ST_SetSRID(ST_MakePoint(:longitude,:latitude), 4326),5174), :distance) ),4326) AS geom) SELECT n.* FROM nodenetwork n JOIN cte_buffer cte on ST_Within(n.geom,cte.geom) """, nativeQuery = true) Optional<Node> findByDistance( @Param("longitude") Double longitude, @Param("latitude") Double latitude, @Param("distance") Double distance );
JPQL( Java Persistence Query Language )
。JPA의 일부로서DB Table이 아닌영속성 컨텍스트내DB Entity를 대상으로SQL Query를 작성
▶Native SQL과 거의 동일하나컴파일러의 도움을 받을 수 있다.
。주로복잡한 Query를 사용할 필요가 있을때Query method대신 사용// JPQL 방식 : Entity의 alias를 설정 @Query(""" SELECT EXISTS ( SELECT m FROM MemberEntity m WHERE m.loginId = ?1 ) """) Boolean existsByLoginIdByJPQL(String loginId);。
@Query내JPQL 문을 작성하며Entity의Alias를 지정하여Query를 하는것을 원칙으로한다.
▶DB Table명으로Query하는게 아닌DB Entity명으로Query를 수행
。JPQL내인자 전달시매개변수로 입력 시 순서대로JPQL내?1,?2...으로 전달
▶ 또는WHERE m.loginId = :loginId로:매개변수명으로 직접 명시적으로 할당가능
Query method:
。JpaRepository를확장한Repository Interface내부에메서드명을Query처럼 작성하여커스텀 Method를 설정
▶ 가장 많이 사용되는 방식
ex )selectById(Integer id)
。Query method를 호출하는 경우메서드명에 따라JPQL로 변환되어 동작
。Query method는 즉 사람이 작성한SQL을Entity기반으로 작성한 것이므로Human error를 방지하기위해test가 필요
▶JpaRepository에서 기본적으로 제공하는save(),delete(),findById()등의메서드에 대해서는 검증할 필요가 없음.
。findById()같은 많이 사용하는메서드는 사전에 구현됨
。복잡한 Query의 경우메서드명도 비정상적으로 길어지므로JPQL을 사용
ex )findAllByNameContaining()
Query method명명규칙
。findBy필드명(필드타입 필드명)에서필드명의 경우 반드시@Entity Class의Field명과Data type이 동일해야한다.
ex )Field명이username이고,Data type : String인 경우,findByusername(String username)
。Containing등의 옵션도 존재
findBy필드명(필드타입 필드명):식별
。DB Table내 해당field의 데이터에 해당하는DB Entity가 존재 시DB Entity를 return
existsBy필드명(필드타입 필드명):존재
。DB Table내 해당field의 데이터에 해당하는DB Entity의 존재여부에 따라boolean으로 return
deleteBy필드명(필드타입 필드명):삭제
。DB Table내 해당field의 데이터에 해당하는DB Entity가 존재 시soft delete
@Query("""JPQL Query"""):org.springframework.data.jpa.repository.Query
。Spring Data JPA를 통해SQL Query를 수행 시JpaRepository를 확장한Repository 인터페이스내메서드에 선언하는어노테이션
▶ 주로JPQL을 통한복잡한 Query를 처리 시 활용// JPQL 방식 @Query(""" SELECT EXISTS ( SELECT m FROM MemberEntity m WHERE m.loginId = ?1 ) """) Boolean existsByLoginIdByJPQL(String loginId);
@Query("""SQL Query""", nativeQuery = true)
。Entity와 상호작용하는JPQL이 아닌DB Table과 상호작용하는Native SQL을 사용하도록 설정
@Modifying
。@Query를 통한JPQL또는Native SQL을 통해INSERT/UPDATE/DELETE를 수행하는 경우@Query에 추가로@Modifying,@Transactional을 선언
▶Native SQL에서Update한 내용을 바로Entity반영 할 필요가 있는 경우@Modifying(clearAutomatically = true)선언하여 적용 시Entity에서도 변경내용을 바로 반영@Modifying @Transactional @Query(""" delete from Route where Route.account.id = ?1 and Route.routeStatus = 'TEMP' """) void deleteTempByAccountId(UUID accountId);@Modifying(clearAutomatically = true) @Transactional @Query(value = """ insert into routelink( id, geojson, link_length, link_slope, link_cost, link_type, drink_toilet, route_id ) select uuid_generate_v4() as id, st_asgeojson(x.geom) as geojson, link_length, link_slope, link_cost, link_type, drink_toilet, :route_id as route_id from linknetwork as x join ( select * from pgr_dijkstraVia( :dijkstra_sql, :node_ids ::bigint[] ) where agg_cost < :max_distance ) as y on x.id = y.edge order by y.seq """, nativeQuery = true) void createPaths( @Param("node_ids") Long[] nodeIds, @Param("max_distance") int maxDistance, @Param("dijkstra_sql") String dijkstra_sql, @Param("route_id") UUID routeId );