Рубрики
Без рубрики

Как работает каскадный тип.БЛОКИРОВКА работает в JPA и спящем режиме

Автор оригинала: Vlad Mihalcea.

После введения поддержки гибернации явной блокировки , а также Каскадных типов пришло время проанализировать Каскадный тип.БЛОКИРОВКА поведение.

Запрос на блокировку в режиме гибернации запускает внутреннее Событие блокировки . Связанный DefaultLockEventListener может каскадно передавать запрос на блокировку дочерним элементам объекта блокировки.

Начиная с каскадного типа.ВСЕ включает каскадный тип.БЛОКИРОВКА также стоит понимать, когда запрос на блокировку распространяется от Родителя к Дочерней сущности.

Мы начнем со следующей модели сущности:

Сообщение является Родительской сущностью как для Сведений о сообщении связи “один к одному”, так и для Комментария отношения “один ко многим”, и эти ассоциации помечены каскадным типом.ВСЕ :

@OneToMany(
    cascade = CascadeType.ALL, 
    mappedBy = "post", 
    orphanRemoval = true)
private List comments = new ArrayList<>();

@OneToOne(
    cascade = CascadeType.ALL, 
    mappedBy = "post",
    fetch = FetchType.LAZY)
private PostDetails details;

Во всех предстоящих тестовых случаях будет использоваться следующий график модели сущностей:

doInTransaction(session -> {
    Post post = new Post();
    post.setName("Hibernate Master Class");

    post.addDetails(new PostDetails());
    post.addComment(new Comment("Good post!"));
    post.addComment(new Comment("Nice post!"));

    session.persist(post);
});

Управляемая сущность загружается в текущем контексте сохранения, и все изменения состояния сущности преобразуются в DML инструкции.

Когда управляемая Родительская сущность блокируется:

doInTransaction(session -> {
    Post post = (Post) session.createQuery(
        "select p " +
        "from Post p " +
        "join fetch p.details " +
        "where " +
        "   p.id = :id")
    .setParameter("id", 1L)
    .uniqueResult();
    session.buildLockRequest(
        new LockOptions(LockMode.PESSIMISTIC_WRITE))
    .lock(post);
});

Блокируется только Родительская сущность, поэтому каскад предотвращается:

select id from Post where id = 1 for update

Hibernate определяет область Параметр блокировки , который (в соответствии с JavaDocs) должен разрешать передачу запроса на блокировку Дочерним сущностям:

“область применения” – это термин, определенный JPA. По сути, это каскадирование блокировки на ассоциации.

session.buildLockRequest(
    new LockOptions(LockMode.PESSIMISTIC_WRITE))
.setScope(true)
.lock(post);

Установка флага области действия ничего не меняет, только заблокированная управляемая сущность:

select id from Post where id = 1 for update

Помимо блокировки сущностей, запрос на блокировку также может повторно связать отдельные сущности. Чтобы доказать это, мы проверим график Post сущности до и после запроса на блокировку сущности:

void containsPost(Session session, 
    Post post, boolean expected) {
    assertEquals(expected, 
        session.contains(post));
    assertEquals(expected, 
        session.contains(post.getDetails()));
    for(Comment comment : post.getComments()) {
        assertEquals(expected, 
            session.contains(comment));
    }
}

Следующий тест демонстрирует, как каскадный тип.БЛОКИРОВКА работает для отдельных объектов:

//Load the Post entity, which will become detached
Post post = doInTransaction(session -> 
   (Post) session.createQuery(
        "select p " +
        "from Post p " +
        "join fetch p.details " +
        "join fetch p.comments " +
        "where " +
        "   p.id = :id")
.setParameter("id", 1L)
.uniqueResult());

//Change the detached entity state
post.setName("Hibernate Training");
doInTransaction(session -> {
    //The Post entity graph is detached
    containsPost(session, post, false);

    //The Lock request associates 
    //the entity graph and locks the requested entity
    session.buildLockRequest(
        new LockOptions(LockMode.PESSIMISTIC_WRITE))
    .lock(post);

    //Hibernate doesn't know if the entity is dirty
    assertEquals("Hibernate Training", 
        post.getName());

    //The Post entity graph is attached
    containsPost(session, post, true);
});
doInTransaction(session -> {
    //The detached Post entity changes have been lost
    Post _post = (Post) session.get(Post.class, 1L);
    assertEquals("Hibernate Master Class", 
        _post.getName());
});

Запрос на блокировку связывает график сущности, но текущий запущенный сеанс гибернации не знает, что сущность стала грязной, находясь в отключенном состоянии. Сущность просто подключается повторно без принудительного ОБНОВЛЕНИЯ или выбора текущего состояния базы данных для дальнейшего сравнения.

Как только объект станет управляемым, любые дальнейшие изменения будут обнаружены механизмом проверки на загрязнение, и сброс также распространит изменения до повторного присоединения. Если во время управления объектом не произойдет никаких изменений, очистка объекта не будет запланирована.

Если мы хотим убедиться, что состояние отделенной сущности всегда синхронизировано с базой данных, нам нужно использовать слияние или обновление .

Отсоединенные объекты распространяют параметры блокировки, когда для параметра область действия установлено значение true :

session.buildLockRequest(
    new LockOptions(LockMode.PESSIMISTIC_WRITE))
.setScope(true)
.lock(post);

Событие блокировки Post сущности распространяется на все Дочерние сущности (поскольку мы используем Каскадный тип.ВСЕ ):

select id from Comment where id = 1 for update
select id from Comment where id = 2 for update
select id from PostDetails where id = 1 for update
select id from Post where id = 1 for update

Каскадирование блокировок далеко не является простым или интуитивно понятным. Явная блокировка требует усердия (чем больше блокировок мы приобретаем, тем больше вероятность блокировки), и в любом случае вам лучше сохранить полный контроль над распространением блокировки дочерних сущностей. Аналогично рекомендациям по программированию с параллелизмом, поэтому ручная блокировка предпочтительнее автоматического распространения блокировки.

Код доступен на GitHub .