Wednesday, May 7, 2014

JPA Mapping Native Query to Bean (POJO, Class) other than the Entity JPA2.1

Removed
Working with JPA 2.0, every time I need to create a new bean (named PersonDetail) from the result of a query, I create a NamedQuery like this:


1
2
3
@NamedQuery(name="findPerson", 
 query = "SELECT new  org.nd.enterprise.bean.PersonDetail(e.personId, e.birthDate)"
   + " FROM Person p WHERE p.personId = :person")

A constructor with the desired output should be set in the PersonDetail bean.

But using the NativeQuery, this is not possible as the Query will be executed like it is given.
In fact while creating the query (NativeQuery or NamedQuery), we can fore the result to be the desired Bean, but this  will generate this error:



1
2
3
4
// Here we declare a @NamedNativeQuery   
List<PersonDetail> rec = entityManager.createNamedQuery("findPerson", PersonDetail.class).getResultList();
// Here we declare just a String query = "select ...."
List<PersonDetail> rec2 = entityManager.createNativeQuery(query,PersonDetail.class).getResultList();

org.hibernate.MappingException: Unknown entity: org.nd.enterprise.bean.PersonDetail

This is normal as it accepts only an Entity and not a Class (See this post to know how to fix that with JPA2.0). Fortenately JPA2.1 comes with the solution, by adding the @ConstructorResult in the @SqlResultSetMapping where you cone construct your Class (In JPA 2.0 only the Entity cn be specified and not another POJO .)




 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 Query q = em.createNativeQuery(
      "SELECT personId, birthDate)"
   + " FROM Person p",
      "PersonDetailsResult");

   @SqlResultSetMapping(
       name="PersonDetailsResult",
       classes={
          @ConstructorResult(
               targetClass=org.nd.enterprise.bean.PersonDetail,
                 columns={
                    @ColumnResult(name="personId"),
                    @ColumnResult(name="birthDate")
                    }
          )
       }
      )

6 comments :

  1. I have tried ConstructorResult with JPA 2.1 but it always returns list of objects to me. Here is my code

    final String SELECT_EXPIRED_OFFLINE_TRANSFERS = "select "
    + "b.paymnt_ref_number, b.request_date, b.paymnt_amt, b.paymnt_status_code, a.trans_date,b.payer_fi_id, b.payee_fi_userid ,a.trans_initiated_by"
    + "from payment_status_history a, payments b "
    + "where a.trans_date >= :fiTransStartDate "
    + "and a.trans_date < :fiTransEndDate "
    + "and a.paymnt_ref_number = b.paymnt_ref_number "
    + "and a.paymnt_status_code = b.paymnt_status_code ";

    Query query = em().createNativeQuery(SELECT_EXPIRED_OFFLINE_TRANSFERS,"OfflineUnsuccessfulTransferResultSet");

    @SqlResultSetMapping(name = "OfflineUnsuccessfulTransferResultSet", classes = {
    @ConstructorResult(
    targetClass = OfflineUnsuccessfulTransferInfo.class,
    columns = {
    @ColumnResult(name = "paymnt_ref_number"),
    @ColumnResult(name = "request_date"),
    @ColumnResult(name = "paymnt_amt"),
    @ColumnResult(name = "paymnt_status_code"),
    @ColumnResult(name = "trans_date"),
    @ColumnResult(name = "Fi_Id"),
    @ColumnResult(name = "name"),
    @ColumnResult(name = "last_changed_by")
    })
    })

    public class OfflineUnsuccessfulTransferInfo {
    private String paymentRefNumber;
    private Date requestDate;
    private String recipientName;
    private BigDecimal amount;
    private String lastChangedBy;
    private PaymentStatus paymentStatus;
    private String fiId;
    private String fiName;
    private Date transactionDate;

    public OfflineUnsuccessfulTransferInfo(String paymentRefNumber, Timestamp requestDate, BigDecimal amount, BigDecimal paymentStatus, Timestamp transactionDate, String fiId, String recipientName, String lastChangedBy) {
    this.paymentRefNumber = paymentRefNumber;
    this.requestDate = requestDate;
    this.recipientName = recipientName;
    this.amount = amount;
    this.lastChangedBy = lastChangedBy;
    // this.paymentStatus = paymentStatus;
    this.fiId = fiId;
    this.transactionDate = transactionDate;
    }
    Getter/Setters

    }

    How can i return list of my objects

    ReplyDelete
    Replies
    1. I think the problem is in the constructor, You declare your paymentStatus once as PaymentStatus and once as BigDecimal.

      Delete
    2. " The target class must define a constructor taking the same number of arguments and types as defined by the columns.".

      You can even define your columns type :
      @ColumnResult(name="paymnt_ref_number", type=String.class),
      .......

      Delete
  2. Thanks Mariem for looking into it. Finally i was able to figure out the problem, actually i was using older version of hibernate due to which it was not using sqlResultMapping. I had following dpendecies in my pom, so though i had updated JPA version but still it was running with older hibernate version which does not support JPA2.1


    org.hibernate
    hibernate-entitymanager
    4.1.7.Final


    org.hibernate.javax.persistence
    hibernate-jpa-2.1-api
    1.0.0.Final


    After updating hibernate-entitymanager jar to latest version it worked fine locally. But now i am struggling to run it on Jboss server as jboss injects jpa2.0 version. I tried to follow below blog but it gives me below mentioned error. Any clue about that?

    http://mariemjabloun.blogspot.ca/2014/03/use-jboss-jpa-21-and-hibernate-43-on.html

    Error: nested exception is java.lang.NoSuchMethodError: javax.persistence.Table.indexes()[Ljavax/persistence/Index;

    ReplyDelete
    Replies
    1. Why not using Wildfly 8 which comes with JPA 2.1 .

      Delete
  3. This comment has been removed by a blog administrator.

    ReplyDelete

Articles les plus consultés