前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JPA(hibernate)一对多根据多的一方某属性进行过滤查询

JPA(hibernate)一对多根据多的一方某属性进行过滤查询

作者头像
天涯泪小武
发布2019-01-17 11:27:54
4.6K0
发布2019-01-17 11:27:54
举报
文章被收录于专栏:SpringCloud专栏

我们经常会碰上某个字段是集合元素(List,Set)的情况,并且我们要过滤出集合中包含某个或某些元素的数据。 譬如一个类User

代码语言:javascript
复制
    /**
     * 检索人
     */
    private Long userId;
    /**
     * 省、直辖市集合
     */
    @ElementCollection
    @CollectionTable
    private List<String> provinces;
    /**
     * 市、区集合
     */
    @ElementCollection
    @CollectionTable
    private List<String> cities;
    /**
     * 行业集合
     */
    @ElementCollection
    @CollectionTable
    private List<Integer> vocations;

包含一个String型的集合,我们希望能查询出province=”1”或者”2”的User集合。 倘若使用Hql或者原生sql是比较简单的,但是使用Criteria查询就不那么简单了,尤其是当User中包含多个集合元素,并且查询条件不确定时。 Jpa中Criteria用来构建复杂查询,之前我的文章中(http://blog.csdn.net/tianyaleixiaowu/article/details/72876732)已经讲过了如何构建动态条件查询,里面就有如何实现查询集合元素中是否包含某元素的功能。 重点看一下那篇文章中的SimpleExpression.java,里面的case IS_MEMBER,调用了CriteriaBuilder的isMember方法,该方法就能查询出你的集合中是否包含某个元素。 请注意,我定义User类时,注解写的是:@ElementCollection,映射的是基本类型不是一个javaBean类,所以无法使用表关联的写法如user.address.id=XXX,这样的hibernate表关联写法。 那么就需要使用isMember这样的写法(注意:需要导入上面提到的那篇文章的几个类,才能用下面的写法):

代码语言:javascript
复制
        Criteria<PtSearchCondition> criteria = new Criteria<>();
        criteria.add(Restrictions.hasMembers("provinces", "110000", "120000"));
        Page page = userRepository.findAll(criteria, new PageRequest(0, 10));
        return page.getContent();

有个地方需要说明一下,@ElementCollection这个注解代表该属性是一个集合属性,它和one-to-many类似,但不是同一个东西,one-to-many注解的另一方也要是一个表,不能只是一个普通的基本类型的集合。 如果你的@ElementCollection注解的集合对象也是一个JavaBean,不是String或者Integer时,譬如User有多个Address,Set《Address》 addressSet,那么Address类需要加@Embeddable注解,否则报错。@Embeddable代表是一个嵌入式的对象,不是一个表映射对象。如果你用的是one-to-many,那么Address就需要加上@Entity,代表需要映射到数据库表。 下面还看查询的问题: 如果你的属性是一个对象的集合,并且是@ElementCollection注解的,那么如何查询呢? 很简单,同样还是使用

代码语言:javascript
复制
    Criteria<PtSearchCondition> criteria = new Criteria<>();
    criteria.add(Restrictions.hasMembers("address.name", "北京"));
    Page page = userRepository.findAll(criteria, new PageRequest(0, 10));

在我的SimpleExpression.java中,有这样一段代码来处理一对多的查询

代码语言:javascript
复制
@Override
    @SuppressWarnings({"rawtypes", "unchecked"})
    public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query,
                                 CriteriaBuilder builder) {
        Path expression;

        //此处是表关联数据,注意仅限一层关联,如user.address,
        //查询user的address集合中,address的name为某个值
        if (fieldName.contains(".")) {
            String[] names = StringUtils.split(fieldName, ".");
            //获取该属性的类型,Set?List?Map?
            expression = root.get(names[0]);
            Class clazz = expression.getJavaType();
            if (clazz.equals(Set.class)) {
                SetJoin setJoin = root.joinSet(names[0]);
                expression = setJoin.get(names[1]);
            } else if (clazz.equals(List.class)) {
                ListJoin listJoin = root.joinList(names[0]);
                expression = listJoin.get(names[1]);
            } else if (clazz.equals(Map.class)) {
                MapJoin mapJoin = root.joinMap(names[0]);
                expression = mapJoin.get(names[1]);
            } else {
                //是many to one时
                expression = expression.get(names[1]);
            }

        } else {
            //单表查询
            expression = root.get(fieldName);
        }

里面使用了SetJoin来完成对多的一方的某字段的匹配查询。 在Restrictions.java中,做了判断多的一方是基本类型还是JavaBean的判断:

代码语言:javascript
复制
/**
     * 集合包含某几个元素,譬如可以查询User类中Set<String> set包含"ABC","bcd"的User集合,
     * 或者查询User中Set<Address>的Address的name为"北京"的所有User集合
     * 集合可以为基本类型或者JavaBean,可以是one to many或者是@ElementCollection
     * @param fieldName
     * 列名
     * @param value
     * 集合
     * @return
     * expresssion
     */
    public static LogicalExpression hasMembers(String fieldName, Object... value) {
        SimpleExpression[] ses = new SimpleExpression[value.length];
        int i = 0;
        //集合中对象是基本类型,如Set<Long>,List<String>
        Criterion.Operator operator = Criterion.Operator.IS_MEMBER;
        //集合中对象是JavaBean
        if (fieldName.contains(".")) {
            operator = Criterion.Operator.EQ;
        }
        for (Object obj : value) {
            ses[i] = new SimpleExpression(fieldName, obj, operator);
            i++;
        }
        return new LogicalExpression(ses, Criterion.Operator.OR);
    }

同理,如果是使用one to many来映射的1对多表关系,同样可以使用上面的方法,写法也完全相同。

代码语言:javascript
复制
Criteria<PtSearchCondition> criteria = new Criteria<>();
        criteria.add(Restrictions.hasMembers("address.name", "北京"));
        criteria.add(Restrictions.hasMembers("provinces", "110000", "120000"));
        Page page = userRepository.findAll(criteria, new PageRequest(0, 10));

以上就能完成Jpa中1对多,根据多的一方的某属性进行过滤匹配。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017年11月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档