简体   繁体   中英

Hibernate gives a strange ClassCast exception (using Transformers)

This code:

@Override
public List<FactCodeDto> getAllFactsWithoutParentsAsFactDto() {
    String completeQuery = FactCodeQueries.SELECT_DTO_FROM_FACT_WITH_NO_PARENTS;
    Query query = createHibernateQueryForUnmappedTypeFactDto(completeQuery);

    List<FactCodeDto> factDtoList = query.list(); //line 133
    return factDtoList;
}

calling this method:

private Query createHibernateQueryForUnmappedTypeFactDto(String sqlQuery) throws HibernateException {
    return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(Transformers.aliasToBean(FactCodeDto.class));
}

gives me a ClassCastException -> part of the trace:

Caused by: java.lang.ClassCastException: org.bamboomy.cjr.dto.FactCodeDto cannot be cast to java.util.Map
    at org.hibernate.property.access.internal.PropertyAccessMapImpl$SetterImpl.set(PropertyAccessMapImpl.java:102)
    at org.hibernate.transform.AliasToBeanResultTransformer.transformTuple(AliasToBeanResultTransformer.java:78)
    at org.hibernate.hql.internal.HolderInstantiator.instantiate(HolderInstantiator.java:75)
    at org.hibernate.loader.custom.CustomLoader.getResultList(CustomLoader.java:435)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2423)
    at org.hibernate.loader.Loader.list(Loader.java:2418)
    at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:336)
    at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:1898)
    at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:318)
    at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:125)
    at org.bamboomy.cjr.dao.factcode.FactCodeDAOImpl.getAllFactsWithoutParentsAsFactDto(FactCodeDAOImpl.java:133)

Which is pretty strange because, indeed, if you look up the source code of Hibernate it tries to do this:

@Override
@SuppressWarnings("unchecked")
public void set(Object target, Object value, SessionFactoryImplementor factory) {
    ( (Map) target ).put( propertyName, value ); //line 102
}

Which doesn't make any sense...

target is of type Class and this code tries to cast it to Map,

why does it try to do that???

any pointers are more than welcome...

I'm using Hibernate 5 (and am upgrading from 3)...

edit: I also use Spring (4.2.1.RELEASE; also upgrading) which calls these methods upon deploy, any debugging pointers are most welcome as well...

edit 2: (the whole FactCodeDto class, as requested)

package org.bamboomy.cjr.dto;

import org.bamboomy.cjr.model.FactCode;
import org.bamboomy.cjr.model.FactCodeType;
import org.bamboomy.cjr.utility.FullDateUtil;
import org.bamboomy.cjr.utility.Locales;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.util.Assert;

import java.util.*;

@Getter
@Setter
@ToString
public class FactCodeDto extends TreeNodeValue {

    private String cdFact;
    private String cdFactSuffix;
    private Boolean isSupplementCode;
    private Boolean isTitleCode;
    private Boolean mustBeFollowed;

    private Date activeFrom;
    private Date activeTo;
    private Boolean isCode;
    private Long idFact;
    private Long idParent;
    private String type;
    Map<Locale, String> description = new HashMap<Locale, String>(3);

    public FactCodeDto() {
    }

    public FactCodeDto(String prefix, String suffix) {
        super();
        this.cdFact = prefix;
        this.cdFactSuffix = suffix;
    }

    public FactCodeDto(String cdFact, String cdFactSuffix, Boolean isSupplementCode,  Boolean mustBeFollowed) {
        super();
        this.cdFact = cdFact;
        this.cdFactSuffix = cdFactSuffix;
        this.isSupplementCode = isSupplementCode;
        this.mustBeFollowed = mustBeFollowed;

    }

    public FactCodeDto(String cdFact, String cdFactSuffix, Boolean isSupplementCode,  Boolean mustBeFollowed, Long idFact, Long idParent, Boolean isCode, Boolean isTitleCode, Date from, Date to, Map<Locale, String> descriptions,String type) {
        super();
        this.cdFact = cdFact;
        this.cdFactSuffix = cdFactSuffix;
        this.isSupplementCode = isSupplementCode;
        this.mustBeFollowed = mustBeFollowed;
        this.idFact = idFact;
        this.idParent = idParent;
        this.isCode = isCode;
        this.isTitleCode = isTitleCode;
        this.activeFrom = from;
        this.activeTo = to;
        if (descriptions != null) {
            this.description = descriptions;
        }

        this.type = type;

    }

    public FactCodeDto(FactCode fc) {
        this(fc.getPrefix(), fc.getSuffix(), fc.isSupplementCode(), fc.isHasMandatorySupplCodes(), fc.getId(), fc.getParent(), fc.isActualCode(), fc.isTitleCode(), fc.getActiveFrom(), fc.getActiveTo(), fc.getAllDesc(),fc.getType().getCode());
    }

    public String formatCode() {
        return FactCode.formatCode(cdFact, cdFactSuffix);
    }

    public boolean isActive() {
        Date now = new Date(System.currentTimeMillis());
        return FullDateUtil.isBetweenDates(now, this.activeFrom, this.activeTo);

    }

    public void setDescFr(String s) {
        description.put(Locales.FRENCH, s);
    }

    public void setDescNl(String s) {
        description.put(Locales.DUTCH, s);
    }

    public void setDescDe(String s) {
        description.put(Locales.GERMAN, s);
    }

    /**
     * public String toString() {
     * StringBuilder sb = new StringBuilder();
     * sb.append(getIdFact() + ": ")
     * .append(getIdParent() + ": ")
     * .append(" " + cdFact + cdFactSuffix + ": " + (isSupplementCode ? "NO Principal " : "   Principal "))
     * .append((mustBeFollowed ? "    Must Be Followed " : "NOT Must Be Followed "));
     * return sb.toString();
     * }
     */

    public Map<Locale, String> getDescription() {
        return description;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        String fullCode = formatCode();
        result = prime * result + ((fullCode == null) ? 0 : fullCode.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        FactCodeDto other = (FactCodeDto) obj;

        return formatCode().equals(other.formatCode());
    }

    @Override
    public boolean isChildOf(TreeNodeValue value) {
        Assert.notNull(value);
        boolean isChild = false;
        if (value instanceof FactCodeDto) {
            if (this.getIdParent() != null) {
                isChild = this.getIdParent().equals(((FactCodeDto) value).getIdFact());
            }

        }
        return isChild;
    }

    @Override
    public boolean isBrotherOf(TreeNodeValue value) {
        Assert.notNull(value);
        boolean isBrother = false;
        if (value instanceof FactCodeDto) {
            if (this.getIdParent() != null) {
                isBrother = this.getIdParent().equals(((FactCodeDto) value).getIdParent());
            }

        }
        return isBrother;
    }

    @Override
    public boolean isParentOf(TreeNodeValue value) {
        Assert.notNull(value);
        boolean isParent = false;
        if (value instanceof FactCodeDto) {
            isParent = this.getIdFact().equals(((FactCodeDto) value).getIdParent());
        }
        return isParent;
    }


    @Override
    public int compareTo(TreeNodeValue to) {
        if (to instanceof FactCodeDto) {
            return formatCode().compareTo(((FactCodeDto) to).formatCode());
        } else return 1;

    }


    public String getCode() {
        return formatCode();
    }


}

我发现 AliasToBean 在 Hibernate 5 中发生了变化。对我来说,为我的字段添加 getter 解决了这个问题。

This exception occurs when the setters and getters are not mapped correctly to the column names. Make sure you have the correct getters and setters for the query(Correct names and correct datatypes). Read more about it here:

http://javahonk.com/java-lang-classcastexception-com-wfs-otc-datamodels-imagineexpirymodel-cannot-cast-java-util-map/

I do some investigation on this question. The problem is that Hibernate converts aliases for column names to upper case — cdFact becomes CDFACT .

Read for a more deeply explanation and workaround here: mapping Hibernate query results to custom class?

In the end it wasn't so hard to find a solution,

I just created my own (custom) ResultTransformer and specified that in the setResultTransformer method:

private Query createHibernateQueryForUnmappedTypeFactDto(String sqlQuery) throws HibernateException {
    return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(new FactCodeDtoResultTransformer());
    //return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(Transformers.aliasToBean(FactCodeDto.class));
}

the code of the custom result transformer:

package org.bamboomy.cjr.dao.factcode;

import org.bamboomy.cjr.dto.FactCodeDto;

import java.util.Date;
import java.util.List;

/**
 * Created by a162299 on 3-11-2015.
 */
public class FactCodeDtoResultTransformer implements org.hibernate.transform.ResultTransformer {

    @Override
    public Object transformTuple(Object[] objects, String[] strings) {

        FactCodeDto result = new FactCodeDto();

        for (int i = 0; i < objects.length; i++) {
            setField(result, strings[i], objects[i]);
        }

        return result;
    }

    private void setField(FactCodeDto result, String string, Object object) {

        if (string.equalsIgnoreCase("cdFact")) {
            result.setCdFact((String) object);
        } else if (string.equalsIgnoreCase("cdFactSuffix")) {
            result.setCdFactSuffix((String) object);
        } else if (string.equalsIgnoreCase("isSupplementCode")) {
            result.setIsSupplementCode((Boolean) object);
        } else if (string.equalsIgnoreCase("isTitleCode")) {
            result.setIsTitleCode((Boolean) object);
        } else if (string.equalsIgnoreCase("mustBeFollowed")) {
            result.setMustBeFollowed((Boolean) object);
        } else if (string.equalsIgnoreCase("activeFrom")) {
            result.setActiveFrom((Date) object);
        } else if (string.equalsIgnoreCase("activeTo")) {
            result.setActiveTo((Date) object);
        } else if (string.equalsIgnoreCase("descFr")) {
            result.setDescFr((String) object);
        } else if (string.equalsIgnoreCase("descNl")) {
            result.setDescNl((String) object);
        } else if (string.equalsIgnoreCase("descDe")) {
            result.setDescDe((String) object);
        } else if (string.equalsIgnoreCase("type")) {
            result.setType((String) object);
        } else if (string.equalsIgnoreCase("idFact")) {
            result.setIdFact((Long) object);
        } else if (string.equalsIgnoreCase("idParent")) {
            result.setIdParent((Long) object);
        } else if (string.equalsIgnoreCase("isCode")) {
            result.setIsCode((Boolean) object);
        } else {
            throw new RuntimeException("unknown field");
        }

    }

    @Override
    public List transformList(List list) {
        return list;
    }
}

in hibernate 3 you could set Aliasses to queries but you can't do that anymore in hibernate 5 (correct me if I'm wrong) hence the aliasToBean is something you only can use when actually using aliasses; which I didn't, hence the exception.

Im my case :

 => write sql query and try to map result to Class List => Use "Transformers.aliasToBean" => get Error "cannot be cast to java.util.Map"

Solution :

      => just put \" before and after query aliases 
      ex: 
      "select first_name as \"firstName\" from test"

The problem is that Hibernate converts aliases for column names to upper case or lower case

I was getting this exception

I named one of the field in DTO as " closedIndexValue " after changing it to " closedindexValue " , The code was working fine.I think it was due to case of letter "I" .

closedIndexValue -> improper camelcase closedindexValue -> proper camelcase

Hibernate Version :- 5.2.4

I solved it by defining my own custom transformer as given below -

import org.hibernate.transform.BasicTransformerAdapter;   

public class FluentHibernateResultTransformer extends BasicTransformerAdapter {

    private static final long serialVersionUID = 6825154815776629666L;

    private final Class<?> resultClass;

    private NestedSetter[] setters;

    public FluentHibernateResultTransformer(Class<?> resultClass) {
        this.resultClass = resultClass;
    }

    @Override
    public Object transformTuple(Object[] tuple, String[] aliases) {
        createCachedSetters(resultClass, aliases);

        Object result = ClassUtils.newInstance(resultClass);

        for (int i = 0; i < aliases.length; i++) {
            setters[i].set(result, tuple[i]);
        }

        return result;
    }

    private void createCachedSetters(Class<?> resultClass, String[] aliases) {
        if (setters == null) {
            setters = createSetters(resultClass, aliases);
        }
    }

    private static NestedSetter[] createSetters(Class<?> resultClass, String[] aliases) {
        NestedSetter[] result = new NestedSetter[aliases.length];

        for (int i = 0; i < aliases.length; i++) {
            result[i] = NestedSetter.create(resultClass, aliases[i]);
        }

        return result;
    }
}

And used this way inside the repository method -

@Override
    public List<WalletVO> getWalletRelatedData(WalletRequest walletRequest,
            Set<String> requiredVariablesSet) throws GenericBusinessException {
        String query = getWalletQuery(requiredVariablesSet);

        try {
            if (query != null && !query.isEmpty()) {
                SQLQuery sqlQuery = mEntityManager.unwrap(Session.class).createSQLQuery(query);
                return sqlQuery.setResultTransformer(new FluentHibernateResultTransformer(WalletVO.class))
                        .list();
            }
        } catch (Exception ex) {
            exceptionThrower.throwDatabaseException(null, false);
        }

        return Collections.emptyList();
    }

It worked perfectly !!!

尝试将列名和字段名都用大写字母。

This exception occurs when the class that you specified in the AliasToBeanResultTransformer does not have getter for the corresponding columns. Although the exception details from the hibernate are misleading.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM