是否可以获得 Hibernate sqlRestriction 的连接表的 SQL 别名?

Posted

技术标签:

【中文标题】是否可以获得 Hibernate sqlRestriction 的连接表的 SQL 别名?【英文标题】:Is it possible to get the SQL alias of a join table for a Hibernate sqlRestriction? 【发布时间】:2011-01-21 16:07:25 【问题描述】:

我有一个 Person 类,它有一个 String 别名集合,表示该人可能经过的其他名称。例如,克拉克肯特可能有别名“超人”和“钢铁侠”。德怀特霍华德还有一个别名“超人”。

@Entity
class Person 

  @CollectionOfElements(fetch=FetchType.EAGER)
  Set<String> aliases = new TreeSet<String>();

Hibernate 在我的数据库中创建两个表,Person 和 Person_aliases。 Person_aliases 是一个包含列 Person_id 和元素的连接表。假设 Person_aliases 有以下数据

--------------------------------
| Person_id     | element      |
--------------------------------
| Clark Kent    | Superman     |
| Clark Kent    | Man of Steel |
| Dwight Howard | Superman     |
| Bruce Wayne   | Batman       |
--------------------------------

我想对所有别名为“超人”的人进行休眠条件查询。

由于此处列出的原因太长,我真的很想将其设为 Criteria 查询,而不是 HQL 查询(除非可以在 Criteria 对象上添加 HQL 限制,在这种情况下我会全力以赴)或原始 SQL 查询。因为根据How do I query for objects with a value in a String collection using Hibernate Criteria?,不可能使用 CriteriaAPI 引用值类型集合的元素,所以我想我会在我的条件对象上添加一个 SqlRestriction。

Criteria crit = session.createCriteria(Person.class);
crit.add(Restrictions.sqlRestriction("XXXXX.element='superman'");

希望 Hibernate 会创建类似的 SQL 语句

    select *
from
    Person this_ 
left outer join
    Person_aliases aliases2_ 
        on this_.id=aliases2_.Person_id 
where
    XXXXX.element='superman' 

但是,我需要在 SQL 查询中使用 Person_aliases 表的表别名填写 XXXXX,在本例中为“aliases2_”。我注意到如果我需要对 Person 表别名的引用,我可以使用 alias。但这不起作用,因为 Person 是此 Criteria 的主表,而不是 Person_aliases。

我应该为 XXXXX 填写什么?如果没有像 alias 这样好的替代令牌,那么有没有办法让我休眠告诉我该别名将是什么?我注意到一个名为 generateAlias() org.hibernate.util.StringHelper 类的方法。这会帮助我预测别名是什么吗?

我真的非常想避免硬编码“aliases2_”。

感谢您的宝贵时间!

【问题讨论】:

【参考方案1】:

正如 xmedeko 所暗示的,当你想做的时候:

crit.add(Restrictions.sqlRestriction(
    "alias.joinedEntity.property='something'"));

你需要这样做:

crit.createCriteria("joinedEntity").add(Restrictions.sqlRestriction(
    "alias.property='something'"));

这已经为我解决了类似的问题,而无需使用 HQL

【讨论】:

我应该使用实际字符串 alias 还是将其替换为像 accountUser 这样的对象名称? 使用字符串 alias 它是一个休眠关键字,不幸的是它是在标准世界中使用别名的唯一方法。 这工作正常,但我找不到与此相关的文档。你认识一个吗? docs.jboss.org/hibernate/orm/3.5/reference/en/html/… - 取决于你的版本。 @pstanton 如果我有另一个别名怎么办?他叫什么名字【参考方案2】:

Criteria API 似乎不允许查询元素集合,请参阅HHH-869(仍然开放)。所以要么尝试建议的解决方法 - 我没有 - 要么切换到 HQL。以下 HQL 查询将起作用:

from Person p where :alias in elements(p.aliases)

【讨论】:

感谢您的回复 - 由于之前的 Stack Overflow 问题,我确实知道不幸的 Criteria API 限制,但希望通过 sqlRestriction 使用本机 SQL sn-p 可能会提供一个“hack”会让我们仍然使用标准。但是我想不出一种方法来获取对连接表名称的引用。如果没有办法,我会像你建议的那样求助于 HQL。但是,我不希望这会占用比我在这里解释的更多空间的原因(这个问题是我们实际查询的简化表示) @Jason 也许有可能,但 不知道如果不对表名进行硬编码。【参考方案3】:

尝试创建另一个标准,例如

Criteria crit = session.createCriteria(Person.class, "person");
Criteria subC = crit.createCriteria("Person_aliases", "Person_aliases");
subC.add(Restrictions.sqlRestriction("alias.element='superman'");

【讨论】:

【参考方案4】:

this link 可以帮助你吗?它建议:

List persons = sess.createCriteria(Person.class)
       .createCriteria("company")
       .add(Restrictions.sqlRestriction("companyName || name like (?)",  "%Fritz%", Hibernate.STRING))
       .list(); 

【讨论】:

在主类上调用 createCriteria() 方法来连接对象解决了我的问题。我在连接对象类的子条件中添加了所有连接字段,我的问题就解决了。【参考方案5】:

查看这个 Hibernate 错误并使用附件:

https://hibernate.atlassian.net/browse/HHH-2952 https://hibernate.atlassian.net/browse/HHH-2381

【讨论】:

【参考方案6】:

这个问题实际上已经很老了,但是由于我今天遇到了同样的问题并且没有答案满足我的需求,所以我根据 Brett Meyer 在HHH-6353 上的comment 提出了以下解决方案,即这个问题不会被修复。

基本上,我扩展了SQLCriterion 类,使其能够处理的不仅仅是基表别名。为方便起见,我编写了一个小型容器类,将用户给定的别名与匹配的子条件实例链接起来,以便能够用为子条件创建的别名 hibernate 替换用户给定的别名。

这里是MultipleAliasSQLCriterion类的代码

public class MultipleAliasSQLCriterion extends SQLCriterion

    /**
     * Convenience container class to pack the info necessary to replace the alias      generated at construction time
     * with the alias generated by hibernate
     */
    public static final class SubCriteriaAliasContainer
    
        /** The alias assigned at construction time */
        private String alias;

        /** The criteria constructed with the specified alias */
        private Criteria subCriteria;

        /**
         * @param aAlias
         *            - the alias assigned by criteria construction time
         * @param aSubCriteria
         *            - the criteria
         */
        public SubCriteriaAliasContainer(final String aAlias, final Criteria aSubCriteria)
        
            this.alias = aAlias;
            this.subCriteria = aSubCriteria;
        

        /**
         * @return String - the alias
         */
        public String getAlias()
        
            return this.alias;
        

        /**
         * @return Criteria - the criteria
         */
        public Criteria getSubCriteria()
        
            return this.subCriteria;
        
    

    private final SubCriteriaAliasContainer[] subCriteriaAliases;

    /**
     * This method constructs a new native SQL restriction with support for multiple aliases
     * 
     * @param sql
     *            - the native SQL restriction
     * @param aSubCriteriaAliases
     *            - the aliases
     */
    public MultipleAliasSQLCriterion(final String sql, final SubCriteriaAliasContainer... aSubCriteriaAliases)
    
        super(sql, ArrayHelper.EMPTY_OBJECT_ARRAY, ArrayHelper.EMPTY_TYPE_ARRAY);

        this.subCriteriaAliases = aSubCriteriaAliases;
    

    @Override
    public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException
    
        // First replace the alias of the base table alias
        String sql = super.toSqlString(criteria, criteriaQuery);

        if (!ArrayUtils.isEmpty(this.subCriteriaAliases))
        
            for (final SubCriteriaAliasContainer subCriteriaAlias : this.subCriteriaAliases)
            
                sql = StringHelper.replace(sql, subCriteriaAlias.getAlias(), criteriaQuery.getSQLAlias(subCriteriaAlias.getSubCriteria()));
            
        

        return sql;
    

我是这样用的

final String sqlRestriction = "...";
final String bankAccountAlias = "ba";
final Criteria bankAccountCriteria = customerCriteria.createCriteria("bankAccount", bankAccountAlias);

SubCriteriaAliasContainer bankAccountSubAliasCon = new SubCriteriaAliasContainer(bankAccountAlias, bankAccountCriteria);        

customerCriteria.add(new MultipleAliasSQLCriterion(sqlRestriction, bankAccountSubAliasCon));

但不需要在创建条件时指定别名 - 您也可以在 SQL 限制中指定别名并将其传递给容器。

final String sqlRestriction = "... VALUES(ba.status_date), (ba.account_number) ...";
final Criteria bankAccountCriteria = customerCriteria.createCriteria("bankAccount");

SubCriteriaAliasContainer bankAccountSubAliasCon = new SubCriteriaAliasContainer("ba", bankAccountCriteria);        

customerCriteria.add(new MultipleAliasSQLCriterion(sqlRestriction, bankAccountSubAliasCon));

【讨论】:

谢谢,这很有帮助! 我想知道您的示例中是否没有两个拼写错误。 bankAccountSubAliasCon 对象不应该传递给 MultipleAliasSQLCriterion 构造函数吗? 这是我使用你的实用程序的方式:` String hibernatePersonAlias = "p";条件 personCriteria = mainCriteria.createCriteria("person", hibernatePersonAlias);字符串 personSalarySqlCriterion = hibernatePersonAlias + ".salary > 1500"; MultipleAliasSQLCriterion.SubCriteriaAliasContainer personAliasContainer = new MultipleAliasSQLCriterion.SubCriteriaAliasContainer(hibernatePersonAlias, personCriteria); mainCriteria.add(new MultipleAliasSQLCriterion(personSalarySqlCriterion, personAliasContainer));` 是的,我想你可能是对的@Stephane。我将对其进行编辑以表示正确使用 嗨 Marius,我尝试做一个类似的实用程序***.com/questions/36172803/…,但它没有按预期工作。知道您可能对此有更好的理解,如果您有时间看一下,我会联系您。谢谢!【参考方案7】:

org.hibernate.criterion.CriteriaQuery 有一个方法 getColumnsUsingProjection,它为您提供别名列名。

您可以实现自己的Criterion,以org.hibernate.criterion.PropertyExpression 为例。

【讨论】:

【参考方案8】:
public class Products 
private Brands brand;
...

public class Brands 
private long id;
...

...

DetachedCriteria dc=DetachedCriteria.forClass(Products.class, "prod");

dc.add(Restrictions.ge("prod.brand.id", Long.parseLong("12345")));

【讨论】:

以上是关于是否可以获得 Hibernate sqlRestriction 的连接表的 SQL 别名?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以获得 Hibernate sqlRestriction 的连接表的 SQL 别名?

基础 - 解决 Hibernate / JDBC 连接池问题

配置NHibernate hibernate.cfg.xml文件以获得更多连接字符串

是否将Lazily提取的对象附加到Hibernate会话(由Hibernate支持的Spring Data)?

如何通过Hibernate即时获取更新的对象?

Hibernate 3中如何获得库表所有字段的名称