在hibernate的createSqlQuery()方法中,是否必须使用addScalar()方法?为什么在执行SQL查询时需要向hibernate指定数据类型?

4
String sql = "select Band.band_id bandId from guest_band Band";
   sessionFactory.getCurrentSession().createSQLQuery(sql)
    .addScalar("bandId", Hibernate.LONG)
    .list();

我了解到addScalar()用于指定hibernate所选项目的数据类型,这里是bandId。 但我的问题是,为什么我们需要向hibernate指定类型?它在内部执行什么操作?其次,如果我们不使用addScalar()会产生异常吗?最后,是否有其他替代方法可以实现这一点?

3个回答

3

虽然不是必须的,但使用以下内容会有所帮助:

来自https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querysql.html

最基本的SQL查询是获取标量(值)列表。

sess.createSQLQuery("SELECT * FROM CATS").list();
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();

这些代码将返回一个包含CATS表中每一列标量值的对象数组(Object[])列表。Hibernate将使用ResultSetMetadata来推断返回的标量值的实际顺序和类型。

为了避免使用ResultSetMetadata所带来的开销,或者更明确地指定返回的内容,可以使用addScalar()方法:

sess.createSQLQuery("SELECT * FROM CATS")
 .addScalar("ID", Hibernate.LONG)
 .addScalar("NAME", Hibernate.STRING)
 .addScalar("BIRTHDATE", Hibernate.DATE)

这个查询指定了以下内容:
SQL查询字符串 要返回的列和类型
这将返回对象数组,但现在它不会使用ResultSetMetadata,而是明确从底层结果集中获取ID、NAME和BIRTHDATE列,分别作为Long、String和Short。
这也意味着只有这三列将被返回,即使查询使用*并且可能返回超过这三列的其他列。
可以省略所有或部分标量的类型信息。
sess.createSQLQuery("SELECT * FROM CATS")
 .addScalar("ID", Hibernate.LONG)
 .addScalar("NAME")
 .addScalar("BIRTHDATE")

这本质上与之前的查询相同,但现在使用ResultSetMetaData来确定NAME和BIRTHDATE的类型,而ID的类型是明确指定的。

从ResultSetMetaData返回的java.sql.Types如何映射到Hibernate类型由Dialect控制。如果没有映射特定类型,或者没有产生预期的类型,则可以通过在Dialect中调用registerHibernateType来自定义它。


1

addScalar用于的一个简单示例:

public byte[] getFile(Integer id){
Query q = session
    .createSQLQuery("select some_file from tbl_name where id=:id")
            .addScalar("some_file", StandardBasicTypes.BINARY);
    q.setInteger("id", id);
    return (byte[]) q.uniqueResult();
}

例如,如果您的数据库中有blob数据类型,在这种情况下,您可以轻松地将结果转换为byte[],但是如果您在不使用addScalar函数的情况下运行查询,则会将结果作为blob返回,您无法直接将blob转换为byte[],您需要编写转换代码:
try{
    Blob blob =(Blob)q.uniqueResult();
    int blobLength = (int) blob.length();  
    byte[] blobAsBytes = blob.getBytes(1, blobLength);
    return blobAsBytes;
} catch (Exception e) {
    return null;
}

在这个问题中,使用addScalar会更容易。

0
据我所知,这并不是必需的。我从来没有使用过.addScalar编写查询。
你可以用类似下面的方式替换它:
Query q = sessionFactory.getCurrentSession().createQuery(
                "select b.band_id " +
                "from guest_band as b "
                );

List idList = q.list();

虽然这可能取决于您的实体设置方式,但应该可以工作。

也许.createSQLQuery.createQuery在这方面是不同的。

请参考此帖子,了解.addScalar()实际上是做什么的:What does addScalar do?

编辑:我熟悉Java,并且我想你在发布中使用的是Java。如果使用C#,则可能会有所不同。


感谢您的回复。但是在那个链接之前,我已经仔细阅读了。 但我的问题是,为什么我们需要为hibernate指定类型?它在内部执行什么操作?如果我们不添加addScalar()是否会出现异常?这是否有任何替代方法来实现?当您将createdSqlQuery()转换为createQuery()时,这并不是正确的转换,因为在createQuery()中,我们使用Java类名作为它执行hql,但是guest_band是DB表名,因为涉及到sql。所以我们不能从createSqlQuery转换为createQuery。据我所知,我们不使用createQuery()添加addScalar()。 - Deca
啊,我现在明白了,我不确定你的实体名称是什么,但这很有道理。那我可以问一下为什么你不使用实体而是使用数据库表名呢?至于你其他关于内部工作的问题,我不确定,抱歉。 - ballBreaker
我的问题是一般性的。我只是举了一个代码片段作为例子。关于内部工作,我想知道当Hibernate通过addScalar()确认数据类型时,它之后会执行什么操作。为什么需要告诉Hibernate这个信息? - Deca

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接