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 );
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의 원리
。Query method호출 시Java Reflection을 활용하여런타인 환경에서실행된메서드명을동적으로 가져와서JPQL문을 설정 후 동작Optional<Orders> findByCodeAndEmail(String code, String email);SELECT o FROM Orders o WHERE o.Code = ?1 and o.Email = ?2。다음
코드의 경우,findByCodeAndEmail()호출 시메서드명에 포함된Code와JPQL의SELECT문의WHERE 절에동적으로 삽입되어 동작
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 Method의페이징 처리
。레포구현체.findAll(Pageable객체)전달 시Page<Entity>로 반환
@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 );