[SPRING] ManyToMany 단방향 관계에 대한 Spring Data JPA 사양
SPRINGManyToMany 단방향 관계에 대한 Spring Data JPA 사양
많은 소유자가 고양이를 소유 할 수있는 설정이 있으며 각 소유자는 여러 고양이를 소유 할 수 있습니다. 이를 감안할 때 주어진 소유자 이름을 가진 모든 고양이를 찾는 데 도움이되는 사양을 작성하고 싶습니다.
다음은 간단한 클래스 설정입니다.
@Entity
public class Cat extends AbstractEntity {
@Column
private String name;
}
* 간결성을위한 게터 / 세터가 없습니다. ID 필드는 수퍼 클래스에 있습니다.
@Entity
public class Owner extends AbstractEntity {
@Column
private String name;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "OWNER_2_CATS",
joinColumns = @JoinColumn(name = "OWNER_ID"),
inverseJoinColumns = @JoinColumn(name = "CAT_ID"))
@OrderColumn(name = "order_column")
private List<Cat> cats = Lists.newArrayList();
}
* 간결성을위한 게터 / 세터가 없습니다. ID 필드는 수퍼 클래스에 있습니다.
그리고 여기 작동하는 쿼리와 작동하지 않는 스펙이있는 저장소가 있습니다.
public interface CatRepository extends AtomicsRepository<Cat, Long> {
// This query works.
@Query("SELECT c FROM Owner o INNER JOIN o.cats c WHERE o.name = ?")
List<Cat> findAllByOwner(String ownerName);
// But how do I accomplish this in a specification?
public static class Specs {
static Specification<Cat> hasOwnerName(final String ownerName) {
return (root, query, cb) -> {
// These next lines don't work! What do I put here?
Root<Owner> owner = query.from(Owner.class);
owner.join("cats");
return cb.equal(owner.get("name"), ownerName);
};
}
}
}
사양 작성을 도와주세요.
내가 문제를 겪고있는 것은이 관계가 관계가 표현되는 방식에 맞지 않아야한다는 것입니다. 소유자는 고양이 목록을 가지고 있지만 고양이는 소유자 목록을 가지고 있지 않습니다.
해결법
-
==============================
1.이 사양의 까다로운 점은 소유자와 직접적인 관계가없는 Cat을 쿼리한다는 것입니다.
이 사양의 까다로운 점은 소유자와 직접적인 관계가없는 Cat을 쿼리한다는 것입니다.
일반적인 아이디어는 다음과 같습니다.
내가 선호하는 접근 방식은 하위 쿼리를 사용하여 소유자를 소개하는 것입니다.
// Subquery using Cat membership in the Owner.cats relation public static class Specs { static Specification<Cat> hasOwnerName(final String ownerName) { return (root, query, cb) -> { query.distinct(true); Root<Cat> cat = root; Subquery<Owner> ownerSubQuery = query.subquery(Owner.class); Root<Owner> owner = ownerSubQuery.from(Owner.class); Expression<Collection<Cat>> ownerCats = owner.get("cats"); ownerSubQuery.select(owner); ownerSubQuery.where(cb.equal(owner.get("name"), ownerName), cb.isMember(cat, ownerCats)); return cb.exists(ownerSubQuery); }; } }
다음과 같은 SQL 쿼리를 생성하는 Hibernate 4.3.x
select cat0_.id as id1_1_ from cat cat0_ where exists ( select owner1_.id from owner owner1_ where owner1_.name=? and (cat0_.id in ( select cats2_.cat_id from owner_2_cats cats2_ where owner1_.id=cats2_.owner_id )) )
대안은 직교 제품을 사용하여 소유자를 소개하는 것입니다.
// Cat membership in the Owner.cats relation using cartesian product public static class Specs { static Specification<Cat> hasOwnerName(final String ownerName) { return (root, query, cb) -> { query.distinct(true); Root<Cat> cat = root; Root<Owner> owner = query.from(Owner.class); Expression<Collection<Cat>> ownerCats = owner.get("cats"); return cb.and(cb.equal(owner.get("name"), ownerName), cb.isMember(cat, ownerCats)); }; } }
다음과 같은 SQL 쿼리를 생성하는 Hibernate 4.3.x
select cat0_.id as id1_1_ from cat cat0_ cross join owner owner1_ where owner1_.name=? and (cat0_.id in ( select cats2_.cat_id from owner_2_cats cats2_ where owner1_.id=cats2_.owner_id ))
-
==============================
2.하위 쿼리로 cat의 id 값을 가져 오는 IN 술어를 만들 수 있습니다.
하위 쿼리로 cat의 id 값을 가져 오는 IN 술어를 만들 수 있습니다.
public static class Specs { static Specification<Cat> hasOwnerName(final String ownerName) { return (root, query, cb) -> { //EntityType<Cat> Cat_ = root.getModel(); final Subquery<Long> queryOwner = query.subquery(Long.class);// Check type of the Cat's ID attribute final Root<Owner> aliasOwner = queryOwner.from(Owner.class); //EntityType<Owner> Owner_ = aliasOwner.getModel(); //final Join<Owner, Cat> aliasCatsOwner = aliasOwner.join(Owner_.cats); final Join<Owner, Cat> aliasCatsOwner = aliasOwner.join("cats"); //queryOwner.select(aliasCatsOwner.<Long> get(Cat_.id))); queryOwner.select(aliasCatsOwner.<Long> get("id")));// Check type and name of the Cat's ID attribute queryOwner.where(cb.equal(Owner.<String> get("name"), ownerName)); //return cb.in(root.get(Cat_.id).value(queryOwner); return cb.in(root.get("id").value(queryOwner);//check the name of ID attribute! }; } }
-
==============================
3.짧은 대답은 다음과 같습니다.
짧은 대답은 다음과 같습니다.
Page<Cat> findByOwner_Name(String ownerName, Pageable p);
그러나 어려움은 누군가 소유하지 않은 고양이를 쿼리하는 방법입니다.
select * from cat where cat.id NOT IN (select cat.id from owner_has_cat);
누구든지 아이디어가 있습니까?
from https://stackoverflow.com/questions/31841471/spring-data-jpa-specification-for-a-manytomany-unidirectional-relationship by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] Heroku의 Spring Boot Gradle 앱 : jarfile에 액세스 할 수 없습니다 (0) | 2019.08.13 |
---|---|
[SPRING] Spring AOP : pointcut에서 최종 클래스와 열거를 피하십시오. (0) | 2019.08.13 |
[SPRING] 왜 스프링 jdbcTemplate batchUpdate 행마다 삽입 (0) | 2019.08.13 |
[SPRING] Gradle의 Spring Boot에서 Tomcat 종속성 제외 (0) | 2019.08.13 |
[SPRING] JPA 동시성 문제 "배치 릴리스시 여전히 JDBC 문이 포함되어 있습니다" (0) | 2019.08.13 |