I have a Java application that has a GUI made with Swing and that uses two databases interchangeably. One of the two databases is mongoDB and the other one is MySQL. Which database to use is chosen with a command line option. For the MySQL database I am also using Hibernate and JPA. The code I have looks like this:
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import java.awt.EventQueue;
import java.util.concurrent.Callable;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Command(mixinStandardHelpOptions = true)
public class App implements Callable<Void> {
private static final Logger LOGGER = LogManager.getLogger(App.class);
@Option(names = { "--database" }, description = "'mongo' or 'mysql'")
private String databaseType = "mysql";
public static void main(String[] args) {
new CommandLine(new App()).execute(args);
}
@Override
public Void call() throws Exception {
EventQueue.invokeLater(() -> {
switch (databaseType) {
case "mysql":
EntityManagerFactory emf;
EntityManager entityManager;
try {
emf = Persistence.createEntityManagerFactory("name");
entityManager = emf.createEntityManager();
// other stuff
} catch (Exception e) {
LOGGER.log(Level.ERROR, "MySQL Exception", e);
}
break;
case "mongo":
// mongo stuff, no EntityManagerFactory here
break;
default:
LOGGER.log(Level.ERROR, "--database must be either 'mysql' or 'mongo'");
System.exit(1);
}
//...
try {
View view = new View();
view.setVisible(true);
} catch (Exception e) {
LOGGER.log(Level.ERROR, "Exception", e);
}
});
return null;
}
In mysql
case I am creating an EntityManagerFactory
and an EntityManager
. The entityManager
created here is passed as argument to the constructor of the repositories and used throughout the whole life of the application.
I was wondering what is the best practice about closing the entityManager
and the factory.
Searching in the documentation I found this:
Closing an EntityManagerFactory should not be taken lightly. It is much better to keep a factory open for a long period of time than to repeatedly create and close new factories. Thus, most applications will never close the factory, or only close it when the application is exiting.
So I was wondering, what is the difference between closing the factory and entity manager at application shutdown and not closing it? Also in my case I'm declaring emf
and entityManager
inside the mysql
case since are not required for mongodb
. In order to close them at application shutdown what should I do? I found something about Runtime.getRuntime().addShutdownHook()
. I tried using it like the code below, but it seems like it is not working.
try {
emf = Persistence.createEntityManagerFactory("name");
entityManager = emf.createEntityManager();
Thread closeHook = new Thread(() -> {
if (emf != null) {
entityManager.close();
emf.close();
LOGGER.log(Level.INFO, "Close entity manager and entity manager factory");
}
});
Runtime.getRuntime().addShutdownHook(closeHook);
// other stuff
} catch (Exception e) {
LOGGER.log(Level.ERROR, "MySQL Exception", e);
}
Short answer, yes, it should be closed. And the reason can be found at this answer:
So in my case, it is true that the EntityManager and factory are closed at application shutdown, but this does not ensure that they are properly dealt with on the other end. I didn't mention it in my question, but in fact the same thing holds true for the Mongo Client as well (see this answer):
About the implementation I made an interface that I called
DBInitializer
. I instantiated an object of typeMongoInitializer
orMySQLInitializer
(both implementingDBInitializer
) inside themain
method. See code for more clarity.DBInitializer:
MySQLInitializer:
MongoInitializer:
App: