0

My question is first, hibernate spring transaction is synchronized ?

And if they not what happen if I will begin two transaction at same time with same object ?

I will be happy to show you this example :

I have a Player object and a Ship object

Player

  • int gold
  • set ships

Ship

  • int price

and I have a method like that :

session.beingTrasaction()

// if player have enough gold, start one query to set gold to gold-price, and second query to add new ship

session.getTransaction().commit();

Is this is safe to use? If I will call to algorithm like this multiple times in same time with same player they will executed serially?

Any help or explanation will be most welcome

Solution :

Thanks to the help i got here, i found my solution by use hibernate locking, I choose to use Pessimistic Lock, And actually it kinda simple.

in my case, this is my implement :

i add to player :

private Long databaseVersion;

@Version
public Long getDatabaseVersion() {
    return databaseVersion;
}
public void setDatabaseVersion(Long databaseVersion) {
        this.databaseVersion = databaseVersion;
}

And my dao implement :

public void buyShip(Player player, Ship ship) {
    Session session = sessionFactory.openSession();
    session.beginTransaction();

    Player p = (Player) session.get(Player.class, player.getId(), LockMode.PESSIMISTIC_WRITE);

    if(p.getGold()>=ship.getPrice()) {
        ship.setPlayer(player);
        addShipToUser(session,ship);

        p.setGold(player.getGold()-ship.getPrice());
        session.update(p);
    }else {
        System.out.println("NOT EN GOLD");
    }


    session.getTransaction().commit();
    session.close();
}

i check it with 5 thread and it work ! all my changes on same player will execute serially, And changes on diffrent players execute in parallel !

Community
  • 1
  • 1
mayan anger
  • 2,322
  • 2
  • 11
  • 19

3 Answers3

1

There is no such thing as an Spring transaction. Spring only provides you a consistent abstraction for transaction management to start and commit/rollback transactions.

public interface PlatformTransactionManager {

    TransactionStatus getTransaction(
            TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

but you have to inject it the actual implementation who manages real transactions. For example the abstraction for hibernate transactions is HibernateTransactionManager and you must inject the SessionFactory of Hibernate who does the actual begin/commit/rollback.

Besides that, if two threads execute the same transaction:

session.beingTrasaction()

if (player.gold > 10){
   player.setGold(gold - 10);
   Ship newShip = new Ship();
   player.addShip(newShip);  // I suppose you have cascade insert otherwise you have to save your transient ship
}

session.getTransaction().commit();

The transactions will be committed in a serialized way. However you could endping up with Players whit a negative amount of gold.

Thread 1                                 Thread 2 
if (player.gold > 10) // true           
                                         if (player.gold > 10) // true
player.gold = gold - 10 // gold=0
                                         player.gold = gold - 10 // gold=-10

You need optimistic or pessimistic locking.

gabrielgiussi
  • 9,245
  • 7
  • 41
  • 71
  • thanks allot babrielgiussi i implement solution by your explanation !, because that i will so glad with you will read my solution on my quesion and say what you think about that, if my implement is ok. – mayan anger Mar 04 '17 at 12:34
  • 1
    A few comments: (1) The databaseVersion that you added is for hibernate optimistic locking. (2) The pessimistic approach that you choosed can work for this relatively simple transaction, but if you start to implement more services that fight for the Player exclusive lock, you could end up with deadlocks that are difficult to track and debug. Also, Hibernate uses database write locks to achieve this, so you are downgrading the concurrency level, in other words if another user want to just read the Player (for example listing all the players near by) he has to wait until this transaction commits – gabrielgiussi Mar 07 '17 at 11:40
  • As a general rule in Hibernate (this is on my own) optimistic locking is the default and you should use pessimistic only when really needed. (3) If you are really using Spring already you should take advantage of its [transactional management](http://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html) that frees you from opening and committing session that is error prone and verbose. – gabrielgiussi Mar 07 '17 at 11:44
1

My question is first, hibernate spring transaction is synchronized ?

By "synchronized", you seem to mean "thread-safe". The Hibernate documentation for its Session interface says:

It is not intended that implementors be threadsafe. Instead each thread/transaction should obtain its own instance from a SessionFactory.

Although that does not affirmatively deny that any particular Session implementation can be thread safe, it certainly establishes that it is dangerous to rely on sessions to be thread safe, nor more specifically to serialize transactions.

Transactions belonging to different sessions can run in parallel, and you can rely on the underlying database's configured level of transaction isolation. Be aware, however, that an entity object can be associated with only one session at a time.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

Spring transaction management "is the same" that Hibernate uses. If I'm not mistaken, your concern is more about concurrency. If you execute this code in a multi threaded environment like a web server, each operation would be ACID but you could not know the exact order and the result could be wrong.

For example, if two threads update the "gold" value for the same player at the same time, the commit part would occur one after the other but not the validation code.

Think of a transaction as an all or nothing atomic "package" of instructions, not a queue of commands.

You could use a synchronized method if you want one thread access at a time. This way when a thread access the code, any other thread would wait the for the first one to leave the method before executing it. This have the downside of only properly working in the same JVM, so if you have multiple servers (or multiple JVM processes in the same server) it will only block threads of the same server/JVM.

You could take a look at some database lock/versioning mechanisms like Optimistic Locking (preferable in most cases) or Pessimistic Locking (where you actually lock the database registry until transaction finishes). These are IMHO, better/easier solutions for concurrency problems.

Thiago Gama
  • 150
  • 5
  • Thank you thiago, i grateful by you explanation and you link, i will so happy sombasy like you will look on my implement in my answer and say to me if this implement is ok, you will do it for me ? – mayan anger Mar 04 '17 at 12:38