2015-09-30 34 views
8

我有一個User實體,UserToApplication實體和Application實體。使用CriteriaBuilder的彈簧數據JPA規範w /一對多關係

單個User可以訪問多個Application。並且一個Application可以被多個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 */ 
} 

我有以下Specification,我使用由firstNmlastNmemail來搜索User

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列表,使得它的方法簽名是:

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

所以,我的問題是,我可以添加@OneToMany映射到我的User實體的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

我找到了解決方案。要映射一個一對多的屬性,在元模型中添加以下:

public static volatile CollectionAttribute<User, Application> applications; 

我還需要添加一個元模型爲Application實體。

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

然後在我的Specification,我可以訪問applications爲用戶使用.join()方法上Root<User>實例。這是我組建的Predicate

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

此外,值得注意的是,我的Specification,因爲它是寫在這個問題如果任何輸入值都爲空將無法正常工作。將空Predicate傳遞給.and()方法CriteriaBuilder將導致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

在Application_類變量聲明應該如下,公共靜態揮發性SingularAttribute <應用程序,龍>的applicationID;而不是公共靜態volatile SingularAttribute applicationId;整體答案是完美的,雖然 – Avi

+0

@Avi謝謝指出!我在我的回答中更正了它。 –