can do this with JPA / Hibernate - java

I am working with postgres and using JPA with Hibernate, in postgres and other DBMS can do this:
SELECT *, function(parameter) AS xyz FROM table WHERE condition
My question is this, the query can display an additional field (xyz), although there is no such column. I can do that with Hibernate JPA.

If you don't have a mapped entity then you will need to use native queries:
Query query = entityManager
.createQuery(
"SELECT t.*, myfunction(:parameter) FROM table t WHERE t.attr = condition");
query.setParameter("parameter", value);
List resultList = query.getResultList();
Otherwise, if you have a mapped entity you can do this with typed queries or the criteria API.
With typed queries:
TypedQuery<Object[]> query = entityManager
.createQuery(
"SELECT t.*, FUNC('MyFunction', :parameter) FROM table t WHERE t.attr = condition",
Object[].class);
query.setParameter("parameter", value);
List<Object[]> resultList = query.getResultList();
With criteria API:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createTupleQuery();
Root<Table> root = query.from(Table.class);
Expression<Long> funcExpr = criteriaBuilder.function("myfunction",
Long.class, root.get("parameter"));
query.multiselect(query.select(root), funcExpr);
List<Tuple> resultList = entityManager.createQuery(query)
.getResultList();

By using org.hibernate.SQLQuery, you can create native SQL queries with Hibernate. You then execute these queries the same way you would with a normal Hibernate query. Obviously you're going to lose many of the benefits of letting Hibernate and JPA manage your queries, but if they do not support the feature you want, it may be the only way to do it.

Another solution is to use Formulas, if you invoke your function on some fields of the table, and you always want the result of the function you can map another property in your entity and annotate it with #Formula("myFunction(field1, field2)"). Hibernate will add this to all of your queries which have your entity.

Related

Difficulty converting this hibernate Criteria to JPA Criteria, issues with multiple associated entities

The hibernate criteria query I'm trying to change looks like this
Criteria crit =
session.createCriteria(device.class)
.createCriteria("deviceConfigurationTemplate")
.createCriteria("deviceModel");
I want to simply change this to use JPA CriteriaQueries instead. The issue I'm having is how javax.persistence to create a query with multiple assoicated entities passed in as strings. I wanted to use JPA root criteria and multiselect them using the criteriaQuery like this
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Device> cq = cb.createQuery(Device.class);
Root<DeviceModel> model = cq.from(DeviceModel.class);
Root<"deviceConfigurationTemplate"> model2 =
cq.from("deviceConfigurationTemplate");
cq.multiselect(model);
The issue here is you can't pass in a string as a parameter to the root object. I'm not sure how else to go about creating a query like this.
Why would you try to pass a string as the type parameter? Once you join along an association, the resulting Join or Path should be parameterized with the association's target type.
Is the query supposed to be equivalent to SELECT d.deviceConfigurationTemplate.deviceModel FROM Device d? If so, the equivalent Criteria query would be:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<DeviceModel> criteria = cb.createQuery(DeviceModel.class);
Path<DeviceModel> model = criteria.from(Device.class).get("deviceConfigurationTemplate").get("model");
criteria.select(model);
With typing made a bit more explicit:
CriteriaQuery<DeviceModel> criteria = cb.createQuery(DeviceModel.class);
Root<Device> from = criteria.from(Device.class);
Path<ConfigurationTemplate> configTemplate = from.get("template");
Path<DeviceModel> model = configTemplate.get("model");
criteria.select(model);
You could also use from.join() instead.

How to convert this Hibernate Criteria query to instead use javax.jpa.CriteriaQuery Multiple Root Entities

Since the hibernate.criteria API is deprecated, I need assistance converting certain methods to JPA criteriaQuery.
The simpler calls with singular criteria and limited parameters I can handle, but how can I convert from a hibernate criteria created with multiple root entities. Here is the org.hibernate.criteria code that needs conversion
Criteria crit =
session.createCriteria(getType())
.createCriteria("deviceInfo"
.createCriteria("deviceSize");
crit.add(Restrictions.ilike("upperName", modelName));
List<Device> matches = crit.list();
The conversion for something like this with a singular call to createCriteria I can manage. They look something like this
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Workflow> cq = cb.createQuery(getType());
Root<getType()> model = cq.from(getType());
cq.select(model);
cq.where(cb.equal(model.get("primaryEntityId"), entityId));
TypedQuery<getType()> q = entityManager.createQuery(cq);
List<Workflow> matches = q.getResultList();
The only thing I can't seem to recreate in JPA is the two association paths passed in to createCriteria

JPA alternative of Hibernate Projections.property

Folks!
Trying to limit amount of columns fetched from DB and found this: Hibernate Criteria Query to get specific columns
Criteria cr = session.createCriteria(User.class)
.setProjection(Projections.projectionList()
.add(Projections.property("id"), "id")
.add(Projections.property("Name"), "Name"))
.setResultTransformer(Transformers.aliasToBean(User.class));
List<User> list = cr.list();
This works awesome when you use Hibernate, but I am trying to do the same with JPA (JPA provider is Hibernate tho). Is it possible to do it with JPA? I mean limit columns with CriteriaBuilder and map to specific Object?
Also, I saw this:
https://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/chapters/query/criteria/Criteria.html
Hibernate offers an older, legacy org.hibernate.Criteria API which should be considered deprecated. No feature development will target those APIs. Eventually, Hibernate-specific criteria features will be ported as extensions to the JPA javax.persistence.criteria.CriteriaQuery. For details on the org.hibernate.Criteria API, see Legacy Hibernate Criteria Queries.
So seems like it is possible to do this via hibernate extensions for JPA?
There is no alternative syntax within JPA, Hibernate is superset of JPA, it provides features beyond JPA specifications.
In JPA you will have to do projections individually explicitly with select statement (in your case multiselect statement).
Criteria Query
// Create instance of CriteriaBuilder, where em is EntityManager
CriteriaBuilder cb = em.getCriteriaBuilder();
// Use CriteriaBuilder interface to create an instance
// of CriteriaQuery. For multiselect result set is Object [].
CriteriaQuery<Object[]> c = cb.createQuery(Object[].class);
// Establish the root of the query by invoking from() to get back a Root object.
Root<User> user = c.from(User.class);
// Establish the SELECT clause of the query by passing the root into the multiselect() method
criteriaQuery.multiselect(user.get("property1"), user.get("property2"));
alternative would be constructor call:
CriteriaQuery<User> c = cb.createQuery(User.class);
Root<User> user = c.from(User.class);
c.select(cb.construct(User.class,
user.get("property1"),
user.get("property2")));
TupleQuery
CriteriaQuery<Tuple> c = cb.createTupleQuery();
Root<User> user = c.from(User.class);
c.select(cb.tuple(user.get("property1"), user.get("property2")));
Query query = entityManager.createQuery(c);
List<Tuple> results = query.getResultList();
There is also array() method (it returns Object[]) as well.
c.select(cb.array(user.get("property1"), user.get("property2")));

Spring Data - Why it's not possible to have paging with native query

Let's say we have an entity called MyEntity. It is possible to query pageable results using #Query and with named queries, e.g.
#Query(value = "select e from MyEntity e where e.enabled = true")
Page<MyEntity> findAllEnabled(Pageable pageable);
However, it is not possible to achieve the same with native query, so this
#Query(value = "select * from my_entity where enabled = true", nativeQuery = true)
Page<MyEntity> findAllEnabled(Pageable pageable);
won't work.
What are the reasons behind this? Is it possible to make Pageable working with native queries?
I don't know if this is still relevant to you: At least in Spring Data JPA 1.9.4 you can specify two queries.
Given a repository:
interface FoobarEntityRepository extends JpaRepository<FoobarEntity, Integer> {
Page findFoobarsSpecialQuery(String someParameter, final Pageable pageable);
}
You can add 2 native queries to your entity, one for the query itself and one for the count statement:
#Entity
#SqlResultSetMappings({
#SqlResultSetMapping(name = "SqlResultSetMapping.count", columns = #ColumnResult(name = "cnt"))
})
#NamedNativeQueries({
#NamedNativeQuery(
name = "FoobarEntity.findFoobarsSpecialQuery",
resultClass = DailyPictureEntity.class,
query = "Select * from foobars f where someValue = :someParameter "
),
#NamedNativeQuery(
name = "FoobarEntity.findFoobarsSpecialQuery.count",
resultSetMapping = "SqlResultSetMapping.count",
query = "Select count(*) as cnt from foobars f where someValue = :someParameter "
)
})
FoobarEntity {
}
The trick is to specify the count query with the suffix .count. This works also with the Spring Data #Query annotation.
Notice that you need a SQL result set mapping for the count query, though.
This works actually pretty nice.
This is description, given in spring data jpa documentation (http://docs.spring.io/spring-data/jpa/docs/1.8.0.M1/reference/html/)
Native queriesThe #Query annotation allows to execute native queries
by setting the nativeQuery flag to true. Note, that we currently don’t
support execution of pagination or dynamic sorting for native queries
as we’d have to manipulate the actual query declared and we cannot do
this reliably for native SQL.
JPQL abstracts SQL implementation and it's providers specifics, and makes it responsibility of ORM framework to generate correct SQL.
So by using Pagination in JPQL form, Spring just needs to generate correct JPQL, and it will be interpreted on ORM level to correct SQL.
While doing so with SQL, would imply that Spring knows how to generated correct SQL for the vast majorities of RDBMS, duplicating ORM functionality, which is too much overhead.
There is a way to use Pageable with native queries with the SpEL capacity of Spring data, it is mention here.
You can find an example in this repository.
/**
* #see DATAJPA-564
*/
#Query(
value = "select * from (select rownum() as RN, u.* from SD_User u) where RN between ?#{ #pageable.offset -1} and ?#{#pageable.offset + #pageable.pageSize}",
countQuery = "select count(u.id) from SD_User u", nativeQuery = true)
Page<User> findUsersInNativeQueryWithPagination(Pageable pageable);
The sort functionnality will not work properly if there is a subquery in the fromclause or your native query and you wish to apply a dynamic sort on it. The way it can be done is to move the subquery in the where clause.
Spring data will append to the end of your request " order by " if there is a Sort object in the Pageable. (with Spring data 1.10.3)
A better way is to convert the native query in jpql if possible.

Using the JPA Criteria API, can you do a fetch join that results in only one join?

Using JPA 2.0. It seems that by default (no explicit fetch), #OneToOne(fetch = FetchType.EAGER) fields are fetched in 1 + N queries, where N is the number of results containing an Entity that defines the relationship to a distinct related entity. Using the Criteria API, I might try to avoid that as follows:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
root.fetch("relatedEntity");
query.select(root).where(builder.equals(join.get("id"), 3));
The above should ideally be equivalent to the following:
SELECT m FROM MyEntity m JOIN FETCH myEntity.relatedEntity r WHERE r.id = 3
However, the criteria query results in the root table needlessly being joined to the related entity table twice; once for the fetch, and once for the where predicate. The resulting SQL looks something like this:
SELECT myentity.id, myentity.attribute, relatedentity2.id, relatedentity2.attribute
FROM my_entity myentity
INNER JOIN related_entity relatedentity1 ON myentity.related_id = relatedentity1.id
INNER JOIN related_entity relatedentity2 ON myentity.related_id = relatedentity2.id
WHERE relatedentity1.id = 3
Alas, if I only do the fetch, then I don't have an expression to use in the where clause.
Am I missing something, or is this a limitation of the Criteria API? If it's the latter, is this being remedied in JPA 2.1 or are there any vendor-specific enhancements?
Otherwise, it seems better to just give up compile-time type checking (I realize my example doesn't use the metamodel) and use dynamic JPQL TypedQueries.
Instead of root.join(...) you can use root.fetch(...) which returns Fetch<> object.
Fetch<> is descendant of Join<> but it can be used in similar manner.
You just need to cast Fetch<> to Join<> it should work for EclipseLink and Hibernate
...
Join<MyEntity, RelatedEntity> join = (Join<MyEntity, RelatedEntity>)root.fetch("relatedEntity");
...
Starting with JPA 2.1 you can use dynamic entity graphs for this. Remove your fetch and specify an entity graph as follows:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
query.select(root).where(builder.equal(join.get("id"), 3));
EntityGraph<MyEntity> fetchGraph = entityManager.createEntityGraph(MyEntity.class);
fetchGraph.addSubgraph("relatedEntity");
entityManager.createQuery(query).setHint("javax.persistence.loadgraph", fetchGraph);
Using root.fetch() on EclipseLink will create a SQL with INNER JOIN because have 3 types and the default is INNER.
INNER, LEFT, RIGHT;
The suggestion is to use CreateQuery.
TypedQuery<T> typedQuery = entityManager.createQuery(query);
EDIT: You can Cast the root to From like this:
From<?, ?> join = (From<?, ?>) root.fetch("relatedEntity");

Categories