spring-data-jpa를 사용하여 엔티티를 어떻게 업데이트합니까?


196

문제는 거의 모든 것을 말합니다. JPARepository를 사용하여 엔티티를 어떻게 업데이트합니까?

JPARepository에는 실제로 저장 또는 작성 중인지 알려주지 않는 저장 메소드 만 있습니다. 예를 들어, 나는 세 개의 필드가 데이터베이스 사용자에게 간단한 개체를 삽입 : firstname, lastnameage:

 @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에 알리려면 어떻게해야합니까? equals를 재정의해도이 문제는 해결되지 않았습니다.


1
데이터베이스에 기존 객체를 저장할 때 ID가 다시 작성됩니까? 절대로 TBH 내 프로젝트에이 없었다
바이런 Voorbach에게

@ByronVoorbach yup 당신이 옳습니다. 방금 테스트했습니다. 질문 수정, thx
Eugene

2
안녕하세요 친구, 당신은이 링크를 볼 수 stackoverflow.com/questions/24420572/...을 당신이 될 saveOrUpdate ()와 같은 방법이 될 수 있습니다
ibrahimKiraz


여기에 아름다운 해결책이 있다고 생각합니다. 여기 에 링크 설명을 입력하십시오
Clebio Vieira

답변:


208

엔터티의 ID는 기본 키로 정의됩니다. 이후 firstnamelastname기본 키의 일부하지, 당신은 치료에 JPA를 말할 수 없다 User같은과의 firstnames와 lastname서로 다른 경우 동일로의 userId들.

따라서 and 로 User식별 된 정보 를 업데이트 하려면 쿼리 로 식별 한 다음 찾은 객체의 해당 필드를 변경해야합니다. 이러한 변경 사항은 트랜잭션이 끝날 때 데이터베이스에 자동으로 플러시되므로 변경 사항을 명시 적으로 저장하기 위해 아무 것도 할 필요가 없습니다.firstnamelastnameUser

편집하다:

아마도 JPA의 전체 의미에 대해 자세히 설명해야 할 것입니다. 퍼시스턴스 API 디자인에는 두 가지 주요 접근 방식이 있습니다.

  • 삽입 / 업데이트 접근법 . 데이터베이스를 수정해야하는 경우 지속성 API 메소드를 명시 적으로 호출 insert해야합니다. 오브젝트를 삽입하거나 오브젝트의 update새 상태를 데이터베이스에 저장하기 위해 호출 합니다.

  • 작업 단위 접근 . 이 경우 지속성 라이브러리로 관리 되는 오브젝트 세트가 있습니다. 이러한 오브젝트에 대한 모든 변경 사항은 작업 단위 (UOW) 종료시 (즉, 일반적인 경우 현재 트랜잭션 종료시) 데이터베이스에 자동으로 플러시됩니다. 데이터베이스에 새 레코드를 삽입해야하는 경우 해당 오브젝트를 관리 합니다. 관리 대상 객체는 기본 키로 식별되므로 사전 정의 된 기본 키가 managed 인 객체를 만들면 동일한 id의 데이터베이스 레코드와 연결되고이 객체의 상태가 해당 레코드에 자동으로 전파됩니다.

JPA는 후자의 접근법을 따릅니다. save()Spring Data JPA는 merge()일반 JPA로 지원되므로 위에서 설명한대로 엔티티를 관리 합니다. save()사전 정의 된 id를 가진 객체 를 호출 하면 새로운 데이터베이스를 삽입하지 않고 해당 데이터베이스 레코드를 업데이트하고 save()라는 이유를 설명합니다 create().


글쎄, 나는 이것을 알고 있다고 생각한다. 나는 spring-data-jpa를 엄격히 언급하고있었습니다. 나는이 답변에 두 가지 문제가 있습니다 .1) 비즈니스 가치는 기본 키의 일부가 아닌 것으로 알려져 있습니다. 따라서 이름과 성을 기본 키로 사용하는 것은 좋지 않습니다. 그리고 2) 왜이 방법이 create라고 불리는 것이 아니라 spring-data-jpa에 대신 저장합니까?
유진

1
"Spring Data JPA의 save ()는 일반 JPA의 merge ()에 의해 지원됩니다"실제로 코드를 보셨습니까? 방금 수행했으며 지속 또는 병합으로 백업했습니다. ID (기본 키)의 존재 여부에 따라 유지되거나 업데이트됩니다. 이것은 저장 방법에 문서화되어야한다고 생각합니다. 따라서 저장은 실제로 병합 또는 지속됩니다.
Eugene

나는 그것이 상태라고 관계없이 객체를 저장해야하기 때문에 저장이라고도 생각합니다. 저장 상태와 동일한 업데이트 또는 삽입을 수행 할 것입니다.
Eugene

1
이것은 나를 위해 작동하지 않습니다. 유효한 기본 키로 엔티티를 저장하려고했습니다. "order / edit / : id"로 페이지에 액세스하면 실제로 ID로 올바른 개체를 제공합니다. 하나님의 사랑을 위해 내가 시도하는 것은 아무것도 없습니다. 그것은 항상 새로운 엔티티를 게시합니다 .. 나는 심지어 EntityManager와 함께 사용자 정의 서비스를 만들고 "병합"을 시도했지만 여전히 작동하지 않습니다. 항상 새 엔티티를 게시합니다.
DtechNet

1
@ DTechNet 나는 DtechNet과 비슷한 문제를 겪고 있었고 내 문제는 Spring Data 저장소 인터페이스에 잘못된 기본 키 유형이 지정되었다는 것이 밝혀졌습니다. 그것은 있어야하는 것 extends CrudRepository<MyEntity, Integer>대신에 말했다 extends CrudRepository<MyEntity, String>. 도움이 되나요? 나는 이것이 거의 1 년 후인 것을 알고 있습니다. 그것이 다른 누군가를 돕기를 바랍니다.
Kent Bull

141

@axtavt로 대답에 초점을 맞추고 있기 때문에 JPA하지spring-data-jpa

쿼리를 통해 엔터티를 업데이트하면 저장이 효율적이지 않습니다. 두 개의 쿼리가 필요하기 때문에 다른 테이블을 조인하고 컬렉션이있는 컬렉션을로드 할 수 있기 때문에 쿼리가 매우 비쌀 수 있습니다. fetchType=FetchType.EAGER

Spring-data-jpa업데이트 작업을 지원합니다.
Repository 인터페이스에서 메소드를 정의하고 @Queryand로 주석을 달아야합니다 @Modifying.

@Modifying
@Query("update User u set u.firstname = ?1, u.lastname = ?2 where u.id = ?3")
void setUserInfoById(String firstname, String lastname, Integer userId);

@Query사용자 지정 쿼리를 정의 @Modifying하기위한 것이며이 spring-data-jpa쿼리가 업데이트 작업이며 필요 executeUpdate()하지 않음 을 알리기위한 것 executeQuery()입니다.

다른 반환 유형을 지정할 수 있습니다
int.-업데이트되는 레코드 수.
boolean-업데이트중인 레코드가 있으면 true입니다. 그렇지 않으면 false입니다.


참고 : 트랜잭션 에서이 코드를 실행하십시오 .


10
트랜잭션에서 실행해야합니다
hussachai

1
야! 스프링 데이터 bean을 사용하고 있습니다. 따라서 자동으로 내 업데이트를 처리합니다. <S는 T를 확장한다> S save (S entity); 자동으로 업데이트를 관리합니다. 나는 당신의 방법을 사용할 필요가 없습니다! 어쨌든 고마워!
bks4line

3
언제든지 :) 저장 방법은 엔티티를 저장하려는 경우 작동합니다 (장면 뒤에서 em.persist () 또는 em.merge ()에 대한 호출을 위임합니다). 어쨌든 사용자 지정 쿼리는 데이터베이스의 일부 필드 만 업데이트하려는 경우에 유용합니다.
hussachai

매개 변수 중 하나가 하위 엔티티 (manyToOne)의 id 인 경우 어떻게 업데이트해야합니까? (책에 저자가 있고 책 저자를 업데이트하기 위해 책 ID와 저자 ID를 전달했습니다)
Mahdi

1
To update an entity by querying then saving is not efficient이것 만이 두 가지 선택이 아닙니다. id를 지정하고 쿼리하지 않고 행 객체를 얻는 방법이 있습니다. a를 수행 한 row = repo.getOne(id)다음 row.attr = 42; repo.save(row);로그를 보면 업데이트 쿼리 만 표시됩니다.
nurettin

21

save () JPAfunction과 함께이 함수를 간단히 사용할 수 있지만, 매개 변수로 전송 된 오브젝트는 데이터베이스에 기존 ID를 포함해야합니다. 그렇지 않으면 ID가없는 오브젝트를 보낼 때 save ()가 행을 직접 추가하기 때문에 작동하지 않습니다. 기존 ID를 가진 개체를 보내면 데이터베이스에서 이미 찾은 열이 변경됩니다.

public void updateUser(Userinfos u) {
    User userFromDb = userRepository.findById(u.getid());
    // crush the variables of the object found
    userFromDb.setFirstname("john"); 
    userFromDb.setLastname("dew");
    userFromDb.setAge(16);
    userRepository.save(userFromDb);
}

4
업데이트하기 전에 데이터베이스에서 객체를로드해야하는 경우 성능에 문제가 있습니까? (내 영어에 죄송합니다)
august0490

3
매우 preffered되지 하나가 아닌 두 개의 쿼리가
티그 Babajanyan

나는 단지 다른 방법을 보여 주었다는 것을 안다! 하지만 왜 ID가 같을 때 jpa가 업데이트 기능을 구현 했습니까?
칼리 포늄

17

다른 사람들이 이미 언급했듯이 save()자체에는 작성 및 업데이트 작업이 모두 포함되어 있습니다.

난 그냥 save()방법 뒤에 무엇에 대한 보충을 추가하고 싶습니다 .

먼저의 확장 / 구현 계층을 보자 CrudRepository<T,ID>. 여기에 이미지 설명을 입력하십시오

좋아,에서 save()구현을 확인하자 SimpleJpaRepository<T, ID>.

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

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

보시다시피 ID가 존재하는지 여부를 먼저 확인합니다. 엔터티가 이미 존재 merge(entity)하는 경우 방법으로 만 업데이트가 발생 하고 그렇지 않으면 방법으로 새 ​​레코드가 삽입됩니다 persist(entity).


7

spring-data-jpa 사용하면 save()@DtechNet과 동일한 문제가 발생했습니다. 모든 사람들 save()이 업데이트 대신 새로운 객체를 만들고 있음을 의미합니다 . 이를 해결하기 위해 version엔터티 및 관련 테이블에 필드를 추가해야했습니다 .


엔티티 및 관련 테이블에 버전 필드를 추가한다는 의미입니다.
jcrshankar


5

이것이 내가 문제를 해결 한 방법입니다.

User inbound = ...
User existing = userRepository.findByFirstname(inbound.getFirstname());
if(existing != null) inbound.setId(existing.getId());
userRepository.save(inbound);

@Transaction여러 db 요청에 대해 위의 방법을 사용하십시오 . 이 경우에 필요하지 않은 userRepository.save(inbound);변경 사항은 자동으로 플러시됩니다.
Grigory Kislin

5

스프링 데이터 save()방법은 새 항목 추가와 기존 항목 업데이트를 모두 수행하는 데 도움이됩니다.

그냥 전화하고 save()인생을 즐기십시오 :))


다른 방법으로 보낸 경우이 방법으로 Id저장하면 새 레코드 저장을 피할 수 있습니다.
Abd Abughazaleh

1
@AbdAbughazaleh 수신 ID가 저장소에 있는지 여부를 확인하십시오. 'repository.findById (id) .map (entity-> {// 무엇을 반환 할 저장소 repository.save (entity)}) .orElseGet (()-> {// 무언가를 반환;}); '
Amir Mhp

1
public void updateLaserDataByHumanId(String replacement, String humanId) {
    List<LaserData> laserDataByHumanId = laserDataRepository.findByHumanId(humanId);
    laserDataByHumanId.stream()
            .map(en -> en.setHumanId(replacement))
            .collect(Collectors.toList())
            .forEach(en -> laserDataRepository.save(en));
}

@JoshuaTaylor는 실제로 그것을 완전히 놓쳤다 :) 주석을 제거합니다 ...
Eugene

1

특히 spring-data-jpa에 동일한 사용자 이름과 이름을 가진 사용자가 실제로 동일하며 엔티티를 업데이트해야한다고 어떻게 말합니까? 이퀄라이저 재정의는 효과가 없었습니다.

이 특별한 목적을 위해 다음과 같은 복합 키를 도입 할 수 있습니다.

CREATE TABLE IF NOT EXISTS `test`.`user` (
  `username` VARCHAR(45) NOT NULL,
  `firstname` VARCHAR(45) NOT NULL,
  `description` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`username`, `firstname`))

매핑 :

@Embeddable
public class UserKey implements Serializable {
    protected String username;
    protected String firstname;

    public UserKey() {}

    public UserKey(String username, String firstname) {
        this.username = username;
        this.firstname = firstname;
    }
    // equals, hashCode
}

사용 방법은 다음과 같습니다.

@Entity
public class UserEntity implements Serializable {
    @EmbeddedId
    private UserKey primaryKey;

    private String description;

    //...
}

JpaRepository는 다음과 같습니다.

public interface UserEntityRepository extends JpaRepository<UserEntity, UserKey>

그런 다음 다음 관용구를 사용할 수 있습니다 : 사용자 정보로 DTO를 수락하고 이름과 이름을 추출하고 UserKey를 작성한 다음이 복합 키로 UserEntity를 작성한 다음 Spring Data save ()를 호출하여 모든 것을 정렬해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.