我在我的应用程序中遇到了似乎是一个交易问题。我正在使用Java 1.6和Hibernate 3.2.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。
答案 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,这就是你以后无法检索它们的原因。
话虽如此,我不相信同花顺的时间也不是这里的罪魁祸首,所以我会等待那些沮丧的呼吸。