Переопределить аннотации в спящем режиме - PullRequest
12 голосов
/ 12 июля 2010

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

Один из способов сделать это - использовать конфигурацию JPA XML для переопределения существующих аннотаций классов. Однако JPA не поддерживает универсальные генераторы, что является обязательным требованием из-за структуры нашей устаревшей базы данных. Другой способ, которым я занимаюсь, - это использовать XML-конфигурации Hibernate для переназначения целых классов и доступа к тегу 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 (для которого также потребуется другая аннотация).

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

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

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

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

@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.

Ответы [ 6 ]

1 голос
/ 07 августа 2010

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

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

1 голос
/ 12 июля 2010

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

1 голос
/ 12 июля 2010

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

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

0 голосов
/ 31 июля 2013

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

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

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

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

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

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

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

Можно ли передать имя динамического свойства как "slot" при сохранении данных для Slot и Dimension и "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
}
0 голосов
/ 12 июля 2010

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

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

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

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

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

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

См. -> https://forum.hibernate.org/viewtopic.php?f=1&t=993012

0 голосов
/ 12 июля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...