JPA сущностные прослушиватели и @embeddable - PullRequest
3 голосов
/ 01 июня 2010

У меня есть иерархия классов объектов JPA, которые все наследуются от класса BaseEntity:

@MappedSuperclass
@EntityListeners( { ValidatorListener.class })
public abstract class BaseEntity implements Serializable {
    // other stuff
}

Я хочу, чтобы все сущности, которые реализуют данный интерфейс, автоматически проверялись при сохранении и / или обновлении. Вот что у меня есть.

Мой ValidatorListener:

public class ValidatorListener {

    private enum Type {
        PERSIST, UPDATE
    }

    @PrePersist
    public void checkPersist(final Object entity) {
        if (entity instanceof Validateable) {
            this.check((Validateable) entity, Type.PERSIST);
        }
    }

    @PreUpdate
    public void checkUpdate(final Object entity) {
        if (entity instanceof Validateable) {
            this.check((Validateable) entity, Type.UPDATE);
        }
    }

    private void check(final Validateable entity, final Type persist) {
        switch (persist) {
        case PERSIST:
            if (entity instanceof Persist) {
                ((Persist) entity).persist();
            }
            if (entity instanceof PersistOrUpdate) {
                ((PersistOrUpdate) entity).persistOrUpdate();
            }
            break;
        case UPDATE:
            if (entity instanceof Update) {
                ((Update) entity).update();
            }
            if (entity instanceof PersistOrUpdate) {
                ((PersistOrUpdate) entity).persistOrUpdate();
            }
            break;

        default:
            break;
        }
    }

}

и вот мой проверяемый интерфейс, с которым он проверяет (внешний интерфейс - просто маркер, внутренний - методы):

public interface Validateable {

    interface Persist extends Validateable {
        void persist();
    }

    interface PersistOrUpdate extends Validateable {
        void persistOrUpdate();
    }

    interface Update extends Validateable {
        void update();
    }

}

Все это работает, однако я хотел бы распространить это поведение на классы Embeddable. Я знаю два решения:

  1. вызвать метод проверки встраиваемого объекта вручную из метода проверки сущности:

    public void persistOrUpdate(){
        // validate my own properties first
        // then manually validate the embeddable property:
        myEmbeddable.persistOrUpdate();
        // this works but I'd like something that I don't have to call manually
    }
    
  2. использовать отражение, проверяя все свойства, чтобы увидеть, относится ли их тип к одному из их типов интерфейса. Это будет работать, но это не красиво. Есть ли более элегантное решение?

Ответы [ 2 ]

1 голос
/ 01 июня 2010

Рассмотрим подход, основанный на аннотациях. Это будет производить меньше кода (кажется) и почти всегда легче понять.

Введите новую аннотацию:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Validators {
  String[] values();
}

Примените эту аннотацию к каждому объекту и встраиваемому объекту, который нуждается в проверке, например ::

@MappedSuperclass
@EntityListeners( { ValidatorListener.class })
@Validators({Type.PERSIST, Type.UPDATE})
public abstract class MyEntity extends BaseEntity implements Serializable, Validateable {
    // other stuff
    @Validators(Type.PERSIST)
    @Embedded
    public Address getAddress() {
        return address;
    }
}

Конечно, каждая сущность и встраиваемый объект должны по-прежнему реализовывать интерфейс Validateable, который становится проще:

public interface Validateable {
  void validate(Type type);
}

Тогда проверка логики становится проще:

  1. проверить, помечена ли сущность @Validators;
  2. если нет, переходите к итерации по встроенным элементам;
  3. проверить, реализует ли сущность Validateable;
  4. если нет, то итерация по встроенным элементам (возможно, выдача предупреждения для объекта: «Объект, помеченный Validators, но не реализующий Validatable интерфейс»)
  5. если оба да, то запустить validate, если соответствующий тип соответствует слушателю;
  6. перебирать внедренные элементы с той же логикой, что и выше.

Этот подход позволяет отделить объявление проверки сущностей и их встраиваемых элементов (аннотаций) от логики проверки (классы Java - сущности и встраиваемые классы). Например, иногда встраиваемый объект может реализовывать Validateable, но проверка не требуется. Кажется, что слушатель также становится проще.

Но если вы не отделили декларации валидации от логики валидации, тогда ваше решение вполне удовлетворительно и, возможно, проще.

0 голосов
/ 01 июня 2010

ОК, вот мое собственное решение:

http://pastebin.com/YPmt7ifm

Я использую Spring BeanUtils для перебора свойств. Тем не менее: кто-то получил более элегантное решение?

...