以下に示すように、別のエンティティを「モニター」として使用して、同時コンテキストで SomeEntity の新しいインスタンスを作成する必要があるシステムがあります。
//Begin transaction
Monitor monitor = (Monitor) session.load(Monitor.class, monitor.getId(), LockOptions.UPGRADE);
SomeEntity entity = createSomeEntity(monitor);
//Save entity and commit transaction
Mysql を使用している場合、これは LockOptions.UPGRADE でモニターをロードしているときに発生するロックで完全に機能しますが、HSQLDB を使用している場合、上記のコードは機能せず、すべてのスレッドがデータベース レベルでロックなしで実行され、作成が発生します。データベース内の SomeEntity の多くのインスタンスの。
主な質問
Hibernate と HSQLDB を使用してデータベース レベルでエンティティをロックする必要があります。
例
必要なものを例示するために、2 つの属性 (id と名前) を持つ Person というエンティティ クラスと、1 つのインスタンス (Person#id) に対して同時に name 属性を更新するメイン プログラムを持つ単純なプロジェクトを作成しました。 = 1)。更新のために競合する 3 つのスレッドがあり、すべてが次の命令によってデータベース レベルで同期されます。
Person person = (Person) session.load(Person.class, 1L, LockOptions.UPGRADE);
上記のコードを一度に 1 つのスレッドだけを使用すると、name 属性が更新され、他のすべてのスレッドはロックの取得を待機します。
ロックが発生した場合、Hibernate によって生成された SQL コードで視覚化でき、次のようになります。
select
person0_.id as id1_0_0_,
person0_.name as name2_0_0_
from
Person person0_
where
person0_.id=? for update
または、session.load(...) が呼び出される行にブレークポイントを設定して、Eclipse IDE でのデバッグ中に視覚化することもできます (1 つのスレッドのみが次の命令に進み、他のすべてのスレッドは待機します)。
したがって、もう一度思い出してください。これは Mysql では機能しますが、 HSQLDB では機能しません。
付属品
package model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String name;
public Person() {}
public Person(String name) {
this();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package app;
import static java.util.concurrent.Executors.newFixedThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import model.Person;
import org.hibernate.LockOptions;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
public class Main {
private static final SessionFactory sessionFactory = init();
public static void main(String[] args) {
populate();
changeNames("Christine", "Isabelle", "Katarina");
}
private static void changeNames(String... names) {
ExecutorService executor = newFixedThreadPool(3);
for (final String name : names) {
executor.execute(new Runnable() {
private Session session = sessionFactory.openSession();
public void run() {
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// At next line I put a breakpoint to debug in Eclipse Juno SR2
Person person = (Person) session.load(Person.class, 1L, LockOptions.UPGRADE);
person.setName(name);
session.saveOrUpdate(person);
transaction.commit();
System.out.println("Name changed to " + name);
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
finally {
session.close();
}
}
});
}
try {
executor.shutdown();
executor.awaitTermination(1, TimeUnit.HOURS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void populate() {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.save(new Person("Old Name"));
transaction.commit();
session.close();
}
private static SessionFactory init() {
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
return configuration.buildSessionFactory(serviceRegistry);
}
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="">
<mapping class="model.Person" />
</session-factory>
</hibernate-configuration>
hibernate.connection.driver_class = org.hsqldb.jdbcDriver
hibernate.dialect = org.hibernate.dialect.HSQLDialect
hibernate.connection.url = jdbc:hsqldb:mem:test;hsqldb.tx=locks;hsqldb.write_delay=false
hibernate.connection.username = sa
hibernate.connection.password =
hibernate.hbm2ddl.auto = update
#hibernate.connection.driver_class = com.mysql.jdbc.Driver
#hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
#hibernate.connection.url = jdbc:mysql://localhost/test
#hibernate.connection.username = root
#hibernate.connection.password =
hibernate.show_sql = true
hibernate.format_sql = true
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.12.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.9</version>
</dependency>
</dependencies>
</project>