将Hibernate查询结果Map到自定义类?

v6ylcynt  于 2023-10-23  发布在  其他
关注(0)|答案(9)|浏览(186)

关于我昨天发布的一个问题:How to populate POJO class from custom Hibernate query?
谁能给我一个例子,说明如何在Hibernate中编写下面的SQL,并得到正确的结果?
SQL:

select firstName, lastName
from Employee

我想做的是,如果在Hibernate中可能的话,把结果放在它们自己的基类中:

class Results {
    private firstName;
    private lastName;
    // getters and setters
}

我相信这在JPA中是可能的(使用EntityManager),但我还没有弄清楚如何在Hibernate中做到这一点(使用SessionFactorySession)。
我正试图更好地学习Hibernate,即使是这个“简单”的查询也被证明是令人困惑的,不知道Hibernate以什么形式返回结果,以及如何将结果Map到我自己的(基类)中。所以在DAO例程的最后,我会这样做:

List<Results> list = query.list();

返回ResultsList(我的基类)。

5kgi1eie

5kgi1eie1#

select firstName, lastName from Employee

query.setResultTransformer(Transformers.aliasToBean(MyResults.class));

你不能在Hibernate 5和Hibernate 4(至少是Hibernate 4.3.6.Final)中使用上面的代码,因为有一个异常。

java.lang.ClassCastException: com.github.fluent.hibernate.request.persistent.UserDto cannot be cast to java.util.Map
    at org.hibernate.property.access.internal.PropertyAccessMapImpl$SetterImpl.set(PropertyAccessMapImpl.java:102)

问题是Hibernate将列名的别名转换为大写--firstName变成了FIRSTNAME。并尝试使用这种策略在DTO中找到名为getFIRSTNAME()的getter和setter setFIRSTNAME()

PropertyAccessStrategyChainedImpl propertyAccessStrategy = new PropertyAccessStrategyChainedImpl(
            PropertyAccessStrategyBasicImpl.INSTANCE,
            PropertyAccessStrategyFieldImpl.INSTANCE,
            PropertyAccessStrategyMapImpl.INSTANCE
    );

在Hibernate看来,只有PropertyAccessStrategyMapImpl.INSTANCE适合。在此之后,它尝试进行转换(Map)MyResults

public void set(Object target, Object value, SessionFactoryImplementor factory) {
    ( (Map) target ).put( propertyName, value );
}

不知道,这是一个bug或功能。

如何解决
使用带引号的别名

public class Results {
    
    private String firstName;

    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

}

String sql = "select firstName as \"firstName\", 
    lastName as \"lastName\" from Employee";

List<Results> employees = session.createSQLQuery(sql).setResultTransformer(
    Transformers.aliasToBean(Results.class)).list();

使用自定义结果Transformer

解决这个问题的另一种方法--使用忽略方法名case的结果Transformer(将getFirstName()视为getFIRSTNAME())。你可以自己写或者使用FluentHibernateResultTransformer。不需要使用引号和别名(如果列名等于DTO名称)。
只需从项目页面下载库(它不需要额外的jar):fluent-hibernate

String sql = "select firstName, lastName from Employee";
List<Results> employees = session.createSQLQuery(sql)
        .setResultTransformer(new FluentHibernateResultTransformer(Results.class))
        .list();

此Transformer也可用于嵌套投影:How to transform a flat result set using Hibernate

yuvru6vn

yuvru6vn2#

参见AliasToBeanResultTransformer
结果转换器Transformer,允许将结果转换为用户指定的类,该类将通过setter方法或匹配别名的字段填充。

List resultWithAliasedBean = s.createCriteria(Enrolment.class)
            .createAlias("student", "st")
            .createAlias("course", "co")
            .setProjection( Projections.projectionList()
                    .add( Projections.property("co.description"), "courseDescription" )
            )
            .setResultTransformer( new AliasToBeanResultTransformer(StudentDTO.class) )
            .list();

StudentDTO dto = (StudentDTO)resultWithAliasedBean.get(0);

您修改的代码:

List resultWithAliasedBean = s.createCriteria(Employee.class, "e")
    .setProjection(Projections.projectionList()
        .add(Projections.property("e.firstName"), "firstName")
        .add(Projections.property("e.lastName"), "lastName")
    )
    .setResultTransformer(new AliasToBeanResultTransformer(Results.class))
    .list();

Results dto = (Results) resultWithAliasedBean.get(0);

对于原生SQL查询,请参阅Hibernate文档:

13.1.5.返回非托管实体

可以将ResultTransformer应用于原生SQL查询,允许它返回非托管实体。

sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
    .setResultTransformer(Transformers.aliasToBean(CatDTO.class))

此查询指定:

  • SQL查询字符串
  • a result transformer Transformer上面的查询将返回一个CatDTO的列表,该列表已被示例化并将NAME和BIRTHNAME的值注入到其相应的属性或字段中。
3hvapo4f

3hvapo4f3#

你需要使用一个构造函数,并在hql中使用new。我让你的代码示例取自这个问题:hibernate HQL createQuery() list() type cast to model directly

class Result {
    private firstName;
    private lastName;
    public Result (String firstName, String lastName){
      this.firstName = firstName;
      this.lastName = lastName;
   }
}

那么你的总部

select new com.yourpackage.Result(employee.firstName,employee.lastName) 
from Employee

Java(使用Hibernate)

List<Result> results = session.createQuery("select new com.yourpackage.Result(employee.firstName,employee.lastName) from Employee").list();
x7rlezfr

x7rlezfr4#

YMMV,但我发现关键因素是你必须确保在SELECT子句中的每个字段都使用SQL“AS”关键字作为别名。我从来不用在化名前加引号。此外,在SELECT子句中使用数据库中实际列的大小写和标点符号,在别名中使用POJO中字段的大小写。这在Hibernate 4和5中很有用。

@Autowired
private SessionFactory sessionFactory;

...

String sqlQuery = "SELECT firstName AS firstName," +
        "lastName AS lastName from Employee";

List<Results> employeeList = sessionFactory
        .getCurrentSession()
        .createSQLQuery(sqlQuery)
        .setResultTransformer(Transformers.aliasToBean(Results.class))
        .list();

如果你有多个表,你也可以在SQL中使用表别名。这个带有一个名为“Department”的附加表的人为示例在数据库字段名中使用了更传统的小写和下划线,而在POJO字段名中使用了 Camel 大小写。

String sqlQuery = "SELECT e.first_name AS firstName, " +
        "e.last_name AS lastName, d.name as departmentName" +
        "from Employee e, Department d" +
        "WHERE e.department_id - d.id";

List<Results> employeeList = sessionFactory
        .getCurrentSession()
        .createSQLQuery(sqlQuery)
        .setResultTransformer(Transformers.aliasToBean(Results.class))
        .list();
qv7cva1a

qv7cva1a5#

java.lang.ClassCastException: "CustomClass" cannot be cast to java.util.Map.
当SQL查询中指定的列与Map类的列不匹配时,会出现此问题。
这可能是由于:

  • 列名的大小写不匹配,或
  • 列名不匹配或
  • 列存在于查询中,但在类中缺失。
pu82cl6c

pu82cl6c6#

Hibernate 5.4中的JPQL案例:

Query<Employee> queryList = session.createQuery("select new xxx.xxx.Employee(e.firstName,e.lastName) from Employee e", Employee.class);
List<Employee> list = queryList.list();

Query<Long> queryCount = session.createQuery("select count(*) from Employee", Long.class);
Long count = queryCount.getSingleResult();
  • JPQL中的select语句与HQL完全相同,只是JPQL需要select_clause,而HQL不需要。
  • setResultTransformer @Deprecated不应使用
  • 更多关于Hibernate_User_Guide.html#hql-select
4dc9hkyq

4dc9hkyq7#

如果你有一个原生查询,这里的所有答案都使用了较新版本的Hibernate的过时方法,所以如果你使用的是5.1+,这是一条路要走:

// Note this is a org.hibernate.query.NativeQuery NOT Query.
NativeQuery query = getCurrentSession()
                .createNativeQuery(
                        "SELECT {y.*} , {x.*} from TableY y left join TableX x on x.id = y.id");

// This maps the results to entities. 
query.addEntity("x", TableXEntity.class);
query.addEntity("y", TableYEntity.class);

query.list()
0wi1tuuw

0wi1tuuw8#

下面是一个忽略大小写的结果Transformer:

package org.apec.abtc.dao.hibernate.transform;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyChainedImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyFieldImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;

/**
 * IgnoreCaseAlias to BeanResult Transformer
 * 
 * @author Stephen Gray
 */
public class IgnoreCaseAliasToBeanResultTransformer extends AliasedTupleSubsetResultTransformer
{

    /** The serialVersionUID field. */
    private static final long serialVersionUID = -3779317531110592988L;

    /** The resultClass field. */
    @SuppressWarnings("rawtypes")
    private final Class resultClass;
    /** The setters field. */
    private Setter[] setters;
    /** The fields field. */
    private Field[] fields;
    private String[] aliases;

    /**
     * @param resultClass
     */
    @SuppressWarnings("rawtypes")
    public IgnoreCaseAliasToBeanResultTransformer(final Class resultClass)
    {
        if (resultClass == null)
        {
            throw new IllegalArgumentException("resultClass cannot be null");
        }
        this.resultClass = resultClass;
        this.fields = this.resultClass.getDeclaredFields();
    }

    @Override
    public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object transformTuple(final Object[] tuple, final String[] aliases)
    {
        Object result;

        try
        {
            if (this.setters == null)
            {
                this.aliases = aliases;

                setSetters(aliases);
            }
            result = this.resultClass.newInstance();

            for (int i = 0; i < aliases.length; i++)
            {
                if (this.setters[i] != null)
                {
                    this.setters[i].set(result, tuple[i], null);
                }
            }
        }
        catch (final InstantiationException | IllegalAccessException e)
        {
            throw new HibernateException("Could not instantiate resultclass: " + this.resultClass.getName(), e);
        }

        return result;
    }

    private void setSetters(final String[] aliases)
    {
        PropertyAccessStrategyChainedImpl propertyAccessStrategy = new PropertyAccessStrategyChainedImpl(
                                                                                                         PropertyAccessStrategyBasicImpl.INSTANCE,
                                                                                                         PropertyAccessStrategyFieldImpl.INSTANCE,
                                                                                                         PropertyAccessStrategyMapImpl.INSTANCE
                                                                        );

        this.setters = new Setter[aliases.length];
        for (int i = 0; i < aliases.length; i++)
        {
            String alias = aliases[i];
            if (alias != null)
            {
                for (final Field field : this.fields)
                {
                    final String fieldName = field.getName();
                    if (fieldName.equalsIgnoreCase(alias))
                    {
                        alias = fieldName;
                        break;
                    }
                }
                setters[i] = propertyAccessStrategy.buildPropertyAccess( resultClass, alias ).getSetter();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("rawtypes")
    public List transformList(final List collection)
    {
        return collection;
    }

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

        IgnoreCaseAliasToBeanResultTransformer that = ( IgnoreCaseAliasToBeanResultTransformer ) o;

        if ( ! resultClass.equals( that.resultClass ) ) {
            return false;
        }
        if ( ! Arrays.equals( aliases, that.aliases ) ) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = resultClass.hashCode();
        result = 31 * result + ( aliases != null ? Arrays.hashCode( aliases ) : 0 );
        return result;
    }
}
ubbxdtey

ubbxdtey9#

写作(存在这种类型的挑战与hibernate工作)
1.自定义查询
1.带有可选参数的自定义配置文件
1.将Hibernate自定义查询结果Map到自定义类。
我不是说自定义的JpaRepository接口,它扩展了SpringBoot上的JpaRepository,你可以用@Query ->编写自定义的Query,这里你不能用可选的参数编写查询,例如。如果param为null,则不将其追加到查询字符串中。你可以使用Hibernate的Criteria API,但由于性能问题,在他们的文档中不推荐使用。
但存在简单易出错且性能好的方法。
编写自己的QueryService类,这些方法将获取字符串(第一个和第二个问题的答案)SQL,并将结果Map到自定义类(第三个问题),其中包含任何关联@OneToMany,@ManyToOne..

@Service
@Transactional
public class HibernateQueryService {

    private final Logger log = LoggerFactory.getLogger(HibernateQueryService.class);
    private JpaContext jpaContext;

    public HibernateQueryService(JpaContext jpaContext) {
        this.jpaContext = jpaContext;
    }

    public List executeJPANativeQuery(String sql, Class entity){
        log.debug("JPANativeQuery executing: "+sql);
        EntityManager entityManager = jpaContext.getEntityManagerByManagedType(Article.class);
        return entityManager.createNativeQuery(sql, entity).getResultList();
    }

/**
 * as annotation @Query -> we can construct here hibernate dialect 
 * supported query and fetch any type of data
 * with any association @OneToMany and @ManyToOne.....
 */
    public List executeHibernateQuery(String sql, Class entity){
        log.debug("HibernateNativeQuery executing: "+sql);
        Session session = jpaContext.getEntityManagerByManagedType(Article.class).unwrap(Session.class);
        return session.createQuery(sql, entity).getResultList();
    }

public <T> List<T> executeGenericHibernateQuery(String sql, Class<T> entity){
    log.debug("HibernateNativeQuery executing: "+sql);
    Session session = jpaContext.getEntityManagerByManagedType(Article.class).unwrap(Session.class);
    return session.createQuery(sql, entity).getResultList();
}

}

用例-您可以编写关于查询参数的任何类型条件

@Transactional(readOnly = true)
    public List<ArticleDTO> findWithHibernateWay(SearchFiltersVM filter){

        Long[] stores = filter.getStores();
        Long[] categories = filter.getCategories();
        Long[] brands = filter.getBrands();
        Long[] articles = filter.getArticles();
        Long[] colors = filter.getColors();

        String query = "select article from Article article " +
            "left join fetch article.attributeOptions " +
            "left join fetch article.brand " +
            "left join fetch article.stocks stock " +
            "left join fetch stock.color " +
            "left join fetch stock.images ";

boolean isFirst = true;

        if(!isArrayEmptyOrNull(stores)){
            query += isFirst ? "where " : "and ";
            query += "stock.store.id in ("+ Arrays.stream(stores).map(store -> store.toString()).collect(Collectors.joining(", "))+") ";
            isFirst = false;
        }

        if(!isArrayEmptyOrNull(brands)){
            query += isFirst ? "where " : "and ";
            query += "article.brand.id in ("+ Arrays.stream(brands).map(store -> store.toString()).collect(Collectors.joining(", "))+") ";
            isFirst = false;
        }

        if(!isArrayEmptyOrNull(articles)){
            query += isFirst ? "where " : "and ";
            query += "article.id in ("+ Arrays.stream(articles).map(store -> store.toString()).collect(Collectors.joining(", "))+") ";
            isFirst = false;
        }

        if(!isArrayEmptyOrNull(colors)){
            query += isFirst ? "where " : "and ";
            query += "stock.color.id in ("+ Arrays.stream(colors).map(store -> store.toString()).collect(Collectors.joining(", "))+") ";
        }

        List<Article> articles = hibernateQueryService.executeHibernateQuery(query, Article.class);

      /**
        *  MapStruct [http://mapstruct.org/][1]
        */
        return articles.stream().map(articleMapper::toDto).collect(Collectors.toList());

    }

相关问题