Java 解决大型列表中的限制的更好方法是什么?

Java 解决大型列表中的限制的更好方法是什么?,java,oracle,hibernate,Java,Oracle,Hibernate,已经确定,当您使用Hibernate的限制时,在(字符串属性,列表)中,必须限制列表的大小 这是因为数据库服务器可能无法处理长查询。除了调整数据库服务器的配置之外 以下是我找到的解决方案: 解决方案1:将列表拆分为较小的列表,然后将较小的列表分别添加到多个限制中。in public List<Something> findSomething(List<String> subCdList) { Criteria criteria = getSession().cre

已经确定,当您使用Hibernate的限制时,在(字符串属性,列表)中,必须限制列表的大小
这是因为数据库服务器可能无法处理长查询。除了调整数据库服务器的配置之外

以下是我找到的解决方案:

解决方案1:将列表拆分为较小的列表,然后将较小的列表分别添加到多个限制中。in

public List<Something> findSomething(List<String> subCdList) {
    Criteria criteria = getSession().createCriteria(getEntityClass());
    //if size of list is greater than 1000, split it into smaller lists. See List<List<String>> cdList
    if(subCdList.size() > 1000) {
        List<List<String>> cdList = new ArrayList<List<String>>();
        List<String> tempList = new ArrayList<String>();
        Integer counter = 0;
        for(Integer i = 0; i < subCdList.size(); i++) {
            tempList.add(subCdList.get(i));
            counter++;
            if(counter == 1000) {
                counter = 0;
                cdList.add(tempList);
                tempList = new ArrayList<String>();
            }
        }

        if(tempList.size() > 0) {
            cdList.add(tempList);
        }
        Criterion criterion = null;
        //Iterate the list of lists, add the restriction for smaller list
        for(List<String> cds : cdList) {
            if (criterion == null) {
                criterion = Restrictions.in("subCd", cds);
            } else {
                criterion = Restrictions.or(criterion, Restrictions.in("subCd", cds));
            }
        }
        criteria.add(criterion);
    } else {
        criteria.add(Restrictions.in("subCd", subCdList));
    }
    return criteria.list();
}
公共列表查找内容(列表子列表){
Criteria=getSession().createCriteria(getEntityClass());
//如果列表的大小大于1000,请将其拆分为较小的列表。请参阅列表cdList
如果(subCdList.size()>1000){
List cdList=new ArrayList();
List templast=new ArrayList();
整数计数器=0;
对于(整数i=0;i0){
添加(模板列表);
}
准则=null;
//迭代列表,为较小的列表添加限制
对于(列表CD:cdList){
如果(条件==null){
标准=限制。在(“subCd”,cds)中;
}否则{
标准=限制。或(标准,限制。in(“subCd”,cds));
}
}
标准。添加(标准);
}否则{
标准。添加(限制。在(“subCd”,subCdList));
}
返回条件。list();
}
这是一个不错的解决方案,因为您只有一个select语句。然而,我认为在DAO层上使用for循环是个坏主意,因为我们不希望连接长时间处于打开状态

解决方案2:使用分离标准。不要传递列表,而是在WHERE子句上查询它

public List<Something> findSomething() {

    Criteria criteria = getSession().createCriteria(getEntityClass());

    DetachedCriteria detached = DetachedCriteria.forClass(DifferentClass.class);
    detached.setProjection(Projections.property("cd"));

    criteria.add(Property.forName("subCd").in(detached));

    return criteria.list();
}
public List findSomething(){
Criteria=getSession().createCriteria(getEntityClass());
DetachedCriteria detached=DetachedCriteria.forClass(DifferentitClass.class);
分离.setProjection(projects.property(“cd”));
标准。在(分离的)中添加(Property.forName(“subCd”);
返回条件。list();
}
此解决方案中的问题在于DetachedCriteria的技术使用。当您想要创建对当前类上完全未连接(或没有关系)的另一个类的查询时,通常会使用它。在本例中,Something.class有一个属性subCd,它是来自不同类的外键。另外,这将生成where子句的子查询

当您查看代码时:
1. 解决方案2更简单、简洁。
2.但是解决方案1只提供一个select查询。
请帮我决定哪一个更有效


谢谢。

对于解决方案1:您可以尝试以下操作,而不是使用For循环

为了避免这种情况,如果传递的参数值的数量大于1000,请使用实用程序方法构建Criteria查询IN子句

class HibernateBuildCriteria {

private static final int PARAMETER_LIMIT = 800;

public static Criterion buildInCriterion(String propertyName, List<?> values) {
      Criterion criterion = null;
      int listSize = values.size();
      for (int i = 0; i < listSize; i += PARAMETER_LIMIT) {
      List<?> subList;
      if (listSize > i + PARAMETER_LIMIT) {
             subList = values.subList(i, (i + PARAMETER_LIMIT));
      } else {
             subList = values.subList(i, listSize);
      }
      if (criterion != null) {
       criterion = Restrictions.or(criterion, Restrictions.in(propertyName, subList));
     } else {
       criterion = Restrictions.in(propertyName, subList);
     }
    }
     return criterion;
   }
 }     
类HibernateBuildCriteria{
私有静态最终int参数_LIMIT=800;
公共静态标准buildInCriterion(字符串属性名称,列表值){
准则=null;
int listSize=values.size();
for(int i=0;ii+参数\U限制){
子列表=值。子列表(i,(i+参数_限制));
}否则{
子列表=值。子列表(i,列表大小);
}
if(条件!=null){
criteria=Restrictions.or(criteria,Restrictions.in(propertyName,subList));
}否则{
标准=限制。in(propertyName,子列表);
}
}
返回准则;
}
}     
使用以下方法:

添加(HibernateBuildCriteria.buildInCriterion(propertyName,list))


希望这能有所帮助。

解决方案1有一个主要缺点:您可能会得到许多不同的准备好的语句,这些语句需要解析,执行计划需要计算和缓存。这个过程可能比数据库缓存了语句的查询的实际执行要昂贵得多。请参阅此了解更多详细信息

解决这个问题的方法是利用Hibernate用于批量获取延迟加载的关联实体。基本上,我使用
ArrayHelper.getBatchSizes
获取ID的子列表,然后对每个子列表执行单独的查询

解决方案2仅当您可以在子查询中投影ID时才适用。但是如果你不能,那么你就不能使用它。例如,应用程序的用户在屏幕上编辑了20个实体,现在他们正在保存更改。您必须按ID读取实体才能合并更改,并且不能在子查询中表达更改


然而,解决方案2的另一种方法是使用临时表。例如,用于批量操作的Hibernate。您可以将ID存储在临时表中,然后在子查询中使用它们。我个人认为这与解决方案1相比是不必要的麻烦(当然,对于这个用例来说,Hibernate的推理对于他们的用例来说是有好处的),但这是一个有效的选择。我认为您很好地重构了解决方案1。谢谢@迪奥尼德:当你在船上得到足够的分数时,你可以投票表决嗨,谢谢你的回答。很抱歉,我现在无法升级。您好,您能进一步解释一下为什么使用批量抓取算法更好吗?如果f