带有hibernate的java中的事务问题 - 未从数据库中提取的最新条目

时间:2010-03-19 10:20:23

标签: java hibernate transactions

我在我的应用程序中遇到了似乎是一个交易问题。我正在使用Java 1.6和Hibernate 3.2.5。

我的应用程序运行每月流程,根据月度活动为数据库中的每个用户创建帐单条目。然后,这些计费条目用于创建每月帐单对象。这个过程是:

  1. 获取过去一个月内有活动的用户
  2. 为每个用户创建相关的结算条目
  3. 获取我们刚刚创建的一组结算条目
  4. 根据这些条目创建月度帐单
  5. 一切正常,直到上面的第3步。正确创建了开票分录(如果我在开票分录创建方法之后添加断点,我可以在数据库中看到它们),但它们不会从数据库中拉出来。结果,生成了错误的每月帐单。

    如果我再次运行代码(不清除数据库),则会创建新的开票项目,步骤3将删除在第一次运行中创建的条目(但不是第二次运行)。对我来说,这非常令人困惑。

    我的代码如下所示:

    for (User user : usersWithActivities) {
    
                createBillingEntriesForUser(user.getId());
    
                userBillingEntries = getLastMonthsBillingEntriesForUser(user.getId());
    
                createXMLBillForUser(user.getId(), userBillingEntries);
        }
    

    调用的方法如下所示:

    @Transactional
        public void createBillingEntriesForUser(Long id) {
    
            UserManager userManager = ManagerFactory.getUserManager();
            User user = userManager.getUser(id);
            List<AccountEvent> events = getLastMonthsAccountEventsForUser(id);
            BillingEntry entry = new BillingEntry();
    
            if (null != events) {
    
                for (AccountEvent event : events) {
    
                    if (event.getEventType().equals(EventType.ENABLE)) {
                        Calendar cal = Calendar.getInstance();
    
                        Date eventDate = event.getTimestamp();
                        cal.setTime(eventDate);
    
                        double startDate = cal.get(Calendar.DATE);
                        double numOfDaysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
                        double numberOfDaysInUse = numOfDaysInMonth - startDate;
    
                        double fractionToCharge = numberOfDaysInUse/numOfDaysInMonth;
    
                        BigDecimal amount = BigDecimal.valueOf(fractionToCharge * Prices.MONTHLY_COST);
                        amount.scale();
                        entry.setAmount(amount);
                        entry.setUser(user);
                        entry.setTimestamp(eventDate);
    
                        userManager.saveOrUpdate(entry);
                    }
    
    
                }
    
            }
    
        }
    
    
    @Transactional
        public Collection<BillingEntry> getLastMonthsBillingEntriesForUser(Long id) {
    
            if (log.isDebugEnabled())
                log.debug("Getting all the billing entries for last month for user with ID " + id);
    
            //String queryString = "select billingEntry from BillingEntry as billingEntry where billingEntry>=:firstOfLastMonth and billingEntry.timestamp<:firstOfCurrentMonth and billingEntry.user=:user";
            String queryString = "select be from BillingEntry as be join be.user as user where user.id=:id and be.timestamp>=:firstOfLastMonth and be.timestamp<:firstOfCurrentMonth";
    
            //This parameter will be the start of the last month ie. start of billing cycle
            SearchParameter firstOfLastMonth = new SearchParameter();
            firstOfLastMonth.setTemporalType(TemporalType.DATE);
    
            //this parameter holds the start of the CURRENT month - ie. end of billing cycle
            SearchParameter firstOfCurrentMonth = new SearchParameter();
            firstOfCurrentMonth.setTemporalType(TemporalType.DATE);
    
            Query query = super.entityManager.createQuery(queryString);
    
            query.setParameter("firstOfCurrentMonth", getFirstOfCurrentMonth());        
            query.setParameter("firstOfLastMonth", getFirstOfLastMonth());
            query.setParameter("id", id);
    
            List<BillingEntry> entries = query.getResultList();
    
            return entries;
        }
    
    public MonthlyBill createXMLBillForUser(Long id, Collection<BillingEntry> billingEntries) {
    
            BillingHistoryManager manager = ManagerFactory.getBillingHistoryManager();
            UserManager userManager = ManagerFactory.getUserManager();
    
            MonthlyBill mb = new MonthlyBill();
            User user  = userManager.getUser(id);
    
            mb.setUser(user);
            mb.setTimestamp(new Date());
    
            Set<BillingEntry> entries = new HashSet<BillingEntry>();
            entries.addAll(billingEntries);
    
            String xml = createXmlForMonthlyBill(user, entries);
            mb.setXmlBill(xml);
            mb.setBillingEntries(entries);
            MonthlyBill bill = (MonthlyBill) manager.saveOrUpdate(mb);
            return bill;
    
        }
    

    这个问题的帮助将非常感激,因为它已经让我的大脑震惊了几个星期!

    提前致谢, Gearoid。

4 个答案:

答案 0 :(得分:0)

您的top方法也是事务性的吗?如果是,大部分时间我都遇到过这种问题,那就是休眠时没有在正确的时间完成的刷新。

尝试在getLastMonthsBillingEntriesForUser方法的开头添加对session.flush()的调用,看看它是否解决了你的问题。

答案 1 :(得分:0)

在调用getLastMonthsBillingEntriesForUser之前,请先调用 session.flush() session.close()

答案 2 :(得分:0)

如果不正确,请更正我的假设......

据我所知,条目和用户之间的关系是多对一的。

那么为什么您的查询执行“一对多”类型的连接?你应该提出你的疑问:

select be from BillingEntry as be where be.user=:user and be.timestamp >= :firstOfLastMonth and be.timestamp < :firstOfCurrentMonth

然后传入User对象,而不是用户ID。此查询将更轻一些,因为它不必获取用户的详细信息。即不必对用户进行选择。

不幸的是,这可能不会导致你的问题,但是值得修复。

答案 3 :(得分:0)

BillingEntry entry = new BillingEntry();的声明移到for循环中。该代码看起来像是一遍又一遍地更新一个条目。

我在这里猜测,但你所编写的内容违背了我认为我对java持久性和休眠的了解。

您确定这些条目是否正确保留?在我看来,正在发生的是正在创建一个新的BillingEntry,然后它会被持久化。此时,循环的下一次迭代只是更改条目的值并调用merge。看起来你在第一次创建新的BillingEntry后没有做任何事情,因此没有生成新的id,这就是你以后无法检索它们的原因。

话虽如此,我不相信同花顺的时间也不是这里的罪魁祸首,所以我会等待那些沮丧的呼吸。