Hibernate:如何使用 HQL 设置 NULL 查询参数值?

发布于 2024-08-19 03:19:56 字数 719 浏览 5 评论 0 原文

如何将 Hibernate 参数设置为“null”?示例:

Query query = getSession().createQuery("from CountryDTO c where c.status = :status  and c.type =:type")
.setParameter("status", status, Hibernate.STRING)
.setParameter("type", type, Hibernate.STRING);

在我的例子中,状态字符串可以为 null。我已经调试了这个,然后 hibernate 生成一个像这样的 SQL 字符串/查询 ....status = null... 然而,这在 MYSQL 中不起作用,因为正确的 SQL 语句必须是“status is null”(Mysql 不理解 status=null 并将其计算为 false,以便根据我读过的 mysql 文档,查询不会返回任何记录...)

我的问题:

  1. < p>为什么 Hibernate 不将 null 字符串正确翻译为“is null”(而是错误地创建“=null”)?

  2. 重写此查询以使其空安全的最佳方法是什么?对于 nullsafe,我的意思是,在“status”字符串为 null 的情况下,它应该创建一个“is null”?

How can I set a Hibernate Parameter to "null"? Example:

Query query = getSession().createQuery("from CountryDTO c where c.status = :status  and c.type =:type")
.setParameter("status", status, Hibernate.STRING)
.setParameter("type", type, Hibernate.STRING);

In my case, the status String can be null. I have debugged this and hibernate then generates an SQL string/query like this ....status = null... This however does not Work in MYSQL, as the correct SQL statement must be "status is null" (Mysql does not understand status=null and evaluates this to false so that no records will ever be returned for the query, according to the mysql docs i have read...)

My Questions:

  1. Why doesnt Hibernate translate a null string correctly to "is null" (and rather and wrongly creates "=null")?

  2. What is the best way to rewrite this query so that it is null-safe? With nullsafe I mean that in the case that the "status" String is null than it should create an "is null"?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

夜声 2024-08-26 03:19:56
  1. 我相信 hibernate 首先将您的 HQL 查询转换为 SQL,然后才尝试绑定您的参数。这意味着它将无法将查询从 param = ? 重写为 param is null

  2. 尝试使用 Criteria api:

    Criteria c = session.createCriteria(CountryDTO.class);
    c.add(Restrictions.eq("type", type));
    c.add(status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status));
    列表结果 = c.list();
    
  1. I believe hibernate first translates your HQL query to SQL and only after that it tries to bind your parameters. Which means that it won't be able to rewrite query from param = ? to param is null.

  2. Try using Criteria api:

    Criteria c = session.createCriteria(CountryDTO.class);
    c.add(Restrictions.eq("type", type));
    c.add(status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status));
    List result = c.list();
    
笑饮青盏花 2024-08-26 03:19:56

这不是一个 Hibernate 特定问题(它只是 SQL 性质),是的,有一个针对 SQL 和 HQL 的解决方案:

@Peter Lang 有正确的想法,并且您有正确的 HQL 查询。我想您只需要一次新的干净运行来获取查询更改;-)

下面的代码绝对有效,如果您将所有查询保留在来自 CountryDTO c 的 orm.xml 中,那么效果会很好,

其中 ((:status 为 null并且 c.status 为 null) 或 c.status = :status) 和 c.type =:type

如果您的参数 String 为 null,则查询将检查该行的状态是否也为 null。否则它将诉诸于与等号进行比较。

注释:

该问题可能是特定的 MySql 怪癖。我只测试过Oracle。

上面的查询假设存在 c.status 为 null 的表行。

where 子句具有优先级,因此首先检查参数。

参数名称“type”可能是 SQL 中的保留字,但这并不重要,因为它在查询运行之前被替换。

如果您需要完全跳过 :status where_clause;您可以像这样编码:

from CountryDTO c where (:status is null or c.status = :status) and c.type =:type

它相当于:

sql.append(" where ");
if(status != null){
  sql.append(" c.status = :status and ");
}
sql.append(" c.type =:type ");

This is not a Hibernate specific issue (it's just SQL nature), and YES, there IS a solution for both SQL and HQL:

@Peter Lang had the right idea, and you had the correct HQL query. I guess you just needed a new clean run to pick up the query changes ;-)

The below code absolutely works and it is great if you keep all your queries in orm.xml

from CountryDTO c where ((:status is null and c.status is null) or c.status = :status) and c.type =:type

If your parameter String is null then the query will check if the row's status is null as well. Otherwise it will resort to compare with the equals sign.

Notes:

The issue may be a specific MySql quirk. I only tested with Oracle.

The above query assumes that there are table rows where c.status is null

The where clause is prioritized so that the parameter is checked first.

The parameter name 'type' may be a reserved word in SQL but it shouldn't matter since it is replaced before the query runs.

If you needed to skip the :status where_clause altogether; you can code like so:

from CountryDTO c where (:status is null or c.status = :status) and c.type =:type

and it is equivalent to:

sql.append(" where ");
if(status != null){
  sql.append(" c.status = :status and ");
}
sql.append(" c.type =:type ");
那支青花 2024-08-26 03:19:56

setParameter(String, Object) 的 javadoc 是明确的,表示 Object 值必须为非空。遗憾的是,如果传入 null,它不会抛出异常。

另一种方法是 setParameter(String, Object, Type),尽管确实允许空值我不确定什么 Type 参数在这里最合适。

The javadoc for setParameter(String, Object) is explicit, saying that the Object value must be non-null. It's a shame that it doesn't throw an exception if a null is passed in, though.

An alternative is setParameter(String, Object, Type), which does allow null values, although I'm not sure what Type parameter would be most appropriate here.

这个俗人 2024-08-26 03:19:56

看来您必须在 HQL 中使用 is null (如果有多个可能为 null 的参数,这可能会导致复杂的排列。)但这里是一种可能的解决方案:

String statusTerm = status==null ? "is null" : "= :status";
String typeTerm = type==null ? "is null" : "= :type";

Query query = getSession().createQuery("from CountryDTO c where c.status " + statusTerm + "  and c.type " + typeTerm);

if(status!=null){
    query.setParameter("status", status, Hibernate.STRING)
}


if(type!=null){
    query.setParameter("type", type, Hibernate.STRING)
}

It seems you have to use is null in the HQL, (which can lead to complex permutations if there are more than one parameters with null potential.) but here is a possible solution:

String statusTerm = status==null ? "is null" : "= :status";
String typeTerm = type==null ? "is null" : "= :type";

Query query = getSession().createQuery("from CountryDTO c where c.status " + statusTerm + "  and c.type " + typeTerm);

if(status!=null){
    query.setParameter("status", status, Hibernate.STRING)
}


if(type!=null){
    query.setParameter("type", type, Hibernate.STRING)
}
谷夏 2024-08-26 03:19:56

HQL 支持 合并,允许对于丑陋的解决方法,例如:

where coalesce(c.status, 'no-status') = coalesce(:status, 'no-status')

HQL supports coalesce, allowing for ugly workarounds like:

where coalesce(c.status, 'no-status') = coalesce(:status, 'no-status')
往事随风而去 2024-08-26 03:19:56

我没有尝试这样做,但是当您使用 :status 两次检查 NULL 时会发生什么?

Query query = getSession().createQuery(
     "from CountryDTO c where ( c.status = :status OR ( c.status IS NULL AND :status IS NULL ) ) and c.type =:type"
)
.setParameter("status", status, Hibernate.STRING)
.setParameter("type", type, Hibernate.STRING);

I did not try this, but what happens when you use :status twice to check for NULL?

Query query = getSession().createQuery(
     "from CountryDTO c where ( c.status = :status OR ( c.status IS NULL AND :status IS NULL ) ) and c.type =:type"
)
.setParameter("status", status, Hibernate.STRING)
.setParameter("type", type, Hibernate.STRING);
寂寞清仓 2024-08-26 03:19:56

对于实际的 HQL 查询:

FROM Users WHERE Name IS NULL

For an actual HQL query:

FROM Users WHERE Name IS NULL
偏爱你一生 2024-08-26 03:19:56

您可以使用

Restrictions.eqOrIsNull("status", status)

插入的

status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status)

You can use

Restrictions.eqOrIsNull("status", status)

insted of

status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status)
冰之心 2024-08-26 03:19:56

这是我在 Hibernate 4.1.9 上找到的解决方案。我必须将一个参数传递给我的查询,有时该参数的值为 NULL。所以我通过了 using:

setParameter("orderItemId", orderItemId, new LongType())

之后,我在查询中使用了以下 where 子句:

where ((:orderItemId is null) OR (orderItem.id != :orderItemId))

如您所见,我正在使用 Query.setParameter(String, Object, Type) 方法,其中我无法使用 Hibernate.LONG我在文档中找到的(可能是旧版本的)。有关类型参数的完整选项集,请查看 org.hibernate.type.Type 接口的实现类列表。

希望这有帮助!

Here is the solution I found on Hibernate 4.1.9. I had to pass a parameter to my query that can have value NULL sometimes. So I passed the using:

setParameter("orderItemId", orderItemId, new LongType())

After that, I use the following where clause in my query:

where ((:orderItemId is null) OR (orderItem.id != :orderItemId))

As you can see, I am using the Query.setParameter(String, Object, Type) method, where I couldn't use the Hibernate.LONG that I found in the documentation (probably that was on older versions). For a full set of options of type parameter, check the list of implementation class of org.hibernate.type.Type interface.

Hope this helps!

可爱暴击 2024-08-26 03:19:56

可能有人正在寻找这个

sqlQuery.setParameter("paramName", null, StandardBasicTypes.INTEGER)

May be someone are looking for this

sqlQuery.setParameter("paramName", null, StandardBasicTypes.INTEGER)
狼性发作 2024-08-26 03:19:56

这似乎也有效 ->

@Override
public List<SomeObject> findAllForThisSpecificThing(String thing) {
    final Query query = entityManager.createQuery(
            "from " + getDomain().getSimpleName() + " t  where t.thing = " + ((thing == null) ? " null" : " :thing"));
    if (thing != null) {
        query.setParameter("thing", thing);
    }
    return query.getResultList();
}

顺便说一句,我对此还很陌生,所以如果出于任何原因这不是一个好主意,请告诉我。谢谢。

this seems to work as wel ->

@Override
public List<SomeObject> findAllForThisSpecificThing(String thing) {
    final Query query = entityManager.createQuery(
            "from " + getDomain().getSimpleName() + " t  where t.thing = " + ((thing == null) ? " null" : " :thing"));
    if (thing != null) {
        query.setParameter("thing", thing);
    }
    return query.getResultList();
}

Btw, I'm pretty new at this, so if for any reason this isn't a good idea, let me know. Thanks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文