Когда нам нужно .save () объект в Spring? - PullRequest
1 голос
/ 18 апреля 2019

В относительно большом проекте Spring Boot у меня есть метод со следующей (чрезмерно упрощенной) последовательностью событий:

Car car = carRepository.save(new Car());
Person person = personRepository.save(new Person(car)); // Car is a field of Person

Engine engine = engineRepository.save(new Engine());
person.getCar().setEngine(engine);

carRepository.save(person.getCar()); // without this the engine and car relation is not registered

Car, Person и Engine - это все @Entity классы (объекты базы данных). Для этого примера их реализация может быть следующей:

// Car.java
@Entity
@Table(name = "car_tbl")
public class Car {
    @Id
    @GeneratedValue
    @Column(name = "car_id")
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "engine_id", unique = true)
    private Engine engine;
}

// Person.java
@Entity
@Table(name = "person_tbl")
public class Person {
    @Id
    @GeneratedValue
    @Column(name = "person_id")
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "car_id", nullable = false, unique = true)
    private Car car;
}

// Engine.java
@Entity
@Table(name = "engine_tbl")
public class Engine {
    @Id
    @GeneratedValue
    @Column(name = "engine_id")
    private Long id;
}

Вышеуказанный метод используется только внутри метода REST API. И OSIV (Open Session In View) включен в свойствах конфигурации.

Вопрос в том, зачем мне сохранять объект person.getCar()? Это тот же самый объект Car, который я только что создал во время той же обработки запроса. В какой момент он становится переходным , а не управляемым контекстом постоянства? Я думал, что все изменения будут сброшены автоматически, потому что OSIV включен.

Как я могу обнаружить подобные случаи, и точно знает , когда использовать метод .save() и когда я могу его игнорировать?

1 Ответ

0 голосов
/ 19 апреля 2019

Заключение

Это зависит от того, содержится ли метод в транзакции или нет.

  • В транзакции: приведенный выше код работает неявно.
  • Не в транзакции: .save() должен быть явно вызван для обновления базы данных.

Как я могу обнаружить подобные случаи и точно знать, когда использовать метод .save() и когда я могу его игнорировать?

  • Метод .save() должен вызываться для модифицированной сущности явно, если:
    1. Изменяющийся код не относится к транзакции .
    2. Недавно созданная сущность: new Car().
    3. Объект сущности получается извне, а не из базы данных в рамках той же транзакции. (Например, уже сохранено в List до начала транзакции.)
  • Метод .save() не должен вызываться для сущностей, которые запрашиваются из JPA в рамках той же транзакции. (Например, carRepository.findById(id) и другие подобные методы).

Сделка

Если приведенная выше последовательность событий вызывается из метода, аннотированного @Transactional, то он будет работать как положено.

Затем созданный объект Car становится управляемым контекстом постоянства, и все изменения в нем, в пределах одной и той же транзакции, сбрасываются после завершения транзакции.

Нет транзакций

Если описанный выше метод не принадлежит какой-либо транзакции, все изменения объектов являются локальными (свойства объекта изменяются, но не сбрасываются в базу данных).

Вот почему вызов persist() или merge() (в вышеприведенном случае save(), который использует эти два для внутреннего использования) должен выполняться явно для сброса изменений.

Лучше использовать транзакции

При сохранении одного объекта выполняется один SQL-запрос, который является атомарным, то есть сама транзакция. Если последовательность событий базы данных не содержится в транзакции, то каждый вызов save() действует как отдельная «транзакция».

Обычно это плохая практика, потому что, если одна из более поздних «транзакций» завершается неудачно (что-то вроде Exception сгенерировано), предыдущие успешные транзакции уже сброшены, возможно, приведя базу данных в недопустимое состояние из бизнеса. логическая перспектива.


Открыть сеанс в представлении

Поведение не имеет ничего общего с OSIV.

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

...