Hibernate выдает странное исключение ClassCast (с использованием Transformers)

Этот код:

@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)...

edit: я также использую 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();
    }


}

person Bamboomy    schedule 30.10.2015    source источник
comment
Ваша цель, которую вы получаете, имеет тип: be.fgov.just.cjr.dto.FactCodeDto, который не является картой.   -  person Bilbo Baggins    schedule 30.10.2015
comment
Да, я знаю, но я хочу получить список типа FactCodeDto и «сказать» ему перейти в спящий режим через setResultTransformer(Transformers.aliasToBean(FactCodeDto.class)) но затем (по какой-то странной причине) Hibernate внутренне пытается привести мой FactCodeDto. класс (цель) на карту... но он не должен этого делать, почему он это делает?   -  person Bamboomy    schedule 30.10.2015
comment
зачем использовать все это раздувание, когда простой hql или критерии решат это?   -  person javaguest    schedule 30.10.2015
comment
Зачем вы усложняете, я не могу понять. Используйте обычный запрос HQL, например Query query = session.createQuery(из FactCode как FC); или какой бы ни был ваш запрос, и запустите его. Раздутый было бы слово.   -  person We are Borg    schedule 30.10.2015
comment
@Bamboomy, можешь показать код для класса be.fgov.just.cjr.dto.FactCodeDto?   -  person kucing_terbang    schedule 30.10.2015


Ответы (9)


Я обнаружил, что AliasToBean изменился в Hibernate 5. Для меня добавление геттера для моего поля решило проблему.

person Alex    schedule 23.11.2015
comment
для меня в аналогичных условиях мне нужно было изменить имя поля, скажем, с zipcode на ZipCode (он же верблюжий случай) и соответственно переименовать метод установки. но в любом случае текст исключения может сбить с толку.. - person ashirman; 05.07.2016

Это исключение возникает, когда сеттеры и геттеры неправильно сопоставлены с именами столбцов. Убедитесь, что у вас есть правильные геттеры и сеттеры для запроса (правильные имена и правильные типы данных). Об этом подробнее здесь:

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

person Jorciney    schedule 11.12.2017
comment
Этот ответ не дает решения, даже при правильном сопоставлении приведение не сработает. - person Bamboomy; 14.12.2017

Я провожу расследование по этому вопросу. Проблема в том, что Hibernate преобразует псевдонимы имен столбцов в верхний регистр — cdFact становится CDFACT.

Прочтите более подробное объяснение и обходной путь здесь: сопоставление результатов запроса Hibernate с пользовательским классом?

person v.ladynev    schedule 25.05.2016

В конце концов, найти решение оказалось не так уж сложно,

Я только что создал свой собственный (пользовательский) 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;
    }
}

в спящем режиме 3 вы можете установить псевдонимы для запросов, но вы больше не можете делать это в спящем режиме 5 (поправьте меня, если я ошибаюсь), поэтому aliasToBean - это то, что вы можете использовать только при фактическом использовании псевдонимов; чего я не делал, отсюда и исключение.

person Bamboomy    schedule 03.11.2015
comment
Можно использовать псевдонимы, см. мой ответ stackoverflow.com/a/37437567/3405171 - person v.ladynev; 26.05.2016
comment
А можно сделать более простой и универсальный трансформер, используя отражение. - person v.ladynev; 26.05.2016

Я мой случай:

 => 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 преобразует псевдонимы имен столбцов в верхний или нижний регистр.

person Sandip Solanki    schedule 27.11.2018

Я получал это исключение

Я назвал одно из полей в DTO как «closedIndexValue» после изменения его на «closedindexValue». Код работал нормально. Я думаю, это было связано с регистром буквы « я".

closedIndexValue -> неправильный верблюжий регистр closedindexValue -> правильный верблюжий регистр

Спящая версия: - 5.2.4

person Akshay Joshi    schedule 26.07.2017

Я решил это, определив свой собственный трансформатор, как указано ниже:

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

И использовал этот способ внутри метода репозитория -

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

Это сработало отлично !!!

person Bagesh Sharma    schedule 22.01.2020

Попробуйте написать имена столбцов и имена полей заглавными буквами.

person shashank joshi    schedule 19.03.2021

Это исключение возникает, когда класс, указанный в AliasToBeanResultTransformer, не имеет геттера для соответствующих столбцов. Хотя детали исключения из спящего режима вводят в заблуждение.

person Aman Kumayu    schedule 13.05.2021