我应该如何使用事务来防止内存不足异常?

时间:2012-04-16 15:27:56

标签: java hibernate memory-management transactions

我无法弄清楚在我的服务类上使用哪些注释来最小化应用程序所需的内存。

采取客户/订单方案,我希望找到单个客户的最大总支出。总共有100万个订单和100个客户,每个客户订购了10,000个订单。

我能想到的最简单的例子是:

public class MyService {
    @Transactional(readOnly=true)
    public int getMaxSpend() {
        int maxSpend = 0;
        List<Customer> customers = customerDao.findAll();
        for(Customer customer : customers) {
            List<Order> orders = orderDao.getOrders(customer);
            for(Order order : orders) {
                if(order.getTotal() > maxSpend) maxSpend = order.getTotal();
            }
        }
        return maxSpend;
    }
}

由于交易跨越每个客户订单的加载,我相信在休眠会话中最终将有一百万个对象远非理想。我想通过在为该客户完成迭代后收集每个客户订单的垃圾来最小化内存中Order个实例的数量。

在以下示例中,客户不会引用他们的订单集合,订单也不会引用他们的客户:

public class MyService {
    @Transactional(propagation=Propagation.NEVER)
    public int getMaxSpend() {
        int maxSpend = 0;
        List<Customer> customers = getAllCustomers();
        for(Customer customer : customers) {
            int customerSpend = getCustomerSpend(customer);
            if(customerSpend > maxSpend) maxSpend = customerSpend;
        }
        return maxSpend;
    }

    @Transactional(readOnly=true)
    private List<Customer> getAllCustomers() {
        return customerDao.findAll();
    }

    @Transactional(readOnly=true)
    private int getCustomerSpend(Customer customer) {
        int customerSpend = 0;
        List<Order> orders = orderDao.getOrders(customer);
        for(Order order : orders) {
            customerSpend += order.getTotal();
        }
        return customerSpend;
    }
}

不幸的是,当我在jprofiler中分析内存时,仍然有100万个Order个实例被加载。我期待一个hibernate异常,因为我认为私有方法不能是事务性的,但这种情况从未发生过。以上是正确的方法,我在某种程度上已经开放了交易,还是有不同的方式来实现我的目标?

2 个答案:

答案 0 :(得分:1)

如果getTotal()返回持久字段的值,最好的方法是使用聚合函数编写HQL查询:

select max(o.total) from Order o

或者,您可以通过在循环内调用clear()(或在已处理对象上调用detach())来定期清除会话缓存。清除缓存会将实体转换为分离状态,并且很快就会被GC收集。使用Hibernate实现批处理操作是一种常见的做法。

答案 1 :(得分:0)

只需编写一个返回所需结果的sql语句。