Spring-data-jpa kullanarak bir varlığı nasıl güncelleyebilirim?


196

Soru hemen hemen her şeyi söylüyor. JPARepository kullanarak bir varlığı nasıl güncelleyebilirim?

JPARepository'nin sadece bir kaydetme yöntemi vardır, bu gerçekten oluşturup güncellemediğini söylemez. Mesela ben üç alanı vardır veritabanı kullanıcı, basit bir nesne ekleyin: firstname, lastnameve 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;
  }
}

Sonra ben sadece save(), bu noktada aslında veritabanına bir insert olduğunu çağırmak :

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

Çok uzak çok iyi. Şimdi bu kullanıcıyı güncellemek istiyorum, diyelim yaşını değiştir. Bu amaçla bir Query, QueryDSL veya NamedQuery kullanabilirsiniz. Ancak, sadece spring-data-jpa ve JPARepository kullanmak istediğimi düşünürsek, bir ekleme yerine güncelleme yapmak istediğimi nasıl anlarım?

Özellikle, spring-data-jpa'ya aynı kullanıcı adı ve adıyla kullanıcıların aslında EQUAL olduğunu ve mevcut varlığın güncellenmesi gerektiğini nasıl söyleyebilirim? Eşit değerlerin geçersiz kılınması bu sorunu çözmedi.


1
Varolan bir nesneyi veritabanına kaydettiğinizde kimliğin yeniden yazıldığından emin misiniz ?? Bunu asla
projemde görmedim

@BronronVoorbach evet haklısın, bunu test et. soruyu da güncelleyin, thx
Eugene

2
Merhaba arkadaşım, bu bağlantıya bakabilirsiniz stackoverflow.com/questions/24420572/… saveOrUpdate () gibi bir yaklaşım olabilir
ibrahimKiraz


Burada güzel bir çözümümüz olduğunu düşünüyorum: Bağlantı açıklamasını buraya girin
Clebio Vieira

Yanıtlar:


208

Varlıkların kimliği birincil anahtarları ile tanımlanır. Yana firstnameve lastnamebirincil anahtar parçaları değil, sen tedavi için JPA söyleyemem Useraynı ile s firstnames ve lastnamebunlar farklı varsa eşit olarak s userIds.

Bu nedenle, bir Usertanımlanmış tarafından güncellenmek istiyorsanız firstnameve lastnamebunu Userbir sorgu ile bulmanız ve ardından bulduğunuz nesnenin uygun alanlarını değiştirmeniz gerekir. Bu değişiklikler işlem sonunda veritabanında otomatik olarak temizlenir, böylece bu değişiklikleri açıkça kaydetmek için herhangi bir şey yapmanız gerekmez.

DÜZENLE:

Belki de JPA'nın genel anlambilimi üzerinde durmalıyım. Kalıcılık API'larının tasarımında iki ana yaklaşım vardır:

  • ekleme / güncelleme yaklaşımı . Veritabanını değiştirmeniz gerektiğinde, kalıcılık API'lerini açıkça çağırmalısınız: insertbir nesne eklemek veya nesnenin updateyeni durumunu veritabanına kaydetmek için çağrı yaparsınız .

  • İş Birimi yaklaşımı . Bu durumda , kalıcılık kitaplığı tarafından yönetilen bir dizi nesneniz vardır . Bu nesnelerde yaptığınız tüm değişiklikler, İş Birimi'nin sonunda (yani, tipik olarak mevcut işlemin sonunda) otomatik olarak veritabanına temizlenir. Veritabanına yeni kayıt eklemeniz gerektiğinde, karşılık gelen nesneyi yönetirsiniz .Yönetilen nesneleri önceden birincil anahtarla bir nesne yaparsanız böylece, onların birincil anahtarlar tanımlanır yönetilen otomatik olarak o kayda yayılacaktır veritabanı aynı id kayıt ve bu nesnenin durumu ile ilişkili olacaktır.

JPA ikinci yaklaşımı izler. save()Baharda Veri JPA tarafından desteklenmektedir merge()bu nedenle sizin varlık yapar, düz JPA yönetilen yukarıda açıklandığı gibi. Bu save(), önceden tanımlanmış kimliği olan bir nesneyi çağırmanın , yeni bir tane eklemek yerine karşılık gelen veritabanı kaydını güncelleyeceği ve neden save()çağrılmadığını açıklayacağı anlamına gelir create().


evet, sanırım bunu biliyorum. Kesinlikle spring-data-jpa'dan bahsediyordum. Şu anda bu cevapla ilgili iki problemim var: 1) İşletme değerlerinin birincil anahtarın bir parçası olması gerekmiyor - bu bilinen bir şey, değil mi? Dolayısıyla birincil anahtar olarak ad ve soyadının olması iyi değildir. Ve 2) Bu yöntem neden create olarak adlandırılmaz, ancak spring-data-jpa'ya kaydedilir?
Eugene

1
"Bahar Verisi'nde save () JPA düz JPA'da merge () ile desteklenir" aslında koda baktınız mı? Sadece yaptım ve her ikisi de ya devam ya da birleştirme ile desteklendi. Kimliğin varlığına (birincil anahtar) bağlı olarak devam eder veya güncellenir. Bu, bence, kaydetme yönteminde belgelenmelidir. Yani kaydetmek aslında EITHER birleşmesi veya devam ediyor.
Eugene

Ben de kaydet denir düşünüyorum, çünkü hangi durumda olursa olsun nesneyi kurtarmak gerekiyordu - bu durumu kaydetmek için eşit bir güncelleme veya ekleme gerçekleştirecek.
Eugene

1
Bu benim için işe yaramayacak. Geçerli bir birincil anahtarla bir varlığa kaydetmeyi denedim. Sayfaya "order / edit /: id" ile erişiyorum ve aslında bana id ile doğru nesneyi veriyor. Tanrı sevgisi için denediğim hiçbir şey varlığı güncelleyemez. Her zaman yeni bir varlık gönderir .. Hatta özel bir hizmet yapmak ve benim EntityManager ile "birleştirme" kullanarak denedim ve hala çalışmaz. Her zaman yeni bir varlık yayınlayacaktır.
DtechNet

1
@DTechNet Size benzer bir sorun yaşıyordum DtechNet ve sorunum, Spring Data veri havuzu arayüzümde yanlış birincil anahtar türünün belirtilmiş olmasıydı. Bu sözü extends CrudRepository<MyEntity, Integer>yerine extends CrudRepository<MyEntity, String>o olmalı gibi. Bu yardımcı olur mu? Bunun neredeyse bir yıl sonra olduğunu biliyorum. Umarım başka birine yardımcı olur.
Kent Bull

141

@Axtavt tarafından cevap odaklanır yana JPAdeğilspring-data-jpa

Bir varlığı sorgulayarak güncellemek için kaydetme verimli değildir, çünkü iki sorgu gerektirir ve sorgu diğer tablolara katılıp tüm koleksiyonları yükleyebileceğinden oldukça pahalı olabilir. fetchType=FetchType.EAGER

Spring-data-jpagüncelleme işlemini destekler.
Yöntemi Depo arabiriminde tanımlamanız ve @Queryve ile açıklama eklemeniz gerekir @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özel sorgu tanımlamak için ve @Modifyingsöylüyorum içindir spring-data-jpabu sorgu bir güncelleme operasyon olduğunu ve onu gerektiriyor executeUpdate()değilexecuteQuery() .

Diğer iade türlerini belirleyebilirsiniz:
int - güncellenmekte olan kayıt sayısı.
boolean- güncellenen bir kayıt varsa true. Aksi takdirde, yanlış.


Not : Bu kodu bir İşlemde çalıştırın .



1
Hey! Teşekkürler bahar veri çekirdekleri kullanıyorum. Böylece otomatik olarak güncellememi halledecek. <S, T> S tasarrufunu (S varlığı) genişletir; otomatik olarak güncellemeyle ilgilenir. yöntemini kullanmak zorunda değildi! Yine de teşekkürler!
bks4line

3
Herzaman :) Varlığı kaydetmek istiyorsanız save yöntemi çalışır (Çağrıyı sahnenin arkasındaki em.persist () veya em.merge () işlevine devreder). Her neyse, özel sorgu veritabanındaki bazı alanları güncellemek istediğinizde kullanışlıdır.
hussachai

parametrelerinizden birinin alt varlık kimliği (manyToOne) ne zaman olduğunu nasıl öğreneceksiniz? (kitabın bir Yazarı var ve kitap yazarını güncellemek için kitap kimliği ve yazar kimliğini geçtiniz)
Mehdi

1
To update an entity by querying then saving is not efficientbunlar sadece iki seçenek değil. İd belirtmenin ve satır nesnesini sorgulamadan almanın bir yolu vardır. Bir row = repo.getOne(id)ve sonra row.attr = 42; repo.save(row);günlükleri izlerseniz, yalnızca güncelleme sorgusunu görürsünüz.
nurettin

21

Save () JPAfunction ile bu işlevi kullanabilirsiniz, ancak parametre olarak gönderilen nesnenin veritabanında varolan bir kimliği içermesi gerekir, aksi takdirde çalışmaz, çünkü kimliği olmayan bir nesneyi gönderdiğimizde save (), doğrudan bir satır ekler ancak mevcut kimliğe sahip bir nesne gönderirsek, veritabanında zaten bulunan sütunları değiştirir.

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
Güncelleme yapmadan önce nesneyi veritabanından yüklemeniz gerekiyorsa performans sorun değil mi? (İngilizcem için özür dilerim)
Ağustos0490

3
biri yerine iki sorgu vardır, ki bu çok tercih edilmez
Tigran Babajanyan

i juste yapmak için başka bir yöntem gösterdi biliyorum! ama neden jpa id aynı olduğunda güncelleme işlevini uyguladı?
Kalifornium

17

Başkaları tarafından daha önce bahsedildiği gibi, save()kendisi hem oluşturma hem de güncelleme işlemini içerir.

Sadece save()yöntemin arkasında neler olduğu hakkında ek bilgi eklemek istiyorum .

İlk olarak, genişleme / uygulama hiyerarşisini CrudRepository<T,ID>, resim açıklamasını buraya girin

Tamam, save()uygulamayı şu adreste kontrol edelim 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);
    }
}

Gördüğünüz gibi, öncelikle kimliğin var olup olmadığını kontrol eder, eğer varlık zaten oradaysa, sadece güncelleme merge(entity)yöntemle gerçekleşir ve aksi takdirde yöntemle yeni bir kayıt eklenir persist(entity).



5

Sorunu şu şekilde çözdüm:

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

@TransactionBirkaç db isteği için yukarıdaki yöntemi kullanın . Bu durumda gerek yok userRepository.save(inbound);, değişiklikler otomatik olarak yıkandı.
Grigory Kislin

5

yay verisi save() yöntemi her ikisini de gerçekleştirmenize yardımcı olacaktır: yeni öğe ekleme ve mevcut bir öğeyi güncelleme.

Sadece arayın save()ve hayatın tadını çıkarın :))


Bu şekilde farklı gönderirsem Idkaydederim, yeni kaydı kaydetmekten nasıl kaçınabilirim.
Abd Abughazaleh

1
@AbdAbughazaleh deponuzda gelen Kimliği olup olmadığını kontrol edin. 'repository.findById (id) .map (entity -> {// bir şey döndürmek repository.save (entity)}) .orElseGet (() -> {// bir şey döndürmek;}); '
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 gerçekten, tamamen :) kaçırdı yorum kaldıracak ...
Eugene

1

Spesifik olarak, spring-data-jpa'ya aynı kullanıcı adı ve adına sahip kullanıcıların aslında EQUAL olduğunu ve varlığı güncellemesi gerektiğini nasıl söyleyebilirim. Geçersiz kılma eşit işe yaramadı.

Bu özel amaç için, böyle bir kompozit anahtar sokulabilir:

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

haritalama:

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

İşte nasıl kullanılır:

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

    private String description;

    //...
}

JpaRepository şöyle görünecektir:

public interface UserEntityRepository extends JpaRepository<UserEntity, UserKey>

Ardından, aşağıdaki deyimi kullanabilirsiniz : DTO'yu kullanıcı bilgileriyle kabul edin, adı ve adı çıkarın ve UserKey'i oluşturun, sonra bu kompozit anahtarla bir UserEntity oluşturun ve sonra sizin için her şeyi sıralaması gereken Spring Data save () öğesini çağırın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.