-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Transaction issue - latest entries not pulled from database
PostPosted: Fri Mar 19, 2010 6:27 am 
Newbie

Joined: Sat Mar 14, 2009 10:48 am
Posts: 7
Hi,

I'm having what seems to be a transactional issue in my application. I'm using Java 1.6 and Hibernate 3.2.5.

My application runs a monthly process where it creates billing entries for a every user in the database based on their monthly activity. These billing entries are then used to create Monthly Bill object. The process is:

1. Get users who have activity in the past month
2. Create the relevant billing entries for each user
3. Get the set of billing entries that we've just created
4. Create a Monthly Bill based on these entries

Everything works fine until Step 3 above. The Billing Entries are correctly created (I can see them in the database if I add a breakpoint after the Billing Entry creation method), but they are not pulled out of the database. As a result, an incorrect Monthly Bill is generated.

If I run the code again (without clearing out the database), new Billing Entries are created and Step 3 pulls out the entries created in the **first** run (but not the second run). This, to me, is very confusing.

My code looks like the following:

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



The methods called look like the following:

Code:
@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;
                
       }


Help with this issue would be greatly appreciated as its been wracking my brain for weeks now!

Thanks in advance,
Gearoid.


Top
 Profile  
 
 Post subject: Re: Transaction issue - latest entries not pulled from database
PostPosted: Fri Mar 19, 2010 6:54 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Hi,

I don't know how are you transaction boundaries as I'm not familiar with the @Transactional annotation,
however can it be that step 2 (Create billing entries) and 3 (Get the set of billing entries) are executed within the same transaction?
In this case, as you want query also the newly created billings, you must assure that a flush() occurs before the query in step 3 is executed. Only in this way the query considers the new inserted objects too.
You can either flush manually (session.flush()) or let it do implicitly by the query by setting the proper flushmode
query.setFlushMode(FlushMode.ALWAYS)


Top
 Profile  
 
 Post subject: Re: Transaction issue - latest entries not pulled from database
PostPosted: Fri Mar 19, 2010 8:20 am 
Newbie

Joined: Sat Mar 14, 2009 10:48 am
Posts: 7
pb00067 wrote:
Hi,

I don't know how are you transaction boundaries as I'm not familiar with the @Transactional annotation,
however can it be that step 2 (Create billing entries) and 3 (Get the set of billing entries) are executed within the same transaction?
In this case, as you want query also the newly created billings, you must assure that a flush() occurs before the query in step 3 is executed. Only in this way the query considers the new inserted objects too.
You can either flush manually (session.flush()) or let it do implicitly by the query by setting the proper flushmode
query.setFlushMode(FlushMode.ALWAYS)


I should have mentioned that we do not use Hibernate explicitly. My code uses the javax.persistence package for objects like Query. We also use javax.persistence.EntityManager. Because of this, we cannot call session.flush() or close(). I have tried calling entityManager.flush() and close() but to no avail.

Do you have any other idea what could be causing this error? Thanks for your input btw.


Top
 Profile  
 
 Post subject: Re: Transaction issue - latest entries not pulled from database
PostPosted: Fri Mar 19, 2010 8:46 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Can it be that you mapped the relation between Billing and User bidirectional?
In such case it it not enough to set entry.setUser(user); but you have also to call user.addBilling(entry)

Beside of this I suggest you to log all sql-activity (queries and result-sets) with p6spy.
First of all I would check if the flush() effectively propagates the insert-sql-statements to the database.

Another scenario which could be happen, is that getLastMonthsBillingEntriesForUser() method
is executed in a different transaction than createBillingEntriesForUser() and for a reason whatever
the transaction relative to createBillingEntriesForUser() has not been commited yet.
In such case if you use a isolation level >= READ_COMMITED getLastMonthsBillingEntriesForUser() cannot see the
new objects as they are not commited yet.

N.B.: With p6spy you also get logged commits and rollbacks !


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.