#java #json #spring #hibernate #spring-data
#java #json #spring #переход в спящий режим #spring-данные
Вопрос:
У меня есть объект с именем Sale
, который имеет список объектов SaleData
, я установил однонаправленную @OneToMany
связь между этими двумя. Теперь я столкнулся с проблемами при выполнении операций CRUD с моим объектом Sale.
CascadeType
установлено значение ALL
.
@Entity
@Table(name = "sale")
public class Sale extends BaseEntity {
@OneToOne(cascade = CascadeType.DETACH)
private Client client;
@OneToMany(cascade = {CascadeType.ALL}) // <--
@JoinColumn(name = "sale_id", nullable = false)
private List<SaleData> saleData = new ArrayList<>();
}
Сохранение (ПУБЛИКАЦИЯ) и удаление работают. Проблема в том, что при отправке для обновления мой JSON, отправленный с контроллера Angular, выглядит следующим образом:
{"id": 1, "client": {
"id": 1,
"firstName": "Test",
"lastName": "Client"
}, "saleData": [
{
"id": 1,
"employee": {
"id": 1,
"firstName": "Herp",
"lastName": "Derp"
}
},
{
"id": 2,
"employee": {
"id": 1,
"firstName": "John",
"lastName": "Smith"
}
}
]}
Вызов обновления из службы Spring выдает:
org.hibernate.PersistentObjectException: detached entity passed to persist: app.sales.SaleData
Реализация сервиса
@Override
@Transactional(rollbackFor = SaleNotFoundException.class)
public Sale update(@NotNull SaleDTO updated) {
Sale sale = repository.findOne(updated.getId());
if (sale == null)
throw new SaleNotFoundException("Sale not found");
return transformSaleObject(sale, updated);
}
Как я понимаю, cascade ALL не отключает объект sale, потому что во втором сценарии, где я заменяю тип на любую комбинацию …
@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH})
.. тогда Обновление и удаление работают, но сохранение нового объекта — нет. Я не так часто использовал Hiberate, но, может быть, кто-нибудь знает, что вызывает такое поведение? Это вопрос неправильного определения отношения?
Обновление: Метод transform — это просто это, но в любом случае, похоже, что теперь для меня все решено 🙂
private Sale transformSaleObject(Sale sale, SaleDTO dto) {
sale.setClient(dto.getClient());
sale.setSaleData(dto.getSaleData());
sale.setService(dto.getService());
sale.setHours(dto.getHours());
sale.setPrice(dto.getPrice());
sale.setSaleStatus(dto.getSaleStatus());
sale.setSaleDate(dto.getSaleDate());
return sale;
}
Я изменил свой метод обновления следующим образом:
public Sale update(@NotNull SaleDTO updated) {
Sale sale = repository.findOne(updated.getId());
if (sale == null)
throw new SaleNotFoundException("Sale not found");
transformSaleObject(sale, updated);
return repository.saveAndFlush(sale);
}
Комментарии:
1. Ваша ошибка означает, что вы пытаетесь сохранить тот же объект (то есть с идентичным идентификатором) во второй раз. Вместо сохранения вы можете выполнить обновление, только если ваша сущность отсоединена (не постоянна). Для удобства вы можете использовать метод
orh.hibernate.Session.saveOrUpdate
. Вы можете найти полезным этот документ2. Данные Spring предназначены только
save(entity)
для сохранения, и вы обновляете их, находя объект по идентификатору и изменяя значения, которые хотите обновить, после этого вы не вызываете никаких других методов, но после завершения транзакции эти изменения фиксируются.3. Может быть и так. Попробуйте удалить
@JoinColumn
аннотацию из отношения @OneToMany кsaleData
коллекции. Вместо этого поместите@JoinColumn(name = "sale_id", nullable = false)
ссылку на продажу внутриSaleData
объекта.4. В однонаправленной связи у меня нет
Sale
ссылки в моемSaleData
классе. Единственная ссылка находится в продаже.5. Это не рекомендуется. Лучшим вариантом является двунаправленная ассоциация «один ко многим» . Посмотрите, что они говорят об однонаправленном «один ко многим» здесь .