ロギングはJTAトランザクションから独立している必要があるため、 JEE環境内にロギングサービスを使用してTransactionManagementType.BEAN
いるため、自分でトランザクションを処理する必要があります。
現在、このロギング サービスは次のようになっています。
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class LoggingService
{
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
@PersistenceUnit(unitName = "MyLoggingUnit")
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory)
{
this.entityManagerFactory = entityManagerFactory;
}
@PostConstruct
private void init()
{
entityManager = entityManagerFactory.createEntityManager();
}
private Logger logger = Logger.getLogger(LoggingService.class.getSimpleName());
public void log(LogLevel logLevel, String userId, String message)
{
LogEntry logEntry = new LogEntry(userId, message, logLevel);
try
{
entityManager.getTransaction().begin();
entityManager.persist(logEntry);
entityManager.getTransaction().commit();
}
catch (Exception e)
{
entityManager.getTransaction().rollback();
logger.log(Level.ERROR, e.getMessage(), e);
}
finally
{
entityManager.close();
}
}
public LastVisit getLastVisit(String userId)
{
LastVisit result = null;
try
{
entityManager.getTransaction().begin();
result = entityManager.find(LastVisit.class, userId);
entityManager.getTransaction().commit();
}
catch (Exception e)
{
entityManager.getTransaction().rollback();
logger.log(Level.ERROR, e.getMessage(), e);
}
finally
{
entityManager.close();
}
return result;
}
public void setLastVisit(LastVisit lastVisit)
{
try
{
entityManager.getTransaction().begin();
entityManager.persist(lastVisit);
entityManager.getTransaction().commit();
}
catch (Exception e)
{
entityManager.getTransaction().rollback();
logger.log(Level.ERROR, e.getMessage(), e);
}
finally
{
entityManager.close();
}
}
}
このサービスを使用するエンドポイントは次のとおりです。
@Path("/recipe")
@Produces(MediaType.APPLICATION_JSON)
public class RecipeEndpoint
{
private RecipeService recipeService;
private LoggingService loggingService;
@EJB
public void setRecipeService(RecipeService recipeService)
{
this.recipeService = recipeService;
}
@EJB
public void setLoggingService(LoggingService loggingService)
{
this.loggingService = loggingService;
}
@POST
@Path("/add")
@Consumes(MediaType.APPLICATION_JSON)
public Response addRecipe(RecipeBo recipeBo)
{
loggingService.log(LogLevel.OK, recipeBo.getUserId(), "addRecipe: " + recipeBo);
try
{
recipeService.addRecipe(recipeBo);
loggingService.log(LogLevel.OK, recipeBo.getUserId(), "recipe successfully added: " + recipeBo);
return Response.ok().entity(new ResponseObject()).build();
}
catch (BadRequestException e)
{
ResponseObject responseObject = new ResponseObject();
responseObject.registerException(RecipeService.SAFE_LOCK_TRIGGERED_TEXT);
return Response.status(Status.BAD_REQUEST).entity(responseObject).build();
}
catch (Exception e)
{
loggingService.log(LogLevel.ERROR, recipeBo.getUserId(), "an error occured while adding a recipe: " + ExceptionToStringMapper.map(e));
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(new ResponseObject(e)).build();
}
}
}
レシピを追加すると、log メソッドが 2 回呼び出されます。データベースに2つのエントリがあるため、必要に応じて機能することを保証できますが、entityManager.close() メソッドのドキュメントを読むと:
アプリケーション管理のエンティティ マネージャーを閉じます。close メソッドが呼び出された後、EntityManager インスタンスのすべてのメソッドと、それから取得されたすべての Query および TypedQuery オブジェクトは、getProperties、getTransaction、および isOpen (false を返す) を除いて、IllegalStateException をスローします。エンティティ マネージャがアクティブなトランザクションに関連付けられているときにこのメソッドが呼び出された場合、永続化コンテキストはトランザクションが完了するまで管理されたままになります。
例外: IllegalStateException - エンティティ マネージャがコンテナ管理の場合
IllegalStateException
...同じ EntityManager を 2 回使用するため、これは実際には機能してスローされてはなりません。(最初に log が呼び出され、persist()
andclose()
が呼び出され、次に log が再度呼び出され、and が再度呼び出されますpersist()
) close()
。なぜそれが機能し、将来的に悪い驚きを起こさないように例外をスローしないのかを理解したい.