2017-09-08 17 views
3

Jersey JAX-RSにリクエストを受け取り、HibernateのJPA実装を使用するRESTクライアントがデータベースからデータを取得し、JSONを返します。entitymanagerを使用する正しい方法

共有エンティティマネージャを使用するとパフォーマンスはかなり良いですが、複数のリクエストがある場合は、主にNullPointerExceptionという例外をHibernateから受け取ります。

要求ごとにエンティティマネージャを使用しても、例外は発生しませんがパフォーマンスは低下します。

EntityManagerを適切に処理する方法は何ですか?

EDIT:

reqUser = EntityManagerHelper.getEntityManager().find(User.class, userId); 
に私はEntityManagerHelper.java を使用してEntityManagerHelper.getEntityManagerを(使用するEntityManagerの参照を変更した以下のアルバートさんのコメントに続いて

例えば

reqUser = entityManager.find(User.class, userId); 

EDIT end

以下は私が使用したコードです。

Jersey.java

package local.domain; 

import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces; 
import javax.ws.rs.QueryParam; 
import javax.ws.rs.core.MediaType; 
import javax.ws.rs.core.Response; 

@Path("/testJersey") 
public class Jersey { 

    static MainClass mainClass = new MainClass(); 

    @GET 
    @Produces("application/json") 
    public Response getAccounts(@QueryParam(value = "userId") final int userId) { 
     String jsonString = mainClass.getAccounts(userId); 
     return Response.ok(jsonString, MediaType.APPLICATION_JSON).build(); 
    }// getJson 


    @Path("/fields/") 
    @GET 
    @Produces("application/json") 
    public Response getAccountFields(@QueryParam(value = "accountId") final int accountId) { 
     String jsonString = mainClass.getAccountFields(accountId); 
     return Response.ok(jsonString, MediaType.APPLICATION_JSON).build(); 
    }// getJson 
} 

MainClass.java

package local.domain; 

import javax.persistence.EntityManager; 
import javax.persistence.EntityManagerFactory; 
import javax.persistence.Persistence; 

import com.google.gson.Gson; 
import com.google.gson.GsonBuilder; 

import local.domain.pojos.Account; 
import local.domain.pojos.User; 

public class MainClass { 
    private EntityManagerFactory entityManagerFactory = null; 
    private EntityManager entityManager; 

    boolean sharedEntityManager = true; 

    public MainClass() { 
     entityManagerFactory = Persistence.createEntityManagerFactory("peristenceConfig"); 
     entityManager = entityManagerFactory.createEntityManager(); 
    } 

    public String getAccounts(int userId) { 
     String result = null; 
     try { 
      User reqUser = null; 
      EntityManager entityManagerLocal = null; 

      if (sharedEntityManager) 
       reqUser = entityManager.find(User.class, userId); 
      else { 
       entityManagerLocal = entityManagerFactory.createEntityManager(); 
       reqUser = entityManagerLocal.find(User.class, userId); 
      } 

      Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); 
      result = gson.toJson(reqUser); 
      if (!sharedEntityManager) 
       entityManagerLocal.close(); 
     } catch (Exception exp) { 
      System.err.println("Exception with userId"+userId); 
      exp.printStackTrace(); 
     } 
     return result; 
    } 

    public String getAccountFields(int accountId) { 
     String result = null; 
     try { 
      Account reqAccount = null; 
      EntityManager entityManagerLocal = null; 

      if (sharedEntityManager) 
       reqAccount = entityManager.find(Account.class, accountId); 
      else { 
       entityManagerLocal = entityManagerFactory.createEntityManager(); 
       reqAccount = entityManagerLocal.find(Account.class, accountId); 
      } 

      Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); 
      result = gson.toJson(reqAccount); 
      if (!sharedEntityManager) 
       entityManagerLocal.close(); 
     } catch (Exception exp) { 
      System.err.println("Exception with accountId"+accountId); 
      exp.printStackTrace(); 
     } 
     return result; 
    } 
} 

<?xml version="1.0" encoding="UTF-8"?> 
<persistence version="2.1" 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"> 
    <persistence-unit name="peristenceConfig"> 
     <class>local.domain.pojos.Account</class> 
     <class>local.domain.pojos.AccountField</class> 
     <class>local.domain.pojos.User</class> 
     <properties> 
      <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> 
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" /> 
      <property name="hibernate.show_sql" value="true" /> 
      <property name="hibernate.connection.url" value="jdbc:mysql://localhost/jpatest" /> 
      <property name="hibernate.default_schema" value="jpatest" /> 
      <property name="hibernate.connection.username" value="username" /> 
      <property name="hibernate.connection.password" value="password" /> 
     </properties> 
    </persistence-unit> 
</persistence> 

のpom.xmlのpersistence.xml

<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>Snippet</groupId> 
    <artifactId>Snippet</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 
    <packaging>war</packaging> 
    <build> 
    <sourceDirectory>src</sourceDirectory> 
    <plugins> 
     <plugin> 
     <artifactId>maven-compiler-plugin</artifactId> 
     <version>3.5.1</version> 
     <configuration> 
      <source>1.8</source> 
      <target>1.8</target> 
     </configuration> 
     </plugin> 
     <plugin> 
     <artifactId>maven-war-plugin</artifactId> 
     <version>3.0.0</version> 
     <configuration> 
      <warSourceDirectory>WebContent</warSourceDirectory> 
     </configuration> 
     </plugin> 
    </plugins> 
    </build> 
    <properties> 
     <jersey.version>2.26-b02</jersey.version> 
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    </properties> 
    <dependencyManagement> 
     <dependencies> 
      <dependency> 
       <groupId>org.glassfish.jersey</groupId> 
       <artifactId>jersey-bom</artifactId> 
       <version>${jersey.version}</version> 
       <type>pom</type> 
       <scope>import</scope> 
      </dependency> 
     </dependencies> 
    </dependencyManagement> 
    <repositories> 
     <repository> 
      <id>JBoss repository</id> 
      <url>http://repository.jboss.org/nexus/content/groups/public/</url> 
     </repository> 
    </repositories> 
    <dependencies> 
     <dependency> 
      <groupId>org.glassfish.jersey.containers</groupId> 
      <artifactId>jersey-container-servlet-core</artifactId> 
     </dependency> 
     <dependency> 
     <groupId>javax.ws.rs</groupId> 
     <artifactId>jsr311-api</artifactId> 
     <version>1.1.1</version> 
    </dependency> 
    <dependency> 
     <groupId>org.hibernate.javax.persistence</groupId> 
     <artifactId>hibernate-jpa-2.1-api</artifactId> 
     <version>1.0.0.Final</version> 
    </dependency> 
    <dependency> 
     <groupId>com.google.code.gson</groupId> 
     <artifactId>gson</artifactId> 
     <version>2.8.0</version> 
    </dependency> 
    <dependency> 
     <groupId>mysql</groupId> 
     <artifactId>mysql-connector-java</artifactId> 
     <version>5.1.6</version> 
    </dependency> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-core</artifactId> 
     <version>5.2.6.Final</version> 
    </dependency> 
    </dependencies> 
</project> 

のPOJO User.java Account.java AccountField.java

package local.domain.pojos; 

import java.io.Serializable; 
import java.util.List; 

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 
import javax.persistence.JoinColumn; 
import javax.persistence.ManyToOne; 
import javax.persistence.NamedQuery; 
import javax.persistence.OneToMany; 
import javax.persistence.Table; 

import com.google.gson.annotations.Expose; 


/** 
* The persistent class for the tbl_account database table. 
* 
*/ 
@Entity 
@Table(name="tbl_account") 
@NamedQuery(name="Account.findAll", query="SELECT a FROM Account a") 
public class Account implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @Column(name="account_id") 
    @Expose 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private int accountId; 

    @Column(name="account_status") 
    @Expose 
    private String accountStatus; 

    //bi-directional many-to-one association to User 
    @ManyToOne 
    @JoinColumn(name="user_id") 
    private User tblUser; 

    //bi-directional many-to-one association to AccountField 
    @OneToMany(mappedBy="tblAccount") 
    @Expose 
    private List<AccountField> tblAccountFields; 

    public Account() { 
    } 

    public int getAccountId() { 
     return this.accountId; 
    } 

    public void setAccountId(int accountId) { 
     this.accountId = accountId; 
    } 

    public String getAccountStatus() { 
     return this.accountStatus; 
    } 

    public void setAccountStatus(String accountStatus) { 
     this.accountStatus = accountStatus; 
    } 

    public User getTblUser() { 
     return this.tblUser; 
    } 

    public void setTblUser(User tblUser) { 
     this.tblUser = tblUser; 
    } 

    public List<AccountField> getTblAccountFields() { 
     return this.tblAccountFields; 
    } 

    public void setTblAccountFields(List<AccountField> tblAccountFields) { 
     this.tblAccountFields = tblAccountFields; 
    } 

    public AccountField addTblAccountField(AccountField tblAccountField) { 
     getTblAccountFields().add(tblAccountField); 
     tblAccountField.setTblAccount(this); 

     return tblAccountField; 
    } 

    public AccountField removeTblAccountField(AccountField tblAccountField) { 
     getTblAccountFields().remove(tblAccountField); 
     tblAccountField.setTblAccount(null); 

     return tblAccountField; 
    } 

} 

package local.domain.pojos; 

import java.io.Serializable; 

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 
import javax.persistence.JoinColumn; 
import javax.persistence.ManyToOne; 
import javax.persistence.NamedQuery; 
import javax.persistence.Table; 

import com.google.gson.annotations.Expose; 


/** 
* The persistent class for the tbl_account_field database table. 
* 
*/ 
@Entity 
@Table(name="tbl_account_field") 
@NamedQuery(name="AccountField.findAll", query="SELECT a FROM AccountField a") 
public class AccountField implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @Column(name="account_field_id") 
    @Expose 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private int accountFieldId; 

    @Column(name="account_field_label") 
    @Expose 
    private String accountFieldLabel; 

    //bi-directional many-to-one association to Account 
    @ManyToOne 
    @JoinColumn(name="account_id") 
    private Account tblAccount; 

    public AccountField() { 
    } 

    public int getAccountFieldId() { 
     return this.accountFieldId; 
    } 

    public void setAccountFieldId(int accountFieldId) { 
     this.accountFieldId = accountFieldId; 
    } 

    public String getAccountFieldLabel() { 
     return this.accountFieldLabel; 
    } 

    public void setAccountFieldLabel(String accountFieldLabel) { 
     this.accountFieldLabel = accountFieldLabel; 
    } 

    public Account getTblAccount() { 
     return this.tblAccount; 
    } 

    public void setTblAccount(Account tblAccount) { 
     this.tblAccount = tblAccount; 
    } 

} 

package local.domain.pojos; 

import java.io.Serializable; 
import java.sql.Timestamp; 
import java.util.List; 

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 
import javax.persistence.Lob; 
import javax.persistence.NamedQuery; 
import javax.persistence.OneToMany; 
import javax.persistence.Table; 

import com.google.gson.annotations.Expose; 


/** 
* The persistent class for the tbl_user database table. 
* 
*/ 
@Entity 
@Table(name="tbl_user") 
@NamedQuery(name="User.findAll", query="SELECT u FROM User u") 
public class User implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @Column(name="user_id") 
    @Expose 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private int userId; 

    @Lob 
    @Column(name="user_email") 
    @Expose 
    private String userEmail; 

    @Column(name="user_last_login") 
    @Expose 
    private Timestamp userLastLogin; 

    @Column(name="user_name") 
    @Expose 
    private String userName; 

    //bi-directional many-to-one association to Account 
    @OneToMany(mappedBy="tblUser") 
    @Expose 
    private List<Account> tblAccounts; 

    public User() { 
    } 

    public int getUserId() { 
     return this.userId; 
    } 

    public void setUserId(int userId) { 
     this.userId = userId; 
    } 

    public String getUserEmail() { 
     return this.userEmail; 
    } 

    public void setUserEmail(String userEmail) { 
     this.userEmail = userEmail; 
    } 

    public Timestamp getUserLastLogin() { 
     return this.userLastLogin; 
    } 

    public void setUserLastLogin(Timestamp userLastLogin) { 
     this.userLastLogin = userLastLogin; 
    } 

    public String getUserName() { 
     return this.userName; 
    } 

    public void setUserName(String userName) { 
     this.userName = userName; 
    } 

    public List<Account> getTblAccounts() { 
     return this.tblAccounts; 
    } 

    public void setTblAccounts(List<Account> tblAccounts) { 
     this.tblAccounts = tblAccounts; 
    } 

    public Account addTblAccount(Account tblAccount) { 
     getTblAccounts().add(tblAccount); 
     tblAccount.setTblUser(this); 

     return tblAccount; 
    } 

    public Account removeTblAccount(Account tblAccount) { 
     getTblAccounts().remove(tblAccount); 
     tblAccount.setTblUser(null); 

     return tblAccount; 
    } 

} 

答えて

2

"正しい" 方法は、アプリケーションの種類ごとに異なる場合があります。あなたの場合、私はEntityManagerを共有することは、(あなたが既に経験している)望ましくない動作を引き起こすと思います。

私はリクエストごとのアプローチが実装する安全な方法だと思います。別の方法としてThreadEntityManagerを使用し、これを管理するにはThreadLocalを使用します。これがSpringの動作です。しかし、これはあなたの実装をより複雑にするかもしれません。

EDIT:NIOを使用している場合もThreadあたりEntityManagerを使用して言及してもいいが、同じThread内で複数のリクエストを処理し、問題を引き起こす可能性があります。

関連する問題