Considere o seguinte modelo JAVA para o hibernate :
@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;
}
e o seguinte modelo para serialização da API (usando o controlador de descanso de inicialização por mola ):
public class PersonVO {
public Long id;
public String fullName;
}
O que eu quero é:
- Aplique alguma filtragem na Pessoa (definida estaticamente)
- Aplique alguma filtragem no PersonVO (obtenha @RequestParam)
Em C # .NET eu poderia fazer como:
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();
O que eu recebi em 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?
}
Eu quero ter separado a "projeção para o modelo VO" da "expressão onde" aplicada a ele, mas aplicada indiretamente se usada uma coluna projetada (como fullName).
Isso é possível em Java? Usando o que? Critério? Querydsl? Corrente? (não adira necessariamente à amostra do java)
java
hibernate
java-stream
projection
querydsl
jvitor83
fonte
fonte
Stream
s, você poderia ter feito algo como -personList.stream().filter(p -> p.active).map(p -> new PersonV0(p.id, p.firstName + " " + p.lastName)).filter(pv -> pv.fullName.equals(fullNameRequestParameter)).collect(Collectors.toList());
onde oPredicate
usado no pingfilter
posteriormap
se baseiaPersonV0
stream()
para consultar o banco de dados. Eu acho que isso pode responder parcialmente à minha pergunta. Mas vou mantê-lo aberto para ver se alguém pode responder isso com um exemplo concreto (de preferência usando o hibernate como orm).Respostas:
A API de critérios JPA não possui essa funcionalidade. Além disso, não é fácil ler 😊
API de critérios JPA
Na API de critérios, você precisa reutilizar o
Expression
.O código de trabalho fica assim:
O código SQL gerado:
API de Critérios JPA e
@org.hibernate.annotations.Formula
O Hibernate possui uma anotação
org.hibernate.annotations.Formula
que pode simplificar um pouco o código.Adicione à entidade um campo computado anotado com
@Formula("first_name || ' ' || last_name")
:E na consulta da API JPA Criteria, faça referência ao campo
fullName
:E o SQL gerado:
API de critérios de hibernação
A API Hibernate Criteria (descontinuada desde o Hibernate 5.2 em favor da API JPA Criteria) permite usar aliases. Mas nem todos os bancos de dados permitem usar aliases (por exemplo
(full_name || ' ' || last_name) as full_name
) em umawhere
cláusula.De acordo com os documentos do PostgreSQL :
Significa a consulta SQL
não funciona no PostgreSQL.
Portanto, usar um alias em uma
where
cláusula não é uma opção.fonte
Observe que você deve chamar os nomes das colunas como "getters" nas interfaces (getFirstName = firstName)
Ele chama projeção baseada em interface. Então você pode criar uma instância de
PersonVO
:Era disso que você precisava?
fonte
Usando esta http://www.jinq.org/ library eu poderia fazê-lo e ser aplicado ao hibernate (e consequentemente ao banco de dados).
Obrigado a todos pela ajuda!
fonte