I have a java servlet which interacts with hibernate . It is necessary to generate a check id on the system thus:
for (long j = 0; j < soldProductQuantity.longValue(); j++) {
checkId = Hotel.getNextMcsCheckAndIncrement();
checkIdString = checkId.toString();
McsCheck.generateCheck(checkIdString);
}
where getNextMcsCheckAndIncrement() is defined as
static public synchronized Long getNextMcsCheckAndIncrement()
It pulls a value from the database using hibernate, does some operations on it, stores the modified value back, then returns the number.
Because getNextMcsCheckAndIncrement is synchronized, I would expect no two checks to have the same number, because no two threads could enter that method at the same time.
Yet I can see in my data repeated instances of multiple check ids. So clearly this isn't working. What am I missing?
The implementation of getNext as asked:
// Increment FIRST, then return the resulting value as the current MCS check value.
static public synchronized Long getNextMcsCheckAndIncrement() {
Hotel theHotel = null;
Long checkCounter;
#SuppressWarnings("unchecked")
List<Hotel> hotelList = Hotel.returnAllObjects();
for (Hotel currentHotel : hotelList) { // there should be only one.
theHotel = currentHotel;
}
checkCounter = theHotel.getMcsCheckCounter()+1;
theHotel.setMcsCheckCounter(checkCounter);
theHotel.update();
return checkCounter;
}
static public List returnAllObjects() {
return Hotel.query ("from Hotel");
}
static public List query(String queryString) {
Session session = HibernateUtil.getSessionFactory().openSession();
List result = session.createQuery(queryString).list();
session.close();
return result;
}
public void update() {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
session.update(this);
transaction.commit();
session.close();
}
Yes, I know it's not the best way to do it, and I'll solve that in time. But the immediate issue is why concurrency fails.
Anonymous in comments gave the correct answer: The problem must be the Hotel object in the hibernate database, not the synchronization method. Although the counter method is correctly synchronized, if the hotel object is being modified outside of this algorithm, those accesses will NOT be synchronized.
The correct answer, therefore, is to closely check all database accesses to the hotel object and ensure that the object is not modified elsewhere when this loop is in progress, or to refactor the counter out of the Hotel object into dedicated storage.
Related
I am new to Java and Hibernate.
I have implemented a functionality where I generate request nos. based on already saved request no. This is done by finding the maximum request no. and incrementing it by 1,and then again save i it to database.
However I am facing issues with multithreading. When two threads access my code at the same time both generate same request no. My code is already synchronized. Please suggest some solution.
synchronized (this.getClass()) {
System.out.println("start");
certRequest.setRequestNbr(generateRequestNumber(certInsuranceRequestAddRq.getAccountInfo().getAccountNumberId()));
reqId = Utils.getUniqueId();
certRequest.setRequestId(reqId);
ItemIdInfo itemIdInfo = new ItemIdInfo();
itemIdInfo.setInsurerId(certRequest.getRequestId());
certRequest.setItemIdInfo(itemIdInfo);
dao.insert(certRequest);
addAccountRel();
System.out.println("end");
}
Following is the output showing my synchronization:
start
end
start
end
Is it some Hibernate issue.
Does the use of transactional attribute in Spring affects the code commit in my Case?
I am using the following Transactional Attribute:
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
EDIT: code for generateRequestNumber() shown in chat room.
public String generateRequestNumber(String accNumber) throws Exception {
String requestNumber = null;
if (accNumber != null) {
String SQL_QUERY = "select CERTREQUEST.requestNbr from CertRequest as CERTREQUEST, "
+ "CertActObjRel as certActObjRel where certActObjRel.certificateObjkeyId=CERTREQUEST.requestId "
+ " and certActObjRel.certObjTypeCd=:certObjTypeCd "
+ " and certActObjRel.certAccountId=:accNumber ";
String[] parameterNames = {"certObjTypeCd", "accNumber"};
Object[] parameterVaues = new Object[]
{
Constants.REQUEST_RELATION_CODE, accNumber
};
List<?> resultSet = dao.executeNamedQuery(SQL_QUERY,
parameterNames, parameterVaues);
// List<?> resultSet = dao.retrieveTableData(SQL_QUERY);
if (resultSet != null && resultSet.size() > 0) {
requestNumber = (String) resultSet.get(0);
}
int maxRequestNumber = -1;
if (requestNumber != null && requestNumber.length() > 0) {
maxRequestNumber = maxValue(resultSet.toArray());
requestNumber = Integer.toString(maxRequestNumber + 1);
} else {
requestNumber = Integer.toString(1);
}
System.out.println("inside function request number" + requestNumber);
return requestNumber;
}
return null;
}
Don't synchronize on the Class instance obtained via getClass(). It can have some strange side effects. See https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=43647087
For example use:
synchronize(this) {
// synchronized code
}
or
private synchronized void myMethod() {
// synchronized code
}
To synchronize on the object instance.
Or do:
private static final Object lock = new Object();
private void myMethod() {
synchronize(lock) {
// synchronized code
}
}
Like #diwakar suggested. This uses a constant field to synchronize on to guarantee that this code is synchronizing on the same lock.
EDIT: Based on information from chat, you are using a SELECT to get the maximum requestNumber and increasing the value in your code. Then this value is set on the CertRequest which is then persisted in the database via a DAO. If this persist action is not committed (e.g. by making the method #Transactional or some other means) then another thread will still see the old requestNumber value. So you could solve this by making the code transactional (how depends on which frameworks you use etc.). But I agree with #VA31's answer which states that you should use a database sequence for this instead of incrementing the value in code. Instead of a sequence you could also consider using an auto-incement field in CertRequest, something like:
#GeneratedValue(strategy=GenerationType.AUTO)
private int requestNumber;
For getting the next value from a sequence you can look at this question.
You mentioned this information in your question.
I have implemented a functionality where I generate request nos. based on already saved request no. This is done by finding the maximum request no. and incrementing it by 1,and then again save i it to database.
On a first look, it seems the problem caused by multi appserver code. Threads are synchronised inside one JVM(appserver). If you are using more than one appserver then you have to do it differently using more robust approach by using server to server communication or by batch allocation of request no to each appserver.
But, if you are using only one appserver and multiple threads accessing the same code then you can put a lock on the instance of the class rather then the class itself.
synchronized(this) {
lastName = name;
nameCount++;
}
Or you can use the locks private to the class instance
private Object lock = new Object();
.
.
synchronized(lock) {
System.out.println("start");
certRequest.setRequestNbr(generateRequestNumber(certInsuranceRequestAddRq.getAccountInfo().getAccountNumberId()));
reqId = Utils.getUniqueId();
certRequest.setRequestId(reqId);
ItemIdInfo itemIdInfo = new ItemIdInfo();
itemIdInfo.setInsurerId(certRequest.getRequestId());
certRequest.setItemIdInfo(itemIdInfo);
dao.insert(certRequest);
addAccountRel();
System.out.println("end");
}
But make sure that your DB is updated by the new sequence no before the next thread is accessing it to get new one.
It is a good practice to generate "the request number (Unique Id)" by using the DATABASE SEQUENCE so that you don't need to synchronize your Service/DAO methods.
First thing:
Why are you getting the thread inside the method. I is not required here.
Also, one thing;
Can you try like this once:
final static Object lock = new Object();
synchronized (lock)
{
.....
}
what I feel is that object what you are calling is different so try this once.
I have a Java class ClassA, this class has hibernate mappings defined.
Somewhere in the code, i need to retrieve all instances of this class saved in the database:
public List<ClassA> getAllClassA(){
Criteria crit = getSession().createCriteria(ClassA.class);
return crit.list();
}
allA = getAllClassA();
Later i will delete some object, while updating the others:
public void removeItem() {
Iterator<ClassA> it = allA.iterator();
while(it.hasNext()){
ClassA cA = it.next();
if(cA.hasCondition()){
dao.remove(cA);
it.remove();
}
else {
cA.update();
}
}
dao.update(allA);
}
//this is in another class
public void update(List<ClassA> allA){ //dao.update
for(ClassA cA: allA){
getSession().saveOrUpdate(cA);
}
}
What happens is that the database is correctly updated (and the desired object removed) but it also spits out the following error:
ERROR
org.hibernate.event.def.AbstractFlushingEventListener.performExecutions:324
- Could not synchronize database state with session org.hibernate.StaleStateException: Batch update returned unexpected
row count from update [0]; actual row count: 0; expected: 1
I know there are other similar questions on Stack Overflow but they seem to arise from different conditions, and were not useful in this case.
Any ideas?
The optimistic locking mechanism prevents lost updates due to concurrent requests. If you say the database is properly updated, it means some other threads might have attempted to update some entities you've just deleted.
There's always a risk of running into such situations. Most of the time it's better to abandon the request and restart the flow from the latest database entity state.
I'm not sure why, but if executing the removal after the update the problem is gone:
public void removeItem() {
Iterator<ClassA> it = allA.iterator();
ClassA toRemove = null;
while(it.hasNext()){
ClassA cA = it.next();
if(cA.hasCondition()){
toRemove = cA;
it.remove();
}
else {
cA.update();
}
}
dao.update(allA);
if(toRemove!=null)dao.remove(toRemove);
}
I am writing code for a webapp that runs a bowling game. The players scores are saved in a database, so that the top ten can be retrieved to be seen on the homepage. Problem is, I cant access the values in the arraylist that is returned from the method that retrieves the scores from the database.
This is a JUnit test method: (CDR is the top player, should assert to true)
<!-- language: c# -->
public class DatabaseTest {
ScoreDB sdbTest = new ScoreDB();
#Test
public void testTopPl(){
assertTrue(sdbTest.listTopPlayers().get(0).getName() == "CDR");
}
}
And this is the database retrieval code:
public List<Player> listTopPlayers( ){
Transaction trns = null;
Session session = HibernateUtil.getSessionFactory().openSession();
List<Player> topPlayers = new ArrayList<Player>();
try {
trns = session.beginTransaction();
List<Player> players = session.createQuery("FROM Player").list();
Collections.sort(players, new ScoreComparator());
for (int i=0; i<10; i++){
topPlayers.add(players.get(i));
}
// System.out.print(topPlayers.get(0).getName() + " test");
trns.commit();
trns = session.beginTransaction();
}catch (RuntimeException e) {
if(trns != null){
trns.rollback();
}
e.printStackTrace();
} finally{
session.flush();
session.close();
}
return topPlayers;
}
}
First of all you should use order by in the database and setMaxResults in your query to only retrive the top 10. Otherwise you may have memory problems.
Second you are calling begin transaction twice. Do it only once, besides, you are in a read only operation that doesn't neeed a transaction.
Finnaly post your problem, are you getting some null pointer or detached entitity exception? If you are getting detatched entity use Hibernate.initialize in the property that you are reading.
And of course, use EQUALS to compare String.
This might be your real problem. Never compare Strings in Java using "==" operator.
Hope these tips would be usefull.
I'm working on an App that has objects that must be available to all instances but also have synchronized access for certain methods within the object.
For instance I have this object:
public class PlanetID implements Serializable {
public PlanetID() {
id = 0;
}
public long generateID() {
id++;
return id;
}
private long id;
}
It's a simple object that creates a long (id) in series. It's necessary that this object generate a unique id every time. At the moment I have a static synchronized method that handles the Datastore access and storage along with the MemCache access and storage. It works for this particular method but I can already see issues with more complex objects that require a user to be able to access non-synchronized variables along with synchronized variables.
Is there some way to make an object global and allow for both synchronized methods and non-synchronized methods along with the storage of the object when those synchronized objects are accessed?
EDIT: I think people focused too much on the example I gave them and not on the bigger question of having a global variable which can be accessed by all instances and having synchronized access to specific methods while allowing asynchronous access to others.
Here's a better example in hopes it makes things a big more clearer.
Ex.
public class Market implements Serializable {
public Market() {
mineral1 = new ArrayList<Listing>();
mineral2 = new ArrayList<Listing>();
mineral3 = new ArrayList<Listing>();
mineral4 = new ArrayList<Listing>();
}
public void addListing(int mineral, String userID, int price, long amount) { //Doesn't require synchronized access
switch (mineral) {
case MINERAL1:
mineral1.add(new Listing(userID, price, amount));
break;
case MINERAL2:
mineral2.add(new Listing(userID, price, amount));
break;
case MINERAL3:
mineral3.add(new Listing(userID, price, amount));
break;
case MINERAL4:
mineral4.add(new Listing(userID, price, amount));
break;
}
}
public void purchased(int mineral, String userID, long amount) { //Requires synchronized access
ArrayList<Listing> mineralList = null;
switch (mineral) {
case MINERAL1:
mineralList = mineral1;
break;
case MINERAL2:
mineralList = mineral2;
break;
case MINERAL3:
mineralList = mineral3;
break;
case MINERAL4:
mineralList = mineral4;
break;
}
Listing remove = null;
for (Listing listing : mineralList)
if (listing.userID == userID)
if (listing.amount > amount) {
listing.amount -= amount;
return;
} else{
remove = listing;
break;
}
mineralList.remove(remove);
Collections.sort(mineralList);
}
public JSONObject toJSON(int mineral) { //Does not require synchronized access
JSONObject jsonObject = new JSONObject();
try {
switch (mineral) {
case MINERAL1:
for (Listing listing : mineral1)
jsonObject.accumulate(Player.MINERAL1, listing.toJSON());
break;
case MINERAL2:
for (Listing listing : mineral2)
jsonObject.accumulate(Player.MINERAL2, listing.toJSON());
break;
case MINERAL3:
for (Listing listing : mineral3)
jsonObject.accumulate(Player.MINERAL3, listing.toJSON());
break;
case MINERAL4:
for (Listing listing : mineral4)
jsonObject.accumulate(Player.MINERAL4, listing.toJSON());
break;
}
} catch (JSONException e) {
}
return jsonObject;
}
public static final int MINERAL1 = 0;
public static final int MINERAL2 = 1;
public static final int MINERAL3 = 2;
public static final int MINERAL4 = 3;
private ArrayList<Listing> mineral1;
private ArrayList<Listing> mineral2;
private ArrayList<Listing> mineral3;
private ArrayList<Listing> mineral4;
private class Listing implements Serializable, Comparable<Listing> {
public Listing(String userID, int price, long amount) {
this.userID = userID;
this.price = price;
this.amount = amount;
}
public JSONObject toJSON() {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("UserID", userID);
jsonObject.put("Price", price);
jsonObject.put("Amount", amount);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return jsonObject;
}
#Override
public int compareTo(Listing listing) {
return (price < listing.price ? -1 : (price == listing.price ? 0 : 1));
}
public String userID;
public int price;
public long amount;
}
}
With GAE, the Java language is NOT going to hide all the datastore abstractions for you.
Stop thinking in terms of global variables and methods. These are Java language constructs. Start thinking in terms of datastore constructs - entities, datastore accesses, and transactions.
On GAE, your code will be simultaneously running on many servers, they will not share global variables, the "shared data" is in the datastore (or memcache)
An entity is an object in the datastore. You can make datastore fetches from anywhere in your code, so they can replace your global variables. You define transactions within your methods to synchronize datastore accesses and ensure a transaction only happens once. You can use transactions in some methods, and don't use transactions when you don't need synchronization.
You shouldn't need your global ArrayLists of minerals. When you handle a purchase, you essentially need a transaction where you fetch a listing from the datastore, or create it if it doesn't exist, update the user, and write it back to the datastore. You probably want to read up on the datastore before continuing.
Other approach beside transaction is to use a single backend instance to keep your global object, and have all access to the object synchronized there. All other instances need to access this backend instance using URLFetch to get the state of the object.
This is a horrible performance bottleneck though if your app want to scale up smoothly, please don't use it, I'm just pointing out alternative approaches. In fact, if possible, please kindly avoid the need to have a synchronized global object on a distributed application in the first place.
Have a look at DatastoreService.allocateIds - it doesn't matter whether or not you're actually using the generated ID to write a datastore entity, you can get unique long numbers out of this in an efficient manner.
Please note however that they're not guaranteed to be in sequence, they're only guaranteed to be unique - the question doesn't state being sequential as a requirement.
public class PlanetID implements Serializable
{
private DatastoreService ds;
public PlanetID()
{
ds = DatastoreServiceFactory.getDatastoreService();
}
public long generateID()
{
return ds.allocateIds("Kind_That_Will_Never_Be_Used", 1L).getStart().getId();
}
}
As noted in comments - you can use transactions to achieve this:
Start a transaction
Create or update the SomeObject entity with index and someText properties.
Commit transaction. If two instances are trying to do this simultaneously then one will get an exception and will need to retry (get the data again, increment, put all in transaction).
Edit:
(removed section on sharded counters as they do not guarantee )
Note that above solution has a write bottleneck at about 1 write/s. If you need a higher performance solution you could look into using backend instances.
Whilst technically possible, you should heed the advice of other answers and use the services supplied by Google, such as datastore and memcache.
However, you could use a single backend which contains your data, then use your favourite RPC method to read and write data into the shared object. You will need to be aware that although it doesn't happen often, backends are not guaranteed not to die randomly - so you could lose all the data in this object.
I'm going through the Sharded Counters example in Java:
http://code.google.com/appengine/articles/sharding_counters.html
I have a question about the implementation of the increment method. In python it explicitly wraps the get() and increment in a transaction. In the Java example it just retrieves it and sets it. I'm not sure I fully understand the Datastore and transactions but it seems like the critical update section should be wrapped in a datastore transaction. Am I missing something?
Original code:
public void increment() {
PersistenceManager pm = PMF.get().getPersistenceManager();
Random generator = new Random();
int shardNum = generator.nextInt(NUM_SHARDS);
try {
Query shardQuery = pm.newQuery(SimpleCounterShard.class);
shardQuery.setFilter("shardNumber == numParam");
shardQuery.declareParameters("int numParam");
List<SimpleCounterShard> shards =
(List<SimpleCounterShard>) shardQuery.execute(shardNum);
SimpleCounterShard shard;
// If the shard with the passed shard number exists, increment its count
// by 1. Otherwise, create a new shard object, set its count to 1, and
// persist it.
if (shards != null && !shards.isEmpty()) {
shard = shards.get(0);
shard.setCount(shard.getCount() + 1);
} else {
shard = new SimpleCounterShard();
shard.setShardNumber(shardNum);
shard.setCount(1);
}
pm.makePersistent(shard);
} finally {
pm.close();
}
}
}
Transactional code (I believe you need to run this in a transaction to gurantee correctness under concurrent transactions?) :
public void increment() {
PersistenceManager pm = PMF.get().getPersistenceManager();
Random generator = new Random();
int shardNum = generator.nextInt(NUM_SHARDS);
try {
Query shardQuery = pm.newQuery(SimpleCounterShard.class);
shardQuery.setFilter("shardNumber == numParam");
shardQuery.declareParameters("int numParam");
List<SimpleCounterShard> shards =
(List<SimpleCounterShard>) shardQuery.execute(shardNum);
SimpleCounterShard shard;
// If the shard with the passed shard number exists, increment its count
// by 1. Otherwise, create a new shard object, set its count to 1, and
// persist it.
if (shards != null && !shards.isEmpty()) {
Transaction tx = pm.currentTransaction();
try {
tx.begin();
//I believe in a transaction objects need to be loaded by ID (can't use the outside queried entity)
Key shardKey = KeyFactory.Builder(SimpleCounterShard.class.getSimpleName(), shards.get(0).getID())
shard = pm.getObjectById(SimpleCounterShard.class, shardKey);
shard.setCount(shard.getCount() + 1);
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
}
}
} else {
shard = new SimpleCounterShard();
shard.setShardNumber(shardNum);
shard.setCount(1);
}
pm.makePersistent(shard);
} finally {
pm.close();
}
}
This section straight out of the docs shows that you are exactly right about needing a transaction:
http://code.google.com/appengine/docs/java/datastore/transactions.html#Uses_For_Transactions
This example demonstrates one use of transactions: updating an entity with a new property value relative to its current value.
Key k = KeyFactory.createKey("Employee", "k12345");
Employee e = pm.getObjectById(Employee.class, k);
e.counter += 1;
pm.makePersistent(e);
This requires a transaction because the value may be updated by another user after this code fetches the object, but before it saves the modified object. Without a transaction, the user's request will use the value of counter prior to the other user's update, and the save will overwrite the new value. With a transaction, the application is told about the other user's update. If the entity is updated during the transaction, then the transaction fails with an exception. The application can repeat the transaction to use the new data.
Its very close to what that sharded example is doing and, like you, I was unable to find any reason why sharded counters would be different.