2010-05-18 12 views
13

Mam klasy użytkownikaKorzystanie saveOrUpdate() w Hibernate tworzy nowe rekordy zamiast aktualizować istniejące

class User { 
    int id; 
    String name; 
} 

gdzie id jest rodzimy generator w User.hbm.xml i name jest podstawowy klucz w DB.

W mojej bazie danych zapisałem kilka informacji o użytkownikach.

Niż chcę się połączyć z tą informacją o użytkowniku.

Na przykład w moim DB mam rzędzie INSERT INTO User VALUES ('Bill');

Main.java

User bill = new User(); 
bill.setName("Bill"); 
session.saveOrUpdate(bill); 

Ten kod zawsze próbuje wstawić nowy Bill wiersz do tabeli zamiast zaktualizować istniejące Bill wiersz.

Odpowiedz

1

Tak, identyfikator nie jest zapisany w bazie danych?

Dlaczego Id nie jest kluczem podstawowym w DB, jeśli zamapuje się go w stan hibernacji jako identyfikator?

Dlaczego po prostu nie nałożono unikalnego ograniczenia na "nazwę" w bazie danych i nie utworzyłem ograniczenia klucza podstawowego dla identyfikatora?

+0

Thx za Twoje zaufanie. 'id' jest kluczem podstawowym, a' name' ma unikalny element constriant Mamy klasę obiektu Użytkownik, na przykład: 'Rachunek użytkownika = nowy użytkownik()' i atrybuty dotyczące rachunku, który otrzymujemy od keybord lub strony internetowej, a moje pytanie jest następujące: : W jaki sposób można zrobić saveOrUpdate(), jeśli plik bill.name istnieje w db, który nazywamy update() w przeciwnym razie zapiszemy rachunek. – kunkanwan

-1

Należy użyć session.update (Object) podczas aktualizacji i session.save (Object) podczas zapisywania

+3

I 'saveOrUpdate()' może zrobić to dla ciebie, co jest celem tej metody ... –

+0

Tak, masz rację, ale chcę, aby funkcja saveOrUpdate() ze stanu hibernacji wykonała pracę dla mnie. Mógłbym zapisać moje saveOrUpdate(), gdzie wybieram opcję select, jeśli "name" istnieje w db i wywołaj save() lub update(). Myślę, że to nie jest dobry pomysł, a może hibernacja ma lepsze rozwiązanie, na przykład przesłonięcie equals() gdzie porównujemy element z klucza biznesowego. – kunkanwan

27

Ten kod zawsze stara rachunek insert do bazy danych, gdy zamiast aktualizacji wiersz o tym, że Bill istnieje w DB ...

W sekcji 10.7. Automatic state detection dokumentacji podstawowej hibernacji:

saveOrUpdate() wykonuje następujące operacje:

  • jeśli obiekt jest już trwałe w tej sesji, nic nie robić
  • jeśli inny obiekt powiązany z sesją ma taki sam identyfikator, rzut ception
  • jeśli obiekt ma właściwość identyfikator, save() to
  • jeśli identyfikator danego obiektu ma wartość przydzielony do nowo instancja obiektu save() to
  • jeśli obiekt jest wersjonowanych przez <version> lub <timestamp>, a wartością właściwości jest przypisana do nowo utworzonego obiektu , save() it
  • inaczej update() przedmiotem

Kiedy zrobić:

User bill = new User(); 
bill.setName("Bill"); 
session.saveOrUpdate(bill); 

Ta nowo utworzona instancja nie ma żadnej wartości i saveOrUpdate() identyfikator przypisany będzie go save(), co zostało udokumentowane. Jeśli tego nie chcesz, ustaw klucz podstawowy na name.

+0

Thx za twoje zaufanie, widzę, dlaczego mój program zawsze próbuje wstawić. Jak mogę to zrobić bez podania nazwy jako klucza podstawowego, ponieważ mam identyfikator, który jest szybszy i łatwiejszy. Mogę to zrobić za pomocą dodatkowego wyboru: 'Rachunek użytkownika = nowy użytkownik(); bill.setName ("Bill"); bill.setId (func()); 'gdzie func() jest funkcją, której wynik jest pusty, gdy wiersz z' name = 'Bill'' nie istnieje, w przeciwnym razie zwracaj numer id tego wiersza; Jak mogę to zrobić bez wyboru? Jak rozwiązać ten problem Pro Developer? – kunkanwan

+0

@kunkanwan: Nie jestem pewien, jak powinienem zrozumieć tego "pro developera". W tej chwili nie motywuje mnie to do dalszej pomocy. –

+0

Obawiam się, że mnie nie rozumiesz. Mój angielski jest słaby. W zdaniu "Pro developer" chcę wiedzieć, jak rozwiązać to w branży oprogramowania (jestem studentem bez doświadczenia), gdzie moja prosta funkcja z dodatkowym wyborem nie jest wystarczająco dobra. Przepraszam jeśli Cię zraniłem. – kunkanwan

14

Więc chcesz:

User u1 = new User("Bill"); 
session.save(u1); 
User u2 = new User("Bill"); 
session.saveOrUpdate(u2); 

zauważyć, że U1 i U2 są takie same Bill i przechowywać go tylko raz? Hibernate nie ma możliwości dowiedzenia się, że Rachunki są tą samą osobą. Patrzy tylko na identyfikator User u2, widzi, że nie jest ustawiony, stwierdza, że ​​u2 powinno zostać wstawione, próbuje i zgłasza wyjątek.

SaveOrUpdate utrzymuje zarówno całkowicie nowy obiekt, jak i załadowany obiekt, który jest aktualnie dołączony do sesji. Tak to działa (zakładając, że masz sposób findByName gdzieś istnieje inny atrybut, powiedzmy favoriteColor):

User u1 = new User("Bill"); 
u1.setFavoriteColor("blue"); 
session.saveOrUpdate(u1); 
User u2 = findByName("Joe"); 
u2.setFavoriteColor("red"); 
session.saveOrUpdate(u2); 

Ale to nie to, co chcesz, prawda?

Twój problem jest pogarszany przez twój podmiot, który ma zastępczy klucz podstawowy i osobno klucz biznesowy (wymuszony przez ograniczenie unikalności). Jeśli nie masz pola id i tylko wymienić jako klucz podstawowy, można użyć seryjnej() (omówienie saveOrUpdate vs seryjnej, look here):

User u1 = new User("Bill"); 
u1.setFavoriteColor("blue"); 
session.save(u1); 
User u2 = new User("Bill"); 
u2.setFavoriteColor("red"); 
u2 = session.merge(u2); 

Ale nie masz, ty Należy wymusić zarówno ograniczenie PK na id i unikalność na nazwę. Będziesz potrzebował pewnej odmiany scalania, która to robi.Z grubsza, chciałbyś coś z tych linii:

public User mergeByName(User user) { 
    User merged; 
    User candidate = findByName(user.getName()); 
    if (candidate != null) { 
     user.setId(candidate.getId()); 
     merged = session.merge(user); 
    } else { 
     session.save(user); 
     merged = user; 
    } 
    return merged; 
} 
1

Pierwsze pytanie: Jeśli musisz pobrać użytkownika o nazwie "Bill". Jak byś to zrobił? To powinno dać ci pomysł.

Aby przeprowadzić aktualizację, musisz mieć tożsamość powiązaną z obiektem użytkownika. Posiadanie tylko zestawu atrybutów nazwy nie pomoże. Jeśli chcesz aktualizować niezależnie bez identyfikatora, użyj HQL jako zapytania.

Query query = session.createQuery("update User u set u.name = :newName where u.name=:name"); 
query.setString("name", "Bill"); 
query.setString("newName", "John"); 
query.executeUpdate(); 
-3

Jeśli pole nazwy jest kluczem podstawowym. Jeśli ustawisz tę samą nazwę więcej niż raz, w jaki sposób utworzy nowy rekord. ? może nie rozumiesz dobrze tego pojęcia.

Powiązane problemy