The easiest way to map a query result to an entity is to provide the entity class as a parameter to the createNativeQuery(String sqlString, Class resultClass) method of the EntityManager and use the default mapping.
Spring Data JPA doesn't provide an automatic mapping of class-based DTOs for native queries. The easiest way to use this projection is to define your query as a @NamedNativeQuery and assign an @SqlResultSetMapping that defines a constructor result mapping.
There is no standard way to get JPA to return a map.
We can use @Query annotation to specify a query within a repository. Following is an example. In this example, we are using native query, and set an attribute nativeQuery=true in Query annotation to mark the query as native. We've added custom methods in Repository in JPA Custom Query chapter.
I have found a couple of solutions to this.
Using Mapped Entities (JPA 2.0)
Using JPA 2.0 it is not possible to map a native query to a POJO, it can only be done with an entity.
For instance:
Query query = em.createNativeQuery("SELECT name,age FROM jedi_table", Jedi.class);
@SuppressWarnings("unchecked")
List<Jedi> items = (List<Jedi>) query.getResultList();
But in this case, Jedi
, must be a mapped entity class.
An alternative to avoid the unchecked warning here, would be to use a named native query. So if we declare the native query in an entity
@NamedNativeQuery(
name="jedisQry",
query = "SELECT name,age FROM jedis_table",
resultClass = Jedi.class)
Then, we can simply do:
TypedQuery<Jedi> query = em.createNamedQuery("jedisQry", Jedi.class);
List<Jedi> items = query.getResultList();
This is safer, but we are still restricted to use a mapped entity.
Manual Mapping
A solution I experimented a bit (before the arrival of JPA 2.1) was doing mapping against a POJO constructor using a bit of reflection.
public static <T> T map(Class<T> type, Object[] tuple){
List<Class<?>> tupleTypes = new ArrayList<>();
for(Object field : tuple){
tupleTypes.add(field.getClass());
}
try {
Constructor<T> ctor = type.getConstructor(tupleTypes.toArray(new Class<?>[tuple.length]));
return ctor.newInstance(tuple);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
This method basically takes a tuple array (as returned by native queries) and maps it against a provided POJO class by looking for a constructor that has the same number of fields and of the same type.
Then we can use convenient methods like:
public static <T> List<T> map(Class<T> type, List<Object[]> records){
List<T> result = new LinkedList<>();
for(Object[] record : records){
result.add(map(type, record));
}
return result;
}
public static <T> List<T> getResultList(Query query, Class<T> type){
@SuppressWarnings("unchecked")
List<Object[]> records = query.getResultList();
return map(type, records);
}
And we can simply use this technique as follows:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table");
List<Jedi> jedis = getResultList(query, Jedi.class);
JPA 2.1 with @SqlResultSetMapping
With the arrival of JPA 2.1, we can use the @SqlResultSetMapping annotation to solve the problem.
We need to declare a result set mapping somewhere in a entity:
@SqlResultSetMapping(name="JediResult", classes = {
@ConstructorResult(targetClass = Jedi.class,
columns = {@ColumnResult(name="name"), @ColumnResult(name="age")})
})
And then we simply do:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table", "JediResult");
@SuppressWarnings("unchecked")
List<Jedi> samples = query.getResultList();
Of course, in this case Jedi
needs not to be an mapped entity. It can be a regular POJO.
Using XML Mapping
I am one of those that find adding all these @SqlResultSetMapping
pretty invasive in my entities, and I particularly dislike the definition of named queries within entities, so alternatively I do all this in the META-INF/orm.xml
file:
<named-native-query name="GetAllJedi" result-set-mapping="JediMapping">
<query>SELECT name,age FROM jedi_table</query>
</named-native-query>
<sql-result-set-mapping name="JediMapping">
<constructor-result target-class="org.answer.model.Jedi">
<column name="name" class="java.lang.String"/>
<column name="age" class="java.lang.Integer"/>
</constructor-result>
</sql-result-set-mapping>
And those are all the solutions I know. The last two are the ideal way if we can use JPA 2.1.
JPA provides an SqlResultSetMapping
that allows you to map whatever returns from your native query into an Entity or a custom class.
EDIT JPA 1.0 does not allow mapping to non-entity classes. Only in JPA 2.1 a ConstructorResult has been added to map return values a java class.
Also, for OP's problem with getting count it should be enough to define a result set mapping with a single ColumnResult
Yes, with JPA 2.1 it's easy. You have very useful Annotations. They simplify your life.
First declare your native query, then your result set mapping (which defines the mapping of the data returned by the database to your POJOs). Write your POJO class to refer to (not included here for brevity). Last but not least: create a method in a DAO (for example) to call the query. This worked for me in a dropwizard (1.0.0) app.
First declare a native query in an entity class:
@NamedNativeQuery (
name = "domain.io.MyClass.myQuery",
query = "Select a.colA, a.colB from Table a",
resultSetMapping = "mappinMyNativeQuery") // must be the same name as in the SqlResultSetMapping declaration
Underneath you can add the resultset mapping declaration:
@SqlResultSetMapping(
name = "mapppinNativeQuery", // same as resultSetMapping above in NativeQuery
classes = {
@ConstructorResult(
targetClass = domain.io.MyMapping.class,
columns = {
@ColumnResult( name = "colA", type = Long.class),
@ColumnResult( name = "colB", type = String.class)
}
)
}
)
Later in a DAO you can refer to the query as
public List<domain.io.MyMapping> findAll() {
return (namedQuery("domain.io.MyClass.myQuery").list());
}
That's it.
If you use Spring-jpa
, this is a supplement to the answers and this question. Please correct this if any flaws. I have mainly used three methods to achieve "mapping result Object[]
to a pojo" based on what practical need I meet:
sql
with its Entity
are enough.The former 2 failed, and I have to use a nativeQuery
.
Here are the examples.
The pojo expected:
public class Antistealingdto {
private String secretKey;
private Integer successRate;
// GETTERs AND SETTERs
public Antistealingdto(String secretKey, Integer successRate) {
this.secretKey = secretKey;
this.successRate = successRate;
}
}
Method 1: Change the pojo into an interface:
public interface Antistealingdto {
String getSecretKey();
Integer getSuccessRate();
}
And repository:
interface AntiStealingRepository extends CrudRepository<Antistealing, Long> {
Antistealingdto findById(Long id);
}
Method 2: Repository:
@Query("select new AntistealingDTO(secretKey, successRate) from Antistealing where ....")
Antistealing whatevernamehere(conditions);
Note: parameter sequence of POJO constructor must be identical in both POJO definition and sql.
Method 3:
Use @SqlResultSetMapping
and @NamedNativeQuery
in Entity
as the example in Edwin Dalorzo's answer.
The first two methods would call many in-the-middle handlers, like customized converters. For example, AntiStealing
defines a secretKey
, before it is persisted, a converter is inserted to encrypt it. This would result in the first 2 methods returning a converted back secretKey
which is not what I want. While the method 3 would overcome the converter, and returned secretKey
would be the same as it is stored (an encrypted one).
Unwrap procedure can be performed to assign results to non-entity(which is Beans/POJO). The procedure is as following.
List<JobDTO> dtoList = entityManager.createNativeQuery(sql)
.setParameter("userId", userId)
.unwrap(org.hibernate.Query.class).setResultTransformer(Transformers.aliasToBean(JobDTO.class)).list();
The usage is for JPA-Hibernate implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With