JPA 2.1 ConstructorResult Causing ClassCastExcepti

2019-05-19 03:45发布

问题:

The objects in my resultset are being cast to 'Object' instead of what I specified in the @SQLResultSetMapping objects.

I'm trying to get a handle on ConstructorResult and have created a query which contains a simple join and am trying to get the result set and loop though it printing it out to make sure I have it right. However when I get to the loop what looks like it should be straight forward isn't.

When I declare the result list it is cast to be of type . I step through the query test class and it successfully runs the query and loads it into the result, but the items in the results list have typed as 'Object' rather than CommentInfoListItemDTO objects. So when I get to the loop, it hits a class cast exception. Why wouldn't the result be cast to CommentInfoListItemDTO objects? Especially when this is specificied in the @SQLResultSetMapping.

The code is posted below... I truncated some of the column names just to shorten them up. If it would help to add it back in let me know.

public List<CommentInfoListItemDTO> getCommentTitleListByPersonId(BigInteger personId) {
    String queryString = "select c.article_id, "
                        ***[columns removed for brevity]***
                        + "c.person_id as comment_person_id, "
                        + "a.party_id as aticle_party_id "
                        + "from article_comment c "
                        + "join article a "
                        + "on a.article_id = c.article_id "
                        + "where c.person_id = :personId";

    Query q = em.createNativeQuery(queryString, "CommentInfoListItemDTOMapping");
    q.setParameter("personId", personId);

    List<CommentInfoListItemDTO> commentInfoList = q.getResultList();

    ***[throws exception on the next line]***
    ***[java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com...CommentInfoListItemDTO]***

    for (CommentInfoListItemDTO listElement : commentInfoList){
            System.out.println("COMMENT TITLE LIST: " + listElement.toString());
        }
    return (commentInfoList);
}

@SqlResultSetMapping --

EDITED -- to show how this was placed in an existing entity class.

EDITED -- I tried adding the data types to the column listing in the mapping. It didn't help. Each record in the resultset is still being cast to java.lang.Object somewhere inside Hibernate and it won't let me cast it back to the DTO. The resultsetmapping is being bound by Hibernate:

INFO: Binding resultset mapping: CommentInfoListItemDTOMapping

@Entity
@SqlResultSetMapping(name = "CommentInfoListItemDTOMapping", classes = {
    @ConstructorResult(targetClass = CommentInfoListItemDTO.class,
            columns = {
            @ColumnResult(name = "article_id", type=BigInteger.class),
            @ColumnResult(name = "article_comment_id", type=BigInteger.class),
            @ColumnResult(name = "parent_comment_id", type=BigInteger.class),
            @ColumnResult(name = "article_title", type=String.class),
            @ColumnResult(name = "article_published", type=Boolean.class),
            @ColumnResult(name = "article_publish_date", type=Calendar.class),
            @ColumnResult(name = "article_availability_state", type=String.class),
            @ColumnResult(name = "article_enable_comments", type=Boolean.class),
            @ColumnResult(name = "comment_title", type=String.class),
            @ColumnResult(name = "comment_hide", type=Boolean.class),
            @ColumnResult(name = "comment_created_timestamp", type=Calendar.class),
            @ColumnResult(name = "comment_person_id", type=BigInteger.class),
            @ColumnResult(name = "aticle_party_id", type=BigInteger.class)
            })
})

@Table(name = "article_comment")
@NamedQueries({
    @NamedQuery(name = "ArticleComment.findAll", query = "SELECT e FROM ArticleComment e")})

public class ArticleComment implements Serializable {
...

POJO

public class CommentInfoListItemDTO implements Serializable {

    private Integer id;
    private BigInteger articleId;
    private BigInteger articleCommentId;
    private BigInteger parentCommentId;
    private String articleTitle;
    private Boolean articlePublished;
    @Temporal(javax.persistence.TemporalType.DATE)
    private Calendar articlePublishDate;
    private String articleAvailabilityState;
    private Boolean articleEnableComments;
    private String commentTitle;
    private Boolean commentHide;
    @Temporal(javax.persistence.TemporalType.DATE)
    private Calendar commentCreatedTimestamp;
    private BigInteger commentPersonId;
    private BigInteger articlePartyId;

    public CommentInfoListItemDTO() {
    }

    public CommentInfoListItemDTO(BigInteger articleId, BigInteger articleCommentId, 
            BigInteger parentCommentId, String articleTitle, Boolean articlePublished, 
            Calendar articlePublishDate, String articleAvailabilityState, 
            Boolean articleEnableComments, String commentTitle, Boolean commentHide, 
            Calendar commentCreatedTimestamp, BigInteger commentPersonId, 
            BigInteger articlePartyId) {
        this.articleId = articleId;
        this.articleCommentId = articleCommentId;
        this.parentCommentId = parentCommentId;
        this.articleTitle = articleTitle;
        this.articlePublished = articlePublished;
        this.articlePublishDate = articlePublishDate;
        this.articleAvailabilityState = articleAvailabilityState;
        this.articleEnableComments = articleEnableComments;
        this.commentTitle = commentTitle;
        this.commentHide = commentHide;
        this.commentCreatedTimestamp = commentCreatedTimestamp;
        this.commentPersonId = commentPersonId;
        this.articlePartyId = articlePartyId;
    }

And finally a screengrab from the debugger showing the resultset as Objects rather than CommentInfoListItemDTO objects. The correct information is in the objects however.

回答1:

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to YourDTO

is thrown when the EclipseLink can't find the name of YourDTO passed to the

em.createNativeQuery("SELECT...","YourDTO");

The exception thrown by Hibernate in similar situation is:

org.hibernate.MappingException: Unknown SqlResultSetMapping [someNonExistingMappingName]

You have to ensure that YourDTO is registered by your persistence provider. How? Observe your logs:

Hibernate:

DEBUG annotations.ResultsetMappingSecondPass - Binding result set mapping: YourDTO

EclipseLink: I didn't find any logs for that.

One additional remark to your mapping: Use @ColumnResult with type for ambiguous types:

@ColumnResult(name = "article_publish_date", type=Calendar.class)
@ColumnResult(name = "article_id", type=BigInteger.class)