JPA Собственный запрос выбора и приведения объекта

У меня есть объект Admin который расширяется User, По умолчанию оба объекта находятся в таблице. User_ моей базы данных Derby (включены поля из Admin). Обычно я бы выбрал User как это:

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

Однако из-за сложности моего запроса я использую собственный запрос, подобный следующему:

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

Хотя это бросает исключение броска. Я полагаю, что это связано с любыми полями из Admin,

У меня вопрос, как я могу выбрать User используя собственный запрос с таким же результатом, как в первом примере (включая те же значения для @LOB а также @ManyToOne (и так далее), как будет возвращать запрос JPQL)?

6 ответов

Решение

Вы можете попробовать один из следующих способов:

  • Используя метод createNativeQuery(sqlString, resultClass)

    Собственные запросы также могут быть определены динамически с помощью 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();
    
  • Использование аннотации @NamedNativeQuery

    Нативные запросы определяются через @NamedNativeQuery а также @NamedNativeQueries аннотации или <named-native-query> Элемент XML.

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

Вы можете прочитать больше в превосходной открытой книге Java Persistence (доступно в формате PDF).

───────
ПРИМЕЧАНИЕ: в отношении использования getSingleResult()Посмотрите, почему вы никогда не должны использовать getSingleResult() в JPA.

Принятый ответ неверен.

createNativeQuery всегда будет возвращать Query:

public Query createNativeQuery(String sqlString, Class resultClass);

призвание getResultList на Query возвращается List:

List getResultList()

При назначении (или кастинге) List<MyEntity>выдается предупреждение о непроверенном назначении.

В то время как, createQuery вернет TypedQuery:

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

призвание getResultList на TypedQuery возвращается List<X>,

List<X> getResultList();

Это правильно напечатано и не даст предупреждение.

С createNativeQuery, с помощью ObjectMapper кажется, единственный способ избавиться от предупреждения. Лично я предпочитаю подавлять предупреждение, так как считаю это недостатком библиотеки, а не тем, о чем мне следует беспокоиться.

Когда ваш собственный запрос основан на объединениях, в этом случае вы можете получить результат в виде списка объектов и обработать его.

один простой пример.

@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 
     }

Пожалуйста, обратитесь к JPA: Как преобразовать собственный набор результатов запроса в коллекцию классов POJO

За 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);

Лучшее решение, которое я нашел, — это использование проекции интерфейса .

Вначале я создал класс DTO, но он просто не работал, замена класса таким интерфейсом отлично работает:

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


public interface VatReportLine {

    double getVatRate();

    long getAmount();
}

Прежде всего создайте модель 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
}

контроллер

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;
    }
}
Другие вопросы по тегам