Присвойте пользовательский идентификатор свойству @id - PullRequest
8 голосов
/ 24 ноября 2010

Я перевожу устаревшую систему для использования Hibernate 3. В настоящее время она генерирует свои собственные идентификаторы. Чтобы продолжить с тем, что система делает в настоящее время, прежде чем я попытаюсь переместить это к чему-то немного лучше, как бы я мог указать (используя аннотации) свой собственный класс, который будет возвращать сгенерированные пользователем идентификаторы при вставке?

Что-то вроде:

@Id
@CustomIdGenerator(Foo.class) // obviously this is not a real annotation
public String getId() { ... }

Где класс Foo имеет один метод, который генерирует идентификатор.

В настоящее время я просто вызываю метод setId(String id) вручную, но надеялся найти лучший способ справиться с этой ситуацией.

Ответы [ 2 ]

7 голосов
/ 24 ноября 2010

Я не думаю, что есть встроенная поддержка для создания пользовательских идентификаторов с помощью пользовательских аннотаций с использованием чистого API JPA-2. Но если вы хотите использовать специфичный для провайдера API, то задача довольно проста. Пример примера

Чтобы быть независимым от провайдера, попробуйте любой из следующих приемов ....

IdGeneratorHolder

public abstract class IdGeneratorHolder {
    /* PersistentEntity is a marker interface */
    public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
             /* sample impelementation */
        if(Product.class.isAssignableFrom(entityType)) {
            return new ProductIdGenerator();

        }
        return null;
    }
}

Общий интерфейс IdGenerator

public interface IdGenerator {
    String generate();
}

Конкретный IdGenerator - Генератор идентификатора продукта

public class ProductIdGenerator implements IdGenerator {
    public String generate() {
            /* some complicated logic goes here */
        return ${generatedId};
    }
}

Теперь установите сгенерированный идентификатор либо в конструкторе без аргументов ИЛИ в методе @PrePersist .

Product.java

public class Product implements PersistentEntity {

    private String id;

    public Product() {
        id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
    }

    @PrePersist
    public void generateId() {
        id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
    }

}

В приведенном выше примере все идентификаторы имеют одинаковый тип, т.е. java.lang.String. Если постоянные сущности имеют идентификаторы разных типов .....

IdGenerator.java

public interface IdGenerator {
    CustomId generate();
}

CustomId.java

   public class CustomId {

    private Object id;

    public CustomId(Object id) {
        this.id = id;
    }

    public String  toString() {
        return id.toString();
    }
    public Long  toLong() {
        return Long.valueOf(id.toString());
    }
}

Item.java

@PrePersist
    public void generateId() {
        id = IdGeneratorHolder.getIdGenerator(getClass()).generate().toLong();
    }

Вы также можете использовать свои собственные аннотации ...

CustomIdGenerator.java

public @interface CustomIdGenerator {
    IdStrategy strategy();
}

IdStrategy.java

  enum IdStrategy {
        uuid, humanReadable,    
    }

IdGeneratorHolder.java

public abstract class IdGeneratorHolder {
    public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
        try { // again sample implementation
            Method method = entityType.getMethod("idMethod");
            CustomIdGenerator gen = method.getAnnotation(CustomIdGenerator.class);
            IdStrategy strategy = gen.strategy();
            return new ProductIdGenerator(strategy);
        }

Еще одна вещь .... Если мы устанавливаем id в методе @PrePersist, метод equals () не может полагаться на поле id (т.е. суррогатный ключ), мы должны использовать бизнес / натуральный ключ для реализации метода equals (). Но если мы установим в поле id какое-то уникальное значение (uuid или «app-uid», уникальное в приложении) в конструкторе без аргументов, это поможет нам реализовать метод equals ().

public boolean equals(Object obj) {
        if(obj instanceof Product) {
            Product that = (Product) obj;
            return this.id ==that.id;
        }
        return false;
    }

Если мы или кто-то еще вызываем (намеренно или по ошибке) аннотированный метод @PrePersist более одного раза, «уникальный идентификатор будет изменен !!!» Поэтому установка id в конструкторе без аргументов предпочтительнее. ИЛИ для решения этой проблемы установите ненулевую проверку ...

  @PrePersist
    public void generateId() {
        if(id != null)
            id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
    }
}

UPDATE

Если мы поместим генерацию идентификатора в не аргументный конструктор, не так ли вызвать проблемы при загрузке объектов из базы данных? потому что спящий вызовет конструктор без аргументов в результате чего существующие идентификаторы будут перегенерированы

Да, вы правы, я пропустил эту часть. :( На самом деле, я хотел сказать вам, что: - в моем приложении каждый объект Entity связан с Entity Entity; поэтому я создал абстрактный суперкласс с двумя конструкторами, и каждый Entity (кроме Organization) расширяет этот класс.

    protected PersistentEntityImpl() {
    }

    protected PersistentEntityImpl(Organization organization) {
        String entityId = UUIDGenerator.generate();
        String organizationId = organization.getEntityId();
        identifier = new EntityIdentifier(entityId, organizationId);
    }

Конструктор no-arg предназначен для провайдера JPA, мы никогда не вызываем конструктор no-arg, а конструктор на основе другой организации. Как вы видете. идентификатор назначается в конструкторе на основе организации. (Я действительно упустил этот момент при написании ответа, извините за это.)

Посмотрите, сможете ли вы реализовать эту или подобную стратегию в своем приложении.

Второй вариант использовал Аннотация @PrePersist. Я положил это в и метод никогда не получил удар и дал мне исключение, заявив, что мне нужно установить идентификатор вручную. Есть что-то еще, что я должен делать?

В идеале провайдер JPA должен вызывать методы @PrePersist (один объявлен в классе, а также все другие методы, объявленные в суперклассах) перед сохранением объекта сущности. Не могу сказать вам, что не так, если вы не покажете код и консоль.

1 голос
/ 24 ноября 2010

Вы можете.

Сначала внедрите org.hibernate.id.IdentifierGenerator

Затем вам нужно будет отобразить его в файле сопоставления xml.Я не смог найти способ сделать это с помощью аннотаций:

<!--
    <identifier-generator.../> allows customized short-naming 
         of IdentifierGenerator implementations.
-->
<!ELEMENT identifier-generator EMPTY>
    <!ATTLIST identifier-generator name CDATA #REQUIRED>
    <!ATTLIST identifier-generator class CDATA #REQUIRED>

Наконец, используйте @GeneratedValue(generator="identifier-name")

Обратите внимание, что это зависит от спящего режима (не JPA)

Обновление: Я взглянул на источники Hibernate, и кажется, что в одном месте, после неудачного определения короткого имени, hibernates пытается вызвать Class.forName(..).Параметр там называется strategy.Итак, вот что вы пытаетесь:

  • попробуйте установить полное имя класса в виде строки в атрибуте generator
  • попробуйте установить класс fqn в виде строки в @GenericGenerator strategy атрибут (с некоторым произвольным именем)

Дайте мне знать, какой (если есть) работал

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