JPA - Zwracanie automatycznie wygenerowanego identyfikatora po utrwaleniu ()

113

Używam JPA (EclipseLink) i Spring. Powiedzmy, że mam prostą jednostkę z automatycznie wygenerowanym identyfikatorem:

@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;

     // ...
}

W mojej klasie DAO mam metodę wstawiania, która wywołuje persist()tę jednostkę. Chcę, aby metoda zwracała wygenerowany identyfikator dla nowej jednostki, ale kiedy ją testuję, zwraca 0zamiast tego.

public class ABCDao {
    @PersistenceContext
    EntityManager em;

    @Transactional(readOnly=false)
    public int insertABC(ABC abc) {
         em.persist(abc);
         // I WANT TO RETURN THE AUTO-GENERATED ID OF abc
         // HOW CAN I DO IT?
         return abc.id; // ???
    }
}

Mam też klasę usług, która otacza DAO, jeśli to robi różnicę:

public class ABCService {
    @Resource(name="ABCDao")
    ABCDao abcDao;

    public int addNewABC(ABC abc) {
         return abcDao.insertABC(abc);
    }
}
sura2k
źródło
Podobny, może odnosić się do stackoverflow.com/q/3328813/366964
Nayan Wadekar
Dzięki za odpowiedzi. Jako trudne rozwiązanie (nie JPA) możemy użyć innego unikalnego identyfikatora, takiego jak unix timestamp.
sura2k
1
możliwy duplikat Kiedy JPA ustawia @GeneratedValue @Id
Raedwald

Odpowiedzi:

184

Gwarantuje się, że identyfikator zostanie wygenerowany tylko w czasie usuwania. Utrwalanie jednostki sprawia, że ​​jest ona tylko „przywiązana” do kontekstu trwałości. Zatem albo wyczyść wyraźnie menedżera encji:

em.persist(abc);
em.flush();
return abc.getId();

lub zwróć samą jednostkę, a nie jej identyfikator. Po zakończeniu transakcji nastąpi opróżnienie, a użytkownicy jednostki poza transakcją zobaczą wygenerowany identyfikator w jednostce.

@Override
public ABC addNewABC(ABC abc) {
    abcDao.insertABC(abc);
    return abc;
}
JB Nizet
źródło
10
Uwaga: to wymaga anotacji pola id z @GeneratedValue- cokolwiek to pociąga za sobą
Mr_and_Mrs_D
Czy możesz wyjaśnić problemy związane z próbą osiągnięcia tego za pomocą złożonego identyfikatora stackoverflow.com/questions/31362100/ ...
bl3e
Czy istnieje spadek wydajności (lub inne negatywne skutki) ręcznego płukania po utrzymywaniu się?
Craig Otis
3
Tak, istnieje: niepotrzebne przejście w obie strony do bazy danych, jeśli transakcja zostanie wycofana, potencjalne wyjątki, jeśli utrwalona jednostka (lub inne opróżnione jednostki) nie jest jeszcze w prawidłowym stanie. Generator sekwencji lub uuid jest prostszy i wydajniejszy i nie ma tych problemów, ponieważ identyfikator jest generowany i przypisywany przed zapisaniem jednostki w bazie danych.
JB Nizet
1
@JBNizet, czy musisz zwrócić instancję, czy przekazana referencja jest nadal ważna? Mam na myśli, czy insertABCtworzy nowy obiekt? Albo zmodyfikować stary?
ryvantage
13
@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;   
}

sprawdź, czy notacja @GeneratedValue znajduje się w Twojej klasie encji, co informuje JPA o automatycznie generowanym zachowaniu właściwości encji

utkal patel
źródło
4

Oto jak to zrobiłem:

EntityManager entityManager = getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(object);
transaction.commit();
long id = object.getId();
entityManager.close();
Koray Tugay
źródło
To nie działa, dając zero jako Return po utrwaleniu danych w tabeli. albo odświeżanie nie działa w tym przypadku .. Co mam z tym zrobić. proszę zasugerować sposób ... Dziękuję
Vikrant Kashyap
1
@VikrantKashyap Opublikuj nowe pytanie z małym kodem i wspomnij o mnie, abym mógł rzucić okiem.
Koray Tugay
2

Możesz także użyć GenerationType.TABLE zamiast IDENTITY, które jest dostępne tylko po wstawieniu.

James
źródło
2
Tylko słowo ostrzeżenia. Kiedy próbowałem GenerationType.TABLE, utworzyłem osobną tabelę o nazwie hibernate_sequences i ponownie uruchomiłem sekwencję od 1.
SriSri
0

Inna opcja zgodna z 4.0:

Przed zatwierdzeniem zmian możesz odzyskać nowe CayenneDataObjectobiekty z kolekcji skojarzonej z kontekstem, na przykład:

CayenneDataObject dataObjectsCollection = (CayenneDataObject)cayenneContext.newObjects();

następnie uzyskaj dostęp ObjectIddo każdego z nich w kolekcji, na przykład:

ObjectId objectId = dataObject.getObjectId();

Na koniec możesz iterować pod wartościami, gdzie zwykle wygenerowany-id będzie pierwszą z wartości (dla pojedynczego klucza kolumny) w mapie zwróconej przez getIdSnapshot(), zawiera również nazwy kolumn powiązane z PK jako klucz (y):

objectId.getIdSnapshot().values()
emecas
źródło
0

Tak to zrobiłem. Możesz spróbować

    public class ABCService {
    @Resource(name="ABCDao")
    ABCDao abcDao;

    public int addNewABC(ABC abc) {
         ABC.setId(0);
         return abcDao.insertABC(abc);
    }
}
Anh Khoa
źródło
-3
em.persist(abc);
em.refresh(abc);
return abc;
Andrey
źródło
Ta metoda nie zadziałała dla mnie. Wystąpił ten błąd: javax.persistence.PersistenceException: org.hibernate.HibernateException: ta instancja nie istnieje jeszcze jako wiersz w bazie danych]
rtcarlson
@rtcarlson, tak to nie zadziała. jeśli tworzysz nowy obiekt, czego potrzebujesz to em.flush()nie em.refresh(abc).
Ibrahim Dauda