Автор оригинала: Vlad Mihalcea.
Oracle предоставляет несколько псевдоколонок, и СТРОКИ
являются одной из них. Столбец ROWID
псевдо указывает адрес базовой записи базы данных, и , согласно документации Oracle , это самый быстрый способ ссылки на строку таблицы.
Как объяснено на Спросите ТОМА , есть некоторые операции, которые могут привести к изменению идентификатора строки (например, разбиение на разделы или сжатие таблиц). Если это так, то вам не следует полагаться на псевдоколонку ROWID, так как ее значение больше не соответствует.
Если ваша база данных никогда не выполняет операцию, которая запускает изменение идентификатора строки, вам следует рассмотреть возможность использования этого метода.
В этой статье я собираюсь показать вам, как вы можете получить доступ к записи базы данных по ее идентификатору строки
при использовании режима гибернации.
Первое, что нужно сделать, это аннотировать объект JPA, используя специфичный для гибернации @RowId .
Учитывая, что у нас есть следующие две сущности:
@Entity(name = "Post") @Table(name = "post") @RowId( "ROWID" ) public class Post { @Id private Long id; private String title; @OneToMany( cascade = CascadeType.ALL, mappedBy = "post", orphanRemoval = true ) private Listcomments = new ArrayList<>(); //Getters and setters omitted for brevity public void addComment(PostComment comment) { comments.add(comment); comment.setPost(this); } public void removeComment(PostComment comment) { comments.remove(comment); comment.setPost(null); } } @Entity(name = "PostComment") @Table(name = "post_comment") @RowId( "ROWID" ) public class PostComment { @Id @GeneratedValue private Long id; @ManyToOne( fetch = FetchType.LAZY ) private Post post; private String review; //Getters and setters omitted for brevity @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PostComment)) return false; return id != null && id.equals(((PostComment) o).getId()); } @Override public int hashCode() { return getClass().hashCode(); } }
Если вы задаетесь вопросом о необычных вариантах реализации equals и хэш-кода, вам следует прочитать следующую статью .
Сопоставление @RowId( "ROWID" )
указывает Hibernate использовать псевдоколонку ROWID
при доступе к записи базы данных при выполнении инструкции UPDATE
.
Предполагая, что в нашей базе данных есть следующие объекты:
Post post = new Post(); post.setId(1L); post.setTitle( "High-Performance Java Persistence" ); entityManager.persist(post); PostComment comment1 = new PostComment(); comment1.setReview("Great!"); post.addComment(comment1); PostComment comment2 = new PostComment(); comment2.setReview("To read"); post.addComment(comment2); PostComment comment3 = new PostComment(); comment3.setReview("Lorem Ipsum"); post.addComment(comment3);
При выполнении следующего тестового случая:
Post _post = doInJPA( entityManager -> { return entityManager.createQuery( "select p " + "from Post p " + "join fetch p.comments " + "where p.id = :id", Post.class) .setParameter( "id", 1L ) .getSingleResult(); } ); List_comments = _post.getComments(); _post.getComments().get( 0 ) .setReview( "Must read!" ); _post.removeComment( _comments.get( 2 ) ); doInJPA( entityManager -> { entityManager.merge( _post ); } );
Hibernate создает следующие инструкции SQL:
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_, p.ROWID AS rowid_0_, c.post_id AS post_id3_1_1_, c.review AS review2_1_1_, c.ROWID AS rowid_1_, c.post_id AS post_id3_1_0__, c.id AS id1_1_0__ FROM post p INNER JOIN post_comment c ON p.id = c.post_id WHERE p.id = 1 -- Merge SELECT query skipped for brevity UPDATE post_comment SET post_id = 1, review = 'Must read!' WHERE ROWID = AAAwmzAAEAAACZDAAA DELETE FROM post_comment WHERE id = 3
Оператор SELECT
включает псевдоколонку ROWID
, которая хранится в текущем контексте сохранения. Во время перехода слияния состояния сущности Hibernate копирует состояние отделенной сущности во вновь выбранные версии сущности , и механизм проверки на наличие ошибок распространяет изменения в базу данных.
Только оператор UPDATE
извлекает выгоду из столбца ROWID
псевдо, оператор DELETE
этого не делает (пока). HHH-11761 позаботится об этом ограничении, но оно будет рассмотрено только в версии 6.0.
Итак, почему вас вообще волнует ROWID
? В конце концов, у каждой сущности есть свой собственный идентификатор, и базовый Первичный ключ также индексируется.
Чтобы понять, почему стоит использовать псевдоколонку ROWID
, лучше визуализировать план выполнения вышеупомянутого UPDATE
оператора:
--------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| --------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | 1 | 537 | 1 (0)| | 1 | UPDATE | POST_COMMENT | | | | | 2 | TABLE ACCESS BY USER ROWID| POST_COMMENT | 1 | 537 | 1 (0)| ---------------------------------------------------------------------------------
Принимая во внимание, что при обновлении строки по ее идентификатору План выполнения:
------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| ------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | 1 | 538 | 1 (0)| | 1 | UPDATE | POST_COMMENT | | | | |* 2 | INDEX UNIQUE SCAN| SYS_C00281229 | 1 | 538 | 1 (0)| ------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID" = 1)
Хотя в этом тривиальном примере стоимость одинакова, на самом деле ДОСТУП К ТАБЛИЦЕ ПО пути доступа ПОЛЬЗОВАТЕЛЯ ROWID выполняется быстрее, чем сканирование по УНИКАЛЬНОМУ ИНДЕКСУ, поскольку экстрактор может загружать страницу данных напрямую, не обращаясь к индексу для извлечения ROWID
.
Hibernate-это не просто инструмент ORM, а полноценная платформа доступа к данным, предлагающая всевозможные оптимизации производительности. Если вы используете Oracle и выполняете множество операторов UPDATE
, имеет смысл использовать сопоставление @RowId
.