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

Гибернация коллекций оптимистичная блокировка

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

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

Механизм проверки на загрязнение обнаруживает изменения состояния сущности и увеличивает версию сущности. Хотя основные изменения свойств всегда принимаются во внимание, коллекции Hibernate в этом отношении более тонкие.

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

В объектно-ориентированном пространстве эта ассоциация может быть представлена в обоих направлениях. У нас может быть ссылка “много к одному” от ребенка к родителю, и у родителя также может быть коллекция “один ко многим детям”.

Поскольку обе стороны потенциально могут контролировать состояние внешнего ключа базы данных, мы должны убедиться, что только одна сторона является владельцем этой связи. В базу данных передаются только изменения состояния владельца стороны. Сторона, не являющаяся владельцем, традиционно называется обратной стороной.

Далее я опишу наиболее распространенные способы моделирования этой ассоциации.

Однонаправленное сопоставление ассоциаций на стороне родителя-владельца-ребенка

Только родительская сторона имеет коллекцию @OneToMany неинверсных дочерних элементов. Дочерняя сущность вообще не ссылается на родительскую сущность.

@Entity(name = "post")
public class Post {
	...
	@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
	private List comments = new ArrayList();
	...
}	

Сопоставление однонаправленного сопоставления родительских и дочерних компонентов на стороне владельца

Дочерняя сторона не всегда должна быть сущностью, и вместо этого мы могли бы смоделировать ее как тип компонента . Встраиваемый объект (тип компонента) может содержать как базовые типы, так и сопоставления ассоциаций, но он никогда не может содержать @Id. Встраиваемый объект сохраняется/удаляется вместе с его владельцем.

У родителя есть @ElementCollection дочерняя ассоциация. Дочерняя сущность может ссылаться на родительскую только через не подлежащую запросу аннотацию Hibernate @Parent .

@Entity(name = "post")
public class Post {
    ...
    @ElementCollection
    @JoinTable(name = "post_comments", joinColumns = @JoinColumn(name = "post_id"))
    @OrderColumn(name = "comment_index")
    private List comments = new ArrayList();
    ...

    public void addComment(Comment comment) {
        comment.setPost(this);
        comments.add(comment);
    }
}   

@Embeddable
public class Comment {
    ...
    @Parent
    private Post post;
    ...
}

Двунаправленное сопоставление ассоциаций родитель-владелец-сторона-потомок

Родитель является стороной-владельцем, поэтому у него есть @OneToMany неинверсная (без директивы mappedBy) дочерняя коллекция. Дочерняя сущность ссылается на родительскую сущность через ассоциацию @ManyToOne , которая не может быть вставлена или обновлена:

@Entity(name = "post")
public class Post {
    ...
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List comments = new ArrayList();
    ...

    public void addComment(Comment comment) {
        comment.setPost(this);
        comments.add(comment);
    }
}   

@Entity(name = "comment")
public class Comment {
    ...
    @ManyToOne
    @JoinColumn(name = "post_id", insertable = false, updatable = false)
    private Post post;
    ...
}

Двунаправленное сопоставление дочерних связей со стороны родителей

Дочерняя сущность ссылается на родительскую сущность через ассоциацию @ManyToOne , а у родителя есть коллекция mappedBy | @OneToMany children. Родительская сторона является обратной стороной, поэтому в базу данных передаются только изменения состояния @ManyToOne.

Даже если есть только одна сторона-владелец, всегда рекомендуется синхронизировать обе стороны с помощью методов add/removeChild ().

@Entity(name = "post")
public class Post {
    ...
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "post")
    private List comments = new ArrayList();
    ...

    public void addComment(Comment comment) {
        comment.setPost(this);
        comments.add(comment);
    }
}   

@Entity(name = "comment")
public class Comment {
    ...
    @ManyToOne
    private Post post;  
    ...
}

Сопоставление однонаправленной ассоциации дочернего владельца со стороны родителя

Дочерняя сущность ссылается на родительскую через ассоциацию @ManyToOne . У родителя нет коллекции @OneToMany children, поэтому дочерняя сущность становится стороной-владельцем. Это сопоставление ассоциаций напоминает связь внешнего ключа реляционных данных.

Раздел 3.4.2 спецификации JPA 2.1 определяет оптимистическую блокировку следующим образом:

Атрибут версии обновляется средой выполнения поставщика сохраняемости при записи объекта в базу данных. Все поля, не связанные с отношениями, и соответствующие связи, а также все отношения, принадлежащие сущности, включаются в проверки версий[35].

[35] Сюда входят отношения, принадлежащие, поддерживаемые в таблицах соединений

Только дочерняя коллекция на стороне владельца может обновлять родительскую версию.

Давайте проверим, как тип ассоциации “родитель-потомок” влияет на управление родительскими версиями. Поскольку мы заинтересованы в грязной проверке коллекции детей, однонаправленная родительская ассоциация, принадлежащая ребенку, будет