I am using JPA in my project.
I came to a query in which I need to make join operation on five tables. So I created a native query which returns five fields.
Old style using ResultSet
@Transactional(readOnly=true)
public void accessUser() {
EntityManager em = this.getEntityManager();
org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
session.doWork(new Work() {
@Override
public void execute(Connection con) throws SQLException {
try (PreparedStatement stmt = con.prepareStatement(
"SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")) {
ResultSet rs = stmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
System.out.print(rsmd.getColumnName(i) + " (" + rsmd.getColumnTypeName(i) + ") / ");
}
System.out.println("");
while (rs.next()) {
System.out.println("Found username " + rs.getString("USERNAME") + " name " + rs.getString("NAME") + " email " + rs.getString("EMAIL") + " passe " + rs.getString("PASSE") + " email " + rs.getInt("LOGIN_TYPE"));
}
}
}
});
}
We have resolved the issue using following way :
//Add actual table name here in Query
final String sqlQuery = "Select a.* from ACTORS a"
// add your entity manager here
Query query = entityManager.createNativeQuery(sqlQuery,Actors.class);
//List contains the mapped entity data.
List<Actors> list = (List<Actors>) query.getResultList();
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
Since others have already mentioned all the possible solutions, I am sharing my workaround solution.
In my situation with Postgres 9.4
, while working with Jackson
,
//Convert it to named native query.
List<String> list = em.createNativeQuery("select cast(array_to_json(array_agg(row_to_json(a))) as text) from myschema.actors a")
.getResultList();
List<ActorProxy> map = new ObjectMapper().readValue(list.get(0), new TypeReference<List<ActorProxy>>() {});
I am sure you can find same for other databases.
Also FYI, JPA 2.0 native query results as map
if you are using Spring, you can use org.springframework.jdbc.core.RowMapper
Here is an example:
public List query(String objectType, String namedQuery)
{
String rowMapper = objectType + "RowMapper";
// then by reflection you can instantiate and use. The RowMapper classes need to follow the naming specific convention to follow such implementation.
}
Using Hibernate :
@Transactional(readOnly=true)
public void accessUser() {
EntityManager em = repo.getEntityManager();
org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
org.hibernate.SQLQuery q = (org.hibernate.SQLQuery) session.createSQLQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")
.addScalar("username", StringType.INSTANCE).addScalar("name", StringType.INSTANCE)
.addScalar("email", StringType.INSTANCE).addScalar("passe", StringType.INSTANCE)
.addScalar("loginType", IntegerType.INSTANCE)
.setResultTransformer(Transformers.aliasToBean(User2DTO.class));
List<User2DTO> userList = q.list();
}