JPA-在persist()之后返回自动生成的ID


113

我正在使用JPA(EclipseLink)和Spring。假设我有一个带有自动生成的ID的简单实体:

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

     // ...
}

在我的DAO类中,我有一个调用persist()此实体的insert方法。我希望该方法为新实体返回生成的ID,但是当我对其进行测试时,它将返回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);
    }
}


感谢您的回答。作为棘手的解决方案(不是JPA),我们可以使用另一个唯一的id,例如unix timestamp。
sura2k 2012年

Answers:


184

该ID仅保证在刷新时生成。持久实体只会使它“附加”到持久性上下文。因此,要么显式刷新实体管理器:

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

或返回实体本身,而不是其ID。当事务结束时,将发生刷新,事务外部实体的用户将因此在实体中看到生成的ID。

@Override
public ABC addNewABC(ABC abc) {
    abcDao.insertABC(abc);
    return abc;
}

10
注意:这需要anotate id字段有@GeneratedValue-无论是限嗣继承
Mr_and_Mrs_D

u能请解释试图与复合id来实现这一目标的问题stackoverflow.com/questions/31362100/...
bl3e

持续冲洗后是否会有性能损失(或其他负面影响)?
Craig Otis

3
是的,有:如果事务最终被回滚,则不必要的数据库往返;如果持久化的实体(或其他刷新的实体)尚未处于有效状态,则可能发生异常。序列或uuid生成器更简单,更有效,并且不存在这些问题,因为在实体写入数据库之前已生成并分配了ID。
JB Nizet

1
@JBNizet,您需要返回实例还是传递的引用仍然有效?我的意思是,是否insertABC创建一个新对象?还是修改旧的?
ryvantage '16

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();

将数据持久保存到表中后,将零作为返回值将无法正常工作。在这种情况下,任何刷新都不起作用..我应该怎么做。请提出一个方法...谢谢
Vikrant Kashyap

1
@VikrantKashyap请用小代码发布一个新问题并提及我,以便我看看。
Koray Tugay

2

您也可以使用GenerationType.TABLE代替IDENTITY,后者仅在插入后才可用。


2
请注意。当我尝试使用GenerationType.TABLE时,它创建了一个单独的表hibernate_sequences并从1重新开始序列
。– SriSri

0

兼容4.0的另一个选项:

在提交更改之前,您可以CayenneDataObject从与上下文关联的集合中恢复新对象,如下所示:

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

然后访问ObjectId集合中每个对象的,例如:

ObjectId objectId = dataObject.getObjectId();

最后,您可以在这些值下进行迭代,通常,generated-id通常是由返回的Map中的第一个值(对于单个列键)getIdSnapshot(),它还包含与PK相关的列名。作为键:

objectId.getIdSnapshot().values()

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 2013年

@rtcarlson,是的,这行不通。如果要创建新对象,则em.flush()不需要em.refresh(abc)
Ibrahim Dauda 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.