Select non-entities with JPA? - java

Is it possible with JPA to retrieve a instances of a non-entity classes with native queries?
I have a non-entity class that wraps two entities:
class Wrap{
Entity1 ent1;
Entity2 ent2
}
#Entity
class Entity1{
...
}
#Entity
class Entity2{
...
}
How can I do something like that?
Query q = entityManager.createNativeQuery("native select here");
List<Wrap> list = q.getResultList();

Is it possible with JPA to retrieve a instances of a non-entity classes with native queries?
No. Native queries can return entities only (if you tell them to do so by passing the resultClass or a resultSetMapping to the createNativeQuery method; if you don't, you will get collections of raw data).
In JPQL, you can use constructor expressions (SELECT NEW...) whith a non-entity constructor. But this is not supported for native queries, you'll have to do it manually.

JPA native query without entity - especially with complex queries (recursive, multiple joins, etc.) ?
This worked for me, but it's hibernate specific :
import org.hibernate.transform.Transformers;
Query query = entityManagerFactory.createEntityManager().createNativeQuery(SQL);
// Transform the results to MAP <Key, Value>
query.unwrap(org.hibernate.SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
query.setParameter("myNamedParam", myParam);
List<Map<String, Object>> list = query.getResultList();
for (Map<String, Object> map : list) {
System.out.println(map);
}
cf. https://www.programmerall.com/article/89371766511/

I think I found the solution.
There is a way to use the NEW keyword in constructing the query.
What I did to resovle this issue :
public List<ProductType> getProductByName(String productName) {
String sqlQuery = "select DISTINCT **NEW** project1.ProductType(o.name, o.revision) from Lhproduct o where o.name = :prodname";
Query qry = getEntityManager().**createQuery(sqlQuery);**
qry.setParameter("prodname",productName);
return qry.getResultList();
}
The ProductType is a non-entity object, a simple plain object implementing Serialiabale. But you need to define the appropriate constructor.
Happy coding :-)
Thanks and Regards,
Hari

Related

Hibernate - sqlQuery map redundant records while using JOIN on OneToMany

I have #OneToMany association between 2 entities (Entity1 To Entity2).
My sqlQueryString consists of next steps:
select ent1.*, ent2.differ_field from Entity1 as ent1 left outer join Entity2 as ent2 on ent1.item_id = ent2.item_id
Adding some subqueries and writing results to some_field2, some_field3 etc.
Execute:
Query sqlQuery = getCurrentSession().createSQLQuery(sqlQueryString)
.setResultTransformer(Transformers.aliasToBean(SomeDto.class));
List list = sqlQuery.list();
and
class SomeDto {
item_id;
some_filed1;
...
differ_field;
...
}
So the result is the List<SomeDto>
Fields which are highlighted with grey are the same.
So what I want is to group by, for example, item_id and
the List<Object> differFieldList would be as aggregation result.
class SomeDto {
...fields...
List<Object> differFieldList;
}
or something like that Map<SomeDto, List<Object>>
I can map it manually but there is a trouble:
When I use sqlQuery.setFirstResult(offset).setMaxResults(limit)
I retrieve limit count of records. But there are redundant rows. After merge I have less count actually.
Thanks in advance!
If you would like to store the query results in a collection of this class:
class SomeDto {
...fields...
List<Object> differFieldList;
}
When using sqlQuery.setFirstResult(offset).setMaxResults(n), the number of records being limited is based on the joined result set. After merging the number of records could be less than expected, and the data in List could also be incomplete.
To get the expected data set, the query needs to be broken down into two.
In first query you simply select data from Entity1
select * from Entity1
Query.setFirstResult(offset).setMaxResults(n) can be used here to limit the records you want to return. If fields from Entity2 needs to be used as condition in this query, you may use exists subquery to join to Entity2 and filter by Entity2 fields.
Once data is returned from the query, you can extract item_id and put them into a collection, and use the collection to query Entity 2:
select item_id, differ_field from Entity2 where item_id in (:itemid)
Query.setParameterList() can be used to set the item id collection returned from first query to the second query. Then you will need to manually map data returned from query 2 to data returned from query 1.
This seems verbose. If JPA #OneToMany mapping is configured between the 2 entity objects, and your query can be written in HQL (you said not possible in comment), you may let Hibernate lazy load Entity2 collection for you automatically, in which case the code can be much cleaner, but behind the scenes Hibernate may generate more query requests to DB while lazy loading the entity sitting at Many side.
The duplicated records are natural from a relational database perspective. To group projection according to Object Oriented principles, you can use a utility like this one:
public void visit(T object, EntityContext entityContext) {
Class<T> clazz = (Class<T>) object.getClass();
ClassId<T> objectClassId = new ClassId<T>(clazz, object.getId());
boolean objectVisited = entityContext.isVisited(objectClassId);
if (!objectVisited) {
entityContext.visit(objectClassId, object);
}
P parent = getParent(object);
if (parent != null) {
Class<P> parentClass = (Class<P>) parent.getClass();
ClassId<P> parentClassId = new ClassId<P>(parentClass, parent.getId());
if (!entityContext.isVisited(parentClassId)) {
setChildren(parent);
}
List<T> children = getChildren(parent);
if (!objectVisited) {
children.add(object);
}
}
}
The code is available on GitHub.

Get distinct values via hibernate

I want to get distinct values by a parameter:
#Transactional
public List<data> getAllFromColumn(String identifier) {
List<data> resultList = em.createQuery("SELECT DISTINCT p.market FROM data p", Data.class).getResultList();
return resultList;
}
My problem is that this only returns me a NullPointerException. Any recommendations what is wrong, or what I can do differently?
I appreciate your answer!
If HQL doesn't support select distinct (which it doesn't seem to according to the syntax), you can do this using group by:
SELECT p.market
FROM data p
GROUP BY p.market;

spring-data #Query mapping result issue

I've created a Repository that extends CrudRepository,
this repository has a method with an #Query notation:
Code:
#Query("select itemType, count(*) as count from Item where User_id = :userId group by itemType")
List<Map<String, Long>> countItemsForUser(#Param("userId") Long userId);
The issue I'm having is that this return a ArrayList of Object(s) and not a List of Map.
I've read somewhere that JPA can't return a Map so that's why I stuff the result in a List>.
I don't know what's the best way to work around this issue or to quickly access the result data.
I've tried casting but that didn't work out either:
for(Object item: items) {
Map<String,Long> castedItem = (HashMap<String,Long>)item;
}
See this example in official documentation of Hibernate.Here
for (Object item:items) {
Object[] tuple = (Object[]) item;
String itemType = (String)tuple[0];
Long count = (Long) tuple[1];
}
Most simple way is to use interface. To let Spring wire query alias
to the interface getter. Example can be found here: https://www.baeldung.com/jpa-queries-custom-result-with-aggregation-functions
also there is #SqlResultSetMapping. See:
JPA- Joining two tables in non-entity class

What will be the Criteria for following SQL?

Can any one help me out with Criteria for following query :
SELECT * From TableA Inner Join TableB On TableA.ID=TableB.ID
I am trying with the following Criteria
Criteria criteria = session.createCriteria(TableA.class);
criteria.setFetchMode("TableB", FetchMode.JOIN);
The above criteria retrives both the table data.
Also if I need only specific columns from TableA how will the criteria Change ?
Thanks for your time.
Edit: TableA has one-to-many relationship with TableB.
Question doesn't make sense. In hibernate, the 2 Tables should actually be entities in which case they would have a relationship between them. Are you trying to randomly join 2 tables and get a result back? If so you have to use sql and use a ResultTransformer to convert the result into objects.
private ResultTransformer getResultsTransformer()
{
ResultTransformer transformer = new AliasToBeanResultTransformer(
MyResultBean.class) {
#Override
public Object transformTuple(Object[] values, String[] aliases)
{
MyResultBean row = new MyResultBean();
for (int i = 0; i < aliases.length; i++)
{
row.set(aliases[i], values[i]);
}
return (row);
}
};
return transformer;
}
Call this as follows:
Query q = session.createSQLQuery(sql);
q.setResultTransformer(getResultsTransformer());
List<MyResultBean> list = q.list();
UPDATE: If Table A has a 1-to-Many with Table B, then I find it easiest to use Alias
Criteria criteria = getSession().createCriteria(TableA.class);
criteria.createAlias("tableB","b");
criteria.add(Restrictions.eqProperty("id", "b.id");
criteria.list();
I hope this helps. Regards,
With JOIN, you need to indicate to Hibernate that you only want the "root entity", whcih is tableA. Add the following to your code:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Retrieving values of a specific field in Hibernate

Consider a class:
class Employee{
Integer empId,
//many other fields
}
I need a DAO method as shown below
List<Integer> getAllEmployeeIds(){
//??
}
I dont want List<Employee> and (NEW EDIT) Set<Intger>
How can i do that in hibernate?Am using hbm files for mapping
Like this. Also, I recommend use querydsl to make it type-safe.
List<Integer> getAllEmployeeIds(){
return (List<Integer>)createQuery("select e.empId from Employee e").list();
}
use an hql query and do something like
String hql = "select E.empId from Employee E";
Query query = session.createQuery(hql);
List<Integer> ids = query.list();
follow the documentation from here.

Categories

Resources