简体   繁体   中英

createNativeQuery mapping to POJO (non-entity)

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!

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 {

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();

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());
     }

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");

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.

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.

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 ie 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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM