it-swarm.com.de

JPA: Nur bestimmte Felder aktualisieren

Gibt es eine Möglichkeit, nur einige Felder eines Entitätsobjekts mit der Methode save aus Spring Data JPA zu aktualisieren?

Zum Beispiel habe ich eine JPA-Entität wie diese:

@Entity
public class User {

  @Id
  private Long id;

  @NotNull
  private String login;

  @Id
  private String name;

  // getter / setter
  // ...
}

Mit seinem CRUD-Repo:

public interface UserRepository extends CrudRepository<User, Long> { }

In Spring MVC Ich habe einen Controller, der ein User-Objekt zur Aktualisierung erhält: 

@RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<?> updateUser(@RequestBody User user) {

   // Assuming that user have its id and it is already stored in the database,
   // and user.login is null since I don't want to change it,
   // while user.name have the new value

   // I would update only its name while the login value should keep the value 
   // in the database
   userRepository.save(user);

   // ...
}

Ich weiß, dass ich den Benutzer mit findOne laden konnte, dann seinen Namen ändern und ihn mit save... aktualisieren kann. Wenn ich jedoch 100 Felder habe und 50 von ihnen aktualisieren möchte, kann es sehr lästig sein, jeden Wert zu ändern. 

Gibt es keine Möglichkeit, etwas wie " überspringen alle Nullwerte beim Speichern des Objekts " zu sagen?

37
Andrea

Ich hatte die gleiche Frage und wie Herr Deinum hervorhebt, lautet die Antwort nein, Sie können nicht speichern. Das Hauptproblem war, dass Spring Data nicht wusste, was mit Nullen zu tun ist. Ist der Nullwert nicht festgelegt oder ist er gesetzt, weil er gelöscht werden muss?

Nach Ihrer Frage zu urteilen, nehme ich an, Sie hatten auch den gleichen Gedanken wie ich. Das heißt, mit save könnten Sie nicht alle geänderten Werte manuell einstellen.

Ist es also möglich, die gesamte manuelle Zuordnung zu vermeiden? Wenn Sie sich für die Einhaltung der Konvention entscheiden, dass Nullen immer "nicht festgelegt" bedeuten und Sie die ursprüngliche Modell-ID haben, dann yes .. Sie können jede Zuordnung selbst vermeiden, indem Sie Springs BeanUtils verwenden.

Sie könnten folgendes tun:

  1. Lesen Sie das vorhandene Objekt
  2. Verwenden Sie BeanUtils, um Werte zu kopieren
  3. Speichern Sie das Objekt

Nun unterstützt das BeanUtils von Spring nicht, dass keine Nullwerte kopiert werden. Daher werden alle Werte überschrieben, die für das vorhandene Modellobjekt nicht mit Null festgelegt sind. Zum Glück gibt es hier eine Lösung:

Wie ignoriere ich Nullwerte mit springframework BeanUtils copyProperties?

Wenn man also alles zusammenbringt, würde man so etwas enden

@RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<?> updateUser(@RequestBody User user) {

   User existing = userRepository.read(user.getId());
   copyNonNullProperties(user, existing);
   userRepository.save(existing);

   // ...
}

public static void copyNonNullProperties(Object src, Object target) {
    BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}

public static String[] getNullPropertyNames (Object source) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    Java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

    Set<String> emptyNames = new HashSet<String>();
    for(Java.beans.PropertyDescriptor pd : pds) {
        Object srcValue = src.getPropertyValue(pd.getName());
        if (srcValue == null) emptyNames.add(pd.getName());
    }
    String[] result = new String[emptyNames.size()];
    return emptyNames.toArray(result);
}
30
William

Mit JPA können Sie es so machen.

CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaUpdate<User> criteria = builder.createCriteriaUpdate(User.class);
Root<User> root = criteria.from(User.class);
criteria.set(root.get("lastSeen"), date);
criteria.where(builder.equal(root.get("id"), user.getId()));
session.createQuery(criteria).executeUpdate();
4
Arjun Nayak

das Problem bezieht sich nicht auf Frühlingsdaten, die mit jpa in Zusammenhang stehen, sondern mit der jpa-Implementierungslib, die Sie verwenden. Falls der Winterschlaf eintritt, können Sie Folgendes sehen:

http://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/

1
Pirulino

Für lange, int und andere Typen; Können Sie den folgenden Code verwenden:

            if (srcValue == null|(src.getPropertyTypeDescriptor(pd.getName()).getType().equals(long.class) && srcValue.toString().equals("0")))
            emptyNames.add(pd.getName());
0
bmck

Wenn Sie die Anforderung als JSON-String lesen, kann dies mit der Jackson-API erfolgen. Hier ist der Code unten. Code vergleicht ein vorhandenes POJO-Element und erstellt ein neues mit aktualisierten Feldern. Verwenden Sie den neuen POJO, um zu bestehen.

public class TestJacksonUpdate {

class Person implements Serializable {
    private static final long serialVersionUID = -7207591780123645266L;
    public String code = "1000";
    public String firstNm = "John";
    public String lastNm;
    public Integer age;
    public String comments = "Old Comments";

    @Override
    public String toString() {
        return "Person [code=" + code + ", firstNm=" + firstNm + ", lastNm=" + lastNm + ", age=" + age
                + ", comments=" + comments + "]";
    }
}

public static void main(String[] args) throws JsonProcessingException, IOException {
    TestJacksonUpdate o = new TestJacksonUpdate();

    String input = "{\"code\":\"1000\",\"lastNm\":\"Smith\",\"comments\":\"Jackson Update WOW\"}";
    Person persist = o.new Person();

    System.out.println("persist: " + persist);

    ObjectMapper mapper = new ObjectMapper();
    Person finalPerson = mapper.readerForUpdating(persist).readValue(input);

    System.out.println("Final: " + finalPerson);
}}

Die endgültige Ausgabe wäre "Notice only lastNm" und "Comments" spiegeln Änderungen wider.

persist: Person [code=1000, firstNm=John, lastNm=null, age=null, comments=Old Comments]
Final: Person [code=1000, firstNm=John, lastNm=Smith, age=null, comments=Jackson Update WOW]
0
Anand

Sie können so etwas schreiben 

@Modifying
    @Query("update StudentXGroup iSxG set iSxG.deleteStatute = 1 where iSxG.groupId = ?1")
    Integer deleteStudnetsFromDeltedGroup(Integer groupId);

Oder Wenn Sie nur die Felder aktualisieren möchten, die geändert wurden, können Sie Anmerkungen verwenden 

@DynamicUpdate

Code-Beispiel:

@Entity
@Table(name = "lesson", schema = "oma")
@Where(clause = "delete_statute = 0")
@DynamicUpdate
@SQLDelete(sql = "update oma.lesson set delete_statute = 1, "
        + "delete_date = CURRENT_TIMESTAMP, "
        + "delete_user = '@currentUser' "
        + "where lesson_id = ?")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})