简体   繁体   English

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

[英]how should i use transactions to prevent out of memory exceptions?

I can't figure out what annotations to use on my service class to minimise the memory required by the application. 我无法弄清楚要在我的服务类上使用哪些注释以最大程度地减少应用程序所需的内存。

Take a customer/order scenario where I'd like to find the largest total spend by a single customer. 以客户/订单方案为例,我想查找单个客户的最大总支出。 There are in total 1 million orders and 100 customers, each customer has made 10,000 orders. 共有100万个订单和100个客户,每个客户已下达10,000个订单。

The simplest example I can think of is: 我能想到的最简单的例子是:

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;
    }
}

Since the transaction spans the loading of each customers orders I believe there will eventually be a million objects in the hibernate session which is far from ideal. 由于交易涉及每个客户订单的加载,因此我相信在休眠会话中最终将有100万个对象,这远非理想。 I'd like to minimise the number of Order instances in memory by garbage collecting each customers orders once the iteration has been completed for that customer. 我想通过为该客户完成迭代后通过垃圾收集每个客户订单来最大程度地减少内存中的Order实例数量。

In the following example Customers do not reference their collection of orders nor do orders reference their customer: 在以下示例中,客户不引用其订单集合,订单也不引用其客户:

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;
    }
}

Unfortunately when I profile the memory in jprofiler there are still 1 million instances of Order loaded. 不幸的是,当我在jprofiler中分析内存时,仍然加载了100万个Order实例。 I expected a hibernate exception as I thought private methods couldn't be transactional but this never happened. 我期待一个休眠异常,因为我认为私有方法无法进行事务处理,但这从未发生。 Is the above the correct approach and I somehow have a transaction open already or is there a different way of accomplishing my goal? 以上是正确的方法,并且我已经以某种方式进行了交易吗?或者是否有其他方法可以实现我的目标?

If getTotal() returns a value of persistent field, the best approach is to write HQL query with aggregate function: 如果getTotal()返回持久字段的值,则最好的方法是使用聚合函数编写HQL查询:

select max(o.total) from Order o

Alternatively, you can periodically clear the session cache by calling clear() inside your loops (or by calling detach() on processed objects). 另外,您可以通过在循环内调用clear()或在已处理对象上调用detach()来定期清除会话缓存。 Clearing the cache will turn entities into detached state, and they'll be collected by GC soon. 清除缓存将使实体变为分离状态,它们将很快由GC收集。 It's a common practice for implementing batch operations with Hibernate. 使用Hibernate实现批处理操作是一种常见的做法。

只需编写一条sql语句即可返回所需的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM