2015-09-30 35 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谢谢指出!我在我的回答中更正了它。 –