java – Optimistic lock; why not throw exception?

Question:

I tried to simulate an optimistic Lock situation where you try to update the same record twice, but the exception is not triggered.

I find it interesting that even after changing the titleholder value and giving a MERGE, the version value is not updated in the object or in the base register.

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("financas");
    EntityManager em1 = emf.createEntityManager();
    EntityManager em2 = emf.createEntityManager();

    em1.getTransaction().begin();
    em2.getTransaction().begin();

    Conta c1 = em1.find(Conta.class,1);
    em1.lock(c1, LockModeType.OPTIMISTIC);
    em1.merge(c1);
    em1.getTransaction().commit();
    Conta c2 = em2.find(Conta.class,1);
    em2.lock(c2, LockModeType.OPTIMISTIC);
    em2.merge(c2);
    em2.getTransaction().commit();

————- UPDATED ———————

Class content counts:

package br.com.financas.modelo;

import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;

@Entity
public class Conta {

    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;
    private String titular;
    private String banco;
    private String agencia;
    private String numero;

    @Version
    private int versao;

    public int getVersao() {
        return versao;
    }
    public void setVersao(int versao) {
        this.versao = versao;
    }
    @OneToMany(mappedBy="conta")
    private List<Movimentacao> movimentacoes;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getTitular() {
        return titular;
    }
    public void setTitular(String titular) {
        this.titular = titular;
    }
    public String getBanco() {
        return banco;
    }
    public void setBanco(String banco) {
        this.banco = banco;
    }
    public String getAgencia() {
        return agencia;
    }
    public void setAgencia(String agencia) {
        this.agencia = agencia;
    }
    public String getNumero() {
        return numero;
    }
    public void setNumero(String numero) {
        this.numero = numero;
    }
    public List<Movimentacao> getMovimentacoes() {
        return movimentacoes;
    }
    public void setMovimentacoes(List<Movimentacao> movimentacoes) {
        this.movimentacoes = movimentacoes;
    }


}

Why is the exception not thrown?

Answer:

The exception is not thrown for two reasons:

1) No change was made to the entity, neither in the first nor in the second context in which it was obtained and persisted, so there is no way to have a conflict in the second context.

2) The transaction from the first context was completed ( committed ) before the entity was fetched from the second context. So even if there was no problem 1 , there would still be no exception because between obtaining and persisting the entity in the second context no changes were made in another context.

For there to be an exception, the code needs to look something like this:

Conta c1 = em1.find(Conta.class,1);
em1.lock(c1, LockModeType.OPTIMISTIC);
c1.setNumero(novoNumero);       // linha adicionada
em1.merge(c1);
//em1.getTransaction().commit();   linha removida
Conta c2 = em2.find(Conta.class,1);
em2.lock(c2, LockModeType.OPTIMISTIC);
em1.getTransaction().commit();  // linha adicionada
c2.setNumero(aindaOutroNumero); // linha adicionada
em2.merge(c2);
em2.getTransaction().commit();

In the code above, the version column was updated and persisted after the entity was retrieved again from another context. So when the entity is finally persisted in the other context, the version value found in the base is different from that existing in the in-memory entity, which will throw a "record was changed by another user" exception.

Scroll to Top