【问题标题】:Is it possible to get the SQL alias of a join table for a Hibernate sqlRestriction?是否可以获得 Hibernate sqlRestriction 的连接表的 SQL 别名?
【发布时间】: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_”。

感谢您的宝贵时间!

【问题讨论】:

    标签: java hibernate orm collections criteria


    【解决方案1】:

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

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

    你需要这样做:

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

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

    【讨论】:

    • 我应该使用实际字符串 {alias} 还是将其替换为像 accountUser 这样的对象名称?
    • 使用字符串 {alias} 它是一个休眠关键字,不幸的是它是在标准世界中使用别名的唯一方法。
    • 这工作正常,但我找不到与此相关的文档。你认识一个吗?
    • @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】:

      【讨论】:

        【解决方案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,我尝试做一个类似的实用程序stackoverflow.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")));
          

          【讨论】:

            猜你喜欢
            • 2014-08-02
            • 1970-01-01
            • 2013-12-02
            • 1970-01-01
            • 2011-05-03
            • 2012-08-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多