Автор оригинала: Vlad Mihalcea.
Вступление
При использовании PostgreSQL возникает соблазн использовать тип SERIAL или BIGSERIAL столбца для автоматического увеличения первичных ключей.
PostgreSQL 10 также добавил поддержку идентификатора, который ведет себя так же, как устаревший ПОСЛЕДОВАТЕЛЬНЫЙ или BIGSERIAL тип.
Эта статья покажет вам, что ПОСЛЕДОВАТЕЛЬНЫЙ, БОЛЬШОЙ и ИДЕНТИФИКАЦИЯ-не очень хорошая идея при использовании JPA и Hibernate.
СЕРИЙНЫЙ или БОЛЬШОЙ СЕРИАЛ
Если вы использовали MySQL, вы знаете, что AUTO_INCREMENT является очень популярным выбором. При переходе на PostgreSQL вы заметите, что типы столбцов SERIAL или BIGSERIAL могут использоваться так же, как AUTO_INCREMENT
в MySQL.
SERIAL-это автоматически увеличивающийся целочисленный столбец, занимающий 4 байта, в то время как BIGSERIAL-это автоматически увеличивающийся столбец bigint, занимающий 8 байт. За кулисами PostgreSQL будет использовать генератор последовательностей для генерации значений ПОСЛЕДОВАТЕЛЬНЫХ столбцов при вставке новой СТРОКИ.
Модель предметной области
Теперь, предполагая, что у нас есть следующая запись
таблица:
CREATE TABLE post ( id SERIAL NOT NULL, title VARCHAR(255), PRIMARY KEY (id) )
Для этой таблицы PostgreSQL создает последовательность с именем post_id_seq
, которая связана со столбцом id
SERIAL.
Таким образом, при вставке строки post
оператор INSERT может просто опустить столбец id
:
INSERT INTO post (title) VALUES ('High-Performance Java Persistence')
Столбец id
также является первичным ключом таблицы post
и использует ПОСЛЕДОВАТЕЛЬНЫЙ тип столбца. Столбцу id
будет автоматически присвоено следующее значение базового генератора последовательностей post_id_seq
.
Чтобы отобразить таблицу post
, нам нужен класс сущностей Post
, который выглядит следующим образом:
Свойство Post
сущность идентификатор
использует тип поколения.ИДЕНТИФИКАТОР
генератор, потому что тип SERIAL
действует как столбец AUTO_INCREMENTED.
@Entity(name = "Post") @Table(name = "post") public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String title; //Getters and setters omitted for brevity }
Теперь, чтобы увидеть, как используется post_id_seq
, рассмотрим следующий тестовый случай:
Post post1 = new Post(); post1.setTitle( "High-Performance Java Persistence, Part 1" ); entityManager.persist(post1); Post post2 = new Post(); post2.setTitle( "High-Performance Java Persistence, Part 2" ); entityManager.persist(post2); entityManager.flush(); assertEquals( 2, ( (Number) entityManager .createNativeQuery( "select currval('post_id_seq')") .getSingleResult() ).intValue() );
После вставки 2 Post
сущностей и очистки контекста сохранения текущее значение последовательности post_id_seq
базы данных будет равно 2, а следующее значение последовательности будет равно 3.
Пакетные вставки JDBC
Как бы удобно это ни выглядело, использование идентификатора с Hibernate не обходится без проблем .
Если мы включим пакетирование JDBC:
И сохранятся 3 Должности
организации:
for (int i = 0; i < 3; i++) { Post post = new Post(); post.setTitle( String.format( "High-Performance Java Persistence, Part %d", i + 1 ) ); entityManager.persist(post); }
Hibernate создаст следующие инструкции SQL INSERT:
INSERT INTO post (title) VALUES ('High-Performance Java Persistence, Part 1') INSERT INTO post (title) VALUES ('High-Performance Java Persistence, Part 2') INSERT INTO post (title) VALUES ('High-Performance Java Persistence, Part 3')
Таким образом, пакетирование будет отключено при вставке объектов.
Это связано с тем, что при сохранении сущности Hibernate необходимо знать идентификатор сущности , чтобы сгенерировать ключ, под которым сущность хранится в текущем контексте сохранения.
Чтобы узнать идентификатор, Hibernate необходимо выполнить инструкцию INSERT, поэтому к моменту сброса контекста сохранения все вставки уже выполнены. Таким образом, Hibernate больше не может паковать инструкции INSERT.
Вывод
Хотя это удобно и даже предлагается во многих книгах по PostgreSQL, типы столбцов SERIAL и BIGSERIAL не являются очень хорошим выбором при использовании JPA и Hibernate. Использование генератора ПОСЛЕДОВАТЕЛЬНОСТИ
является лучшей альтернативой, поскольку идентификатор может быть сгенерирован до выполнения инструкции INSERT.
За кулисами типы столбцов SERIAL и BIGSERIAL в любом случае используют последовательность базы данных, поэтому единственная разница заключается в том, что генератор ПОСЛЕДОВАТЕЛЬНОСТИ
вызывает последовательность в отдельном цикле базы данных. Однако это также можно оптимизировать с помощью оптимизаторов с объединением и с объединением-lo .
Если сервер базы данных находится рядом с серверами приложений, а сеть работает быстро, дополнительное перемещение базы данных туда и обратно не станет узким местом для производительности. По всем этим причинам вам следует предпочесть использование генератора ПОСЛЕДОВАТЕЛЬНОСТЕЙ вместо ИДЕНТИФИКАЦИИ, независимо от того, используете ли вы PostgreSQL, Oracle или SQL Server.