jpa - ipp.pt · ao adicionar o atributo version a uma entidade, jpa usa, por omissão, a...

16
JPA Demonstração das Estratégias Optimistic Locking e Pessimistic Locking Locking é uma técnica para tratamento de concorrência em transações em bases de dados. Quando duas ou mais transações em bases de dados acedem ao mesmo dado, locking permite assegurar que apenas uma transação de cada vez consegue mudar esse dado. Em JPA há duas estratégias para tratar o locking: optimistic locking pessimistic locking. Como exemplo vamos considerar as atualizações do saldo de uma conta após dois depósitos realizados aproximadamente ao mesmo tempo. Quando dois programas podem modificar o mesmo dado, se ambos os programas realizam a sequência ler-modificar-escrever de um modo intercalado podem corromper esse dado. Só o último depósito afeta o saldo perdendo-se a primeira modificação. No exemplo que vamos apresentar, uma conta tem o saldo inicial de 100. O programa1 efetua um depósito de 20 e o programa2 efetua um depósito de 30 na mesma conta, realizando as sequências ler-modificar-escrever de um modo intercalado. A primeira modificação do saldo perde-se e só o último depósito afeta o saldo, obtendo- se o saldo final de 130. programa1 Inicia transação Efetua depósito 1 Efetua commit Lê objeto programa2 Inicia transação Efetua depósito 2 Efetua commit Lê objeto

Upload: others

Post on 19-Jul-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

JPA Demonstração das Estratégias Optimistic Locking e Pessimistic Locking

Locking é uma técnica para tratamento de concorrência em transações em bases de

dados.

Quando duas ou mais transações em bases de dados acedem ao mesmo dado, locking

permite assegurar que apenas uma transação de cada vez consegue mudar esse dado.

Em JPA há duas estratégias para tratar o locking:

• optimistic locking

• pessimistic locking.

Como exemplo vamos considerar as atualizações do saldo de uma conta após dois

depósitos realizados aproximadamente ao mesmo tempo.

Quando dois programas podem modificar o mesmo dado, se ambos os programas

realizam a sequência ler-modificar-escrever de um modo intercalado podem

corromper esse dado. Só o último depósito afeta o saldo perdendo-se a primeira

modificação.

No exemplo que vamos apresentar, uma conta tem o saldo inicial de 100. O programa1

efetua um depósito de 20 e o programa2 efetua um depósito de 30 na mesma conta,

realizando as sequências ler-modificar-escrever de um modo intercalado.

A primeira modificação do saldo perde-se e só o último depósito afeta o saldo, obtendo-

se o saldo final de 130.

programa1

Inicia transação

Efetua depósito 1

Efetua commit

Lê objeto

programa2

Inicia transação

Efetua depósito 2

Efetua commit

Lê objeto

Page 2: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

Setup

1. H2 em modo servidor para permitir múltiplas ligações simultâneas Configurar driver na Unidade de Persistência

Configurar h2 para modo Servidor preencher JDBC URL com JDBC URL: jdbc:h2:tcp://localhost/~/bd/Contas Ficheiro persistence.xml:

<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="JPA2PU" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <class>Conta</class> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/bd/Contas"/> <property name="javax.persistence.jdbc.user" value=""/> <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/> <property name="javax.persistence.jdbc.password" value=""/> <property name="javax.persistence.schema-generation.database.action" value="create"/> <property name="eclipselink.logging.level.sql" value="FINE"/> <property name="eclipselink.logging.parameters" value="true"/> </properties> </persistence-unit> </persistence>

2. Criar as classes Conta, Iniciar, Util, UtilizadorA e UtilizadorB

@Entity

public class Conta {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private int id;

private double saldo;

private Conta() {

}

public Conta(double depositoInicial) {

this.saldo = depositoInicial;

}

public double getSaldo() {

return saldo;

}

public void depositar(double deposito) {

saldo += deposito;

}

}

Page 3: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

public class Iniciar {

public static void main(String[] args) {

EntityManagerFactory emf

= Persistence.createEntityManagerFactory("JPA2PU");

EntityManager em = emf.createEntityManager();

em.getTransaction().begin();

Conta conta = new Conta(100);

em.persist(conta);

em.getTransaction().commit();

em.close();

emf.close();

}

}

public class Util {

public static void parar(String msg) {

try {

System.out.println(msg);

System.in.read();

}

catch (Exception e) {}

}

}

public class UtilizadorA {

public static void main(String[] args) {

EntityManagerFactory emf

= Persistence.createEntityManagerFactory("JPA2PU");

EntityManager em = emf.createEntityManager();

em.getTransaction().begin();

Conta conta = em.find(Conta.class, 1);

Util.parar("A: Saldo=" + conta.getSaldo() + " Continuar?");

conta.depositar(20);

Util.parar("A: Saldo=" + conta.getSaldo() + " Continuar?");

em.getTransaction().commit();

em.close();

emf.close();

System.out.println("A (Fim) - Saldo: " + conta.getSaldo());

}

}

public class UtilizadorB {

public static void main(String[] args) {

EntityManagerFactory emf

= Persistence.createEntityManagerFactory("JPA2PU");

EntityManager em = emf.createEntityManager();

em.getTransaction().begin();

Conta conta = em.find(Conta.class, 1);

Util.parar("B: Saldo=" + conta.getSaldo() + " Continuar?");

conta.depositar(30);

Util.parar("B: Saldo=" + conta.getSaldo() + " Continuar?");

em.getTransaction().commit();

em.close();

emf.close();

System.out.println("B (Fim) - Saldo: " + conta.getSaldo());

}

}

Page 4: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

3. Iniciar h2 em modo servidor h2: h2\bin\h2.bat

4. Executar o método main da classe Iniciar A base de dados é criada.

5. Inspecionar a base de dados criada usando a página H2 Console

Sem Locking 6. Executar intercaladamente os métodos main da classe UtilizadorA e da classe

UtilizadorB. Aplicação funciona incorretamente: Só o último depósito afeta o saldo perdendo-se a primeira modificação.

Numa conta com o saldo de 100 são efetuados dois depósitos, um de 20 e outro de 30,

resultando num saldo final de apenas 130.

Page 5: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

Com Optimistic Locking Para usar a estratégia otimista é necessário adicionar uma propriedade dedicada à

persistência para guardar na base de dados o número da versão da entidade.

@Entity

public class Conta {

@Version

private int versionId = 1;

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private int id;

private double saldo;

private Conta() {

}

public Conta(double depositoInicial) {

this.saldo = depositoInicial;

}

public double getSaldo() {

return saldo;

}

public void depositar(double deposito) {

saldo += deposito;

}

}

Este campo de dados versão:

• Não deve ser modificado pela aplicação

• Pode ser numérico ou timestamp mas um valor numérico é recomendado

• É definido pela anotação @Version

No commit da transação a coluna versão da entidade:

• Na estratégia optimistic é incrementada se o estado da entidade mudou durante a

transação

• Na estratégia optimistic_force_increment é incrementada mesmo se o estado da

entidade não mudou durante a transação

Em qualquer uma das duas estratégias otimistas, se no commit da transação, a entidade

tem na base de dados uma versão diferente da versão que tinha quando foi carregada,

uma exceção OptimisticLockException é lançada, significando que outro programa

entretanto modificou a entidade. Podemos apanhar esta exceção e decidir o que fazer.

O locking otimista deixa transações concorrentes processar simultaneamente,

permitindo ler e atualizar uma entidade, mas deteta colisões, verificando no commit se a

versão foi atualizada na base de dados desde que a entidade foi lido, caso em que lança

uma exceção.

Page 6: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

A estratégia otimista deve ser usada quando se assume que a maior parte das transações

concorrentes não entram em conflito. As vantagens desta estratégia são não necessitar

de colocar locks na base de dados o que dá melhor escalabilidade. A desvantagem é que

a aplicação deve refrescar e voltar a tentar atualizações que falhem.

Locking de uma entidade

Existem 5 tipos de locking: • OPTIMISTIC

• OPTIMISTIC_FORCE_INCREMENT

• PESSIMISTIC_READ

• PESSIMISTIC_WRITE

• PESSIMISTIC_FORCE_INCREMENT

7. Classe Conta com atributo version Alteração apenas da classe Conta para inclusão do atributo version.

Sem try … catch Necessário criar novamente a base de dados porque agora a tabela Conta tem mais uma coluna (versionId)

Executar intercaladamente os métodos main da classe UtilizadorA e da classe UtilizadorB Aplicação funciona incorretamente Programa termina com exceção lançada.

Page 7: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

Método main do UtilizadorB: UPDATE CONTA SET SALDO = ?, VERSIONID = ? WHERE ((ID = ?) AND

(VERSIONID = ?))

bind => [130.0, 2, 1, 1]

Este método main termina com exceção lançada: The object [jpa2.Conta@d109c4f] cannot be updated because it has changed or been deleted since it was last read. Só o primeiro depósito afeta o saldo perdendo-se a última modificação.

Nesta versão do programa duas transações concorrentes tentam atualizar o saldo de um

objeto conta. No commit da primeira transação JPA incrementa o atributo versionId da

conta na base de dados. Quando a segunda transação efetua o commit, JPA lança uma

exceção OptimisticLockException porque o atributo versionId da entidade conta é

diferente do valor que tinha quando a entidade conta foi lida, e a transação é rolled

back.

A aplicação deve refrescar e voltar a tentar efetuar updates que falham.

Com try … catch public class UtilizadorC {

public static void main(String[] args) {

EntityManagerFactory emf

= Persistence.createEntityManagerFactory("JPA2PU");

EntityManager em = emf.createEntityManager();

Page 8: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

boolean depositado = false;

do {

try {

em.getTransaction().begin();

Conta conta = em.find(Conta.class, 1);

Util.parar("C: Saldo=" + conta.getSaldo() + " Continuar?");

conta.depositar(20);

Util.parar("C: Saldo=" + conta.getSaldo() + " Continuar?");

em.getTransaction().commit();

depositado = true;

} catch (final Exception e) {

System.out.println("Saldo mudou desde que foi lido. Tente

outra vez.");

}

} while (!depositado);

em.close();

emf.close();

}

}

public class UtilizadorD {

public static void main(String[] args) {

EntityManagerFactory emf

= Persistence.createEntityManagerFactory("JPA2PU");

EntityManager em = emf.createEntityManager();

boolean depositado = false;

do {

try {

em.getTransaction().begin();

Conta conta = em.find(Conta.class, 1);

Util.parar("C: Saldo=" + conta.getSaldo() + " Continuar?");

conta.depositar(30);

Util.parar("C: Saldo=" + conta.getSaldo() + " Continuar?");

em.getTransaction().commit();

depositado = true;

} catch (final Exception e) {

System.out.println("Saldo mudou desde que foi lido. Tente

outra vez.");

}

} while (!depositado);

em.close();

emf.close();

}

}

Executar intercaladamente os métodos main da classe UtilizadorC e da classe UtilizadorD Aplicação funciona corretamente

Page 9: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

Output do método main de UtilizadorD:

SELECT ID, SALDO, VERSIONID FROM CONTA WHERE (ID = ?)

bind => [1]

C: Saldo=100.0 Continuar? C: Saldo=130.0 Continuar?

UPDATE CONTA SET SALDO = ?, VERSIONID = ? WHERE ((ID = ?) AND

(VERSIONID = ?))

bind => [130.0, 2, 1, 1]

org.eclipse.persistence.exceptions.OptimisticLockException Exception Description: The object [jpa2.Conta@3023df74] cannot be updated because it has changed or been deleted since it was last read. Saldo mudou desde que foi lido. Tente outra vez.

SELECT ID, SALDO, VERSIONID FROM CONTA WHERE (ID = ?)

bind => [1]

C: Saldo=120.0 Continuar? C: Saldo=150.0 Continuar?

UPDATE CONTA SET SALDO = ?, VERSIONID = ? WHERE ((ID = ?) AND

(VERSIONID = ?))

bind => [150.0, 3, 1, 2]

A aplicação funciona corretamente. O primeiro depósito é efetuado, o segundo depósito

lança a exceção OptimisticLockException, mas a aplicação volta a tentar e efetua

corretamente o segundo depósito. Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia

Optimistic Locking, se nenhuma outra estratégia é configurada.

Page 10: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

Com Pessimistic Locking 8. Configurar as aplicaçãoes com a estratégia pessimistic locking

O lock numa entidade é colocado pelo EntityManager (em).

Pode ser especificado com um dos seguintes valores: • LockModeType.OPTIMISTIC

• LockModeType.OPTIMISTIC_FORCE_INCREMENT

• LockModeType.PESSIMISTIC_READ

• LockModeType.PESSIMISTIC_WRITE

• LockModeType.PESSIMISTIC_FORCE_INCREMENT

Pode ser colocado:

• Quando a entidade é carregada da base de dados através do método find Conta conta =

em.find(Conta.class, contaPK, LockModeType.PESSIMISTIC_WRITE);

• Através do método refresh em.refresh(conta, LockModeType.PESSIMISTIC_WRITE);

• Quando carregada através de uma query: Query query = em.createQuery(...);

query.setLockMode(LockModeType.PESSIMISTIC_WRITE);

• Depois de ser carregada da base de dados: em.lock(conta, LockModeType. PESSIMISTIC_WRITE);

O modo lock de uma entidade pode ser obtido: LockModeType modoLock = em.getLockMode(entity);

No locking pessimistic, em vez de esperar até ao commit da transação, na esperança de

que nenhuma outra transação tenha mudado os dados, um lock na base de dados é

obtido imediatamente. Assim a transação nunca falha, contudo também não permite

execução paralela de transações.

O locking pessimistic é efetuado ao nível da base de dados enquanto o locking

optimistic é efetuado ao nível da entidade.

Locks pessimistas são propagados para a base de dados usando queries SQL. Se existe

um lock pessimista, a aplicação espera pela base de dados até o lock ser liberto, não

lançando exceção.

public class UtilizadorA {

public static void main(String[] args) {

EntityManagerFactory emf

= Persistence.createEntityManagerFactory("JPA2PU");

Page 11: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

EntityManager em = emf.createEntityManager();

em.getTransaction().begin();

Conta conta = em.find(Conta.class, 1);

em.lock(conta, LockModeType.PESSIMISTIC_WRITE);

Util.parar("A: Saldo=" + conta.getSaldo() + " Continuar?");

conta.depositar(20);

Util.parar("A: Saldo=" + conta.getSaldo() + " Continuar?");

em.getTransaction().commit();

em.close();

emf.close();

}

}

public class UtilizadorB {

public static void main(String[] args) {

EntityManagerFactory emf

= Persistence.createEntityManagerFactory("JPA2PU");

EntityManager em = emf.createEntityManager();

em.getTransaction().begin();

Conta conta = em.find(Conta.class, 1);

em.lock(conta, LockModeType.PESSIMISTIC_WRITE);

Util.parar("B: Saldo=" + conta.getSaldo() + " Continuar?");

conta.depositar(30);

Util.parar("B: Saldo=" + conta.getSaldo() + " Continuar?");

em.getTransaction().commit();

em.close();

emf.close();

}

}

Executar intercaladamente os métodos main da classe UtilizadorA e da classe UtilizadorB UtilizadorA:

SELECT ID, SALDO, VERSIONID FROM CONTA WHERE (ID = ?) FOR UPDATE

bind => [1]

UtilizadorB:

SELECT ID, SALDO, VERSIONID FROM CONTA WHERE (ID = ?) FOR UPDATE

bind => [1] org.h2.jdbc.JdbcSQLException: Timeout trying to lock table.

Caused by: org.h2.jdbc.JdbcSQLException: Concurrent update in table "CONTA":

another transaction has updated or deleted the same row [90131-194]

Caused by: java.lang.IllegalStateException: Entry is locked [1.4.194/101]

Page 12: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

O método main da classe UtilizadorB termina com exceção lançada devido a Timeout.

A ligação (EntityManager) deste programa não consegue obter um lock na base de

dados porque outra ligação (EntityManager de UtilizadorA) mantém o lock. Para obter

o lock seria necessário que a ligação do utilizadorA libertasse o lock durante o “lock

timeout” cujo valor, por omissão, é 1000 ms.

O “lock timeout” pode ser colocado individualmente para cada ligação ou para toda a

aplicação na “Persistence Unity”:

<property name="javax.persistence.jdbc.url"

value="jdbc:h2:tcp://localhost/~/bd/Contas;LOCK_TIMEOUT=80000"/>

Ficheiro persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.1" . . . >

<persistence-unit name="JPA2PU" transaction-type="RESOURCE_LOCAL">

<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

<class>jpa2.Conta</class>

<properties>

<property name="javax.persistence.jdbc.url"

value="jdbc:h2:tcp://localhost/~/bd/Contas;LOCK_TIMEOUT=80000"/>

<property name="javax.persistence.jdbc.user" value=""/>

<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>

. . .

</properties>

</persistence-unit>

</persistence>

Agora as aplicações funcionam corretamente Começamos por executar a classe Iniciar para colocar o objeto conta com a versão 1

Page 13: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

Em seguida executamos intercaladamente os métodos main da classe UtilizadorA e da classe UtilizadorB, mas a classe UtilizadorB não consegue avançar porque não consegue obter um lock na base de dados UtilizadorA:

SELECT ID, SALDO, VERSIONID FROM CONTA WHERE (ID = ?) FOR UPDATE bind => [1] UPDATE CONTA SET SALDO = ?, VERSIONID = ? WHERE ((ID = ?) AND (VERSIONID = ?)) bind => [120.0, 2, 1, 1]

Após a ligação do UtilizadorA libertar o lock na base de dados, o programa do UtilizadorB avança. UtilizadorB:

SELECT ID, SALDO, VERSIONID FROM CONTA WHERE (ID = ?) FOR UPDATE bind => [1] UPDATE CONTA SET SALDO = ?, VERSIONID = ? WHERE ((ID = ?) AND (VERSIONID = ?)) bind => [150.0, 3, 1, 2]

Page 14: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

A principal vantagem de usar pessimistic locking é a garantia de que, uma vez o lock

obtido, a edição terá sucesso. Útil em aplicações altamente concorrentes nas quais

optimistic locking poderia causar muitos erros de locking optimistic.

Uma desvantagem de usar pessimistic locking é que necessita de recursos adicionais da

base de dados, obrigando a transação e a ligação à base de dados serem mantidas

durante a edição.

Locking na base de dados H2 H2 permite múltiplas ligações concorrentes à mesma base de dados. Para garantir que todas

as ligações vejam sempre os dados consistentes, H2 usa, por omissão, locking ao nível da

tabela.

H2 usa 2 tipos de locks:

• read locks ou shared locks

• write locks ou exclusive locks

Geração de locks:

• Instruções SELECT geram locks read

• Instruções que modificam os dados geram locks write

• A instrução SELECT … FOR UPDATE coloca um lock write na tabela sem modificar os

dados

• As instruções COMMIT e ROLLBACK libertam todos os locks

Antes da leitura de um ou mais registos de uma tabela, a base de dados tenta adicionar um shared lock à tabela, o que só é possível se a tabela não tem um exclusive lock. Se o shared lock é adicionado com sucesso, a tabela pode ser lida. Outras ligações podem também obter um shared lock na mesma tabela. Se há um lock exclusive (write) na tabela, então a ligação que quer ler da tabela espera até que a outra ligação liberte o lock exclusive.

Page 15: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

Antes da escrita numa tabela (atualizar ou apagar uma linha) é necessário obter um exclusive lock. Para obter um exclusive lock, nenhuma outra ligação pode ter qualquer lock na tabela.

Se uma ligação não consegue obter o lock durante um tempo específico, então uma

exceção timeout é lançada. O timeout por omissão é 1 segundo (1000 ms).

Por omissão H2 usa o nível de isolamento transacional “read committed”:

• os locks read são libertos imediatamente após cada instrução

• os locks write são libertos quando a transação efetua o commit ou o rollback

Resumo:

Na estratégia otimista, que obriga a entidade a ter versão, JPA compara o número da

versão desse objeto na base de dados com o número da versão do objeto em memória.

Se os números das versões são diferentes, o que indica que o objeto já foi modificado

por outro EntityManger, a transação falha e uma exceção OptimisticLockException é

lançada.

Para evitar corrupção dos dados é necessário verificar se há colisões entre programas,

isto é, se há sequências ler, modificar, escrever intercaladas.

Versão na entidade Lock Colisão entre programas

Sem versão

Sem lock, lock NONE Não há verificação

Lock OPTIMISTIC,

OPTIMISTIC_FORCE_INCREMENT,

PESSIMISTIC_FORCE_INCREMENT

Exceção

PersistenceException

Lock PESSIMISTIC_READ,

PESSIMISTIC_WRITE Nunca há colisão

Com versão

Sem lock, lock NONE,

OPTIMISTIC, OPTIMISTIC_FORCE_INCREMENT

Exceção

OptimisticLockException

Lock PESSIMISTIC_READ, PESSIMISTIC_WRITE,

PESSIMISTIC_FORCE_INCREMENT

Nunca há colisão

Mensagens das exceções:

• PersistenceException: Invalid lock mode type on for an entity that does not have

a version locking index. Only a PESSIMISTIC lock mode type can be used

when there is no version locking index.

• OptimisticLockException

Exception Description: The object [model.Conta@229c0fa6] cannot be updated

because it has changed or been deleted since it was last read.

Page 16: JPA - ipp.pt · Ao adicionar o atributo version a uma entidade, JPA usa, por omissão, a estratégia Optimistic Locking, se nenhuma outra estratégia é configurada. Com Pessimistic

Resumo dos passos na execução da

Demonstração das Estratégias Optimistic Locking e Pessimistic Locking

Numa conta com o saldo de 100 são efetuados dois depósitos. Um programa efetua um

depósito de 20 e outro programa efetua um depósito de 30. As sequências ler-modificar-

escrever são realizadas intercaladamente.

1 – Classe Conta sem atributo Version. Programas sem qualquer estratégia de locking.

Aplicação funciona incorretamente. A primeira modificação do saldo perde-se e só o

último depósito afeta o saldo, obtendo-se o saldo final de 130.

2 – Classe Conta com atributo Version. Programas sem qualquer estratégia de locking.

JPA usa, por omissão, a estratégia Optimistic Locking.

Aplicação funciona incorretamente. A primeira modificação do saldo é efetuada, mas a

execução do segundo depósito lança a exceção OptimisticLockException.

3 – Classe Conta com atributo Version. Programas sem qualquer estratégia de locking

mas com try … catch.

Aplicação funciona corretamente. O primeiro depósito é efetuado, o segundo depósito

lança a exceção OptimisticLockException, mas a aplicação volta a tentar e efetua

corretamente o segundo depósito.

4 – Classe Conta com ou sem atributo Version. Programas com a estratégia Pessimistic

Locking e sem try … catch.

Aplicação funciona incorretamente. O primeiro depósito é efetuado porque obtém um

lock exclusivo na tabela da base de dados. O segundo depósito tenta obter um lock

exclusivo, pelo que espera até o lock da tabela ser liberto. Como o lock timeout, por

omissão, é 1 segundo, ao fim deste tempo a execução do segundo depósito lança a

exceção JdbcSQLException.

5 – Classe Conta com ou sem atributo Version. Programas com a estratégia Pessimistic

Locking e sem try … catch. Lock Timeout colocado com o valor de 80 segundos na

“Persistence Unity”.

Aplicação funciona corretamente. O primeiro depósito é efetuado porque obtém um

lock exclusivo na tabela da base de dados. O segundo depósito tenta obter um lock

exclusivo, pelo que espera até o lock da tabela ser liberto. Obtém o lock exclusivo e

efetua o depósito.