Search code examples
javasqlspringhibernatesqltransaction

Q : hibernate spring transaction is synchronized ? If not , how i update db safe thread?


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 !


Solution

  • 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.