40

I have got an Object Admin which extends User. By default both Objects are in the table User_ of my Derby Database (included fields from Admin). Normally I'd select an User like this:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root user= query.from(User.class);
Predicate predicateId = cb.equal(category.get("id"), id);
query.select(user).where(predicateId);
return em.createQuery(query).getSingleResult();

However due to the complexity of my query I'm using a native query like this:

Query query = em.createNativeQuery("SELECT USER.* FROM USER_ AS USER WHERE ID = ?");
query.setParameter(1, id);
return (User) query.getSingleResult();

Though this throws a cast exception. I figure this is due to any fields from Admin.

My question is, how can I select a User using a native query with an equal result as the first example (including the same values for @LOB and @ManyToOne (et cetera) as the JPQL query would return)?

Menno
  • 12,175
  • 14
  • 56
  • 88

6 Answers6

71

You might want to try one of the following ways:

  • Using the method createNativeQuery(sqlString, resultClass)

    Native queries can also be defined dynamically using the EntityManager.createNativeQuery() API.

    String sql = "SELECT USER.* FROM USER_ AS USER WHERE ID = ?";
    
    Query query = em.createNativeQuery(sql, User.class);
    query.setParameter(1, id);
    User user = (User) query.getSingleResult();
    
  • Using the annotation @NamedNativeQuery

    Native queries are defined through the @NamedNativeQuery and @NamedNativeQueries annotations, or <named-native-query> XML element.

    @NamedNativeQuery(
        name="complexQuery",
        query="SELECT USER.* FROM USER_ AS USER WHERE ID = ?",
        resultClass=User.class
    )
    public class User { ... }
    
    Query query = em.createNamedQuery("complexQuery", User.class);
    query.setParameter(1, id);
    User user = (User) query.getSingleResult();
    

You can read more in the excellent open book Java Persistence (available in PDF).

───────
NOTE: With regard to use of getSingleResult(), see Why you should never use getSingleResult() in JPA.

Paul Vargas
  • 41,222
  • 15
  • 102
  • 148
  • 1
    @PaulVargas What if the result is an Integer and not an entity – Pallav Jha May 07 '16 at 11:41
  • 2
    @PaulVargas, I would recommend doing something like this: Integer val = (Integer) query.getSingleResult(); However, I usually recommend against query.getSingleResult() as it throws to many checked exceptions. If you get your results as a list, you can then handle no results and more than one accordingly. – Joe Jul 06 '16 at 22:06
  • Hey, @Joe. Thanks! -- I continued with `query.getSingleResult()` because the *OP*. :) – Paul Vargas Jul 06 '16 at 22:22
10

The accepted answer is incorrect.

createNativeQuery will always return a Query:

public Query createNativeQuery(String sqlString, Class resultClass);

Calling getResultList on a Query returns List:

List getResultList()

When assigning (or casting) to List<MyEntity>, an unchecked assignment warning is produced.

Whereas, createQuery will return a TypedQuery:

public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass);

Calling getResultList on a TypedQuery returns List<X>.

List<X> getResultList();

This is properly typed and will not give a warning.

With createNativeQuery, using ObjectMapper seems to be the only way to get rid of the warning. Personally, I choose to suppress the warning, as I see this as a deficiency in the library and not something I should have to worry about.

Derek White
  • 320
  • 2
  • 13
7

When your native query is based on joins, in that case you can get the result as list of objects and process it.

one simple example.

@Autowired
EntityManager em;

    String nativeQuery = "select name,age from users where id=?";   
    Query query = em.createNativeQuery(nativeQuery);
    query.setParameter(1,id);

    List<Object[]> list = query.getResultList();

    for(Object[] q1 : list){

        String name = q1[0].toString();
        //..
        //do something more on 
     }
shankar
  • 431
  • 7
  • 13
2

Please refer JPA : How to convert a native query result set to POJO class collection

For Postgres 9.4,

List<String> list = em.createNativeQuery("select cast(row_to_json(u) as text) from myschema.USER_ u WHERE ID = ?")
                   .setParameter(1, id).getResultList();

User map = new ObjectMapper().readValue(list.get(0), User.class);
Darshan Patel
  • 2,839
  • 2
  • 25
  • 38
  • which way is good for pass native query? 1) Using EntityManager 2) Using JpaRepository Interface – bhavya Mar 03 '21 at 11:03
0

The best solution I found is using Interface projection .

At the beginning, I created a DTO class but it just didn't work, replacing the class with an interface like this works great:

@Query(value = "SELECT vat as vatRate, SUM(...) as amount from ...", nativeQuery = true)
List<VatReportLine> getSalesVats();


public interface VatReportLine {

    double getVatRate();

    long getAmount();
}
user1928596
  • 1,503
  • 16
  • 21
-1

First of all create a model POJO

import javax.persistence.*;
@Entity
@Table(name = "sys_std_user")
public class StdUser {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "class_id")
    public int classId;
    @Column(name = "user_name")
    public String userName;
    //getter,setter
}

Controller

import com.example.demo.models.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import java.util.List;

@RestController
public class HomeController {
    @PersistenceUnit
    private EntityManagerFactory emf;

    @GetMapping("/")
    public List<StdUser> actionIndex() {
        EntityManager em = emf.createEntityManager(); // Without parameter
        List<StdUser> arr_cust = (List<StdUser>)em
                .createQuery("SELECT c FROM StdUser c")
                .getResultList();
        return arr_cust;
    }

    @GetMapping("/paramter")
    public List actionJoin() {
        int id = 3;
        String userName = "Suresh Shrestha";
        EntityManager em = emf.createEntityManager(); // With parameter
        List arr_cust = em
                .createQuery("SELECT c FROM StdUser c WHERE c.classId = :Id ANd c.userName = :UserName")
                .setParameter("Id",id)
                .setParameter("UserName",userName)
                .getResultList();
        return arr_cust;
    }
}
Ram Pukar
  • 1,583
  • 15
  • 17
  • 5
    It is not a good practice to write your data access code inside the rest controller. Store your data access code in @ Repository classes. Call your repositories from @ Service classes. Access the @ Service classes from the @ RestController. – Georgios Syngouroglou Jan 30 '18 at 13:19