JPA - возврат автоматически сгенерированного идентификатора после persist ()

114

Я использую JPA (EclipseLink) и Spring. Скажем, у меня есть простая сущность с автоматически сгенерированным идентификатором:

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

     // ...
}

В моем классе DAO у меня есть метод вставки, который вызывает persist()эту сущность. Я хочу, чтобы метод возвращал сгенерированный идентификатор для нового объекта, но когда я его тестирую, он возвращается 0.

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; // ???
    }
}

У меня также есть класс обслуживания, который обертывает DAO, если это имеет значение:

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

    public int addNewABC(ABC abc) {
         return abcDao.insertABC(abc);
    }
}
sura2k
источник
Аналогичный, можно сослаться на stackoverflow.com/q/3328813/366964
Nayan Wadekar
Спасибо за ответы. И в качестве хитрого решения (не JPA) мы можем использовать другой уникальный идентификатор, например временную метку unix.
sura2k
1
возможный дубликат, когда JPA устанавливает @GeneratedValue @Id
Raedwald

Ответы:

185

Идентификатор гарантированно создается только во время сброса. Сохранение сущности только делает ее «прикрепленной» к контексту сохранения. Итак, либо явным образом очистите диспетчер сущностей:

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

или вернуть сам объект, а не его идентификатор. Когда транзакция завершится, произойдет сброс, и пользователи сущности вне транзакции увидят сгенерированный идентификатор в сущности.

@Override
public ABC addNewABC(ABC abc) {
    abcDao.insertABC(abc);
    return abc;
}
JB Nizet
источник
10
NB: это должно пометить поле id с помощью @GeneratedValue- что бы это ни
повлекло
Не могли бы вы объяснить проблемы,
возникающие
Есть ли снижение производительности (или какие-либо другие негативные эффекты) ручной промывки после сохранения?
Крейг Отис,
3
Да, есть: ненужное обращение к базе данных в оба конца, если транзакция откатывается, возможные исключения, если сохраняемая сущность (или другие сброшенные сущности) еще не в допустимом состоянии. Генератор последовательности или uuid проще и эффективнее, и у него нет этих проблем, потому что идентификатор создается и назначается до того, как объект будет записан в базу данных.
JB Nizet
1
@JBNizet, вам нужно вернуть экземпляр или переданная ссылка еще действительна? Я имею в виду, insertABCсоздает ли новый объект? Или доработать старую?
ryvantage
13
@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;   
}

убедитесь, что в вашем классе сущности присутствует нотация @GeneratedValue. Это сообщает JPA о автоматически сгенерированном поведении вашего свойства сущности.

Уткал Патель
источник
4

Вот как я это сделал:

EntityManager entityManager = getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(object);
transaction.commit();
long id = object.getId();
entityManager.close();
Корай Тугай
источник
Он не работает, давая ноль в качестве возврата после сохранения данных в таблице. либо обновление не работает в этом случае .. Что мне делать для этого. пожалуйста, предложите способ ... Спасибо
Викрант Кашьяп
1
@VikrantKashyap Пожалуйста, напишите новый вопрос с небольшим кодом и упомяните меня, чтобы я мог посмотреть.
Корай Тугай
2

Вы также можете использовать GenerationType.TABLE вместо IDENTITY, которое доступно только после вставки.

Джеймс
источник
2
Просто предупреждение. Когда я попробовал GenerationType.TABLE, он создал отдельную таблицу с именем hibernate_sequences и перезапустил последовательность с 1.
ШриСри,
0

Другой вариант, совместимый с 4.0:

Перед фиксацией изменений вы можете восстановить новый CayenneDataObjectобъект (ы) из коллекции, связанной с контекстом, например:

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

затем откройте ObjectIdдля каждого из них в коллекции, например:

ObjectId objectId = dataObject.getObjectId();

Наконец, вы можете выполнять итерацию по значениям, где обычно сгенерированный идентификатор будет первым из значений (для одного ключа столбца) в карте, возвращаемой getIdSnapshot(), он также содержит имена столбцов, связанных с PK как ключ (ы):

objectId.getIdSnapshot().values()
emecas
источник
0

Вот как я это сделал. Можешь попробовать

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

    public int addNewABC(ABC abc) {
         ABC.setId(0);
         return abcDao.insertABC(abc);
    }
}
Ань Кхоа
источник
-3
em.persist(abc);
em.refresh(abc);
return abc;
Андрей
источник
Этот метод у меня не сработал. Получил эту ошибку: javax.persistence.PersistenceException: org.hibernate.HibernateException: этот экземпляр еще не существует как строка в базе данных]
rtcarlson
@rtcarlson, да это не сработает. если вы создаете новый объект, то , что вам нужно , это em.flush()не em.refresh(abc).
Ибрагим Дауда