Переопределить аннотации спящего режима

Я разрабатываю Java-приложение, которое использует Hibernate и связано с экземпляром Oracle. Другой клиент хочет использовать одно и то же приложение, но требует его запуска на MS SQL Server. Я хотел бы избежать внесения изменений в существующие аннотации и вместо этого создать пакет xml файлов, которые мы можем удалить в зависимости от среды.

Один из способов сделать это - использовать конфигурацию JPA XML для переопределения существующих аннотаций классов. Однако JPA не поддерживает генераторные генераторы, что является требованием из-за структуры нашей старой базы данных. Другой способ, которым я занимаюсь, - использовать Hibernate XML configs для переназначения целых классов и доступа к тегу generator xml. Это решение имеет некоторые проблемы:

  • Hibernate не позволяет выборочно переопределять элементы сущности
  • Hibernate не позволяет повторно отображать один и тот же класс (например, org.hibernate.AnnotationException: Use of the same entity name twice)

Есть ли у кого-нибудь опыт переопределения аннотаций с использованием файлов конфигурации Hibernate XML или JPA - единственный способ?

Обновление с помощью примера

В Oracle, Последовательности используются для генерации уникальных идентификаторов при вставке новых записей в базу данных. Затем идентификатор будет аннотирован следующим образом:

@Id
@GeneratedValue(generator="EXAMPLE_ID_GEN", strategy=GenerationType.SEQUENCE)
@SequenceGenerator(name="EXAMPLE_ID_GEN", sequenceName="SEQ_EXAMPLE_ID")
@Column(name = "EXAMPLE_ID")
public String getExampleId() {
    return this.exampleId;
}

Однако MS SQL Server не имеет концепции последовательностей (идеологических различий). Поэтому вы можете использовать генератор таблицы для имитации последовательностей.

@Id
@GeneratedValue(generator="EXAMPLE_ID_GEN", strategy=GenerationType.TABLE)
@TableGenerator(name="EXAMPLE_ID_GEN", tableName="SEQUENCE", valueColumnName="VALUE", pkColumnName="SEQUENCE", pkColumnValue="EXAMPLE_ID")
public String getExampleId() {
    return this.exampleId;
}

Две различные конфигурации для двух разных типов баз данных. Имейте в виду, что это устаревшая база данных, и мы не собираемся переписывать наше приложение для поддержки идентификаторов SQL Server, собственного генератора идентификаторов для SQL Server (для чего также потребуется другая аннотация).

Чтобы облегчить это, я рассмотрел использование Hibernate @GenericGenerator и назовет его классу собственного создания, которое моделирует org.hibernate.id.SequenceGenerator (или что-то подобное), а также настраивает структуру таблицы, расширяя org.hibernate.id.TableStructure.

Вернуться к исходному вопросу - возможно ли это с помощью переопределения XML?

Как я решил эту проблему

Итак, в конце концов, я обнаружил, что JPA и Hibernate не предоставляют функциональные возможности, которые я искал. Вместо этого я создал специальный генератор, который проверил диалоги базы данных и соответствующим образом установил TableStructure. Когда я изучил все варианты, я закончил использование аннотации Hibernate @GenericGenerator. Это пример аннотации генерации Id:

@Id
@GeneratedValue(generator="EXAMPLE_ID_GEN")
@GenericGenerator(name = "EXAMPLE_ID_GEN", strategy="com.my.package.CustomIdGenerator", parameters = {
        @Parameter(name = "parameter_name", value="parameter_value")
})
public String getExampleId() {
    return this.exampleId;
}

Это решение требует, чтобы каждый объект Hibernate был модифицирован с помощью нового генератора Id.

Ответ 1

Я думаю, что если вы не используете AnnotationConfiguration при настройке SessionFactory, аннотации будут опущены.

Итак, используйте Configuration.

Ответ 2

Для вашей проблемы с генератором (для которой обычно было бы использовать "родной генератор", но не работает для вас из-за работы с устаревшим db), возможно, вы можете расширить SQLServerDialect и переопределить getNativeIdentifierGeneratorClass, чтобы вернуть (возможно, настраиваемый) генератор, который делает то, что вам нужно для вашего старого db.

Ответ 3

Если вы переписываете аннотации в XML файлах HBM, вы можете поддерживать два набора таких XML и выбирать, какие из них использовать с помощью директив сопоставления Hibernate. Я сделал это в Hibernate Core, но не в среде J2EE/JPA, поэтому я не знаю, есть ли какие-либо ошибки в этом отношении.

Самый большой недостаток - это, вероятно, большая работа, чтобы удалить все ваши аннотации и перестроить их в XML.

Ответ 4

Я бы сказал, что если ваши аннотации являются специфичными для базы данных, вы делаете это неправильно.

Ответ 5

Я столкнулся с необходимостью сочетать n-match legacy с новыми схемами/базами данных в приложении Grails (GORM), которое, конечно же, запускает Hibernate 3 под ним.

Не сказал бы "вы делаете это неправильно" - но я бы сохранил JPA @Annotations до самых оснований, таких как @Entity и @Column, и оставьте его на диалекте Hibernate, который также указан в файле конфигурации XML.

Вы можете поэкспериментировать с подклассом Oracle10gDialect с тем, который назначает генератор последовательности для всех таблиц, а не Sybase, который этого не делает.

Пожалуйста, просмотрите сообщение о том, как это реализовать.

UPDATE: Что мы и Джеймс предлагаем (почти в ту же минуту), это настроить несколько секций последовательности сохранения вашего файла persistence.xml.

Это позволяет использовать @Entity и @Id без предоставления деталей в классе. Подробности поступают в свойство hibernate.dialect. Я предложил подклассифицировать Oracle10gDialect (и james SQLServerDialect) - это сделало бы выбор в отношении именования таблиц, стратегии генерации идентификатора и т.д.

Смотрите → https://forum.hibernate.org/viewtopic.php?f=1&t=993012

Ответ 6

В моем случае:

Стойка и слот - это объекты, имеющие пользовательские генераторы идентификаторов. Я использую однонаправленное отображение "один к одному". Таблица измерений хранит данные с помощью Autogenerated Custom ID в качестве внешнего ключа для нескольких таблиц (например, Rack и Slot). И моя схема выглядит следующим образом: Rack ------ > Dimension < ----------- Slot где Dimension будет содержать данные для таблицы Rack и Slot сгенерированным идентификатором.

Здесь беспокойство заключается в том, что когда я сохраняю данные следующим образом: -

Rack rack = new Rack(params);
Dimension dim = new Dimension(params);
rack.setDimension(dim);
session.save(rack);

Данные сохраняются успешно с тем же Автогенерированным идентификатором в таблицах Rack и Dimension.

Но когда я сохраняю данные для таблицы Slot:

Slot Slot = new Slot(params);
Dimension dim = new Dimension(params);
slot.setDimension(dim);
session.save(slot);

отображается сообщение об ошибке: -

attempted to assign id from null one-to-one property: rack

Могу ли я передать динамическое имя свойства в качестве "слота" при сохранении данных для слотов и измерений и "стойки" при сохранении данных для Rack и Dimension.


@GenericGenerator(name = "foreign", strategy = "foreign", parameters = {
    @Parameter(name = "property", value = "slot"),
    @Parameter(name = "property", value = "rack")})

Rack.java

@Entity
@Table(name="tablename")
@GenericGenerator(name = "customseq", strategy = "CustomIdGenerator")
public class Rack {
  @Id
  @GeneratedValue(generator = "customseq")
  @Column(name = "uni_id")
  private String id;
  @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  @PrimaryKeyJoinColumn
  private Dimension dimension;
  // Getters and Setters
}

Slot.java

@Entity
@Table(name="tablename")
@GenericGenerator(name = "customseq", strategy = "CustomIdGenerator")
public class Rack {
    @Id
    @GeneratedValue(generator = "customseq")
    @Column(name = "uni_id")
    private String id;
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Dimension dimension;
    // Getters and Setters
}

Dimension.java

public class Dimension implements Serializable{
  @Id
  @Column(name = "systemid")
  @GeneratedValue(generator = "foreign")
  @GenericGenerator(name = "foreign", strategy = "foreign", parameters = {
    @Parameter(name = "property", value = "slot"),
    @Parameter(name = "property", value = "rack")})
  private String systemid;

  @OneToOne(mappedBy = "dimension", fetch = FetchType.LAZY)
  @PrimaryKeyJoinColumn
  private Rack rack;
  @OneToOne(mappedBy = "dimension", fetch = FetchType.LAZY)
  @PrimaryKeyJoinColumn
  private Slot slot;
  // Getters and Setters
}