Java - JPA - Генераторы - @SequenceGenerator
Я изучаю JPA и в замешательстве @SequenceGenerator
аннотаций.
Насколько я понимаю, он автоматически присваивает значение числовым идентификаторам полей / свойств объекта.
Q1. Использует ли этот генератор последовательностей возможность генерирования растущих числовых значений в базе данных или генерирует число самостоятельно?
Q2. Если JPA использует функцию автоматического увеличения базы данных, будет ли она работать с хранилищами данных, которые не имеют функции автоматического увеличения?
Q3. Если JPA генерирует числовое значение самостоятельно, то как реализация JPA узнает, какое значение генерировать дальше? Проверяет ли он сначала базу данных, чтобы увидеть, какое значение было сохранено последним, чтобы сгенерировать значение (последнее + 1)?
Q4. Пожалуйста, также пролить свет на
sequenceName
а также allocationSize
свойства @SequenceGenerator
аннотаций.
6 ответов
sequenceName
это имя последовательности в БД. Так вы указываете последовательность, которая уже существует в БД. Если вы идете по этому маршруту, вы должны указать allocationSize
который должен быть таким же значением, которое последовательность БД использует в качестве своего "автоматического приращения".
Использование:
@GeneratedValue(generator="my_seq")
@SequenceGenerator(name="my_seq",sequenceName="MY_SEQ", allocationSize=1)
Если вы хотите, вы можете позволить ему создать последовательность для вас. Но чтобы сделать это, вы должны использовать SchemaGeneration для его создания. Для этого используйте:
@GeneratedValue(strategy=GenerationType.SEQUENCE)
Также вы можете использовать автогенерацию, которая будет использовать таблицу для генерации идентификаторов. Вы также должны использовать SchemaGeneration в какой-то момент при использовании этой функции, чтобы можно было создать таблицу генератора. Для этого используйте:
@GeneratedValue(strategy=GenerationType.AUTO)
Поскольку эти вопросы очень распространены при использовании JPA ad Hibernate, этот ответ является базовым для этой статьи, которую я написал в своем блоге.
А теперь вернемся к вашим вопросам:
Q1. Использует ли этот генератор последовательности возможности базы данных по генерации возрастающих числовых значений или генерирует число самостоятельно?
Используя GenerationType.SEQUENCE
стратегия по @GeneratedValue
аннотации, поставщик JPA попытается использовать объект последовательности базы данных базовой базы данных, поддерживающей эту функцию (например, Oracle, SQL Server, PostgreSQL, MariaDB).
Если вы используете MySQL, который не поддерживает объекты последовательности базы данных, Hibernate вернется к использованию GenerationType.TABLE
вместо этого, что нежелательно, поскольку генерация TABELE работает плохо.
Итак, не используйте GenerationType.SEQUENCE
стратегия с MySQL.
Q2. Если JPA использует функцию автоматического увеличения базы данных, будет ли она работать с хранилищами данных, у которых нет функции автоматического увеличения?
Я полагаю, вы говорите о GenerationType.IDENTITY
Когда ты говоришь database auto-increment feature
.
Чтобы использовать AUTO_INCREMENT
или IDENTITY
столбец, вам нужно использовать GenerationType.IDENTITY
стратегия по @GeneratedValue
аннотация.
Q3. Если JPA генерирует числовое значение самостоятельно, то как реализация JPA знает, какое значение генерировать следующим? Сначала он обращается к базе данных, чтобы узнать, какое значение было сохранено последним, чтобы сгенерировать значение (last + 1)?
Единственный раз, когда поставщик JPA генерирует значения самостоятельно, - это когда вы используете оптимизаторы на основе последовательностей, например:
Эти оптимизаторы являются мясом для уменьшения количества вызовов последовательности базы данных, поэтому они умножают количество значений идентификаторов, которые могут быть сгенерированы с помощью одного вызова последовательности базы данных.
Чтобы избежать конфликтов между оптимизаторами идентификаторов Hibernate и другими сторонними клиентами, вы должны использовать pooled
или pooled-lo
вместо того hi/lo
. Даже если вы используете устаревшее приложение, которое было разработано для использования hi/lo, вы можете перейти наpooled
или pooled-lo
оптимизаторы, как описано в этой статье.
Q4. Пожалуйста, пролей немного света на
sequenceName
а такжеallocationSize
свойства@SequenceGenerator
аннотация.
В sequenceName
Атрибут определяет объект последовательности базы данных, который будет использоваться для генерации значений идентификаторов. ЭТО объект, который вы создали с помощьюCREATE SEQUENCE
Заявление DDL.
Итак, если вы предоставите это сопоставление:
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "seq_post"
)
@SequenceGenerator(
name = "seq_post"
)
private Long id;
Hibernate будет использовать seq_post
объект базы данных для генерации значений идентификаторов:
SELECT nextval('hibernate_sequence')
В allocationSize
определяет множитель значения идентификатора, и если вы предоставите значение больше 1, то Hibernate будет использовать pooled
оптимизатор, чтобы уменьшить количество вызовов последовательности базы данных.
Итак, если вы предоставите это сопоставление:
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "seq_post"
)
@SequenceGenerator(
name = "seq_post",
allocationSize = 5
)
private Long id;
Затем, когда вы сохраняете 5 объектов:
for (int i = 1; i <= 5; i++) {
entityManager.persist(
new Post().setTitle(
String.format(
"High-Performance Java Persistence, Part %d",
i
)
)
);
}
Будет выполнено только 2 вызова последовательности базы данных вместо 5:
SELECT nextval('hibernate_sequence')
SELECT nextval('hibernate_sequence')
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 1', 1)
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 2', 2)
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 3', 3)
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 4', 4)
INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence, Part 5', 5)
Прочтите эту статью для получения более подробной информации.
Хотя этот вопрос очень старый, и я наткнулся на него из-за собственных проблем с последовательностями JPA 2.0 и Oracle.
Хочу поделиться своим исследованием некоторых вещей -
Отношение между @SequenceGenerator(allocSize) GenerationType.SEQUENCE и INCREMENT BY в определении последовательности базы данных
Убедитесь, что для @SequenceGenerator(allocSize) установлено то же значение, что и INCREMENT BY в определении последовательности базы данных, чтобы избежать проблем (то же самое относится и к начальному значению).
Например, если мы определим последовательность в базе данных со значением INCREMENT BY, равным 20, установите для параметра allocationsize в SequenceGenerator также значение 20. В этом случае JPA не будет выполнять вызов к базе данных, пока не достигнет следующих 20 отметок, в то время как он увеличивает каждый значение на 1 внутри. Это сохраняет вызовы базы данных, чтобы каждый раз получать следующий порядковый номер. Побочный эффект этого - всякий раз, когда приложение повторно развертывается или сервер перезапускается между ними, он вызывает базу данных, чтобы получить следующий пакет, и вы увидите скачки в значениях последовательности. Также нам нужно убедиться, что определение базы данных и настройки приложения синхронизированы, что может быть невозможно все время, поскольку обе они управляются разными группами, и вы можете быстро потерять контроль над ними. Если значение базы данных меньше, чем размер размещения, вы увидите ошибки ограничения PrimaryKey из-за повторяющихся значений Id. Если значение базы данных выше, чем размер размещения, вы увидите скачки в значениях Id.
Если для последовательности базы данных INCREMENT BY задано значение 1 (что обычно делают администраторы баз данных), установите для параметра allocSize также значение 1, чтобы они были синхронизированы, но JPA вызывает базу данных для получения следующего порядкового номера каждый раз.
Если вы не хотите каждый раз обращаться к базе данных, используйте стратегию GenerationType.IDENTITY и установите значение @Id, установленное триггером базы данных. С GenerationType.IDENTITY, как только мы вызываем em.persist, объект сохраняется в БД, а возвращаемому объекту присваивается значение id, поэтому нам не нужно делать em.merge или em.flush. (Это может зависеть от поставщика JPA.. Не уверен)
Еще одна важная вещь -
JPA 2.0 автоматически запускает команду ALTER SEQUENCE, чтобы синхронизировать allocSize и INCREMENT BY в последовательности базы данных. Поскольку в основном мы используем другое имя схемы (имя пользователя приложения), а не фактическую схему, в которой существует последовательность, а имя пользователя приложения не будет иметь привилегий ALTER SEQUENCE, в журналах может отображаться следующее предупреждение:
000004c1 Runtime W CWWJP9991W: openjpa.Runtime: Warn: невозможно кэшировать значения последовательности для последовательности "RECORD_ID_SEQ". Ваше приложение не имеет разрешения на запуск команды ALTER SEQUENCE. Убедитесь, что у него есть соответствующее разрешение на запуск команды ALTER SEQUENCE.
Поскольку JPA не может изменить последовательность, JPA каждый раз вызывает базу данных для получения следующего порядкового номера независимо от значения @SequenceGenerator.allocationSize. Это может быть нежелательным последствием, о котором мы должны знать.
Чтобы JPA не запускала эту команду, установите это значение в файле persistence.xml. Это гарантирует, что JPA не будет пытаться запустить команду ALTER SEQUENCE. Это пишет другое предупреждение, хотя -
00000094 Runtime W CWWJP9991W: openjpa.Runtime: Warn: для свойства "openjpa.jdbc.DBDictionary=disableAlterSeqenceIncrementBy" установлено значение true. Это означает, что оператор SQL "ALTER SEQUENCE...INCREMENT BY" не будет выполнен для последовательности "RECORD_ID_SEQ". OpenJPA выполняет эту команду, чтобы убедиться, что значение INCREMENT BY последовательности, определенное в базе данных, соответствует allocSize, который определен в последовательности объекта. Поскольку этот оператор SQL отключен, пользователь должен убедиться, что определение последовательности объекта соответствует последовательности, определенной в базе данных.
Как отмечено в предупреждении, здесь важно убедиться, что @ SequenceGenerator.allocationSize и INCREMENT BY в определении последовательности базы данных синхронизированы, включая значение по умолчанию @SequenceGenerator(allocSize), равное 50. В противном случае это приведет к ошибкам.
Я использую это, и это работает правильно
@Id
@GeneratedValue(generator = "SEC_ODON", strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "SEC_ODON", sequenceName = "SO.SEC_ODON",allocationSize=1)
@Column(name="ID_ODON", unique=true, nullable=false, precision=10, scale=0)
public Long getIdOdon() {
return this.idOdon;
}
У меня есть схема MySQL со значениями автогена. я использую strategy=GenerationType.IDENTITY
тег и, кажется, работает хорошо в MySQL, я думаю, что это должно работать и на большинстве движков БД.
CREATE TABLE user (
id bigint NOT NULL auto_increment,
name varchar(64) NOT NULL default '',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
User.java
:
// mark this JavaBean to be JPA scoped class
@Entity
@Table(name="user")
public class User {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private long id; // primary key (autogen surrogate)
@Column(name="name")
private String name;
public long getId() { return id; }
public void setId(long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name=name; }
}
Мне пришлось создать последовательность на postgresql после создания таблицы. Помните, что это не очень хорошая идея, чтобы Spring заботился о последовательности БД. Удалять@GeneratedValue(strategy=GenerationType.SEQUENCE)
,
добавить свою модель
@GeneratedValue(generator="my_seq")
@SequenceGenerator(name="my_seq",sequenceName="MY_SEQ", allocationSize=1)
и запустить на SQL
CREATE SEQUENCE my_seq
INCREMENT BY 1;