为hibernate考虑以下JAVA模型:
@Entity
@Table
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long id;
@Column
public String firstName;
@Column
public String lastName;
@Column
public Boolean active;
}
和以下用于API序列化的模型(使用spring boot rest控制器):
public class PersonVO {
public Long id;
public String fullName;
}
我想要的是:
在C#.NET中,我可以这样:
IQueryable<Person> personsQuery = entityFrameworkDbContext.Persons;
// FIRST POINT - Here i could make some predefined filtering like 'only active', 'from the same city'... at the database model
personsQueryWithPreDefinedFilters = personsQuery.Where(person => person.active == true);
IQueryable<PersonVO> personsProjectedToVO = personsQueryWithPreDefinedFilters.Select(person => new PersonVO()
{
id = person.id,
fullName = person.firstName + " " + person.lastName
});
// SECOND POINT - At this point i could add more filtering based at PersonVO model
if (!String.IsNullOrWhiteSpace(fullNameRequestParameter)) {
personsProjectedToVO = personsProjectedToVO.Where(personVO => personVO.FullName == fullNameRequestParameter);
}
// The generated SQL at database is with both where (before and after projection)
List<PersonVO> personsToReturn = personsProjectedToVO.ToList();
我在Java中得到的是:
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
CriteriaQuery<PersonVO> cq = cb.createQuery(PersonVO.class);
Root<Person> root = cq.from(Person.class);
// FIRST POINT - Here i could make some predefined filtering like 'only active', 'from the same city'... at the database model
cq.where(cb.equal(root.get(Person_.active), true));
Expression<String> fullName = cb.concat(root.get(Person_.firstName), root.get(Person_.lastName));
cq.select(cb.construct(
PersonVO.class,
root.get(Person_.id),
fullName
));
// SECOND POINT - At this point i could add more filtering based at PersonVO model??? HOW???
if (fullNameRequestParameter != null) {
cq.where(cb.equal(fullName, fullNameRequestParameter));
// i only could use based at the fullName expression used, but could i make a Predicate based only on PersonVO model without knowing or having the expression?
}
我想将“到VO模型的投影”与对其应用的“ where表达式”分开,但是如果使用投影列(例如fullName),则将其间接应用。
在Java中可能吗?使用什么?条件? Querydsl?流? (不一定要坚持使用Java示例)
JPA Criteria API没有这种功能。另外,不容易阅读😊
在Criteria API中,您需要重用Expression
。
工作代码如下:
public List<PersonVO> findActivePersonByFullName(String fullName) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<PersonVO> cq = cb.createQuery(PersonVO.class);
Root<Person> root = cq.from(Person.class);
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get("active"), true));
Expression<String> fullNameExp =
cb.concat(cb.concat(root.get("firstName"), " "), root.get("lastName"));
cq.select(cb.construct(
PersonVO.class,
root.get("id"),
fullNameExp
));
if (fullName != null) {
predicates.add(cb.equal(fullNameExp, fullName));
}
cq.where(predicates.toArray(new Predicate[0]));
return entityManager.createQuery(cq).getResultList();
}
生成的SQL代码:
select
person0_.id as col_0_0_,
((person0_.first_name||' ')||person0_.last_name) as col_1_0_
from
person person0_
where
person0_.active=?
and (
(
person0_.first_name||?
)||person0_.last_name
)=?
@org.hibernate.annotations.Formula
Hibernate具有注释org.hibernate.annotations.Formula
,可以稍微简化代码。
向实体添加带@Formula("first_name || ' ' || last_name")
的计算字段:
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long id;
@Column
public String firstName;
@Column
public String lastName;
@Column
public boolean active;
@Formula("first_name || ' ' || last_name")
private String fullName;
//...getters and setters
}
并且在JPA Criteria API查询中,请参考字段fullName
:
public List<PersonVO> findActivePersonByFullName(String fullName) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<PersonVO> cq = cb.createQuery(PersonVO.class);
Root<Person> root = cq.from(Person.class);
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get("active"), true));
cq.select(cb.construct(
PersonVO.class,
root.get("id"),
root.get("fullName")
));
if (fullName != null) {
predicates.add(cb.equal(root.get("fullName"), fullName));
}
cq.where(predicates.toArray(new Predicate[0]));
return entityManager.createQuery(cq).getResultList();
}
以及生成的SQL:
select
person0_.id as col_0_0_,
person0_.first_name || ' ' || person0_.last_name as col_1_0_
from
person person0_
where
person0_.active=?
and person0_.first_name || ' ' || person0_.last_name=?
Hibernate Criteria API(自Hibernate 5.2开始支持JPA Criteria API弃用)允许使用别名。但并非所有数据库都允许在(full_name || ' ' || last_name) as full_name
子句中使用别名(例如where
)。
输出列的名称可以用来引用列中的值ORDER BY和GROUP BY子句,但不在WHERE或HAVING子句中;在那里,您必须改写表达式。
表示SQL查询
select p.id,
(p.first_name || ' ' || p.last_name) as full_name
from person p
where p.active = true
and full_name = 'John Doe'
在PostgreSQL中不起作用。
因此,不能在where
子句中使用别名。