はじめに
タイトルにあるエラーが発生したときの調査結果を残しておく。載せてるコードは実際のものではなく、エラーを発生させるための最小限のコード。
環境
- Java8
- JavaEE7(参照実装Payara4.1)
エラーを発生させるプログラム
以下はEntityManagerをInjectさせるために定義
EntityManagerProducer.java
@ApplicationScoped
public class EntityManagerProducer {
@PersistenceContext
private EntityManager entityManager;
@Produces
@RequestScoped
public EntityManager getEntityManager() {
return entityManager;
}
}
User.java
@Entity
@Table(name = "user")
public class User {
@Id
private Integer id;
public Integer getId(){
return this.id;
}
}
UserController.java
@ApplicationScoped
@Path("users")
public class UserController {
@Inject
private EntityManager entityManager;
@GET
public Response list(){
IntStream.of(2,1,3).parallel().forEach(l -> {
entityManager.createQuery("SELECT u FROM User u WHERE u.id = :id", User.class)
.setParameter("id", l)
.getResultList()
.stream()
.findAny();
});
return Response.ok().build();
}
}
対応方法
その1)parallel()を使わない
UserController.java
@ApplicationScoped
@Path("users")
public class UserController {
@Inject
private EntityManager entityManager;
@GET
public Response list(){
IntStream.of(2,1,3).forEach(l -> {
entityManager.createQuery("SELECT u FROM User u WHERE u.id = :id", User.class)
.setParameter("id", l)
.getResultList()
.stream()
.findAny();
});
return Response.ok().build();
}
}
その2)EntityManagerのDIにPersistenceContextアノテーションを利用する
UserController.java
@ApplicationScoped
@Path("users")
public class UserController {
@PersistenceContext
private EntityManager entityManager;
@GET
public Response list(){
IntStream.of(2,1,3).parallel().forEach(l -> {
entityManager.createQuery("SELECT u FROM User u WHERE u.id = :id", User.class)
.setParameter("id", l)
.getResultList()
.stream()
.findAny();
});
return Response.ok().build();
}
}
おわりに
原因と対応方法はわかったけど、@PersistenceContext
と@Inject
のライフサイクルなど詳細な仕組みの違いなどはよく理解していない。ただ一つ言えることは、I/Oが発生する処理ではparallel
を利用しない方がいい。