[英]JPA criteria predicate query combining Integer and String arguments
I'm using JPA (EclipseLink flavor), framework JSF 2.0, querying a MySQL contacts table containing 4 fields: 我正在使用JPA(EclipseLink风格)框架JSF 2.0,查询包含4个字段的MySQL联系人表:
contactId(int Pk), firstName (String), surname(String), countryId(int Foreign key)
The below code works fine if only using string arguments so that the user can enter as many or as few search strings as required (ie: if the search boxes are left blank, this is equivalent to select * and the entire dataset is returned). 如果仅使用字符串参数,则以下代码可以正常工作,以便用户可以根据需要输入任意数量的搜索字符串(即:如果搜索框为空,则相当于选择*,并返回整个数据集)。
The problem is in combining an integer argument (countryId) which is the foreign key that adds countries to the search criteria. 问题在于结合使用整数参数(countryId),该参数是将国家/地区添加到搜索条件中的外键。 After some research, I'm still having trouble understanding the right way to do this.
经过研究后,我仍然难以理解正确的方法。
Does the integer need to be converted to a string representation within the searchContacts method? 是否需要在searchContacts方法中将整数转换为字符串表示形式? I gathered that JPA Criteria API provides typcasting but not type conversion methods?
我收集到JPA Criteria API提供类型转换,但不提供类型转换方法? If so, What is the best way to include an integer in the below method and pass this integer into the predicateArray?
如果是这样,在下面的方法中包括一个整数并将该整数传递给predicateArray的最佳方法是什么?
Thanks in advance! 提前致谢!
public ListDataModel<Contacts> searchContacts(String firstname, String surName, int countryId) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Contacts> query = cb.createQuery(Contacts.class);
Root<Contacts> cont = query.from(Contacts.class);
query.select(cont);
List<Predicate> predicateList = new ArrayList<Predicate>();
Predicate firstnamePredicate, surnamePredicate, countryPredicate;
if ((firstname != null) && (!(firstname.isEmpty()))) {
firstnamePredicate = cb.like(cb.upper(cont.<String>get("firstname")), "%" + firstname.toUpperCase() + "%");
predicateList.add(firstnamePredicate);
}
if ((surName != null) && (!(surName.isEmpty()))) {
surnamePredicate = cb.like(cb.upper(cont.<String>get("surname")), "%" + surName.toUpperCase() + "%");
predicateList.add(surnamePredicate);
}
// here is where I am stuck and trying the solution suggested by meskobalazs, except I changed null to 0 since countryId is an integer
if (countryId != 0) {
countryPredicate = cb.equal(cont.<Integer>get("countryid"), countryId);
predicateList.add(countryPredicate);
}
Predicate[] predicateArray = new Predicate[predicateList.size()];
predicateList.toArray(predicateArray);
query.where(predicateArray);
ListDataModel<Contacts> contactList = new ListDataModel<Contacts>(em.createQuery(query).getResultList());
return contactList;
}
} }
The above caused the following EJB Exception: 以上导致以下EJB异常:
Caused by: Exception [EclipseLink-6078] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.QueryException
Exception Description: The class of the argument for the object comparison is incorrect.
Expression: [
Base com.manaar.domains.Contacts]
Mapping: [org.eclipse.persistence.mappings.ManyToOneMapping[countryid]]
Argument: [1]
Query: ReadAllQuery(referenceClass=Contacts )
at org.eclipse.persistence.exceptions.QueryException.incorrectClassForObjectComparison(QueryException.java:595)
at org.eclipse.persistence.mappings.OneToOneMapping.buildObjectJoinExpression(OneToOneMapping.java:287)
at org.eclipse.persistence.internal.expressions.RelationExpression.normalize(RelationExpression.java:803)
The countryid is an integer with a manyToOne mapping in the contacts entity to the country entity. countryid是一个整数,在contact实体与country实体之间具有manyToOne映射。 The contacts entity is:
联系人实体为:
@Entity
@Table(name = "contacts")
@XmlRootElement
public class Contacts implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "CONTACTSID")
private Integer contactsid;
@JoinColumn(name = "countryid", referencedColumnName = "COUNTRYID")
@ManyToOne
private Country countryid;
The country entity is: 国家实体是:
@Entity
@Table(name = "country")
@XmlRootElement
public class Country implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "COUNTRYID")
private Integer countryid;
@Size(max = 255)
@Column(name = "COUNTRYNAME")
private String countryname;
.....
The Predicate
class is not typed (generic), so you don't need to collect different predicates any differently. Predicate
类没有类型化(泛型),因此您不需要以不同的方式收集不同的谓词。 This should work just fine: 这应该可以正常工作:
if (countryId != null) {
countryPredicate = cb.equal(cont.<Integer>get("countryid"), countryId);
predicateList.add(countryPredicate);
}
This is just a suggestion, but you also might consider building the canonical metamodels for your entities (eg with Apache Dali), so you can get
your fields in a typesafe manner. 这只是一个建议,但您也可以考虑为您的实体构建规范的元模型(例如,使用Apache Dali),以便以类型安全的方式
get
字段。
Instead of 代替
cont.<MyType>get("typeName")
you could write: 你可以这样写:
cont.get(_MyType.typeName)
This is especially useful, if you are refactoring your entities, because you might forget updating the Strings, but you can't forget updating your metamodels, because the code does not even compile otherwise. 如果您要重构实体,这将特别有用,因为您可能会忘记更新字符串,但不会忘记更新元模型,因为代码甚至不会编译。
I have missed a few design problems with your entities. 我错过了与您的实体有关的一些设计问题。 There are two possible solutions.
有两种可能的解决方案。
The first one is creating an actual Integer
field in Contacts
and map it to the same field as countryId
. 第一个是在
Contacts
创建一个实际的Integer
字段,并将其映射到与countryId
相同的字段。
So you should rename the Country
field to country
, and create a new field. 因此,您应该将
Country
字段重命名为country
,然后创建一个新字段。
@Column(name = "countryid", insertable = false, updatable = false)
private Integer countryId;
@JoinColumn(name = "countryid", referencedColumnName = "countryid")
@ManyToOne
private Country country;
After you do this, you are able to use my original solution. 完成此操作后,您便可以使用我的原始解决方案。
The second solution is using a Join
in your query. 第二种解决方案是在查询中使用
Join
。
if (countryId != null) {
Join<Contacts, Country> country = cont.join("countryid");
countryPredicate = cb.equal(country.<Integer>get("countryId"), countryId);
predicateList.add(countryPredicate);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.