Làm cách nào để cập nhật một thực thể bằng spring-data-jpa?


193

Vâng, câu hỏi khá nhiều nói lên tất cả. Sử dụng JPARep repository, làm cách nào để cập nhật một thực thể?

JPARep repository chỉ có một phương thức lưu , nó không cho tôi biết nếu nó thực sự tạo hoặc cập nhật. Ví dụ, tôi chèn một đối tượng đơn giản cho người dùng cơ sở dữ liệu, trong đó có ba lĩnh vực: 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;
  }
}

Sau đó, tôi chỉ cần gọi save(), mà tại thời điểm này thực sự là một chèn vào cơ sở dữ liệu:

 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);

Càng xa càng tốt. Bây giờ tôi muốn cập nhật người dùng này, nói thay đổi tuổi của anh ấy. Với mục đích này, tôi có thể sử dụng Truy vấn, hoặc QueryDSL hoặc NamedQuery, bất cứ điều gì. Nhưng, xem xét tôi chỉ muốn sử dụng spring-data-jpa và JPARep repository, làm thế nào để tôi nói với nó rằng thay vì chèn tôi muốn thực hiện cập nhật?

Cụ thể, làm thế nào để tôi nói với spring-data-jpa rằng người dùng có cùng tên người dùng và tên thực sự là THIẾT BỊ và thực thể hiện có phải được cập nhật? Ghi đè bằng không giải quyết được vấn đề này.


1
Bạn có chắc chắn ID sẽ được viết lại khi bạn lưu một đối tượng hiện có trong cơ sở dữ liệu ?? Chưa bao giờ có điều này trong dự án của tôi
Byron Voorbach

@ByronVoorbach yup bạn đúng, chỉ cần thử nghiệm này. cũng cập nhật câu hỏi, thx
Eugene

2
Xin chào bạn, bạn có thể xem liên kết này stackoverflow.com/questions/24420572/, bạn có thể là một cách tiếp cận như saveOrUpdate ()
ibrahimKiraz 6/12/2015


Tôi nghĩ rằng chúng tôi có một giải pháp tuyệt vời ở đây: nhập mô tả liên kết tại đây
Clebio Vieira

Câu trả lời:


206

Danh tính của các thực thể được xác định bởi các khóa chính của chúng. Vì firstnamelastnamekhông phải là một phần của khóa chính, bạn không thể yêu cầu JPA coi Users với cùng firstnames và lastnames bằng nhau nếu chúng có userIds khác nhau .

Vì vậy, nếu bạn muốn cập nhật một Userxác định bởi nó firstnamelastname, bạn cần tìm thấy nó Userbằng một truy vấn, và sau đó thay đổi các trường thích hợp của đối tượng bạn tìm thấy. Những thay đổi này sẽ được tự động chuyển đến cơ sở dữ liệu khi kết thúc giao dịch, do đó bạn không cần phải làm gì để lưu những thay đổi này một cách rõ ràng.

BIÊN TẬP:

Có lẽ tôi nên giải thích về ngữ nghĩa tổng thể của JPA. Có hai cách tiếp cận chính để thiết kế API bền bỉ:

  • chèn / cập nhật phương pháp . Khi bạn cần sửa đổi cơ sở dữ liệu, bạn nên gọi các phương thức API kiên trì một cách rõ ràng: bạn gọi insertđể chèn một đối tượng hoặc updateđể lưu trạng thái mới của đối tượng vào cơ sở dữ liệu.

  • Đơn vị tiếp cận công việc . Trong trường hợp này, bạn có một tập hợp các đối tượng được quản lý bởi thư viện kiên trì. Tất cả các thay đổi bạn thực hiện đối với các đối tượng này sẽ được tự động chuyển sang cơ sở dữ liệu vào cuối Đơn vị công việc (nghĩa là vào cuối giao dịch hiện tại trong trường hợp điển hình). Khi bạn cần chèn bản ghi mới vào cơ sở dữ liệu, bạn thực hiện quản lý đối tượng tương ứng . Các đối tượng được quản lý được xác định bởi các khóa chính của chúng, do đó, nếu bạn tạo một đối tượng với khóa chính được xác định trước được quản lý , nó sẽ được liên kết với bản ghi cơ sở dữ liệu của cùng một id và trạng thái của đối tượng này sẽ được tự động truyền tới bản ghi đó.

JPA theo cách tiếp cận sau. save()trong Spring Data JPA được hỗ trợ bởi merge()JPA đơn giản, do đó nó làm cho thực thể của bạn được quản lý như mô tả ở trên. Điều đó có nghĩa là việc gọi save()một đối tượng có id được xác định trước sẽ cập nhật bản ghi cơ sở dữ liệu tương ứng thay vì chèn một đối tượng mới và cũng giải thích tại sao save()không được gọi create().


Vâng, tôi nghĩ rằng tôi biết điều này. Tôi đã nghiêm túc đề cập đến spring-data-jpa. Bây giờ tôi có hai vấn đề với câu trả lời này: 1) các giá trị kinh doanh không được coi là một phần của khóa chính - đó là một điều đã biết, phải không? Vì vậy, có tên và họ là khóa chính là không tốt. Và 2) Tại sao phương thức này sau đó không được gọi là tạo mà thay vào đó là lưu trong spring-data-jpa?
Eugene

1
"Save () trong Spring Data JPA được hỗ trợ bởi merge () trong JPA đơn giản" bạn có thực sự xem mã không? Tôi vừa làm và cả hai đều được hỗ trợ bằng cách duy trì hoặc hợp nhất. Nó sẽ tồn tại hoặc cập nhật dựa trên sự hiện diện của id (khóa chính). Điều này, tôi nghĩ, nên được ghi lại trong phương pháp lưu. Vì vậy, tiết kiệm thực sự là hợp nhất EITHER hoặc tồn tại.
Eugene

Tôi nghĩ nó cũng được gọi là lưu, bởi vì nó được cho là lưu đối tượng cho dù nó ở trạng thái nào - nó sẽ thực hiện cập nhật hoặc chèn tương đương với trạng thái lưu.
Eugene

1
Điều này sẽ không làm việc cho tôi. Tôi đã thử lưu vào một thực thể với khóa chính hợp lệ. Tôi truy cập trang với "order / edit /: id" và nó thực sự mang lại cho tôi đối tượng chính xác theo Id. Không có gì tôi cố gắng vì tình yêu của Chúa sẽ cập nhật thực thể. Nó luôn đăng một thực thể mới .. Tôi thậm chí đã thử tạo một dịch vụ tùy chỉnh và sử dụng "hợp nhất" với EntityManager của mình và nó vẫn không hoạt động. Nó sẽ luôn luôn đăng một thực thể mới.
DtechNet

1
@DTechNet Tôi đã gặp một vấn đề tương tự với bạn, DtechNet, và hóa ra vấn đề của tôi là tôi đã nhập sai loại khóa chính được chỉ định trong giao diện kho lưu trữ dữ liệu Spring của tôi. Nó nói extends CrudRepository<MyEntity, Integer>thay vì extends CrudRepository<MyEntity, String>thích nó nên có. cái đó có giúp ích không? Tôi biết điều này là gần một năm sau. Hy vọng nó sẽ giúp người khác.
Kent Bull

139

Kể từ khi câu trả lời bằng @axtavt tập trung vào JPAkhôngspring-data-jpa

Để cập nhật một thực thể bằng cách truy vấn sau đó lưu không hiệu quả vì nó yêu cầu hai truy vấn và có thể truy vấn có thể khá tốn kém vì nó có thể tham gia các bảng khác và tải bất kỳ bộ sưu tập nào có fetchType=FetchType.EAGER

Spring-data-jpahỗ trợ cập nhật hoạt động.
Bạn phải xác định phương thức trong giao diện Kho lưu trữ. Và chú thích nó với @Query@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);

@Querylà để xác định truy vấn tùy chỉnh và @Modifyingđể nói spring-data-jparằng truy vấn này là một hoạt động cập nhật và nó executeUpdate()không yêu cầu executeQuery().

Bạn có thể chỉ định các loại trả lại khác:
int- số lượng hồ sơ đang được cập nhật.
boolean- đúng nếu có một bản ghi đang được cập nhật. Nếu không, sai.


Lưu ý : Chạy mã này trong Giao dịch .


10
Hãy chắc chắn rằng bạn chạy nó trong giao dịch
hussachai

1
Chào! Cảm ơn đang sử dụng đậu dữ liệu mùa xuân. Vì vậy, nó sẽ tự động chăm sóc bản cập nhật của tôi. <S kéo dài T> S save (thực thể S); tự động chăm sóc cập nhật. tôi không phải sử dụng phương pháp của bạn! Dù sao đi nữa cũng xin cám ơn!
bks4line

3
Bất cứ lúc nào :) Phương thức lưu sẽ hoạt động nếu bạn muốn lưu thực thể (Nó sẽ ủy quyền cuộc gọi cho em.persist () hoặc em.merge () đằng sau cảnh). Dù sao, truy vấn tùy chỉnh là hữu ích khi bạn muốn cập nhật chỉ một số trường trong cơ sở dữ liệu.
hussachai

Làm thế nào về khi một trong các tham số của bạn là id của thực thể phụ (manyToOne) thì nên cập nhật như thế nào? (sách có Tác giả và bạn đã chuyển id sách và id tác giả để cập nhật tác giả sách)
Mahdi

1
To update an entity by querying then saving is not efficientđây không phải là hai lựa chọn duy nhất Có một cách để xác định id và lấy đối tượng hàng mà không cần truy vấn nó. Nếu bạn làm một row = repo.getOne(id)và sau đó row.attr = 42; repo.save(row);và xem nhật ký, bạn sẽ chỉ thấy truy vấn cập nhật.
Nurettin

21

Bạn có thể chỉ cần sử dụng hàm này với save () JPAfunction, nhưng đối tượng được gửi dưới dạng tham số phải chứa id hiện có trong cơ sở dữ liệu nếu không nó sẽ không hoạt động, vì save () khi chúng tôi gửi một đối tượng không có id, nó sẽ thêm một hàng trực tiếp vào cơ sở dữ liệu, nhưng nếu chúng ta gửi một đối tượng có id hiện có, nó sẽ thay đổi các cột đã được tìm thấy trong cơ sở dữ liệu.

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
hiệu suất không phải là vấn đề nếu bạn phải tải đối tượng từ cơ sở dữ liệu trước khi cập nhật? (xin lỗi vì tiếng Anh của tôi)
august0490

3
có hai truy vấn thay vì một truy vấn, điều này không được ưu tiên
Tigran Babajanyan

tôi biết tôi vừa xuất hiện một phương pháp khác để làm! nhưng tại sao jpa thực hiện chức năng cập nhật khi id giống nhau?
Kaliforniaium

17

Như những gì đã được đề cập bởi những người khác, save()bản thân nó chứa cả hoạt động tạo và cập nhật.

Tôi chỉ muốn thêm bổ sung về những gì đằng sau save()phương pháp.

Đầu tiên, chúng ta hãy xem phân cấp mở rộng / thực hiện của CrudRepository<T,ID>, nhập mô tả hình ảnh ở đây

Ok, hãy kiểm tra việc save()thực hiện tại 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);
    }
}

Như bạn có thể thấy, nó sẽ kiểm tra xem ID có tồn tại hay không trước tiên, nếu thực thể đã ở đó, chỉ cập nhật sẽ xảy ra theo merge(entity)phương thức và nếu không, một bản ghi mới được chèn theo persist(entity)phương thức.


7

Sử dụng spring-data-jpa save(), tôi gặp vấn đề tương tự như @DtechNet. Ý tôi là mọi người đều save()tạo đối tượng mới thay vì cập nhật. Để giải quyết điều này tôi đã phải thêm versiontrường vào thực thể và bảng liên quan.


ý bạn là gì khi thêm trường phiên bản vào thực thể và bảng liên quan.
jcrshankar


5

Đây là cách tôi giải quyết vấn đề:

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

Sử dụng @Transactionphương thức trên cho một số yêu cầu db. Một trong trường hợp này không cần userRepository.save(inbound);, thay đổi tự động tuôn ra.
Grigory Kislin

5

save()phương pháp dữ liệu mùa xuân sẽ giúp bạn thực hiện cả hai: thêm mục mới và cập nhật một mục tồn tại.

Chỉ cần gọi save()và tận hưởng cuộc sống :))


Theo cách này nếu tôi gửi khác Idsẽ lưu nó, làm thế nào tôi có thể tránh lưu bản ghi mới.
Abd Abughazaleh

1
@AbdAbughazaleh kiểm tra xem Id đến có tồn tại trong kho của bạn hay không. bạn có thể sử dụng 'repository.findById (id) .map (entity -> {// do Something return repository.save (entity)}) .orElseGet (() -> {// do Something return;}); '
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 thực sự, đã bỏ lỡ hoàn toàn :) sẽ xóa bình luận ...
Eugene

1

Cụ thể làm thế nào để tôi nói với spring-data-jpa rằng những người dùng có cùng tên người dùng và tên đầu tiên thực sự là THIẾT BỊ và nó có nghĩa vụ phải cập nhật thực thể. Ghi đè bằng không hoạt động.

Đối với mục đích cụ thể này, người ta có thể giới thiệu một khóa tổng hợp như thế này:

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`))

Ánh xạ:

@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
}

Đây là cách sử dụng nó:

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

    private String description;

    //...
}

JpaRep repository sẽ trông như thế này:

public interface UserEntityRepository extends JpaRepository<UserEntity, UserKey>

Sau đó, bạn có thể sử dụng như sau thành ngữ : chấp nhận DTO với thông tin người dùng, trích xuất tên và tên và tạo UserKey, sau đó tạo UserEntity với khóa tổng hợp này và sau đó gọi Spring Data save () sẽ sắp xếp mọi thứ cho bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.