Как расширить немодифицируемую модель для использования с JPA? - PullRequest
0 голосов
/ 07 мая 2019

Какова лучшая практика для создания постоянства (скажем, с помощью Spring Boot или просто JPA или самого Hibernate) для модели данных, исходящей из неизменяемой зависимости? Типичные ограничения, такие как невозможность переопределить поле или то, что позволяют шаблоны, такие как Decorator, и то, что не замедляет мой прогресс. Я пробовал кое-что, но я всегда получаю результат, в котором необходимо либо изменить исходную модель (например, добавить аннотации, чтобы сделать ее нативно совместимой -> разветвление, которое я не хочу), либо написать тонну кода-обертки который слишком сильно скопировал бы оригинальную модель - но даже сейчас это не работает:

Я пытался

  • Создание JpaRepository для исходного класса. Не работает, потому что приведение расширенного класса к родительскому классу не работает.
  • Расширить исходный класс пользовательским классом, который получает необходимые аннотации, такие как @Entity, которые можно использовать в таком хранилище. Но проблемы здесь были
    • что в исходном классе отсутствует аннотация @Id, которую можно исправить, используя новый идентификатор в расширенном классе, но
    • данная модель также имеет непростую архитектуру, включая списки других классов, которые являются частью самой модели. Поэтому могут понадобиться другие аннотации, такие как @ElementCollection, которые нельзя добавить, поскольку переопределение полей невозможно.
      • Скрытие с созданием нового поля с тем же именем в новом классе не работает:
      • Ошибка типа Could not determine type for: java.util.List, at table: yeah_this_one, for columns:[org.hibernate.mapping.Column(objects)] указывает, что исходное поле не может быть полностью скрыто (изменили имя таблицы и столбца в новом классе, чтобы убедиться в этом).
      • Так что, конечно, добавление @ElementCollection (которое, как говорят, решает эту проблему) здесь тоже не поможет.
    • @AttributeOverride также не работает для переопределения аннотаций для установки идентификатора или других настроек, можно изменить только имя и столбец.

Я застрял в этом состоянии и мне интересно, является ли это вообще правильным подходом.

Настройка или то, что я ожидаю, чтобы работать из моего понимания:

Общая идея основана на этом учебнике REST по Spring Boot , который я попытался расширить с помощью модели из зависимости.

Предположим, что существует исходный класс модели Model из зависимости, которую нельзя изменить. ModelEntity будет расширенным классом, который будет действовать как способ втянуть модель в постоянство Spring.

В области зависимости исходный класс будет выглядеть так:

// Given dependency, not modifiable
@Some existing annotation
public class Model extends AnotherClassFromDep {

    @more annotations
    private IdLikeClassFromDep modelId;

    //more complex attribute
    @Nullable
    private List<RefClassFromDep> objects = new ArrayList<>();

    // more attributes, getter, setter etc.
}

В рамках моей программы:

В сочетании с этим небольшим дополнительным orm.xml можно аннотировать оригинал Model как MappedSuperclass без его изменения (согласно https://stackoverflow.com/a/2516951/1844976).

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">
    <mapped-superclass class="package.name.of.original.Model">

    </mapped-superclass>
</entity-mappings>

Это позволяет создать такой класс, который расширяет исходную модель POJO для добавления аннотаций JPA:

@Entity
public class ModelEntity extends Model {

    // some @Id attribute is necessary, which should correspond to
    // the already existing ID attribute from the original `Model`
    // in the best case, but an additional one would work too
    private @Id @GeneratedValue Long id;

    // Different approaches to solve the List error from above, for
    // instance hiding the original attribute
    @ElementCollection
    private List<RefClassFromDep> objects;

    public ModelEntity(){
        super();
    }

}

В текущем состоянии проблемы мешают мне идти дальше. Но в целом я бы ожидал, что это сработает с JpaRepository:

// of course, creating a JpaRepository with original `Model` wouldn't
// work, because it has no `@Entity`
public interface ModelRepository extends JpaRepository<ModelEntity, IdLikeClassFromDep> {
}

Таким образом, возможен фактический доступ к нему:

@Configuration
public class LoadDatabase {

    @Bean
    CommandLineRunner initDatabase(ModelRepository modelRepository) {
        return args -> {
            // depending on the implementation above, either create a 
            // Model and cast it or directly create a ModelEntity, set
            // attriubtes and save it through the JpaRepository
            modelRepository.save(model);
        };
    }
}

Мне могут помочь как более абстрактные, так и специфические идеи и комментарии, связанные с кодом. Спасибо!

1 Ответ

1 голос
/ 07 мая 2019

В старые времена Jpa / Hibernate настраивались через XML. Вы должны были предоставить persistence.xml для общей конфигурации. В этом файле вы добавили тег <mapping-file>, указывающий на другой файл orm.xml. В этом файле вы настроили отображение для ваших сущностей (что в наши дни выполняется с помощью аннотаций JPA).

См. https://vladmihalcea.com/how-to-use-external-xml-mappings-files-outside-of-jar-with-jpa-and-hibernate/

Хотя описанные выше методы считаются устаревшими, они все еще поддерживаются. У LocalContainerEntityManagerFactoryBean есть метод setMappingResources, позволяющий указать на файл orm.xml. В поиске путей и расположений по умолчанию есть некоторая особенность, но она хорошо документирована: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html#setMappingResources-java.lang.String...-

Обратите внимание, что сторонний класс, который вы настраиваете таким образом, должен соответствовать соглашениям Java Beans (конструктор без аргументов, методы получения и установки)

...