मैं स्प्रिंग-डेटा-जेपा का उपयोग करके किसी इकाई को कैसे अपडेट करूं?


194

खैर सवाल बहुत ज्यादा सब कुछ कहते हैं। 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, जो भी हो। लेकिन, यह देखते हुए कि मैं बस स्प्रिंग-डेटा-जेपीए और जेपीएरेपोसिटरी का उपयोग करना चाहता हूं, मैं इसे कैसे बताऊं कि एक इंसर्ट के बजाय मैं एक अपडेट करना चाहता हूं?

विशेष रूप से, मैं स्प्रिंग-डेटा-जेपीए को कैसे बताऊं कि एक ही उपयोगकर्ता नाम और पहले नाम वाले उपयोगकर्ता वास्तव में एक्वल हैं और मौजूदा इकाई को अपडेट किया जाना चाहिए? बराबरी पर रहने से इस समस्या का समाधान नहीं हुआ।


1
क्या आप सुनिश्चित हैं कि जब आप डेटाबेस में किसी मौजूदा ऑब्जेक्ट को सहेजते हैं तो आईडी फिर से लिखी जाती है ?? मेरे प्रोजेक्ट tbh पर यह कभी नहीं हुआ
बायरन वूरबाक

@ByronVoorbach हाँ, आप सही हैं, बस इसका परीक्षण किया। प्रश्न को भी अपडेट करें, thx
यूजीन

2
नमस्कार दोस्तो , आप इस लिंक को देख सकते हैं stackoverflow.com/questions/24420572/… आप saveOrUpdate ()
ibrahimKiraz


मुझे लगता है कि हमारे पास यहां एक सुंदर समाधान है: लिंक विवरण यहां दर्ज करें
क्लेबियो विइरा

जवाबों:


207

संस्थाओं की पहचान उनकी प्राथमिक कुंजी द्वारा परिभाषित की गई है। चूंकि firstnameऔर lastnameप्राथमिक कुंजी के हिस्से नहीं हैं, आप जेपीए को Userउसी firstnameएस और lastnameएस के साथ इलाज करने के लिए नहीं कह सकते हैं, यदि वे अलग-अलग userIdएस हैं।

इसलिए, यदि आप एक अद्यतन करना चाहते Userइसके द्वारा की पहचान firstnameऔर lastname, आपको लगता है कि खोजने की जरूरत है Userएक प्रश्न के द्वारा, और फिर वस्तु की उचित क्षेत्रों को बदलने के अपने पाया। इन परिवर्तनों को लेनदेन के अंत में डेटाबेस में स्वचालित रूप से फ़्लश किया जाएगा, ताकि आपको इन परिवर्तनों को स्पष्ट रूप से सहेजने के लिए कुछ भी करने की आवश्यकता न हो।

संपादित करें:

शायद मुझे जेपीए के समग्र शब्दों पर विस्तार से जानकारी देनी चाहिए। दृढ़ता एपीआई के डिजाइन के लिए दो मुख्य दृष्टिकोण हैं:

  • डालने / अद्यतन दृष्टिकोण । जब आपको डेटाबेस को संशोधित करने की आवश्यकता होती है, तो आपको दृढ़ता से एपीआई के तरीकों को स्पष्ट रूप से कॉल करना चाहिए: आप insertकिसी ऑब्जेक्ट को सम्मिलित करने के लिए कॉल करते हैं, या updateडेटाबेस में ऑब्जेक्ट की नई स्थिति को बचाने के लिए।

  • कार्य दृष्टिकोण की इकाई । इस मामले में आपके पास दृढ़ता पुस्तकालय द्वारा प्रबंधित वस्तुओं का एक सेट है । इन ऑब्जेक्ट्स में आपके द्वारा किए गए सभी परिवर्तन कार्य की इकाई के अंत में डेटाबेस में स्वचालित रूप से फ़्लश हो जाएंगे (यानी विशिष्ट मामले में वर्तमान लेनदेन के अंत में)। जब आपको डेटाबेस में नया रिकॉर्ड डालने की आवश्यकता होती है, तो आप संबंधित ऑब्जेक्ट को प्रबंधित करते हैंप्रबंधित वस्तुओं, उनके प्राथमिक कुंजी द्वारा की पहचान कर रहे हैं ताकि अगर आप पूर्वनिर्धारित प्राथमिक कुंजी के साथ एक वस्तु बनाने में कामयाब रहे , यह अपने आप उस रिकॉर्ड को प्रचारित किया जाएगा एक ही आईडी के डेटाबेस रिकॉर्ड है, और इस वस्तु के राज्य के साथ संबद्ध किया जाएगा।

जेपीए बाद के दृष्टिकोण का अनुसरण करता है। save()स्प्रिंग डेटा में जेपीए merge()सादे जेपीए द्वारा समर्थित है , इसलिए यह आपकी इकाई को ऊपर वर्णित के अनुसार प्रबंधित करता है। इसका अर्थ है कि save()पूर्वनिर्धारित आईडी के साथ किसी ऑब्जेक्ट पर कॉल करने से नया डेटाबेस डालने के बजाय संबंधित डेटाबेस रिकॉर्ड अपडेट हो जाएगा, और यह भी बताता है कि क्यों save()नहीं कहा जाता है create()


अच्छी तरह से हाँ मुझे लगता है कि मुझे यह पता है। मैं सख्ती से वसंत-डेटा-जपा की बात कर रहा था। मुझे इस उत्तर के साथ दो समस्याएं हैं: 1) व्यावसायिक मूल्य प्राथमिक कुंजी का हिस्सा नहीं माना जाता है - यह एक ज्ञात चीज है, है ना? इसलिए प्राथमिक कुंजी के रूप में firstname और lastname होना अच्छा नहीं है। और 2) इस पद्धति को तब निर्मित क्यों नहीं कहा जाता है, लेकिन वसंत-डेटा-जम्पा के बजाय इसे बचाएं?
यूजीन

1
"सेव () इन स्प्रिंग डेटा जेपीए मर्ज () प्लेन जेपीए द्वारा समर्थित है" क्या आपने वास्तव में कोड को देखा था? मैंने अभी किया और यह दोनों या तो जारी या विलय द्वारा समर्थित है। यह आईडी (प्राथमिक कुंजी) की उपस्थिति के आधार पर बनी रहेगी या अपडेट होगी। यह, मुझे लगता है, को बचाने के तरीके में प्रलेखित किया जाना चाहिए। तो वास्तव में सहेजें EITHER मर्ज या बनी रहती है।
यूजीन

मुझे लगता है कि इसे सेव भी कहा जाता है, क्योंकि यह ऑब्जेक्ट को बचाने के लिए माना जाता है चाहे वह किसी भी स्थिति में हो - यह एक अपडेट या इंसर्ट करने वाला है जो स्टेट सेव करने के बराबर है।
यूजीन

1
यह मेरे लिए काम नहीं करेगा। मैंने एक मान्य प्राथमिक कुंजी के साथ एक इकाई को बचाने की कोशिश की है। मैं "ऑर्डर / एडिट /: आईडी" के साथ पृष्ठ तक पहुंचता हूं और यह वास्तव में मुझे आईडी द्वारा सही वस्तु देता है। कुछ भी नहीं मैं भगवान के प्यार के लिए प्रयास करें इकाई को अपडेट करेगा। यह हमेशा एक नई इकाई पोस्ट करता है .. मैंने एक कस्टम सेवा बनाने और अपने EntityManager के साथ "मर्ज" का उपयोग करने की भी कोशिश की और यह अभी भी काम नहीं करेगा। यह हमेशा एक नई इकाई पोस्ट करेगा।
DtechNet

1
@DechNet मैं आपको DtechNet के लिए एक समान समस्या का सामना कर रहा था, और यह मेरी समस्या थी कि मुझे अपने स्प्रिंग डेटा रिपॉजिटरी इंटरफ़ेस में गलत प्राथमिक कुंजी प्रकार निर्दिष्ट किया गया था। इसने कहा कि extends CrudRepository<MyEntity, Integer>इसके बजाय extends CrudRepository<MyEntity, String>ऐसा होना चाहिए। क्या उससे मदद हुई? मुझे पता है कि यह लगभग एक साल बाद है। आशा है कि यह किसी और की मदद करता है।
कैंट बुल

140

चूंकि @axtavt द्वारा उत्तर पर ध्यान केंद्रित JPAनहीं किया गया हैspring-data-jpa

क्वेरी के द्वारा किसी इकाई को अपडेट करने के लिए तब बचत करना कुशल नहीं है क्योंकि इसके लिए दो प्रश्नों की आवश्यकता होती है और संभवतः यह क्वेरी काफी महंगी हो सकती है क्योंकि यह अन्य तालिकाओं में शामिल हो सकती है और किसी भी संग्रह को लोड कर सकती है fetchType=FetchType.EAGER

Spring-data-jpaअद्यतन कार्रवाई का समर्थन करता है।
आपको रिपॉजिटरी इंटरफ़ेस में विधि को परिभाषित करना होगा । @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);

@Queryकस्टम क्वेरी को परिभाषित करने के लिए है और @Modifyingयह बताने के लिए है spring-data-jpaकि यह क्वेरी एक अपडेट ऑपरेशन है और इसके लिए इसकी आवश्यकता executeUpdate()नहीं है executeQuery()

आप अन्य रिटर्न प्रकार निर्दिष्ट कर सकते हैं:
int- अपडेट किए जाने वाले रिकॉर्ड की संख्या।
boolean- सच है अगर कोई रिकॉर्ड अपडेट किया जा रहा है। नहीं तो झूठा।


नोट : इस कोड को एक लेनदेन में चलाएं ।


10
सुनिश्चित करें कि आप इसे लेन
hussachai

1
अरे! धन्यवाद वसंत डेटा सेम का उपयोग कर रहा हूँ। तो यह स्वचालित रूप से मेरे अद्यतन का ख्याल रखेगा। <S का विस्तार T> S save (S निकाय); स्वचालित रूप से अद्यतन का ख्याल रखता है। मैं अपने तरीके का उपयोग नहीं किया है! फिर भी धन्यवाद!
bks4line

3
कभी भी :) यदि आप इकाई को बचाना चाहते हैं तो बचाने का तरीका काम करता है (यह कॉल को या तो em.persist () या em.merge () को दृश्य के पीछे करेगा)। वैसे भी, जब आप डेटाबेस में कुछ फ़ील्ड अपडेट करना चाहते हैं, तो कस्टम क्वेरी उपयोगी होती है।
हुसैचाई

जब आपका एक पैरामीटर सब यूनिट (manyToOne) का आईडी हो तो कैसे अपडेट करें? (पुस्तक में एक लेखक है और आपने पुस्तक लेखक को अद्यतन करने के लिए पुस्तक आईडी और लेखक आईडी पारित किया है)
महदी

1
To update an entity by querying then saving is not efficientये केवल दो विकल्प नहीं हैं। आईडी को निर्दिष्ट करने और इसे उद्धृत किए बिना पंक्ति वस्तु प्राप्त करने का एक तरीका है। यदि आप एक row = repo.getOne(id)और करते हैं और row.attr = 42; repo.save(row);लॉग देखते हैं, तो आप केवल अपडेट क्वेरी देखेंगे।
नुरेटिन

21

आप इस फ़ंक्शन को सेव () JPAfunction के साथ उपयोग कर सकते हैं, लेकिन पैरामीटर के रूप में भेजी गई वस्तु में डेटाबेस में एक मौजूदा आईडी होनी चाहिए अन्यथा यह काम नहीं करेगा, क्योंकि जब हम आईडी के बिना किसी ऑब्जेक्ट को भेजते हैं, तो इसे सेव करते हैं, यह सीधे एक पंक्ति में जोड़ता है डेटाबेस, लेकिन अगर हम किसी ऑब्जेक्ट को मौजूदा आईडी से भेजते हैं, तो यह डेटाबेस में पहले से मौजूद कॉलम को बदल देता है।

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
अगर आपको अपडेट करने से पहले डेटाबेस से ऑब्जेक्ट को लोड करना है, तो प्रदर्शन एक समस्या नहीं है? (माई इंग्लिश के लिए खेद है)

3
वहाँ एक के बजाय दो क्वेरी है, जो बहुत पहले से नहीं है
तिगरां बबजनैन

मुझे पता है मैं juste ने एक और तरीका दिखाया है! लेकिन जब आईडी ही है तो 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);
    }
}

जैसा कि आप देख सकते हैं, यह जांच करेगा कि आईडी पहले से मौजूद है या नहीं, यदि इकाई पहले से मौजूद है, तो केवल अपडेट merge(entity)विधि द्वारा होगा और यदि कोई है, तो एक नया रिकॉर्ड persist(entity)विधि द्वारा डाला जाता है ।


7

स्प्रिंग-डेटा- 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कई डीबी अनुरोध के लिए उपरोक्त विधि का उपयोग करें । इस मामले में कोई जरूरत नहीं है userRepository.save(inbound);, परिवर्तन स्वचालित रूप से flushed।
ग्रिगोरी किसलिन

5

स्प्रिंग डेटा save()विधि आपको दोनों को निष्पादित करने में मदद करेगी: नई आइटम जोड़ना और अस्तित्व में आइटम को अपडेट करना।

बस कॉल करें save()और जीवन का आनंद लें :))


इस तरह से अगर मैंने अलग-अलग भेजा है तो Idइसे बचाएंगे, मैं नए रिकॉर्ड को कैसे बचा सकता हूं।
अब्द अबुगज़लेह

1
@AbdAbughazaleh चेक करें कि क्या आने वाली आईडी आपकी रिपॉजिटरी में मौजूद है या नहीं। आप 'repository.findById (id) .map (एंटिटी -> {// कुछ रिटर्न रिपॉजिटरी। save (एंटिटी)}) .orElseGet (() -> {// do some return रिटर्न;}) का उपयोग कर सकते हैं। ’
आमिर म्ह

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 वास्तव में, चूक गया कि पूरी तरह से :) टिप्पणी को हटा देगा ...
यूजीन

1

विशेष रूप से मैं स्प्रिंग-डेटा-जेपीए को कैसे बताऊं कि जिन उपयोगकर्ताओं के पास एक ही उपयोगकर्ता नाम और फर्स्टनाम है वे वास्तव में एक्वल हैं और यह इकाई को अपडेट करने वाला है। बराबरी का काम नहीं किया।

इस विशेष प्रयोजन के लिए कोई इस तरह एक समग्र कुंजी पेश कर सकता है:

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>

फिर, आप निम्नलिखित मुहावरे का उपयोग कर सकते हैं : उपयोगकर्ता जानकारी के साथ डीटीओ को स्वीकार करें, नाम और फर्स्टनाम निकालें और UserKey बनाएं, फिर इस समग्र कुंजी के साथ एक UserEntity बनाएं और फिर स्प्रिंग डेटा सेव () को खोलें जो आपके लिए सब कुछ छाँट दे।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.