Java 如何优化hibernate方法在循环中的调用?

Java 如何优化hibernate方法在循环中的调用?,java,spring,hibernate,optimization,jpa,Java,Spring,Hibernate,Optimization,Jpa,我有一个使用spring+hibernate构建的java web应用程序 我有这样的代码: for (Account account : accountList){ Client client = clientService.findById(account.getFkClient()); // fkClient is foreign key to Client if (client != null) { ... anObject.setN

我有一个使用spring+hibernate构建的java web应用程序

我有这样的代码:

for (Account account : accountList){

    Client client = clientService.findById(account.getFkClient());  // fkClient is foreign key to Client

    if (client != null) {
        ...
        anObject.setName(client.getName());
        anObject.setAccountNo(account.getAccountNo());  
        ...
    }
    else {
        ...
        anObject.setAccountNo(account.getAccountNo());
        ...
    }

    ...
}
accountList是从hibernate调用中检索到的帐户实体的列表。在for循环中,使用hibernate调用InsideclientService.findById方法从帐户中检索客户机实体

这些是调用所涉及的类:

public class ClientService implements  IClientService {
    private IClientDAO clientDAO;

    ...

    @Override
    public Client findById(Long id) throws Exception {
        return clientDAO.findById(id);
    }
}

public class ClientDAO extends AbstractHibernateDAO<Client, Long> implements IClientDAO {

    @Override
    public Client findById(Long id) throws Exception {
        return super.findById(id);
    }
}

public class AbstractHibernateDAO<T,Y extends Serializable> extends HibernateDaoSupport {

    protected Class<T> domainClass = getDomainClass();

    private Class<T> getDomainClass() {
        if (domainClass == null) {
            ParameterizedType thisType = (ParameterizedType) getClass().getGenericSuperclass();
            domainClass = (Class<T>) thisType.getActualTypeArguments()[0];
        }
        return domainClass;
    }

    public T findById(final Y id) throws SystemException {
        return (T) this.execute(new HibernateCallback<T>() {

            @Override
            public T doInHibernate(Session session) throws HibernateException, SQLException {
                return (T) session.get(domainClass, id);
            }
        });
    }
}
public类ClientService实现IClientService{
私人IClientDAO客户端;
...
@凌驾
公共客户端findById(长id)引发异常{
返回clientDAO.findById(id);
}
}
公共类ClientDAO扩展了抽象HibernateDAO实现了IClientDAO{
@凌驾
公共客户端findById(长id)引发异常{
返回super.findById(id);
}
}
公共类AbstractHibernateDAO扩展了HibernateDaoSupport{
受保护类domainClass=getDomainClass();
私有类getDomainClass(){
if(domainClass==null){
ParameteredType thisType=(ParameteredType)getClass().getGenericSuperclass();
domainClass=(类)thisType.getActualTypeArguments()[0];
}
返回域类;
}
公共T findById(最终Y id)引发系统异常{
返回(T)this.execute(新的HibernateCallback(){
@凌驾
public T doInHibernate(会话会话)抛出HibernateeException、SQLException{
return(T)session.get(domainClass,id);
}
});
}
}
注意:clientService和clientDAO是SpringBeans对象

我的问题是如何使用hibernate优化循环中的clientService.findById?我觉得findById调用会使循环过程变慢

accountList通常包含7000多条记录,因此我需要类似预编译查询机制的东西,就像jdbc中的PreparedStatements一样。使用hibernate可以做到这一点吗


注意:上面的代码已经通过删除不相关的部分进行了简化,出于隐私原因,方法、变量和类名都是虚构的。如果您发现输入错误,请在注释部分告诉我,因为我是手动键入代码的。

在Hibernate/JPA中,您可以使用Hibernate查询语言/JPA查询语言编写查询并创建NamedQuery。在服务器启动时编译NAMEDQUEY,这样您就可以将其视为某种准备好的语句。 您可以尝试编写HQL查询,它可以通过单个查询获取所有实体实例

我将用JPQL为您提供一个示例,但您也可以用HQL编写它

@NamedQueries({
    @NamedQuery(name = "QUERY_BY_ID",
    query = "SELECT u from SomeEntity se WHERE se.id IN (:idList)"),
})
class SomeEntity {
}

class SomeEntityDao {
    public List<SomeEntity> findIdList(List<Long> idList) {
        Query query = entityManager.createNamedQuery("QUERY_BY_ID");
        query.setParameter("idList", idList);

        return query.getResultList();
    }
}
@namedquerys({
@NamedQuery(name=“按\u ID查询”,
query=“从某个实体se中选择u,其中se.id位于(:idList)”,
})
类实体{
}
类sometentitydao{
公共列表findIdList(列表idList){
Query Query=entityManager.createNamedQuery(“Query_BY_ID”);
query.setParameter(“idList”,idList);
返回query.getResultList();
}
}

不清楚您想要实现什么。我不会在循环中进行服务调用。为什么不使用
命名查询
? 检索附加到给定
帐户的所有
客户端
,然后迭代该
客户端列表

SELECT c from Client c JOIN c.account a WHERE a.id IN (:accounIds)
但这确实取决于业务需求! 我也不清楚你为什么不打电话:

Client client = account.getClient();

您可能希望加载已获取的客户端的accountList。使用即时抓取,或。如果
帐户
实体不包含
客户
,您应该有很好的理由。

我找到了最佳解决方案。我将从表Account和客户机中选择列的查询合并到一个视图(View\u Account\u Client),然后为视图创建实体类(AccountClientView),并使用hibernate获取它,结果是wow,它大大提高了性能。使用真实代码,完成循环可能需要15-20分钟,但使用View,只需要8-10秒

@Entity
@Table(name = "VIEW_ACCOUNT_CLIENT")
public class AccountClientView implements Serializable {

    ...

}

您好,是的,该帐户不包含客户端属性(您指的是@ManyToOne映射,对吗?)。只有fkClient映射到引用客户端表中主键的外键列。我不知道为什么设计是这样的,因为我不是最初的开发者。使用account.getClient()是否会提高性能?顺便说一句,代码使用带注释的hibernate。我认为我不能使用select join way(
select c from Client c join c.account a
),因为循环中同时使用了客户机和帐户实体(参见上面的代码,我已经对其进行了更新)。您好,我尝试添加了
getClient()
setClient())
使用
@ManyToOne
帐户
实体,但我得到了
映射异常:帐户列:FK_客户端(应使用insert=“false”update=“false”)映射)
。问题是我无法将
insert=“false”update=“false”
添加到
get/setFkClient()
中,因为它们已在其他地方使用。我也无法在不中断当前代码的情况下删除get/setFkClient()。您可以尝试将此
FkClient
属性设置为瞬态,然后在
get/setClient()
中对其进行操作。但我想我无法进一步帮助您,因为我不知道您的整个应用程序和逻辑:(没关系,我找到了最好的解决方案。我将查询放入视图,然后为视图创建实体类并使用hibernate获取它,结果是哇,它大大提高了性能。使用真实代码,完成循环可能需要15-20分钟,但使用View,只需要8-10秒。