前提として、Hibernateのversionは5.3.3。pom.xmlのdependenciesは次のようになります。
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.3.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
まず以下のようなMETA-INF\persistence.xml
を準備します。dialectやdriverがDb2になっていますが、ここではRDBMSの種類は問題ではなく、たとえばMySQLやPostgresを選択しても同様の問題が発生する--はず。
<?xml version="1.0" encoding="UTF-8"?>
<persistence
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"
version="2.1">
<persistence-unit name="app">
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.DB2Dialect" />
<property name="hibernate.connection.driver_class" value="com.ibm.db2.jcc.DB2Driver" />
<property name="hibernate.connection.url" value="jdbc:db2://[url]:[port]/[DBNAME]" />
<property name="hibernate.connection.username" value="[user name]" />
<property name="hibernate.connection.password" value="[password]" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
そのあと次のようなmainメソッドを作成&実行したところ、Javaアプリケーションが終了しなくなりました(´・ω・`) たとえばeclipseで「右クリック > Run As > Java Application」と選択し、mainメソッドを実行すると、通常であればその中身を順に実行したあと「terminated」という表示が出現します。しかしこの手順でmainメソッドを実行すると、mainメソッドの最終行までは行きついているようなのですが、いつまでたっても「terminated」という表示が出てきません。あるいは実行可能jarにまとめて、このmainメソッドを実行すると、このプロセスがいつまでたっても終了しません。kill -9
などで強制終了をかけない限り、mainメソッドから抜け出せないという現象が発生します。
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class Main {
public static void main(String[] args) {
EntityManagerFactory factory = null;
EntityManager manager = null;
EntityTransaction transaction = null;
try {
factory = Persistence.createEntityManagerFactory("app");
manager = factory.createEntityManager();
transaction = manager.getTransaction();
transaction.begin();
// DO SOMETHING: DBへの操作を実施する。
transaction.commit();
} catch (RuntimeException e) {
if (transaction != null && transaction.isActive()) {
transaction.rollback();
}
throw e;
}
}
}
__さてこの「mainメソッドの最終行にまで行きついたにも関わらず、アプリケーションが終了しない」問題ですが、EntityManagerFactory
をclose
することで解決することができました。__たとえばmainメソッドを以下のように書き換えてやると、mainメソッドの終了とともにeclipseは「terminated」を表示し、実行可能jarはプロセスを完了するようになりました。
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class Main {
public static void main(String[] args) {
EntityManagerFactory factory = null;
EntityManager manager = null;
EntityTransaction transaction = null;
try {
factory = Persistence.createEntityManagerFactory("app");
manager = factory.createEntityManager();
transaction = manager.getTransaction();
transaction.begin();
// DO SOMETHING: DBへの操作を実施する。
transaction.commit();
} catch (RuntimeException e) {
if (transaction != null && transaction.isActive()) {
transaction.rollback();
}
throw e;
} finally {
if (manager != null) {
manager.close();
}
if (factory != null) {
factory.close();
}
}
}
}
要するにEntityManagerFactory
のクローズ漏れが原因でした。EntityManagerFactory
のJavaDocを見ると、次のように記述されています: "When the application has finished using the entity manager factory, and/or at application shutdown, the application should close the entity manager factory." JavaEEやSpringを利用する場合、@Autowired
や@Inject
などを利用し、インスタンスの生成をすべてフレームワークにお任せするのが基本ですが、上述の例のようにインスタンスのライフサイクルを自ら制御する場合、アプリケーションの終了とともにEntityManagerFactory#close
を呼び出して、アプリケーションが完了することをJPAにも通知してやる必要があるようです(´・ω・`)