Нарушение уникального ограничения при вставке после перезапуска сервера

Я использую сервер JBoss EAP 7 с базой данных Oracle 11g и Hibernate для JPA. Я заметил кое-что странное. Когда я впервые создаю базу данных и запускаю сервер, все работает нормально. Я отправляю запросы от клиента, а сервер сохраняет данные в БД.

Если я перезапущу сервер и попытаюсь сделать то же самое, я получу уникальное исключение нарушения ограничения для каждого запроса:

java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SCHEMA.SYS_C0010299) violated

at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:447)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:951)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:513)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:227)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:208)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1046)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1336)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3613)
at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3694)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1354)
at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:537)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)

Я проверил ограничение в sqlplus с помощью запроса ниже. (Я выполнил запрос как системный пользователь, а не как пользователь сервера, если это имеет значение).

SELECT A.TABLE_NAME,C.TABLE_NAME,COLUMN_NAME FROM ALL_CONS_COLUMNS A
JOIN ALL_CONSTRAINTS C ON A.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE C.CONSTRAINT_NAME = 'SYS_C0010299';

Кажется, это происходит с первичным ключом одной из моих таблиц. Этот первичный ключ создается с помощью последовательности.

@Id
@Column(name="ID_COL")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "SEQ_NAME_GEN")
@SequenceGenerator(name = "SEQ_NAME_GEN", sequenceName = "SEQ_NAME")
private Long id;

Если я создам новую новую БД, приложение сначала снова будет работать нормально, пока я не перезапущу сервер. Почему так происходит?

Это отношение класса сущности к другому классу сущности:

// other class
@OneToMany(cascade=CascadeType.ALL, mappedBy="otherClass")
@MapKey(name = "mapKey")
private Map<MapKey, ConstraintViolationEntityClass>
    map;

// problematic class (ConstraintViolationEntityClass)
@Column(name = "MAP_KEY")
@Enumerated(EnumType.ORDINAL)
private EnumType enumType;
@ManyToOne
@JoinColumn(name = "OTHER_CLASS_ID", nullable = false)
private OtherClass otherClass;

И это код SQL, который я использовал для создания таблицы для ConstraintViolationEntityClass:

create table schema.ConstraintViolationEntityTable (
  id_col number(10) not null primary key,
  map_key number(2) not null,
  other_class_id number(10) not null,
  constraint other_class_fk foreign key (other_class_id) references schema.other_class(id)
);

Это мой persistence.xml:

<persistence-unit name="unit1" transaction-type="JTA">
  <jta-data-source>java:jboss/OracleDS</jta-data-source>
  <exclude-unlisted-classes>false</exclude-unlisted-classes>
  <properties>
    <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/>
    <property name="hibernate.show_sql" value="true" />
    <property name="hibernate.format_sql" value="true" />
    <property name="hibernate.hbm2ddl.auto" value="validate" />
  </properties>
</persistence-unit>

По какой-то причине некоторые первичные ключи строк, вставленных успешными запросами, отрицательны. И проверяя dba_sequences, last_number последовательности - 43, хотя в таблице всего 24 строки (12 строк добавляются на каждый запрос клиента)


person devil0150    schedule 09.04.2018    source источник
comment
это таблица «многие ко многим», я, кажется, помню, что иногда Hibernate пытался вставить дважды в этом сценарии. Может быть, проверьте dba_sequences, убедитесь, что ваша последовательность не воссоздается, или что-то в этом роде   -  person Peter M    schedule 09.04.2018
comment
Не могли бы вы поделиться своими свойствами гибернации?   -  person Rohit    schedule 09.04.2018
comment
@PeterM Я добавил дополнительную информацию в описание единственного отношения таблицы. Я проверил последовательность до и после запуска сервера, и она не изменилась. @ Rohit Я добавлю persistence.xml в сообщение   -  person devil0150    schedule 09.04.2018
comment
Я нашел этот пост, он может помочь - stackoverflow.com/questions/ 12745751 /   -  person Peter M    schedule 09.04.2018
comment
похоже, что, возможно, Hibernate кэширует последовательность, но не обновляет оракул. Когда вы смотрите на last_number в dba_sequences, оказывается ли оно меньше, чем у вас в таблице?   -  person Peter M    schedule 09.04.2018
comment
@PeterM по какой-то причине sqlplus показывает отрицательные значения для большинства идентификаторов и для некоторых внешних ключей другой таблицы. Приращение на опущено, поэтому по умолчанию оно равно 1. Следует ли мне установить его на 50?   -  person devil0150    schedule 09.04.2018
comment
Я думаю, что @PeterM прав. Размер выделения SequenceGenerator по умолчанию - 50. Каков размер приращения вашей последовательности?   -  person Rohit    schedule 09.04.2018
comment
Поскольку ваш persistence.xml использует hibernate.hbm2ddl.auto=validate, как вы генерируете базу данных? Возможно ли, что последовательность будет создана иначе, чем если бы использовался hibernate.hbm2ddl.auto=create-drop?   -  person crizzis    schedule 09.04.2018
comment
каков ваш start_with для вашей последовательности в dba_sequences, wierd, что у вас будут отрицательные значения для вашего идентификатора   -  person Peter M    schedule 09.04.2018
comment
@crizzis Я создаю базу данных вручную с помощью сценария sql. последовательности создаются с помощью create sequence SEQ_NAME start with 1;. Как я могу проверить, отличается ли это от того, что требуется для гибернации?   -  person devil0150    schedule 10.04.2018
comment
Что ж, самый быстрый способ - позволить hbm2ddl выполнять свою работу и искать определение последовательности в all_sequences. вы также можете попробовать использовать инструмент создания схемы, как описано здесь: access.redhat.com/documentation/en-us/   -  person crizzis    schedule 10.04.2018
comment
@crizzis Я попробовал это с create-drop и увидел операторы sql, которые спящий режим использует для создания последовательностей. Единственная разница была increment by 50. Я модифицировал свой собственный сценарий, чтобы использовать его, и теперь он работает. Можете ли вы или PeterM написать это в качестве ответа? Если у вас есть объяснение, почему это может привести к нарушению ограничений и отрицательным идентификаторам, включите его.   -  person devil0150    schedule 10.04.2018
comment
Я бы сказал, что @PeterM заслуживает похвалы. Как он правильно указал, размер выделения по умолчанию - 50. Вы можете найти его в javadoc для @SequenceGenerator: docs.oracle.com/javaee/7/api/javax/persistence/.   -  person crizzis    schedule 11.04.2018


Ответы (1)


Как указано в ответе, на который ссылается PeterM, размер выделения по умолчанию для генераторов последовательностей составляет 50, что является основной причиной проблемы, поскольку вы определили последовательность с приращением 1. Я просто прокомментирую проблему с отрицательными значениями. .

Размер выделения 50 (установлен в SequenceGenerator.allocationSize) означает, что Hibernate будет:

  • создайте последовательность с INCREMENT BY 50 (если вы позволите)
  • взять следующее значение n из последовательности
  • начать выделять идентификаторы с n-50 по n
  • повторите два вышеуказанных шага, когда закончатся числа

Поскольку вы увеличили последовательность на 1, легко увидеть, откуда берутся отрицательные значения (и почему последуют нарушения ограничений). Если вы попытаетесь вставить более 50 строк, вы столкнетесь с нарушением ограничений без перезапуска сервера.

person crizzis    schedule 11.04.2018
comment
Связанный ответ - person Marmite Bomber; 11.04.2018