2015-09-30 16 views
8

Tôi có một thực thể User, một thực thể UserToApplication và một thực thể Application.Đặc điểm JPA dữ liệu mùa xuân sử dụng CriteriaBuilder w/mối quan hệ một đến nhiều

Một đơn User có thể có quyền truy cập vào nhiều hơn Application. Và một đơn Application có thể được sử dụng bởi nhiều hơn một User.

Đây là thực thể 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 */ 
} 

Đây là thực thể 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; 
    } 
} 

Và đây là thực thể 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 */ 
} 

Tôi có sau Specification mà tôi sử dụng để tìm kiếm một User bởi firstNm, lastNm, và email.

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); 
      } 
     }; 
    } 

} 

Và đây là User_ metamodel mà tôi có cho đến nay.

@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; 
} 

Bây giờ, tôi muốn cũng phải vượt qua trong một danh sách các id ứng dụng cho Specification, chẳng hạn chữ ký phương pháp của nó sẽ là:

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

Vì vậy, câu hỏi của tôi là, tôi có thể thêm @OneToMany ánh xạ tới số User_ metamodel cho trường Collection<Application> applications của thực thể User của tôi và sau đó làm cách nào để tham chiếu nó trong Specification?

My hiện Specification sẽ tương tự với truy vấn SQL sau:

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

Và những gì tôi muốn đạt được trong mới Specification sẽ là một cái gì đó như thế này:

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); 

Có thể để làm loại ánh xạ này trong metamodel, và làm thế nào tôi có thể đạt được kết quả này trong số Specification của tôi?

Trả lời

11

Tôi đã tìm được giải pháp. Để ánh xạ một một đến nhiều thuộc tính, trong metamodel tôi thêm như sau:

public static volatile CollectionAttribute<User, Application> applications; 

Tôi cũng cần thêm một metamodel cho đơn vị Application.

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

Sau đó, trong tôi Specification, tôi có thể truy cập vào applications cho người dùng, sử dụng phương pháp .join() trên Root<User> dụ. Đây là số Predicate tôi đã được tạo.

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

Ngoài ra, nó là đáng chú ý là tôi Specification vì nó được viết trong câu hỏi sẽ không hoạt động nếu một trong các giá trị đầu vào là rỗng. Một số trống Predicate được chuyển đến phương thức .and() của CriteriaBuilder sẽ gây ra một NullPointerException. Vì vậy, tôi đã tạo một ArrayList loại Predicate, sau đó thêm từng Predicate vào danh sách nếu thông số tương ứng không trống. Cuối cùng, tôi chuyển đổi ArrayList thành một mảng để chuyển nó tới hàm .and() của CriteriaBuilder. Đây là cuối cùng 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

Tuyên bố biến trong lớp Application_ phải như sau, công khai tĩnh biến động SingularAttribute applicationId; thay vì công khai tĩnh SingularAttribute applicationId; Câu trả lời tổng thể là hoàn hảo mặc dù – Avi

+0

@Avi cảm ơn vì đã chỉ ra điều đó! Tôi đã sửa nó trong câu trả lời của tôi. –

Các vấn đề liên quan