Является ли правильным двойное сохранение нового экземпляра сущности с JpaRepository данных Spring 2? - PullRequest
0 голосов
/ 25 января 2019

У меня есть две сущности в двунаправленном отношении «многие ко многим».

A <-> «многие ко многим» <-> B

У меня есть конечная точка, где клиент может создатьэкземпляра A, и в то же время добавьте к этому A некоторое количество объектов B, передав массив ключей идентификаторов объектов B.Помните , что эти объекты B уже существуют в базе данных .Не существует бизнес или программных решений для тесной связи их создания с созданием A.

Так что класс A выглядит так, а B - то же самое, но со ссылками на A.

@Entity
class A {
    @Id
    @GeneratedValue
    int id;

    @ManyToMany
    List<B> bs;

    String someValue;

    int someValue2;

    // With some getters and setters omitted for brevity
}

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

public A createA(@RequestBody A aToCreate) {
    A savedA = aRepository.save(aToCreate);
    savedA.getbs().forEach(b -> Service.callWithBValue(b.getImportantValue());
}

И клиент отправил бы такой JSON-запрос, чтобы создать новый A, который содержал бы ссылки на B с идентификатором 3 и Bс идентификатором 4.

 {
     "bs": [{id:3}, {id:10}],
     "someValue": "not important",
     "someValue2": 1
 }

Хорошо, так что все работает нормально, я вижу, что все поля десериализованы хорошо, и затем я иду, чтобы сохранить мой новый экземпляр A, используя.

aRepository.save(aToCreate);

И этопрекрасно работает ... за исключением того факта, что мне нужны все данные, связанные с экземплярами сущности b, но объект A, возвращаемый aRepository.save(), заполнил только поля автозаполнения на A и ничего не сделал с сущностями B.Они все еще просто полые сущности, у которых установлены только свои идентификаторы.

Wut.

Итак, я осматриваюсь, и, очевидно, SimpleJpaRepository делает это.

@Transactional
public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

Ипоскольку объект A является совершенно новым, он сохраняет только объект A, но не объединяет его, поэтому я не получаю никаких богатых данных B.Итак, хорошо, если я изменю свой код, чтобы принять это во внимание, я получу это.

public A createA(@RequestBody A aToCreate) {
    A savedA = aRepository.save(aRepository.save(aToCreate));
    savedA.getbs().forEach(b -> Service.callWithBValue(b.getImportantValue());
}

, который работает просто отлично.При втором проходе через службу хранилища он сливается, а не сохраняется, поэтому отношения В становятся гидратированными.

Мой вопрос: это правильно, или я могу сделать что-то еще, что выглядит не столь уж неэффективно иУжасно?

Чтобы было ясно, это имеет значение ТОЛЬКО при создании нового экземпляра A, и когда A находится в базе данных, это больше не проблема, потому что SimpleJpaRepository попадет в строку em.merge()код.Также я пробовал разные аннотации CascadingType для отношений, но ни одна из них не является тем, что я хочу.Каскадирование - это сохранение состояния представления родительской сущности своих дочерних объектов своим дочерним элементам, но я хочу гидрировать дочерние сущности при создании нового экземпляра вместо того, чтобы совершать две поездки в базу данных.

Ответы [ 2 ]

0 голосов
/ 25 января 2019

Это то, чем я закончил. Это дает вызывающей стороне возможность сохранять и гидрировать экземпляр за один вызов, а также объясняет, что происходит. Все мои экземпляры репозитория теперь расширяют этот базовый экземпляр.

public interface BaseRepository<T, ID> extends JpaRepository<T, ID> {

    /**
     * Saves an instance twice so that it's forced to persist AND then merge. This should only be used for new detached entities that need to be saved, and who also have related entities they want data about hydrated into their object.
     */
    @Transactional
    default T saveAndHydrate(T save) {
        return this.save(this.save(save));
    }
}
0 голосов
/ 25 января 2019

В случае нового A, aToCreate и savedA - это один и тот же экземпляр, потому что именно это и определяет спецификации JPA:

https://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#persist(java.lang.Object)

Makeуправляемый и постоянный экземпляр.

Spring Data просто возвращает один и тот же экземпляр, поэтому постоянное / объединение можно абстрагировать в один метод.

Если экземпляры B, которые вы хотите связать с A,существующих сущностей, то вам нужно извлечь ссылку на эти существующие экземпляры и установить их на A. Вы можете сделать это без попадания в базу данных, используя метод T getOne(ID id) в JpaRepository Spring Data:

https://docs.spring.io/spring-data/jpa/docs/2.1.4.RELEASE/api/

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

...