【问题标题】:Hibernate gives a strange ClassCast exception (using Transformers)Hibernate 给出了一个奇怪的 ClassCast 异常(使用 Transformers)
【发布时间】:2015-10-30 10:05:05
【问题描述】:

这段代码:

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

调用这个方法:

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

给了我一个 ClassCastException -> 跟踪的一部分:

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)

这很奇怪,因为事实上,如果你查看 Hibernate 的源代码,它会尝试这样做:

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

这没有任何意义......

target 是 Class 类型,此代码尝试将其强制转换为 Map,

为什么要这么做???

任何指针都非常受欢迎......

我正在使用 Hibernate 5(并且正在从 3 升级)...

编辑:我还使用 Spring(4.2.1.RELEASE;也在升级),它在部署时调用这些方法,也欢迎任何调试指针......

编辑 2:(根据要求,整个 FactCodeDto 类)

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


}

【问题讨论】:

  • 你得到的目标是类型:be.fgov.just.cjr.dto.FactCodeDto 这不是地图
  • 是的,我知道,但我想获得一个 FactCodeDto 类型的列表,并通过 setResultTransformer(Transformers.aliasToBean(FactCodeDto.class)) “告诉”它休眠,但随后(出于某种奇怪的原因) Hibernate 在内部尝试将我的 FactCodeDto.class (target) 转换为 Map……但它不应该那样做,为什么要那样做?
  • 当一个简单的 hql 或标准可以解决它时,为什么还要使用所有这些膨胀?
  • 你为什么要把它弄复杂是我无法理解的。使用普通的 HQL 查询,例如 Query query = session.createQuery("from FactCode as FC");或无论您的查询是什么并运行它。臃肿就是这个词。
  • @Bamboomy 你能显示be.fgov.just.cjr.dto.FactCodeDto类的代码吗?

标签: java spring hibernate hibernate-5.x


【解决方案1】:

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

【讨论】:

  • 对于我来说,在类似的情况下,我需要将字段名称从 zipcode 更改为 ZipCode(又名驼峰式)并相应地重命名 setter 方法。但无论如何,异常文本可能会混淆..
【解决方案2】:

settersgetters 未正确映射到列名时会发生此异常。 确保您有正确的查询获取器和设置器(正确的名称和正确的数据类型)。 在此处阅读更多信息:

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

【讨论】:

  • 这个答案没有提供解决方案,即使有正确的映射,演员表也不起作用。
【解决方案3】:

我对这个问题做了一些调查。问题是 Hibernate 将列名的别名转换为大写——cdFact 变成了CDFACT

在此处阅读以获取更深入的解释和解决方法: mapping Hibernate query results to custom class?

【讨论】:

    【解决方案4】:

    最终找到解决方案并不难,

    我刚刚创建了自己的(自定义)ResultTransformer 并在 setResultTransformer 方法中指定:

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

    自定义结果转换器的代码:

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

    在 hibernate 3 中,您可以将 Aliasses 设置为查询,但在 hibernate 5 中您不能再这样做(如果我错了,请纠正我)因此 aliasToBean 是您只能在实际使用别名时使用的东西;我没有,因此例外。

    【讨论】:

    【解决方案5】:

    我的情况是:

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

    解决方案:

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

    问题在于 Hibernate 将列名的别名转换为大写或小写

    【讨论】:

      【解决方案6】:

      我遇到了这个异常

      在将 DTO 中的一个字段更改为 "closedindexValue" 后,我将其命名为 "closedIndexValue" ,代码运行良好。我认为这是由于大小写字母“我”。

      closedIndexValue -> 不正确的驼峰式 closedindexValue -> 正确的驼峰式

      休眠版本:- 5.2.4

      【讨论】:

        【解决方案7】:

        我通过定义我自己的自定义转换器来解决它,如下所示 -

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

        并在repository方法中使用了这种方式-

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

        效果很好!!!

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

            当您在 AliasToBeanResultTransformer 中指定的类没有对应列的 getter 时,会发生此异常。尽管来自休眠的异常细节具有误导性。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-10-05
              • 1970-01-01
              • 1970-01-01
              • 2018-01-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-12-19
              相关资源
              最近更新 更多