2015-09-30 7 views
8

私はUserエンティティ、UserToApplicationエンティティ、Applicationエンティティを持っています。SpringデータJPA仕様のCriteriaBuilderを使用した1対多の関係

単一Userは、複数のApplicationにアクセスできます。単一のApplicationは1つ以上のUserで使用できます。

ここにはUserエンティティがあります。

@Entity 
@Table(name = "USER", schema = "UDB") 
public class User { 
    private Long userId; 
    private Collection<Application> applications; 
    private String firstNm; 
    private String lastNm; 
    private String email; 

    @SequenceGenerator(name = "generator", sequenceName = "UDB.USER_SEQ", initialValue = 1, allocationSize = 1) 
    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator") 
    @Column(name = "USER_ID", unique = true, nullable = false) 
    public Long getUserId() { 
     return userId; 
    } 

    public void setUserId(Long userId) { 
     this.userId = userId; 
    } 

    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) 
    public Collection<Application> getApplications() { 
     return applications; 
    } 

    public void setApplications(Collection<Application> applications) { 
     this.applications = applications; 
    } 

    /* Other getters and setters omitted for brevity */ 
} 

ここにはUserToApplicationエンティティがあります。

@Entity 
@Table(name = "USER_TO_APPLICATION", schema = "UDB") 
public class Application { 
    private Long userToApplicationId; 
    private User user; 
    private Application application; 

    @SequenceGenerator(name = "generator", sequenceName = "UDB.USER_TO_APP_SEQ", initialValue = 0, allocationSize = 1) 
    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator") 
    @Column(name = "USER_TO_APPLICATION_ID", unique = true, nullable = false) 
    public Long getUserToApplicationId() { 
     return userToApplicationId; 
    } 

    public void setUserToApplicationId(Long userToApplicationId) { 
     this.userToApplicationId = userToApplicationId; 
    } 

    @ManyToOne 
    @JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID", nullable = false) 
    public User getUser() { 
     return user; 
    } 

    public void setUser(User user) { 
     this.user = user; 
    } 

    @ManyToOne 
    @JoinColumn(name = "APPLICATION_ID", nullable = false) 
    public Application getApplication() { 
     return application; 
    } 
} 

そしてここにはApplicationエンティティがあります。

@Entity 
@Table(name = "APPLICATION", schema = "UDB") 
public class Application { 
    private Long applicationId; 
    private String name; 
    private String code; 

    /* Getters and setters omitted for brevity */ 
} 

私はfirstNmlastNm、およびemailによってUserを検索するために使用する次のSpecificationを持っています。

public class UserSpecification { 

    public static Specification<User> findByFirstNmLastNmEmail(String firstNm, String lastNm, String email) { 
     return new Specification<User>() { 
      @Override 
      public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) { 
       final Predicate firstNmPredicate = null; 
       final Predicate lastNmPredicate = null; 
       final Predicate emailPredicate = null; 

       if (!StringUtils.isEmpty(firstNm)) { 
        firstNmPredicate = cb.like(cb.lower(root.get(User_.firstNm), firstNm)); 
       } 
       if (!StringUtils.isEmpty(lastNm)) { 
        lastNmPredicate = cb.like(cb.lower(root.get(User_.lastNm), lastNm)); 
       } 
       if (!StringUtils.isEmpty(email)) { 
        emailPredicate = cb.like(cb.lower(root.get(User_.email), email)); 
       } 
       return cb.and(firstNmPredicate, lastNmPredicate, emailPredicate); 
      } 
     }; 
    } 

} 

ここで私が今までに持っていたメタモデルはUser_です。

@StaticMetamodel(User.class) 
public class User_ { 
    public static volatile SingularAttribute<User, String> firstNm; 
    public static volatile SingularAttribute<User, String> lastNm; 
    public static volatile SingularAttribute<User, String> email; 
} 

さて、私はそのメソッドのシグネチャがになるよう、またSpecificationにアプリケーションIDのリストを渡したいと思います:

public static Specification<User> findByFirstNmLastNmEmailApp(String firstNm, String lastNm, String email, Collection<Long> appIds) 

だから、私の質問は、私が追加することができます@OneToManyUserエンティティのCollection<Application> applicationsフィールドのUser_メタモデルにマッピングし、次にSpecificationでそれをどのように参照しますか?

私の現在のSpecification次のSQLクエリのようになります。

select * from user u 
where lower(first_nm) like '%firstNm%' 
and lower(last_nm) like '%lastNm%' 
and lower(email) like '%email%'; 

そして、私はこのようなものになるだろう新しいSpecificationに達成したいと思います:それは可能

select * from user u 
join user_to_application uta on uta.user_id = u.user_id 
where lower(u.first_nm) like '%firstNm%' 
and lower(u.last_nm) like '%lastNm%' 
and lower(u.email) like '%email%' 
and uta.application_id in (appIds); 

ですメタモデルでこのようなマッピングを行うにはどうすればよいですか?Specificationでこの結果を得るにはどうすればいいですか?

答えて

11

解決策が見つかりました。私もApplicationエンティティのメタモデルを追加する必要

public static volatile CollectionAttribute<User, Application> applications; 

:多くの属性に1をマップするには、メタモデルで私は、次の追加しました。

@StaticMetamodel(Application.class) 
public class Application_ { 
    public static volatile SingularAttribute<Application, Long> applicationId; 
} 

はその後、私のSpecificationに、私はRoot<User>インスタンス上.join()メソッドを使用して、ユーザーのためのapplicationsにアクセスすることができました。ここには私が形成したPredicateがあります。

final Predicate appPredicate = root.join(User_.applications).get(Application_.applicationId).in(appIds); 

また、入力値のいずれかが空の場合、それは問題で書かれているように私Specificationが動作しないことは注目に値します。 .and()メソッドに渡されたPredicateCriteriaBuilderの場合、NullPointerExceptionが返されます。そこでPredicateタイプのArrayListを作成し、対応するパラメータが空でない場合は、それぞれPredicateをリストに追加しました。最後に、ArrayListを配列に変換して、CriteriaBuilder.and()関数に渡します。ここで、最終Specificationある:

public class UserSpecification { 

    public static Specification<User> findByFirstNmLastNmEmailApp(String firstNm, String lastNm, String email, Collection<Long> appIds) { 
     return new Specification<User>() { 
      @Override 
      public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) { 
       final Collection<Predicate> predicates = new ArrayList<>(); 
       if (!StringUtils.isEmpty(firstNm)) { 
        final Predicate firstNmPredicate = cb.like(cb.lower(root.get(User_.firstNm), firstNm)); 
        predicates.add(firstNmPredicate); 
       } 
       if (!StringUtils.isEmpty(lastNm)) { 
        final Predicate lastNmPredicate = cb.like(cb.lower(root.get(User_.lastNm), lastNm)); 
        predicates.add(lastNmPredicate); 
       } 
       if (!StringUtils.isEmpty(email)) { 
        final Predicate emailPredicate = cb.like(cb.lower(root.get(User_.email), email)); 
        predicates.add(emailPredicate); 
       } 
       if (!appIds.isEmpty()) { 
        final Predicate appPredicate = root.join(User_.applications).get(Application_.applicationId).in(appIds); 
        predicates.add(appPredicate); 
       } 

       return cb.and(predicates.toArray(new Predicate[predicates.size()])); 
      } 
     }; 
    } 

} 
+0

、パブリック静的揮発性SingularAttribute <アプリケーション、ロング>アプリケーションIDは、以下のようにApplication_クラスの変数宣言があるべきです。パブリックstatic volatile SingularAttributeの代わりに applicationId;全体の答えは完璧です – Avi

+0

@Aviそれを指摘してくれてありがとう!私は私の答えでそれを修正しました。 –