In the following example, we will use multiple LockModeType.PESSIMISTIC_READ locks (shared locks). According to JPA specification, a provider should allow multiple PESSIMISTIC_READ locks to read entities at the same time. However, specification also says:
It is permissible for an implementation to use LockModeType.PESSIMISTIC_WRITE where LockModeType.PESSIMISTIC_READ was requested, but not vice versa.
Let's see how Hibernate applies multiple shared locks.
Example
The Entity
@Entity
public class Article {
@Id
@GeneratedValue
private long id;
private String content;
.............
}
Using multiple Shared locks simultaneously
public class PessimisticSharedLocksExample {
private static EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("example-unit");
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newFixedThreadPool(3);
try {
persistArticle();
es.execute(() -> {
readArticle();
});
es.execute(() -> {
//simulating other user by using different thread
readArticle();
});
es.shutdown();
es.awaitTermination(1, TimeUnit.MINUTES);
} finally {
entityManagerFactory.close();
}
}
private static void readArticle() {
log("before acquiring read lock");
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
Article article = em.find(Article.class, 1L, LockModeType.PESSIMISTIC_READ);
log("After acquiring read lock", article);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
em.getTransaction().commit();
em.close();
log("Article after read commit", article);
}
public static void persistArticle() {
log("persisting article");
Article article = new Article("test article");
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
em.persist(article);
em.getTransaction().commit();
em.close();
log("Article persisted", article);
}
private static void log(Object... msgs) {
System.out.println(LocalTime.now() + " - " + Thread.currentThread().getName() +
" - " + Arrays.toString(msgs));
}
} 10:33:15.049 - main - [persisting article] 10:33:15.130 - main - [Article persisted, Article{id=1, content='test article'}] 10:33:15.131 - pool-2-thread-1 - [before acquiring read lock] 10:33:15.132 - pool-2-thread-2 - [before acquiring read lock] 10:33:15.144 - pool-2-thread-2 - [After acquiring read lock, Article{id=1, content='test article'}] 10:33:16.145 - pool-2-thread-2 - [Article after read commit, Article{id=1, content='test article'}] 10:33:16.152 - pool-2-thread-1 - [After acquiring read lock, Article{id=1, content='test article'}] 10:33:17.154 - pool-2-thread-1 - [Article after read commit, Article{id=1, content='test article'}]
As seen in above output, second read transaction blocks till the first transaction committed. It is exactly what happens with LockModeType.PESSIMISTIC_WRITE lock.
On setting hibernate.show_sql=true in persistence.xml and running above example again:
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table Article (id bigint not null, content varchar(255), primary key (id))
10:36:54.202 - main - [persisting article]
Hibernate: call next value for hibernate_sequence
Hibernate: insert into Article (content, id) values (?, ?)
10:36:54.280 - main - [Article persisted, Article{id=1, content='test article'}]
10:36:54.280 - pool-2-thread-1 - [before acquiring read lock]
10:36:54.280 - pool-2-thread-2 - [before acquiring read lock]
Hibernate: select article0_.id as id1_0_0_, article0_.content as content2_0_0_ from Article article0_ where article0_.id=? for update
Hibernate: select article0_.id as id1_0_0_, article0_.content as content2_0_0_ from Article article0_ where article0_.id=? for update
10:36:54.302 - pool-2-thread-2 - [After acquiring read lock, Article{id=1, content='test article'}]
10:36:55.314 - pool-2-thread-2 - [Article after read commit, Article{id=1, content='test article'}]
10:36:55.329 - pool-2-thread-1 - [After acquiring read lock, Article{id=1, content='test article'}]
10:36:56.331 - pool-2-thread-1 - [Article after read commit, Article{id=1, content='test article'}]
As seen, Hibernate uses 'Select for update' statement which locks the rows for write. Hibernate uses the same sql statement in case of LockModeType.PESSIMISTIC_WRITE . Also EclipseLink has same behavior for shared locks.
Example ProjectDependencies and Technologies Used: - h2 1.4.196: H2 Database Engine.
- hibernate-core 5.2.12.Final: The core O/RM functionality as provided by Hibernate.
Implements javax.persistence:javax.persistence-api version 2.1 - JDK 1.8
- Maven 3.3.9
|
|