Автоматическое создание ключей 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 без какого-либо конкретного параметра последовательности вы не сможете сохранить назначенные идентификаторы.

есть некоторые обходные пути, если вы готовы пойти на некоторые компромиссы:

  1. один из способов переключиться на назначенные идентификаторы. Вы можете использовать UUID идентификаторы, которые работают как для MySQL, так и для Oracle, и вы также можете назначить значения вручную.

  2. другой способ-использовать пользовательский генератор таблиц.

    сначала вы определяете идентифицируемый интерфейс:

    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/