20

I have an easy problem at the first glance:

entityManager()
.createNativeQuery("select count(*) as total, select sum(field) as total_sum ... blabla") 

And I want to write select result into POJO, like this:

public class AggregateStatsDto {

    private int total;

    private long totalSum;

    // getters, setters, cosntructors
}

What the best way to achieve this?

I can use JPA 2.1 and tried to use @SqlResultSetMapping in conjuction with @ConstructorResult:

@SqlResultSetMapping(name = "AggregateStatsResult", classes = {
        @ConstructorResult(targetClass = AggregateStatsDto.class,
                columns = {
                        @ColumnResult(name = "total"),
                        @ColumnResult(name = "totalSum")
                })
})
public class AggregateStatsDto {

        private long total;

        private int totalSum;

        // getters, setters, cosntructors
    }

Query:

AggregateStatsDto result = (AggregateStatsDto) entityManager()
    .createNativeQuery("select count(*) as total, select sum(field) as total_sum ... blabla", "AggregateStatsResult")
    .getSingleResult();

But no luck. It seems that it wants @Entity anyway. But I want just a POJO.

org.hibernate.MappingException: Unknown SqlResultSetMapping [AggregateStatsResult]"

Thanks in advance!

fasth
  • 2,232
  • 6
  • 26
  • 37
  • If you want your class to be a pojo why don't you fetch the result of the query manually into your pojo? It will be much simpler. And your query seems to be wrong, at least this part: "select count(*) as total, select sum(field) as total_sum ..." – Multisync Nov 09 '14 at 19:13
  • do you mean iterate over objects? it is pretty ugly... but I will be forced to use it as a last effort – fasth Nov 09 '14 at 20:25
  • 1
    [getting result set into DTO with native SQL Query in Hibernate](http://stackoverflow.com/a/3937519/2754530) – wypieprz Nov 09 '14 at 22:53

7 Answers7

14

Put your @SqlResultSetMapping annotation in a class which is an actual entity, not in the DTO Class. Your entity manager can not discover your mapping when you annotate a SqlResultSetMapping in a non-entity.

@SqlResultSetMapping(name = "AggregateStatsResult", classes = {
    @ConstructorResult(targetClass = AggregateStatsDto.class,
            columns = {
                    @ColumnResult(name = "total"),
                    @ColumnResult(name = "totalSum")
            })
})
@Entity
public class SomeOtherClassWhichIsAnEntity {
jeff
  • 3,618
  • 9
  • 48
  • 101
  • I was need to retrieve plain POJO not entity. I tried your approach without success. Anyway I solved this, see answer :) – fasth Feb 01 '15 at 11:34
  • 4
    you misunderstood my answer. With my approach, you are still retrieving plain POJO (AggregateStatsDto), just that you need to put the SqlResultSetMapping annotation into some other class which is an Entity for the entity manager to recognize it. – jeff Feb 02 '15 at 14:44
  • 2
    It is really annoying that JPA expects us to create this intermeidate entity class to return an adhoc query thi sway... – Adam Hughes Jun 15 '21 at 18:38
  • This presumes that you have an entity for your queried table. But what if you query against a view, which is a join of multiple tables, that are not tracked by hibernate? – Marian Klühspies Jan 04 '22 at 09:15
  • No. All we are doing is putting the SqlResultSetMapping annotation into some arbitrary Java class that is annotated as an Entity. Said entity is not part of your query, but just uses as the medium to make JPA aware of the defined mapping. – jeff Jan 04 '22 at 22:16
8

I resolved my problem in following way: This is a query:

  final Query query = Sale.entityManager().createNativeQuery(...);

Then I accessed to internal Hibernate session inside entity manager and applied scalars/resultTransformer. Thats all!

 // access to internal Hibernate of EntityManager
        query.unwrap(SQLQuery.class)
                .addScalar("total", LongType.INSTANCE)
                .addScalar("amountOfSales", LongType.INSTANCE)
                .addScalar("amountOfProducts", LongType.INSTANCE)
                .setResultTransformer(Transformers.aliasToBean(SaleStatsInfo.class));

        ...
        query.getSingleResult();
fasth
  • 2,232
  • 6
  • 26
  • 37
1

This is not the suggested way . The more efficient way is mentioned above with @SqlResultSetMapping but in case you want something more hardcore you can access the Entity Manager's result by the object it returns .

For instance :

 Query q = getEntityManager().createNativeQuery(queryString);

Trggering that with q.getSingleResult();brings back to u an Object of type java.util.HashMap which you can use to access your results row by row .

For instance:

    Query q = getEntityManager().createNativeQuery(queryString);
    java.util.HashMap res =  (java.util.HashMap) q.getSingleResult();
     for (Object key : res.keySet()) {
         System.out.println(key.toString());
         System.out.println( res.get(key).toString());
     }
0

I was stuck for a while on this one. I found that the most common solution is below. Do not forget to add name of the mapping on the createNativeQuery method 2nd parameter. Your pojo should then have no annotations but will be get mapped from entity class.

//Place SqlResultSetMapping annotation in Entity class:

@SqlResultSetMapping(name = "AggregateStatsResult", classes = {
        @ConstructorResult(targetClass = AggregateStatsDto.class,
                columns = {
                        @ColumnResult(name = "total"),
                        @ColumnResult(name = "totalSum")
                })
})
@Entity
public EntityFromExistingTableInDB{
    ...fields
}

//1st param is your query, 2nd param is the name of your mapping above
Query query = entityManager.createNativeQuery("yourQuery", "AggregateStatsResult");
  • as far as I understand the question, it was explicitly asked for a solution for non-entities. In my case, I need to extract DTOs from an SQL view for example, that has no entity. – Marian Klühspies Jan 04 '22 at 09:08
-1

ANSWER MAY BE MISSING INFORMATION

Here is the most direct way to do it in my opinion.

Declare a POJO/DTO and label it with @NativeQueryResultEntity. Note I am using Lombok @Data to declare getters/setters here.

import com.pls.core.jpa.nativequery.NativeQueryResultEntity;
import lombok.Data;

@Data
@NativeQueryResultEntity
public class FieldsDTO{

    @NativeQueryResultColumn(index = 0)
    private String fieldA;

    @NativeQueryResultColumn(index = 1)
    private String fieldB;
   
    @NativeQueryResultColumn(index = 2)
    private String fieldC;
}

Then use NativeQueryResultsMapper

String queryStr = "select field_a, field_b, field_c from db.table"
Query query = entityManager.createNativeQuery(queryStr);
List result = NativeQueryResultsMapper.map(query.getResultList(), FieldsDTO.class);

That's all you should need to do. result will be a list of FieldsDTO objects.

Adam Hughes
  • 14,601
  • 12
  • 83
  • 122
  • 2
    I like your approach, but I can't find com.pls.core.jpa package with `NativeQueryResultEntity`. I've searched in Maven repository. Whhich version of jpa is this and where I can find it? – LosmiNCL Oct 11 '21 at 18:06
  • I'm sorry, I think there may be custom code in my answer that I didn't realize was custom... – Adam Hughes Oct 11 '21 at 23:24
-2

Hibernate will fill any pojo if you privide a propper constructor.

select new Pojo(p1, p2, p3,...) from entity)

will return a list of Pojo instances.

Hannes
  • 2,018
  • 25
  • 32
  • Honestly not sure. I used this type of mapping alot with aggregations and when there is no hsql function mapping there is no replacement. So try using `createQuery` instead. – Hannes Nov 09 '14 at 21:04
  • 1
    Unfortunately _constructor expressions_ work only with JPQL and Criteria API. – wypieprz Nov 09 '14 at 22:24
  • I can't use anything except native SQL since have to use some postgres specific and window functions which is not supported by JPQL, HQL, Criteria. – fasth Nov 10 '14 at 03:07
  • It seems to be that you have to live with `Object[]`. – Hannes Nov 10 '14 at 18:58
-2

I struggled a lot in this issue and finally a found a good solution.

Situation: I have a native query and I am fetching only few fields or all fields from the database table or may be I also have some operations on columns while I am fetching data from table.

Now I want to map the output of this nativequery which we get using getResultList() method into a own POJO class/non-JPA class. The output of getResultList() is a list of objects i.e. List, so we need to manually loop through and set values into our bean/pojo class. Looks at the snippet below.

So my query is this -

// This is your POJO/bean class where you have settter/getter 
// methods of values you want to fetch from the database
List<YourPOJO> yourPOJOList = new ArrayList<YourPOJO>();
YourPOJO pojo = null;

StringBuffer SQL = new StringBuffer();
// I am creating some random SQL query
SQL.append("SELECT tb1.col1, ");
SQL.append(" tb1.col2, ");
SQL.append(" tb2.col3, ");
SQL.append("FROM Table1 tb1, Table2 tb2 ");
SQL.append("WHERE tb1.ID = tb2.ID       ");

qry = getEntityManager().createNativeQuery(SQL.toString());
List<Object[]> cdList = qry.getResultList();

for(Object[] object : cdList){
pojo = new YourPojo();

// Here you manually obtain value from object and map to your pojo setters
pojo.setCode(object[0]!=null ? object[0].toString() : "");

pojo.setProductName(object[1]!=null ? object[1].toString() : "");

pojo.setSomeCode(object[2]!=null ? object[2].toString() : "");

yourPOJOList.add(pojo);
}
old_soul_on_the_run
  • 279
  • 1
  • 6
  • 15