Как эффективно обновить с помощью JpaRepository - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть родитель, который хранит список детей.Когда я обновляю дочерние элементы (добавляю / редактирую / удаляю), есть ли способ автоматически решить, какой дочерний элемент удалить или изменить на основе внешнего ключа?Или мне нужно вручную проверять все дочерние элементы, чтобы увидеть, какие они новые или модифицированные?

Родительский класс

@Entity
@EntityListeners(PermitEntityListener.class)
public class Permit extends Identifiable {

    @OneToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL, mappedBy = "permit")
    private List<Coordinate> coordinates;
}

Дочерний класс

@Entity
public class Coordinate extends Identifiable {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "permit_id", referencedColumnName = "id")
    private Permit permit;

    private double lat;

    private double lon;
}

Родительский контроллер

@PutMapping("")
public @ResponseBody ResponseEntity<?> update(@RequestBody Permit permit) {

    logger.debug("update() with body {} of id {}", permit, permit.getId());
    if (!repository.findById(permit.getId()).isPresent()) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
    }

    Permit returnedEntity = repository.save(permit);
    repository.flush();
    return ResponseEntity.ok(returnedEntity);
}

= EDIT =

Создание контроллера

@Override
    @PostMapping("")
    public @ResponseBody ResponseEntity<?> create(@RequestBody Permit permit) {

        logger.debug("create() with body {}", permit);
        if (permit == null || permit.getId() != null) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
        }

        List<Coordinate> coordinates = permit.getCoordinates();
        if (coordinates != null) {
            for (int x = 0; x < coordinates.size(); ++x) {
                Coordinate coordinate = coordinates.get(x);
                coordinate.setPermit(permit);
            }
        }

        Permit returnedEntity = repository.save(permit);
        repository.flush();
        return ResponseEntity.ok(returnedEntity);
    }

Обновление контроллера

@PutMapping("")
public @ResponseBody ResponseEntity<?> update(@RequestBody Permit permit) {

    logger.debug("update() with body {} of id {}", permit, permit.getId());
    if (!repository.findById(permit.getId()).isPresent()) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
    }

    List<Coordinate> repoCoordinate = coordinateRepository.findByPermitId(permit.getId());
    List<Long> coordinateIds = new ArrayList<Long>();
    for (Coordinate coordinate : permit.getCoordinates()) {
        coordinate.setPermit(permit);
        //if existing coordinate, save the ID in coordinateIds
        if (coordinate.getId() != null) {
            coordinateIds.add(coordinate.getId());
        }
    }
    //loop through coordinate in repository to find which coordinate to remove
    for (Coordinate coordinate : repoCoordinate) {
        if (!(coordinateIds.contains(coordinate.getId()))) {
            coordinateRepository.deleteById(coordinate.getId());
        }
    }

    Permit returnedEntity = repository.save(permit);
    repository.flush();
    return ResponseEntity.ok(returnedEntity);
}

Я проверил это, и оно работает, есть линет упрощенного способа сделать это?

Ответы [ 2 ]

0 голосов
/ 24 февраля 2019

Вы были близки к решению.Единственное, чего вам не хватает, это orphanRemoval = true в вашем сопоставлении один ко многим:

@Entity
@EntityListeners(PermitEntityListener.class)
public class Permit extends Identifiable {

    @OneToMany(mappedBy = "permit", cascade=CascadeType.ALL, orphanRemoval=true)
    private List<Coordinate> coordinates;

}

Пометка сопоставления для удаления потерянных объектов скажет лежащему в основе ORM удалить любые сущности, которые больше не принадлежат какой-либо родительской сущности.,Поскольку вы удалили дочерний элемент из списка, он будет удален при сохранении родительского элемента.Создание новых элементов и обновление старых основано на CascadeType.Поскольку у вас есть CascadeType.ALL, все элементы в списке без идентификатора будут сохранены в базе данных и назначены новый идентификатор при сохранении родительского объекта, а все элементы, которые уже есть в списке и имеют идентификатор, будут обновлены.

В дополнение к этому, вам может потребоваться обновить метод установки для координат списка, чтобы он выглядел примерно так:

public void setCoordinates(List<Coordinates> coordinates) {
    this.coordinates = coordinates;
    this.coordinates.forEach(coordinate -> coordinates.setPermit(this));
}

Или просто используйте @JsonManagedReference и @JsonBackReference, если вы работаете с JSON.

0 голосов
/ 18 февраля 2019

У меня есть родитель, который хранит список детей.

Позволяет написать для него DDL.

TABLE parent (
  id integer pk
)
TABLE child(
  id integer pk
  parent_id integer FOREIGN KEY (parent.id)
)

Когда я обновляю детей (добавляю / редактирую / удаляю), есть ли способ автоматически решить, какой дочерний элементудалить или изменить на основе внешнего ключа?

Предполагается, что у вас есть новый дочерний элемент № 5, связанный с родительским элементом № 2, и:

  1. FK в DDL являетсяправильно
  2. Сущности знают FK
  3. Вы используете тот же jpa-контекст
  4. Транзакция выполняется правильно

Затем каждый вызовparent.getChilds() должен (!) Вернуть все сущности, которые существуют до того, как ваша транзакция была выполнена и тот же экземпляр сущности, которую вы только что зафиксировали в базе данных.

Затем,если вы удалите дочерний элемент № 5 из родительского элемента № 2, и транзакция, успешно выполненная parent.getChilds(), должна вернуть все объекты без дочерний элемент № 5.

Особый случай:

Если выудалите родительский # 2, и у вас есть каскадное удаление в DDL, а также в Java-коде все чиldrens должны быть удалены из базы данных, а также родительский номер 2 в базе данных, которую вы только что удалили.В этом случае родительский номер 2 больше не связан с jpa-контекстом, а все дочерние элементы родительского номера 2 больше не связаны с jpa-контекстом.

= Edit =

Youможет использовать merge.Это будет работать для конструкций, подобных этой:

POST {
  "coordinates": [{
    "lat":"51.33",
    "lon":"22.44"
  },{
    "lat":"50.22",
    "lon":"22.33"
  }]
}

Будет создана одна строка в таблице "allow" и две строки в таблице "координата", обе координаты привязаны к строке разрешения.Результат будет включать в себя набор идентификаторов.

Но: Вам нужно будет выполнить валидацию (убедитесь, что идентификатор равен нулю, проверьте, что координаты не ссылаются на другое разрешение, ...)!

Удаление координат должно быть выполнено методом DELETE:

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