【问题标题】:Querying ManyToMany relationship with Hibernate Criteria使用 Hibernate Criteria 查询多对多关系
【发布时间】:2010-09-20 19:55:37
【问题描述】:

我不知道如何描述这个问题,所以我认为一个例子是问我问题的最佳方式:

我有两个具有多对多关系的表:

DriversLicence LicenseClass

LicenceClass 是“汽车”、“摩托车”和“中刚性”之类的东西。

使用 Hibernate Criteria,我如何找到同时具有“汽车”和“摩托车”LicenceClasses 的所有许可证?

2008 年 12 月 11 日更新 我发现这可以通过使用自定义 ResultTransformer 轻松实现。然而问题是结果转换器仅在查询返回其结果后应用,它实际上并没有成为 SQL 的一部分。所以我想我现在的问题是“你能做我最初在 SQL 中描述的事情吗?是否有 Hibernate Criteria 模拟?”

【问题讨论】:

    标签: java sql hibernate criteria many-to-many


    【解决方案1】:

    您仍然可以使用点符号来处理关系。例如,假设您有 DriversLicence.licenceClass 属性和 LicenceClass.type 属性,那么:

    session.createCriteria(DriversLicence.class)
       .add(Expression.or(
         Expression.eq("licenceClass.type", "Car"),
         Expression.eq("licenceClass.type", "Motorbike")
       )
    ).list();
    

    不过,我个人只是避免在这种情况下使用条件,因为它不是动态查询,而是使用:

    session.createQuery("from DriversLicence where licenceClass.type in (:types)")
      .setParameterList("types", myListOfTypes)
      .list();
    

    【讨论】:

    • 我认为这行不通。我想找到所有同时“汽车”和“摩托车”LicenceClasses 的许可证。都不是
    • 啊……好点子。虽然不是最有效的,但您可以: select dl from DriversLicence dl join d.licenceType lt where "Car" = some elements(lt.type) and "Motorbike" = some elements(lt.type) 这也可以更改为使用标准(超出字符!)
    • 是的,这正是我想要的——但你如何根据标准来做到这一点?如果您愿意,请发布另一个答案!
    【解决方案2】:

    '我认为这行不通。我想查找所有同时“汽车”和“摩托车”的许可证

    在 Nick 提供的 sn-p 中使用 Expression.and(....) 代替 Expression.or(....)

    【讨论】:

    • 不,这也不正确。我开始认为这个看似简单的用例在 hibernate 中是不可能的 - 有人愿意确认并解释原因吗?
    【解决方案3】:

    我遇到了类似的问题,但使用 HQL 进行了修复,我有一个与“用户”类相关的“企业”类,也与“角色”类相关,当我需要所有这些时,它们具有多对多的关系与特定用户相关的企业我执行以下操作;

    Select e from Enterprise As e inner join e.Users As u inner join u.Roles As r 
    Where u.UserCode=?
    

    我想在你的情况下你应该做类似的事情;

    Select dl from LicenceClass As l inner join l.DriversLicences As dl
    Where 
    l.LicenseClass.Name = ? OR 
    l.LicenseClass.Name=? OR 
    l.LicenseClass.Name=?
    

    希望对你有帮助。

    【讨论】:

      【解决方案4】:

      这是我最终使用 HQL 实现它的方法:

      public List<DriversLicence> findDriversLicencesWith(List<LicenceClass> licenceClasses) {
          String hqlString = "select dl from DriversLicenceImpl dl where 1=1 ";
          for (int i = 0; i < licenceClasses.size(); i++) {
              hqlString += " and :licenceClass" + i + " = some elements(dl.licenceClasses)";
          }
      
          Query query = getSession().createQuery(hqlString);
          for (int i = 0; i < licenceClasses.size(); i++) {
              query.setParameter("licenceClass" + i, licenceClasses.get(i));
          }
          return query.list();
      }
      

      或者使用带有 sqlRestriction 的 Hibernate Criteria:

      for (LicenceClass licenceClass : licenceClasses) {               
          criteria.add(Restrictions.sqlRestriction("? = some(select " + LicenceClass.PRIMARY_KEY + " from " +
                          LICENCE_CLASS_JOIN_TABLE + "  where {alias}." +
                          DriversLicence.PRIMARY_KEY + " = " + DriversLicence.PRIMARY_KEY + ")",
                          licenceClass.getId(), Hibernate.LONG));
      }
      

      LICENCE_CLASS_JOIN_TABLE 是 hibernate 生成的表的名称,用于支持 driversLicence 和 LicenceClass 之间的多对多关系。

      【讨论】:

        【解决方案5】:

        另一种选择是链式连接(每个 LicenseClass 一个连接)。我使用了这样的标准构建器和谓词

         List<Predicate> predicates = new ArrayList<>();
         for(Integer lcId : licenceClassIdList) {
             SetJoin<DriversLicence, LicenceClass> dlClasses = dlRoot.join(DriversLicence_.licenceClasses);
             predicates.add(builder.equal(dlClasses.get(LicenseClass_.id), lcId));
         }
         Predicate predicate = builder.and(predicates.toArray(new Predicate[predicates.size()]));
        

        注意 dlRoot 是 Root 类的对象,您可以从 CriteriaQuery 类中获取它。结果谓词就是您要查找的内容...

        【讨论】:

          猜你喜欢
          • 2010-10-17
          • 1970-01-01
          • 2010-12-18
          • 1970-01-01
          • 2013-07-16
          • 2015-07-20
          • 1970-01-01
          • 2023-04-07
          • 2017-01-20
          相关资源
          最近更新 更多