When I was doing some work with OpenJPA, I found there is a possible memory leaking issue when OpenJPA is used for data access without the proper configuration of the parameter of auto detach (openjpa.AutoDetach).
The scenario that the memory issue may happen:
- One
EntityManagerFactory and One EntityManager are used throughout the whole session;
- The session contains a lot of queries, they are all read-only queries;
- Most queries are to retrieve database records based on primary keys;
- The records retrieved are approximately evenly distributed across primary keys;
- The database being queried has a large amount of data.
It seems that OpenJPA keeps references to data retrieved from the queries as cache. This prevents JVM garbage collecting them. Eventually, out of memory error happens.
This time, it seems that the call to clear method on EntityManager does not even work. Finally, we added the following properties and it seemed to solve the issue:
openjpa.AutoDetach=nontx-read
In fact, it is not a good practice to use EntityManager as shown in the scenario. Maybe the initial consideration is to re-use the single EntityManager as much as possible to help improve the performance of the session that is created in a batch process.
In general, the EntityManager object should not live in the whole long session. Instead it should be created on demand and closed and disregarded when finishing its unit of work. However, i haven’t got time to test how worse the performance can degrade if it is implemented this way.
The file “beans.xml” used for you application is already a kind of configuration file. However, this file is designed for the developer who can quickly customize the application with less coding changes. It is not recommended for an end user to change the file.
For example, you can, of course, define the database connection properties in the beans.xml file straight away. This way, every time a user wants the application to connect to a database at a different location, the beans.xml file needs to be modified. For a complicated application, the file can grow very complicated as well. Therefore, making changes to the beans.xml file directly may not seem obvious to the end user.
Fortunately, Spring have already come up with a solution for this problem. You can define the properties in the “beans.xml” file with place holders and use the Spring provided bean PropertyPlaceholderConfigurer to replace the place holders with the value from a properties file. Here is an example of the use of PropertyPlaceholderConfigurer.
In the example from the above link, it specifies one location for the properties file with a classpath: “classpath:com/foo/jdbc.properties”. In addition to that, you can also specify a file on your file system. When using a file on file system, remember to add “file:/” to an absolute path; otherwise, Spring will treat it as a relative path even the value starts with “/”.
PropertyPlaceholderConfigurer also allows you to define some default values using the property “properties“. The following is an example of using the default values if the file defined by “locations” property does not exist. Please note setting “ignoreResourceNotFound” to true is necessary. If not set, the application will throw exception if the defined property does not exist even we’ve defined the default values.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<value>file:///etc/app/jdbc.properties</value>
</property>
<property name="properties">
<props>
<prop key="jdbc.driverClassName">com.mysql.jdbc.Driver</prop>
<prop key="jdbc.url">jdbc:mysql://localhost:3306/mytechtip</prop>
<prop key="jdbc.username">jdbc_username</prop>
<prop key="jdbc.password">xxx</prop>
</props>
</property>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
Java beans in Spring can also have their collection properties populated in the beans.xml file. The collection properties here mean things like List, Map and Array.
For example, if you want to create a JPA EntityManagerFactory with a set of connection properties, these properties can be configured in beans.xml like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="entityManagerFactoryCreator"
class="com.mytechtip.example.EntityManagerFactoryCreator">
<property name="persistenceUnitName" value="HRMN_Repository"/>
<property name="jpaProperties">
<props>
<prop key="openjpa.ConnectionURL">the_url</prop>
<prop key="openjpa.ConnectionDriverName">the_driver_class_name</prop>
<prop key="openjpa.ConnectionUserName">the_user_name</prop>
<prop key="openjpa.ConnectionPassword">the_password</prop>
</props>
</property>
</bean>
</beans>
Here, jpaProperties in the actual java class “EntityManagerFactoryCreator” is an instance of Properties. And you can just use this properties to create EntityManagerFactory.
// ...
private Properties jpaProperties;
//...
public void setJpaProperties(Properties prop) {
this.jpaProperties = prop;
}
// ...
public EntityManagerFactory create() {
return Persistence.createEntityManagerFactory("myPersistenceUnit", prop);
}
Actually, Spring already have something similar for you to access EntityManagerFactory with ease. The beans used for the task are:
org.springframework.orm.jpa.LocalEntityManagerFactoryBean
org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
More information / examples can be found here about the support of JPA entity manager factory creatation in Spring.