我在 JEE 环境中有一个日志服务,TransactionManagementType.BEAN
因为日志应该独立于 JTA 事务,所以我必须自己处理事务。
目前这个日志服务看起来像这样:
@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 方法两次。我可以保证,因为我在数据库中有两个条目,所以它可以按我的需要工作,但是当我阅读 entityManager.close() 方法的文档时:
关闭应用程序管理的实体管理器。调用 close 方法后,EntityManager 实例上的所有方法以及从中获取的任何 Query 和 TypedQuery 对象都将抛出 IllegalStateException,getProperties、getTransaction 和 isOpen 除外(将返回 false)。如果在实体管理器与活动事务相关联时调用此方法,则持久性上下文将保持受管理状态,直到事务完成。
抛出: IllegalStateException - 如果实体管理器是容器管理的
...这实际上不能工作并抛出一个,IllegalStateException
因为我两次使用相同的 EntityManager。(第一次调用 log 时,调用persist()
and close()
,然后再次调用 log 并再次调用persist()
and close()
)。我想了解它为什么会起作用并且不会抛出异常以免将来出现任何不好的惊喜。