Ну, вопрос в значительной степени говорит обо всем. Используя JPARepository, как мне обновить сущность?
В JPARepository есть только метод сохранения , который не сообщает мне, действительно ли он создается или обновляется. Например, вставить простой объект в базе данных пользователя, который имеет три поля: firstname
, lastname
а age
:
@Entity
public class User {
private String firstname;
private String lastname;
//Setters and getters for age omitted, but they are the same as with firstname and lastname.
private int age;
@Column
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@Column
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
private long userId;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public long getUserId(){
return this.userId;
}
public void setUserId(long userId){
this.userId = userId;
}
}
Затем я просто вызываю save()
, который на самом деле является вставкой в базу данных:
User user1 = new User();
user1.setFirstname("john"); user1.setLastname("dew");
user1.setAge(16);
userService.saveUser(user1);// This call is actually using the JPARepository: userRepository.save(user);
Все идет нормально. Теперь я хочу обновить этого пользователя, скажем, изменить его возраст. Для этой цели я мог бы использовать QueryDSL или NamedQuery. Но, учитывая, что я просто хочу использовать spring-data-jpa и JPARepository, как мне сказать, что вместо вставки я хочу сделать обновление?
В частности, как мне сказать spring-data-jpa, что пользователи с одинаковыми именем пользователя и именем на самом деле равны и что существующий объект должен быть обновлен? Переопределение равных не решило эту проблему.
источник
Ответы:
Идентичность сущностей определяется их первичными ключами. Поскольку
firstname
иlastname
не являются частями первичного ключа, вы не можете указать JPA рассматриватьUser
s с одинаковымиfirstname
s иlastname
s как равные, если они имеют разныеuserId
s.Итак, если вы хотите обновить
User
идентифицированное по егоfirstname
иlastname
, вам нужно найти этоUser
по запросу, а затем изменить соответствующие поля объекта, который вы нашли. Эти изменения будут автоматически сброшены в базу данных в конце транзакции, поэтому вам не нужно ничего делать, чтобы сохранить эти изменения явно.РЕДАКТИРОВАТЬ:
Возможно, мне следует остановиться на общей семантике JPA. Существует два основных подхода к разработке постоянных API:
вставить / обновить подход . Когда вам нужно изменить базу данных, вы должны явно вызывать методы API персистентности: вы вызываете
insert
для вставки объекта илиupdate
для сохранения нового состояния объекта в базе данных.Подход единицы работы . В этом случае у вас есть набор объектов, управляемых библиотекой постоянства. Все изменения, которые вы вносите в эти объекты, будут автоматически сброшены в базу данных в конце единицы работы (т.е. в конце текущей транзакции в типичном случае). Когда вам нужно вставить новую запись в базу данных, вы делаете соответствующий объект управляемым . Управляемые объекты идентифицируются по их первичным ключам, так что если вы сделаете объект с заранее определенным первичным ключом удался , он будет связан с записью базы данных одного и тем же идентификатором, и состоянием этого объекта будет распространяться на эту запись автоматически.
JPA следует последнему подходу.
save()
в Spring Data JPA поддерживаетсяmerge()
простым JPA, поэтому он управляет вашей сущностью, как описано выше. Это означает, что вызовsave()
объекта с предопределенным идентификатором обновит соответствующую запись базы данных, а не вставит новую, а также объясняет, почемуsave()
не вызываетсяcreate()
.источник
extends CrudRepository<MyEntity, Integer>
а не так,extends CrudRepository<MyEntity, String>
как должно быть. Это помогает? Я знаю, это почти год спустя. Надеюсь, это поможет кому-то еще.Так как ответ на @axtavt фокусируется на
JPA
неspring-data-jpa
Обновить сущность путем запроса, тогда сохранение неэффективно, поскольку требует двух запросов, и, возможно, запрос может быть довольно дорогим, поскольку он может объединять другие таблицы и загружать любые коллекции, которые имеют
fetchType=FetchType.EAGER
Spring-data-jpa
поддерживает операцию обновления.Вы должны определить метод в интерфейсе репозитория и добавить к нему пометки
@Query
и@Modifying
.@Query
предназначен для определения пользовательского запроса и@Modifying
для сообщения оspring-data-jpa
том, что этот запрос является операцией обновления, а для этогоexecuteUpdate()
не требуетсяexecuteQuery()
.Вы можете указать другие типы возврата:
int
- количество обновляемых записей.boolean
- true, если запись обновляется. В противном случае, ложь.Примечание . Запустите этот код в транзакции .
источник
To update an entity by querying then saving is not efficient
это не единственные два варианта. Есть способ указать id и получить объект строки, не запрашивая его. Если вы сделаете a,row = repo.getOne(id)
а затемrow.attr = 42; repo.save(row);
и посмотрите логи, вы увидите только запрос на обновление.Вы можете просто использовать эту функцию с save () JPAfunction, но объект, отправляемый в качестве параметра, должен содержать существующий идентификатор в базе данных, иначе он не будет работать, потому что save (), когда мы отправляем объект без идентификатора, добавляет непосредственно строку в базы данных, но если мы отправляем объект с существующим идентификатором, он изменяет столбцы, уже найденные в базе данных.
источник
Как уже упоминалось другими,
save()
сам содержит операции создания и обновления.Я просто хочу добавить дополнение о том, что стоит за
save()
методом.Во-первых, давайте посмотрим иерархию расширения / реализации
CrudRepository<T,ID>
,Хорошо, давайте проверим
save()
выполнение наSimpleJpaRepository<T, ID>
,Как вы можете видеть, он сначала проверит, существует ли идентификатор или нет, если сущность уже существует, будет происходить только обновление по
merge(entity)
методу, а если еще, новая запись вставляется поpersist(entity)
методу.источник
Используя spring-data-jpa
save()
, у меня возникла та же проблема, что и у @DtechNet. Я имею в виду, что каждыйsave()
создавал новый объект вместо обновления. Чтобы решить эту проблему, я должен был добавитьversion
поле к сущности и связанную таблицу.источник
Вот как я решил проблему:
источник
@Transaction
вышеуказанный метод для запроса нескольких дБ. В этом случае нет необходимостиuserRepository.save(inbound);
, изменения сбрасываются автоматически.save()
Метод весенних данных поможет вам выполнить и добавление нового элемента, и обновление существующего элемента.Просто позвони
save()
и наслаждайся жизнью :))источник
Id
, сохраню его, как я могу избежать сохранения новой записи.источник
Для этой конкретной цели можно ввести составной ключ, например:
Отображение:
Вот как это использовать:
JpaRepository будет выглядеть так:
Затем вы можете использовать следующую идиому: принять DTO с информацией о пользователе, извлечь имя и имя и создать UserKey, затем создать UserEntity с этим составным ключом и затем вызвать Spring Data save (), который должен все уладить за вас.
источник