规格和(null)多对一关系

时间:2014-10-06 10:17:03

标签: spring-data-jpa specifications many-to-one

我有一个MVC控制器,它将联系人列表作为JSON返回。在前端,我使用jquery datatables插件。前端有一个搜索字段来过滤实体列表。

我的实体:

@Entity
public class Contact implements Serializable {

    protected final static Logger   LOGGER              = LoggerFactory.getLogger(Contact.class);

    private static final long       serialVersionUID    = -3691953100225344828L;

    @Id
    @GeneratedValue(generator = "hibernate-uuid")
    @Column(length = 36, unique = true)
    private String                  id;

    @Version
    @JsonIgnore
    private int                     version;

    private String                  firstname;
    private String                  lastname;

    @ManyToOne
    private Company                 company;

    ... GETTER/SETTER ...
}

@Entity
public class Company implements Serializable {

    protected final static Logger   LOGGER              = LoggerFactory.getLogger(Company.class);

    private static final long       serialVersionUID    = -7863930456400256944L;

    @Id
    @GeneratedValue(generator = "hibernate-uuid")
    @Column(length = 36, unique = true)
    private String                  id;

    private String                  companyName;
    private String                  companyName1;
    private String                  companyName2;

    ... GETTER/SETTER ...
}

我在搜索字段使用服务器端处理,在服务器端使用规范。

public class ContactSpecifications {

    public static Specification<Contact> contactFirstnameLike(final String needle) {
        return new Specification<Contact>() {

            @Override
            public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.like(cb.lower(root.<String> get(Contact_.firstname)), needle != null ? needle.toLowerCase() : null);
            }
        };
    }

    public static Specification<Contact> contactLastnameLike(final String needle) {
        return new Specification<Contact>() {

            @Override
            public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.like(cb.lower(root.<String> get(Contact_.lastname)), needle != null ? needle.toLowerCase() : null);
            }
        };
    }

    public static Specification<Contact> contactFullnameLike(final String needle) {
        return new Specification<Contact>() {

            @Override
            public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.or(cb.like(cb.lower(root.<String> get(Contact_.lastname)), needle != null ? needle.toLowerCase() : null), cb.like(cb.lower(root.<String> get(Contact_.firstname)), needle != null ? needle.toLowerCase() : null));
            }
        };
    }

    public static Specification<Contact> contactCompanyCompanyNameLike(final String needle) {
        return new Specification<Contact>() {

            @Override
            public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                final Path<Company> company = root.<Company> get(Contact_.company);
                return cb.like(cb.lower(company.<String> get(Company_.companyName)), needle != null ? needle.toLowerCase() : null);
            }
        };
    }
}

我的数据库查询

 contactRepository.findAll(specifications, new PageRequest(0,100));

和规格

 specifications = Specifications.where(ContactSpecifications.contactFullnameLike(needle)).or(ContactSpecifications.contactCompanyCompanyNameLike(needle));

needle是来自前端和掩码的搜索键,周围有%(例如“%asdf%”)

我的问题是,如果联系人没有公司,那么规格就无法按预期运行。

例如,我有3个联系人:

  1. 姓氏:Schmitz,名字:Max,公司:( null)
  2. 姓氏:Schmitz,名字:Moritz,公司:XY
  3. 姓氏:Muster,名字:Max,公司:XY

    • 如果我现在将Schmitz作为搜索键输入,则只返回联系人2,请不要联系1。
    • 如果我输入max作为搜索键,则只返回联系人3,联系1而不是
    • 仅当搜索键为空/空时,所有联系人都返回
  4. 我错过了什么?

    亲切的问候 Rizzi的

1 个答案:

答案 0 :(得分:6)

回答自己;)

在研究sql查询后,我找到了解决方案。我必须改写我的规格。在相关实体上,我必须添加一个左连接路径,以防止条件构建器自动使用交叉/内部连接。

内部联接仅返回已设置所有字段的实体。如果某个实体关系为null,则该实体从结果列表中删除。正常的内部联接行为。

所以...

正确的规范必须是这样的。

public static Specification<Contact> contactCompanyCompanyNameLike(final String needle) {
    return new Specification<Contact>() {

        @Override
        public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            final Join<Contact,Company> company = root.join(Contact_.company, JoinType.LEFT);
            return cb.like(cb.lower(company.<String> get(Company_.companyName)), needle != null ? needle.toLowerCase() : null);
        }
    };
}

通过这些小修改,它现在开始正常工作。

亲切的问候Rizzi