Deprecation of Hibernate Criteria and how we can still prevent it

Thanks @vlad for your suggestion. I have create a [new issue] on HHH jira.(https://hibernate.atlassian.net/projects/HHH/issues/HHH-13180?filter=reportedbyme)

One limitation that could help make the JPA creiteria API more attractive would be improving the compatibility of the JPAModelGen annotation processor with Gradle. Currently, this annotation processor disables Gradle’s “incremental build” capability, which significantly impacts build times . If there is a desire to encourage a movement to JPA Creiteria, perhaps providing first-class support would help to encourage that move?

https://hibernate.atlassian.net/browse/HHH-13390

We just finished migrating a large project from Hibernate Criteria to JPA Criteria.

With a simple wrapper around the API and a lot of delegate methods and helpers, the migration is straight forward and LOC stayed nearly the same. E.g:

The code is typesafe and easier to read and understand in many cases.

We ran into four main issues:

  1. sqlRestriction
    As mentioned above, support is missing but we were able to replace all usages with built-in or custom function calls

  2. Custom Types
    We are unable to query custom types such as org.joda.money.Money directly. Hibernate criteria allowed us to write something like Restrictions.eq("price", new Money(EUR, 10). This fails with a NPE when using JPA criteria, but might be an issue in the Jadira UserType library. We have to investigate further.

  3. @AnyType
    This was the most annoying problem. We are using Hibernate’s @AnyType in a couple of entities. JPA Criteria does not support this type at all. It is not included in the metamodel and cannot be used in queries. Since @AnyType is not deprecated, I would expect support for it in HQL and criteria queries.
    For now, we worked around it by adding two virtual columns for each such type. E.g.

	@Any(metaDef = "likeable", metaColumn = @Column(name = "target_type"), optional = false, fetch = FetchType.LAZY)
	@JoinColumn(name = "target_id", nullable = false, updatable = false)
	private Likeable target;

	@Column(name = "target_id", insertable = false, updatable = false, nullable = false)
	private Long targetId;
	@Column(name = "target_type", insertable = false, updatable = false, nullable = false)
	private String targetType;
  1. AttributeConverter
    This is an issue with the metamodel rather than JPA criteria. Types that convert from basic values to collections using @Convert are not recognized by the metamodel. There is an open ticket for this: https://hibernate.atlassian.net/browse/HHH-11803.
	@Convert(converter = StringToListConverter.class)
	private List<String> groups;

    org.hibernate.metamodel.internal.MetadataContext - HHH015007: Illegal argument on static metamodel field injection : Community_#groups; expected type :  org.hibernate.metamodel.model.domain.internal.SingularAttributeImpl; encountered type : javax.persistence.metamodel.ListAttribute

Are there any plans to support @Convert and @AnyType in JPA criteria?

Thomas

What about DTO’s?

Many of us use a DTO’s for sending data to the client instead of entities, even in many cases we use criteria api for fetching them directly, but with JPA Criteria this is no longer supported, it only works with entities. Yes i can a make a tuple and convert to the target dto, but it is not the same, with DTO’s i can define an hbm and use the dto for querying any where in the project.

At first, i have the same attitude as the creator of the post, but i convinced myself of using it because what i like of java the most is the strong typing. Yes JPA criteria has stolen what critera api gave us: the simplicity of query writing, and i found frustrating that this tool accustomed us to this and suddendly take it away, saying that meta model is going to avoid mistakes when we write queries is just a lie, because now we sometimes interchange similar properties of the entity, so errors are always there no matter the tool. But as i said, convinced myself to use it.

So if you remove criteria api completely, i will no longer be capable of using hbm for mapping DTO’s, or is there any way for getting arounf of this? i mean use dtos for querying just as i use entities.

Thanks.

I don’t know what you mean by “using hbm for mapping DTOs”, but maybe Blaze-Persistence Entity-Views which works on top of JPA/Hibernate is what you are looking for.

Thanks for the advice, i did not know about the existence of that library and i will take a look.

What i mean about “using hbm for mapping DTO’s” is that i can wirte and xml based file (with hbm.xml extension), pointing to the dto, the subquery and the properties the query will fullfill. And that´s it, i can use criteria api as this dto was an entity. No needed for third party libs.

I am not denying the goodness of blaze, but it seems that i need to annotate my DTO’s specifying it is a View of some entity, and put a reference to that entity. So basically the client has to know server-side only objects, which is something that i am not agreed with.

I know hibernate is opensource and i am not forced to use it and you not to maintain some functionalities, but not all java spec’s are fine and if your are going to get implementing one, it will be much nicer if you do not remove awsome functionalities.

Thanks again.

Maybe you can give an example for that? I really don’t know what it is that you are doing or looking for. The description you are giving is very vague.

I don’t know what your “client” is, but the mapping has to live somewhere. Putting it into XML just makes it harder for you as a developer because you have to jump back and forth. I don’t know why people think it is so bad to have annotations in such a model. The annotations are just that, annotations. You don’t need JPA or Hibernate on your classpath if you want to work with such classes. Every JVM will just ignore annotations for which there are no class files available at runtime.

If you really must/want to do that, you can create a bunch of pure interfaces describing your model and create subtypes that then include the mapping. That’s totally fine with Blaze-Persistence Entity-Views but also with plain JPA. This is where the targetEntity annotation member for e.g. @ManyToOne/@OneToOne comes to help, which allows you in your concrete entity model to define the actual model type.

Maybe i had to start saying that not all apps are web apps, there are a lot of client-server apps, like swing for example.

Maybe you can give an example for that? I really don’t know what it is that you are doing or looking for. The description you are giving is very vague.

I have a common.jar which define the object com.example.dto.TransferDto, in ther server.jar file i have the entity com.example.entity.Transfer. The dto is a representation of the entity but with pure native data types, so the Transfer entity is not known by the client.jar. Then in the server.jar there are a package with some hibernate mapping files, that maps a query to a pojo (the DTO). So in the server i can create a criteria with TransferDto object, and hibernate will build a query and has a subquery in it (the subquery will be the query i defined in the hibernate mapping file [Transfer.dto.hbm.xml]). And that’s it, i have not to worry to convert a Entity to a Pojo to send it to the the client, hibernate does the job for me. This situation is not supported by JPA Criteria because it only works with entities.

The annotations are just that, annotations. You don’t need JPA or Hibernate on your classpath if you want to work with such classes. Every JVM will just ignore annotations for which there are no class files available at runtime.
Maybe not, but the common.jar in the development process would need the dependencies, in this case de definition of the entities in aother jar/project, which is something i am not agree with, because the common is that, a common for both sides and it has not to know anything of any side.

Again, i am not saying that JPA Criteria must dissapear, it has its advanges/disadvantages, the only thing i am requesting is that dont quit nice functionality of hibernate, or complement the JPA, spec’s are always that can change for the better. This situation is a real case and i am pretty sure not only for me. Many times i have to write database views, and query them as they were an object thanks to the mapping file and criteria api. If hibernate removes that posibility, our path to solve our needs will be much longer and maybe hard to maintaing. Strong typing not always is the best in all cases.

Thanks

Please do me a favor and post the hbm.xml and the query as example so that we can understand what it is that you are actually doing. We will just be wasting each others time if I have to guess how your code looks like. If you are using the ResultTransformer API e.g. AliasToBeanResultTransformer, beware that you can also use that with JPA Criteria. You can set this by unwrapping the query and then calling org.hibernate.Query#setResultTransformer.

I have no idea what you want to say with that. Like I wrote before, provide us example code and say what doesn’t work for you, then we can talk about possible solutions.

Either way, I believe that what you are looking for is the AliasToBeanResultTransformer and if you need more sophisticated mappings, you can always give Blaze-Persistence Entity-Views a shot, which will solve all of your issues in an even better and more performant way.

Well, what i am talking about is part of hibernate, i assume that all know this, but here is the example, a very basic one.

The entity:

@Entity
public class Concept{
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	int id;
	
	@ManyToOne(optional = false, fetch = FetchType.LAZY)
	Project project;
    
    @ManyToOne(optional = true, fetch = FetchType.LAZY)
	Concept parent;
    
    @Column(nullable = false, precision = 16, scale = 6)
	BigDecimal quantity;
    
    @Column(nullable = false, precision = 16, scale = 6)
	BigDecimal cost;
    
    @Column(nullable = false, precision = 16, scale = 6)
	BigDecimal import;
    
    //..getters and setters
}

I can query the entity with criteria api and with JPA criteria too:

Project project = getSession().find(Project.class, projectId);
List<Concept> firstLevel = getSession().createCriteria(Concept.class)
			.add(Property.forName("project").eq(project))
			.add(Property.forName("parent").isNull())
			.list();
return entitiesToDtos(firsLevel);

When a client request the concepts for first level, i fetch the entities with the above way, and then i iterate them and create a DTO for each one. To avoid that i create an Concept.dto.hbm.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping auto-import="false">
  <class mutable="false" name="com.common.dto.ConceptDto">
    <subselect>
			select 
				e.id as id,
                e.project_id as projectId,
                coalesce(e.parent_id, 0) as parentId,
                e.quantity as quantity,
                e.cost as cost,
                e.import as import

			from CONCEPT e

		</subselect>
    <id name="id"/>
    <property name="projectId"/>
    <property name="parentId"/>
    <property name="quantity"/>
    <property name="cost"/>
    <property name="import"/>
  </class>
</hibernate-mapping>

With that mapping loaded into hibernate, i can query that dto using criteria api but NOT with jpa criteria, it excepts with error that the object is not and entity:

return getSession().createCriteria(ConceptDto.class)
			.add(Property.forName("projectId").eq(projectId))
			.add(Property.forName("parentId").eq(0))
			.list();

I query a pojo like it was an entity, with no additional dependencies, no additional tools nor frameworks, just hibernate. The query in the mapping file can be as complex as the needs requires: joins, unions, views, anything i want and hibernate do all the work. I can not query that object in jpa because it only works with entities. If criteria api were removed completely, i lost the option to querying pojos.

Thanks

Hibernate is really big and the documentation only discusses DTOs through AliasToBeanResultTransformer here Hibernate ORM 5.6.15.Final User Guide and here Hibernate ORM 5.6.15.Final User Guide through the @ConstructorResult annotation. So never assume that anyone “knows” what you mean by the prose text you write.

That’s simply not true. What you are doing here does not require the legacy Hibernate Criteria API. You are defining a subselect entity which you can just as well query through the JPA Criteria API.

Here is the annotation version of this if you are looking for that:

@Entity
@Subselect("select e.id as id, e.project_id as projectId, coalesce(e.parent_id, 0) as parentId, e.quantity as quantity, e.cost as cost, e.import as import from CONCEPT e")
public class ConceptDto {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	int id;
	
	@Column(nullable = false)
	int projectId;
    
	@Column(nullable = false)
	int parentId;
    
	@Column(nullable = false, precision = 16, scale = 6)
	BigDecimal quantity;
    
	@Column(nullable = false, precision = 16, scale = 6)
	BigDecimal cost;
    
	@Column(nullable = false, precision = 16, scale = 6)
	BigDecimal import;
    
	//..getters and setters
}

and the documentation

No you don’t. You actually are querying an entity, so this works just as well with JPA Criteria.

Well, i suppose that i missunderstood subselect mappings, with your example it is more clear to me that how hibernate works. Thanks for this.

Now i only have to deal with the query as an annotation, add server-only dependency (to my perspective) to the common jar project for compilation only and get used to manage the query in the class not in the xml, but if that’s the way it is, i have to change my mind.

I think real problem for us is that we used to code in some way and then we have to change it, nobody like changes when the thing is working. Moreover when the change forces us to work in a manner we dont like. And yes there are a lot of tools out there, but our projects are in hibernate and it is less cost work with this changes than change the tool.

I only hope jpa spec designers evolve it to a fluent api, the code is prettier and clean, with current jpa criteria i have to define a lot of typed-vars wich results in many runtime errors.

Thanks for your time and patinent.

You can stick to hbm.xml if you like, it’s the same semantics as the annotation example I gave, just a different way of configuration. Like I wrote, this kind of mapping is an entity to Hibernate, regardless how you map it (XML or annotations).

And we don’t like to maintain 2 query APIs hence we removed the legacy Hibernate one in favor of the one forced onto use by JPA. That’s just how it is, things change. You can stick to 5.x if you like this approach that much and can’t live without it, but it sounds to me that this is not such a big issue after clearing up some misunderstandings. I understand the JPA Criteria API is sometimes cumbersome, but there are APIs out there that work on top of it that strive to provide better user experience like e.g. QueryDSL.

The JPA spec is mainly driven by users. Implementers of the spec don’t really have an interest in standardizing for the purpose of standardization. So I don’t think this will happen unless there is enough interest and someone steps up to do the work. I don’t know what you mean by “typed-vars” but you can use type inference in Java 11 i.e. var root = criteriaQuery.from(MyEntity.class); so you can avoid lengthy type declarations. With the JPA static metamodel (the classes ending with _ i.e. MyEntity_) you can even access sub-parts in a type safe manner. Not sure what the issue is there for you.

If you are interested in a fluent query API, give Blaze-Persistence a shot, which is built on top of JPQL/HQL and also exposes/unifies some of the vendor specific extensions like entity joins.

No i cant, when i define hbm mapping like the example i gave before, hibernate complains with this exception:
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: ConceptDto is not mapped [select generatedAlias0 from ConceptDto as generatedAlias0 where generatedAlias0.id=10]
But if i use annotations in ConceptoDto class like you suggested, it works as expected. So @Entity in a class or hbm mapping is not the same thing perhaps.

Well, it was really necessary to implement jpa? why dont you just continue with criteria api and ignore jpa? Maybe there is something here that we dont know which forces you to implement jpa instead of continue with criteria api. Hibernate was fabulous without jpa at first, now continue being fabulous with some headache hehe.

Again, is not true at all. Once i define a var somVar = ArrayList<Integer>(); and infered type is assigned i can not assign that var to another type somVar = ArrayList<String>();, so var keyword is just sugar syntax for avoiding write the original full type but has no real benefit. What i mean by typed-vars is that i have to define a lot of CriteriaQuery<Someclass> and Root<Someclass> variables inside a logic method, some times more than 10 queries for different objects, so i have a lot of vars and many times it gets confused and frustrating writing a jpa query because it is soooooo verbose and unclear. As the opposite side very good for the compiler and that’s it. I dont see the real gain writing a strong typed query for an output that will be parsed in the sql engine.

Thanks

It complains because you didn’t define ConceptDto as entity-name in the <class/> tag.

I don’t know what to answer here - seems you just want to argue because you don’t like the new way of doing things. Was it really necessary that you use this API? Let’s just leave it at that.

Well, you can’t reassign a var variable, but I don’t see why this is a deal breaker. I don’t know what to tell you - if you don’t like it, don’t use it. If you want something improved, get involved in the community/spec and try to get things you think are valuable into the projects.

You can write your custom utility methods to reduce the amount of code needed for common things you think are cumbersome. Or, if you don’t want to write custom code, there are many query builder libraries (QueryDSL, Blaze-Persistence, …) that work on top of JPA which try to implement a different querying model.

If you don’t care about safety, why don’t you just concatenate strings to produce HQL and execute that HQL directly?

I would just like to know what you guys chatted about this change, if you really take into advantages and disadvantages of each one, which one produces cleaner code, simplicity of use, learning curve… etc.

Writing a lot of boilerplate code is not real solution. And if you are trying to selling me the use of another tool to overcome the lack of this one, maybe there is a problem with this one. May be that path choosen was not the best.

And yes, i am not happy with the change because it is frustrating writing queries this way, but well i am considering my options.

Greetings and happy 2022!!

There are always people that will dislike a certain querying model and prefer another, but hey, guess what, you can choose whatever you want. Hibernate is a JPA implementation and many users come from the Spring or Java EE ecosystem where the JPA Criteria API is widely used, so for us it is only natural to commit to that API. The legacy Hibernate Criteria API had many issues and was simply not feasibly maintainable anymore. It had its own AST model distinct from the one of HQL and JPA Criteria and rendered directly to SQL. So with Hibernate 6.0 we unified the AST models to SQM, which now implements the JPA Criteria API and HQL compiles to that. This is way easier to maintain for us and all the new features and optimizations we do work for both, HQL and the Criteria API so this is a big win.

I don’t want to sell you anything. You came with a question because you don’t like the JPA Criteria API and I simply gave you alternatives. There is no way to resurrect the legacy Hibernate Criteria API. If you like the query style it offered, you will have to find like minded people and develop a library that mimics that and translates to HQL or SQM directly.

An very easy way to migrate hibernate legacy criteria is to use BaseCriteria:
https://awsdc.gitlab.io/

1 Like

How do we do something similar to this with JPA Criteria?

This was possible with Hibernate criteria using Subqueries.propertiesIn. Are we out of luck now?

The Hibernate extension of the JPA CriteriaBuilder is HibernateCriteriaBuilder which offers you a more or less stable API. I might be wrong with this, but I believe that tuple comparisons are not deemed stable enough yet, which is why this feature is only defined on NodeBuilder, which is defined in terms of the incubating SQM APIs.
You can use NodeBuilder.tuple(null, root.get("attr1"), root.get("attr2")).in(subquery) to model the same thing in terms of JPA Criteria API.