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() снова). Я хочу понять, почему это работает и не бросает исключения, чтобы не получить неприятных сюрпризов в будущем.

0 ответов

Другие вопросы по тегам