Transaction issue in java with hibernate - latest entries not pulled from database - java

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:
Get users who have activity in the past month
Create the relevant billing entries for each user
Get the set of billing entries that we've just created
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:
for (User user : usersWithActivities) {
createBillingEntriesForUser(user.getId());
userBillingEntries = getLastMonthsBillingEntriesForUser(user.getId());
createXMLBillForUser(user.getId(), userBillingEntries);
}
The methods called look like the following:
#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.

Is your top method also transactional ? If yes most of the time i've encountered that kind of problem, it was a flush that was not done at the right time by hibernate.
Try to add a call to session.flush() at the beginning of the getLastMonthsBillingEntriesForUser method, see if it address your problem.

Call session.flush() AND session.close() before getLastMonthsBillingEntriesForUser gets called.

Please correct my assumptions if they are not correct...
As far as I can tell, the relationship between entry and user is a many to one.
So why is your query doing a "one to many" type join? You should rather make your query:
select be from BillingEntry as be where be.user=:user and be.timestamp >= :firstOfLastMonth and be.timestamp < :firstOfCurrentMonth
And then pass in the User object, not the user id. This query will be a little lighter in that it will not have to fetch the details for the user. i.e. not have to do a select on user.
Unfortunately this is probably not causing your problem, but it's worth fixing nevertheless.

Move the declaration of BillingEntry entry = new BillingEntry(); to within the for loop. That code looks like it's updating one entry over and over again.
I'm guessing here, but what you've coded goes against what I think I know about java persistence and hibernate.
Are you certain that those entries are being persisted properly? In my mind, what is happening is that a new BillingEntry is being created, it is then persisted. At this point the next iteration of the loop simply changes the values of an entry and calls merge. It doesn't look like you're doing anything to create a new BillingEntry after the first time, thus no new id's are generated which is why you can't retrieve them later.
That being said, I'm not convinced the timing of the flush isn't a culprit here either, so I'll wait with bated breathe for the downvotes.

Related

HBase - delete columns of rows with range of timestamp without scanning

I was wonder if I could delete some columns of some rows with timestamp without scanning the whole database
my code is like below:
public static final void deleteBatch(long date, String column, String...ids) throws Exception{
Connection con = null; // connection instance
HTable table = null; // htable instance
List<Delete> deletes = new ArrayList<Delete>(ids.length);
for(int i = 0; i < ids.length; i++){
String id = ids[i];
Delete delete = new Delete(id.getBytes());
delete.addColumn(/* CF */, Bytes.toString(column));
/*
also tried:
delete.addColumn(/* CF */, Bytes.toString(column), date);
*/
delete.setTimestamp(date);
deletes.add(delete);
}
table.delete(deletes);
table.close();
}
this works, but deletes all column prior to given date,
I want something like this:
Delete delete = new Delete(id.getBytes());
delete.setTimestamp(date-1, date);
I don't want to delete prior or after a specific date, I want to delete exact time range I give.
Also my MaxVersion of HTableDescriptor is set to Integer.MAX_VALUE to keep all changes.
as mentioned in the Delete API Documentation:
Specifying timestamps, deleteFamily and deleteColumns will delete all
versions with a timestamp less than or equal to that passed
it delets all columns which their timestamps are equal or less than given date.
how can I achieve that?
any answer appreciated
After struggling for weeks I found a solution for this problem.
the apache HBase has a feature called coprocessor which hosts and manages the core execution of data level operations (get, delete, put ...) and can be overrided(developed) for custom computions like data aggregation and bulk processing against the data outside the client scope.
there are some basic implemention for common problems like bulk delete and etc..

EJB JPA based Application is throwing NPE after exactly 1 hour and 10 minutes

After monitoring ADF's behavior using EJB JPA,we noticed that every 1 hour and 10 minutes exactly the Iterator refreshes, which is causing a null pointer exception in getcurrentrow (DCIteratorBinding) function . but how can we avoid refreshing the iterator(ps:we tried the session timeout).
if i use this function from sessionbean it will work fine with a table and a form in my JSFF view
/** <code>select o from Person o</code> */
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<Person> getPersonFindAll() {
return em.createNamedQuery("Person.findAll",
Person.class).getResultList();
} /** <code>select o from Person o</code> */
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<Person> getPersonFindAll() {
return em.createNamedQuery("Person.findAll",
Person.class).getResultList();
}
but if i use this function without a table in my JSFF view it will call it again after 1 hour and 10 minutes causing loss in modified and unsaved work that the user made
public Person getPersonById() {
Person person = new Person();
ADFContext adfCtx = ADFContext.getCurrent();
Map pageFlowScope = adfCtx.getPageFlowScope();
Object idObj = pageFlowScope.get("id");
System.out.println(" getPersonById !!!!!!!!!!!!!!!! id = "+idObj);
String id = (String) idObj;
person = em.find(Person.class, id);
return person;
}
This look like a classic issue of misconfigured Application Module.
Cause : Your application module is timing out and releasing it's transaction before the official adfc-config timeout value.
To Fix : Go to the application module containing this VO > Configuration > Edit the default > Modify Idle Instance Timeout to be the same as your adf session timeout (Take time to validate the other configuration aswell)

Unique ID number in static method through concurrency JAVA

I want to get unique ID from my domain object (table) ID each time a method is called. So that ID's do not repeat. I have a function that returns unique ID.
public static Long generateID (Short company)
throws Exception
{
IDDAO iDDAO = SpringApplicationContext.getBean (IDDAO.class);
ID iD = iDDAO.findByNaturalKey (new IDNatKey (company);
if (iD != null)
{
// Check if ID has reached limit, then reset the ID to the first ID
if (iD.getLatestIDno ().longValue () == iD.getLastIDno ().longValue ())
{
iD.setLatestIDno (iD.getFrstIDno ());
}
// Get next ID
iD.setLatestIDno (iD.getLatestIDno () + 1);
// update database with latest id
iDDAO.update (iD);
return iD.getLatestIDno ();
}
}
The issue is that if access the application from two machines and press button from UI to generate ID exactly at the same time, there are sometimes duplicate IDs returned from this method
e.g.
Long ID = TestClass.generateID (123);
This gives me duplicate sometimes.
I made the method like this
public static synchronized Long generateID (Short company)
throws Exception
so that only one thread can go in this function at a time, but the duplicate issue is still there.
I do not want to use database sequences as I do not want gaps in the ID sequences if the transaction rolls back, in that case sequence will be incremented still, which I do not want.Gaps at the middle are OK but not at end. E.g we have 1, 2 and 3 as IDs and 2 rolls back, that is OK. But if 3 rolls back, we should get 3 again when another user comes, in case of sequence, it will give 4
Please help me tell what I am doing incorrect ? static synchronized will still cause other threads to go inside this function at same time ? I have many other static (but not synchronized) functions in the class. Will this cause issue with them too if I make it static synchronized ?
Thanks
Aiden
You can use java.util.UUID. it will generate a universal uniqueId.
Keep 2 unique IDs:
a db-provided, internal transaction ID, created by an autoincrement every time a new transaction is built. Gaps may appear if transactions are rolled back.
a pretty, gap-less "ticket ID", assigned only once the transaction commits successfully.
Assign both from the DB - it is best to keep all shared state there, as the DB will guarantee ACID, while Java concurrency is far trickier to get right.
In that case I think you try the below :
synchronized(this){if (iD != null)
{
// Check if ID has reached limit, then reset the ID to the first ID
if (iD.getLatestIDno ().longValue () == iD.getLastIDno ().longValue ())
{
iD.setLatestIDno (iD.getFrstIDno ());
}
// Get next ID
iD.setLatestIDno (iD.getLatestIDno () + 1);
// update database with latest id
iDDAO.update (iD);
return iD.getLatestIDno ();
}}

Excatly how to test? (Unittest, JUnit, PhpUnit)

Well this question might be too localized.
Lets suppose I have forum system to test. Lets delete an user and his posts. Let me use a pseudo-code for the sake of simplificity:
class User
{
function add() { ... }
function delete (userID)
{
container::getOrCreateUserPostObject.deletePostsByUserID (userID)
DELETE FROM users WHERE ID = userID
}
}
class UserPost
{
function deletePostsByUserID (userID)
{
DELETE FROM posts WHERE USERID = userID
}
}
this now must be tested:
function testDeleteUser()
{
container::getOrCreateUserObject.add();
container::getOrCreateUserObject.add();
container::getOrCreateUserObject.delete (1)
// now check in the DB that how many records left, really one was deleted etc.
}
another test
function testDeletePosts
{
container::getOrCreateUserPostObject.deletePostsByUserID (1);
// again, now check in the DB that how many records left, really one was deleted etc.
}
this looks OK so far. The user deletion and user posts deletion works, and their test standalone.
Yes, standalone. We checked if its OK to delete an user and checked if its OK to delete his post. We didnt check if we delete an user with his posts works! There are two good working "lego" elements but is that OK if we put them together?
If I put this "global" test to testDeleteUser() then I repeat the post-deletion test code...
I don't know if i get you right, but in a test, you should not really rely on specific user id's like you are doing in testDeletePosts(), you should rather add a user here as well, add some posts, and delete these posts again. So your test is completely independent.
Update:
Something like this for checking the referential integrity
function testDeleteUsersAndPosts
{
addedUsers[0] = user.add();
addedPosts[0] = post.add(addedUsers[0], 'first Post')
addedPosts[1] = post.add(addedUsers[0], 'second Post')
addedUsers[1] = user.add();
addedPosts[2] = post.add(addedUsers[1], 'third Post for the second user')
// Check how many posts you have
allPosts = post.get().count()
for (id in addedUsers)
{
user.delete(id)
}
// Check how many posts you have now
allPostsNow = post.get().count();
return allPostsNow == (allPosts -3)
}
And something like this for checking the Post deletion only
function testDeletePosts
{
userID = user.add();
addedPost = post.add(userID, 'first Post')
// Check how many posts you have
allPosts = post.get().count()
post.delete(addedPost)
return post.get(addedPost) == false
}

Arrange entites added to GAE by newest first when added?

I created a google app engine client using eclipse and the android demo google hands out. I Created the back end and a few models. When I add an entities from android to my database on GAE it orders it by date not by newest created first. The key it just the current date and tie on android. Im not sure how to work with the back end, as google created it for me in my project. Is there a fast change I can make to it so instead or it ordering it by data when I add an item it will just keep the newest listings on top?
Edited question, this is my endpoint class Google generated for me. How can I modify it to receive the newest added entities first?
#Api(name = "quotesendpoint", namespace = #ApiNamespace(ownerDomain = "projectquotes.com" ownerName = "projectquotes.com", packagePath = ""))
public class quotesEndpoint {
/**
* This method lists all the entities inserted in datastore.
* It uses HTTP GET method and paging support.
*
* #return A CollectionResponse class containing the list of all entities
* persisted and a cursor to the next page.
*/
#SuppressWarnings({ "unchecked", "unused" })
#ApiMethod(name = "listquotes")
public CollectionResponse<quotes> listquotes(
#Nullable #Named("cursor") String cursorString,
#Nullable #Named("limit") Integer limit) {
EntityManager mgr = null;
Cursor cursor = null;
List<quotes> execute = null;
try {
mgr = getEntityManager();
Query query = mgr.createQuery("select from quotes as quotes");
if (cursorString != null && cursorString != "") {
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query.setFirstResult(0);
query.setMaxResults(limit);
}
execute = (List<quotes>) query.getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null)
cursorString = cursor.toWebSafeString();
// Tight loop for fetching all entities from datastore and accomodate
// for lazy fetch.
for (quotes obj : execute)
;
} finally {
mgr.close();
}
return CollectionResponse.<quotes> builder().setItems(execute)
.setNextPageToken(cursorString).build();
The order you see in datastore viewer in GAE is not significant as it is just a display of the current data in your datastore and shown in the increasing order of entity id(if using auto id). This could coincidentally also have an increasing order of date. You cannot modify this display pattern.
What matters is the order seen by your queries and this is determined by indexes. So if you need to get your entities in the descending order of date, then if your date entry is left as indexed, GAE will be automatically having an index for date. You just need to query your entities by specifying a descending sort order on the date property.
EDIT:
Based on the code added, below modifications should be done to query the entities in descending order of date.
1, Add a new date property in your entity:
private Date entrydate;
2, While creating an entity, add the current date to this property
yourentity.setEntryDate(new Date())
3, While querying, set ordering based on descending order of date
query.setOrdering("entrydate desc");

Categories

Resources