Автоматическое создание ключей Hibernate с MySQL и Oracle
Я работаю над Java приложение, которое должно выполнять операции CRUD (используя спящий режим 4.3.8) на двух разных базах данных с одной и той же схемой базы данных. Есть в MySQL (версия 5.1.73) и Oracle (11g Express Edition Release 11.2.0.2.0 - 64bit) база данных.
классы Java с аннотации JPA были созданы из таблиц базы данных с генерацией кода Hibernate.
в проблема в том, что теперь нам нужно использовать автогенерацию первичного ключа, а MySQL использует GenerationType.Личность и Oracle использует GenerationType.Последовательность. Кроме того, нам нужна возможность вручную установить первичный ключ самостоятельно в некоторых редких случаях.
следующий код в аннотированном классе работает с автоматической генерацией ключей для обеих баз данных, но терпит неудачу, если первичный ключ установлен самостоятельно.
@GeneratedValue(strategy=GenerationType.AUTO, generator="sequence_generator")
@SequenceGenerator(name="sequence_generator", sequenceName="SEQUENCE1")
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
без @GeneratedValue и @SequenceGenerator аннотации можно установить первичный ключ вручную, но автоматическая генерация не работает.
2 ответов
даже если вы используете GenerationType.AUTO
без какого-либо конкретного параметра последовательности вы не сможете сохранить назначенные идентификаторы.
есть некоторые обходные пути, если вы готовы пойти на некоторые компромиссы:
один из способов переключиться на назначенные идентификаторы. Вы можете использовать UUID идентификаторы, которые работают как для MySQL, так и для Oracle, и вы также можете назначить значения вручную.
-
другой способ-использовать пользовательский генератор таблиц.
сначала вы определяете идентифицируемый интерфейс:
public interface Identifiable<T extends Serializable> { T getId(); }
затем вы расширяете генератор таблиц:
public class AssignedTableGenerator extends TableGenerator { @Override public Serializable generate(SessionImplementor session, Object obj) { if(obj instanceof Identifiable) { Identifiable identifiable = (Identifiable) obj; Serializable id = identifiable.getId(); if(id != null) { return id; } } return super.generate(session, obj); } }
этот генератор способен смешивать назначенные идентификаторы с синтетическими генерируемыми:
doInTransaction(session -> { for (int i = 0; i < 5; i++) { session.persist(new AssignTableSequenceIdentifier()); } AssignTableSequenceIdentifier tableSequenceIdentifier = new AssignTableSequenceIdentifier(); tableSequenceIdentifier.id = -1L; session.merge(tableSequenceIdentifier); session.flush(); });
формирование следующих утверждений:
select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update insert into sequence_table (sequence_name, next_val) values (default,1) update sequence_table set next_val=2 where next_val=1 and sequence_name=default select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update update sequence_table set next_val=3 where next_val=2 and sequence_name=default select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update update sequence_table set next_val=4 where next_val=3 and sequence_name=default select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update update sequence_table set next_val=5 where next_val=4 and sequence_name=default select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update update sequence_table set next_val=6 where next_val=5 and sequence_name=default select identityvs0_.id as id1_0_0_ from assigneTableIdentifier identityvs0_ where identityvs0_.id=-1 insert into assigneTableIdentifier (id) values (1, 2) insert into assigneTableIdentifier (id) values (2, 4) insert into assigneTableIdentifier (id) values (5, -1)
для Oracle вы можете объединить последовательность и назначенные генераторы, как описано в этот статья.
короче говоря, учитывая следующий генератор:
public class AssignedSequenceStyleGenerator
extends SequenceStyleGenerator {
@Override
public Serializable generate(SessionImplementor session,
Object obj) {
if(obj instanceof Identifiable) {
Identifiable identifiable = (Identifiable) obj;
Serializable id = identifiable.getId();
if(id != null) {
return id;
}
}
return super.generate(session, obj);
}
}
вы можете сопоставить его с вашими сущностями следующим образом:
@Id
@GenericGenerator(
name = "assigned-sequence",
strategy = "com.vladmihalcea.book.hpjp.hibernate.identifier.AssignedSequenceStyleGenerator",
parameters = @org.hibernate.annotations.Parameter(
name = "sequence_name",
value = "post_sequence"
)
)
@GeneratedValue(
generator = "assigned-sequence",
strategy = GenerationType.SEQUENCE
)
private Long id;
попробуйте что-то вроде этого:
@Id
@Column( name = "ID" )
@TableGenerator(
name = "AppSeqStore",
table = "APP_SEQ_STORE",
pkColumnName = "APP_SEQ_NAME",
pkColumnValue = "LISTENER_PK",
valueColumnName = "APP_SEQ_VALUE",
initialValue = 1,
allocationSize = 1 )
@GeneratedValue( strategy = GenerationType.TABLE, generator = "AppSeqStore" )
и эта таблица в базе данных:
CREATE TABLE APP_SEQ_STORE (
APP_SEQ_NAME VARCHAR(255) NOT NULL,
APP_SEQ_VALUE NUMBER(10) NOT NULL,
PRIMARY KEY(APP_SEQ_NAME)
)
INSERT INTO APP_SEQ_STORE VALUES ('LISTENER_PK', 0)
все это работает в Oracle, MS Sql Server и MySql, используя JBoss в качестве сервера приложений.
более подробная информация здесь: http://www.developerscrappad.com/408/java/java-ee/ejb3-jpa-3-ways-of-generating-primary-key-through-generatedvalue/