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

时间:2010-02-26 22:59:23

标签: java hibernate orm collections criteria

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

@Entity
class Person {

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

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

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

我想为所有使用“超人”别名的人制作一个hibernate Criteria查询。

由于这里列出的原因太长了,我真的想把它作为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}这样的好的替代令牌,那么我有没有办法让hibernate告诉我该别名会是什么?我注意到一个名为generateAlias()org.hibernate.util.StringHelper类的方法。这有助于我预测别名会是什么吗?

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

谢谢你的时间!

8 个答案:

答案 0 :(得分:9)

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

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

你需要改为:

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

这为我解决了类似的问题而没有使用HQL

答案 1 :(得分:4)

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

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

答案 2 :(得分:3)

答案 3 :(得分:3)

尝试创建另一个标准,如

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

答案 4 :(得分:2)

五月this link可以帮到你吗?它建议:

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

答案 5 :(得分:1)

这个问题实际上很老了,但是因为我今天遇到了同样的问题并且没有答案满足我的需求,我提出了以下解决方案,基于comment的Brett Meyer HHH-6353 ,这个问题不会得到解决。

基本上,我扩展了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));

答案 6 :(得分:0)

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

您可以使用Criterion作为示例来实现自己的org.hibernate.criterion.PropertyExpression

答案 7 :(得分:-4)

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")));