JPA/Hibernate: EntityManager.close() и IllegalStateException?
У меня есть служба регистрации в среде 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 дважды. (при первом вызове журнала звонки persist()
а также close()
, затем журнал вызывается снова и вызывает persist()
а также close()
снова). Я хочу понять, почему это работает и не бросает исключения, чтобы не получить неприятных сюрпризов в будущем.