在JPA @GeneratedValue列中手动指定主键的值

我有一个具有主键/ id字段的实体,如下所示:

@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; 

这很好用。 我正在使用EclipseLink创建DDL-Schema,并且正确创建了这样的列:

 `id` bigint(20) NOT NULL AUTO_INCREMENT 

但是,我有几个实体,我想要自己指定PK(这是​​一个将数据从旧数据库传输到我们正在构建的新数据库的小应用程序)。 如果我指定POJO的ID(使用setId(Long id) )并保持它,则EclipseLink 保存它(即保存记录,但id由eclipseLink自动生成)。

有没有办法手动指定具有@GeneratedValue的列的值?

关于这个问题的一些想法:

我试图通过不使用@GeneratedValue解决问题,而只是手动将列定义为AUTO_INCREMENTed。 然而,这迫使我总是手动提供ID,因为EclipseLinkvalidation主键(因此它可能不是null,零或负数)。 exception消息显示我应该指定eclipselink.id_validation,但是这似乎没有任何区别(我注释了@PrimaryKey(validation = IdValidation.NONE)但仍然得到相同的消息)。

澄清一下我使用EclipseLink(2.4.0)作为持久性提供程序,我无法切换它(项目的大部分依赖于eclipselink特定的查询提示,注释和类)。


编辑(回答答案):

自定义排序:我尝试实现自己的排序。 我尝试了inheritanceDefaultSequence,但是EclipseLink会告诉我Internal Exception: org.eclipse.persistence.platform.database.MySQLPlatform could not be found 。 但是我已经检查过了:这个类在类路径上。

所以我inheritance了另一个类NativeSequence:

 public class MyNativeSequence extends NativeSequence { public MyNativeSequence() { super(); } public MyNativeSequence(final String name) { super(name); } @Override public boolean shouldAlwaysOverrideExistingValue() { return false; } @Override public boolean shouldAlwaysOverrideExistingValue(final String seqName) { return false; } } 

但是,我得到的是以下内容:

 javax.persistence.RollbackException: Exception [EclipseLink-7197] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.ValidationException Exception Description: Null or zero primary key encountered in unit of work clone [de.dfv.datenbank.domain.Mitarbeiter[ id=null ]], primary key [null]. Set descriptors IdValidation or the "eclipselink.id-validation" property. at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102) ... Caused by: Exception [EclipseLink-7197] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.ValidationException Exception Description: Null or zero primary key encountered in unit of work clone [de.dfv.datenbank.domain.Mitarbeiter[ id=null ]], primary key [null]. Set descriptors IdValidation or the "eclipselink.id-validation" property. at org.eclipse.persistence.exceptions.ValidationException.nullPrimaryKeyInUnitOfWorkClone(ValidationException.java:1451) ... 

(为清晰起见缩短了堆栈跟踪)。 这是我之前收到的相同信息。 我不应该inheritanceNativeSequence的子类吗? 如果是这样,我不知道在Sequence或StandardSequence中为抽象方法实现什么。

值得注意的是,简单地子类化(不覆盖任何方法)该类按预期工作。 但是,在shouldAlwaysOverrideExistingValue(...)修复false将不会生成单个值(我逐步执行程序并且getGeneratedValue()不会被调用一次)。

此外,当我在一个事务中插入8个特定类型的实体时,它在数据库中产生了11条记录(到底是什么?!)。

编辑(2012-09-01):我仍然没有问题的解决方案,实现我自己的序列并没有解决它。 我需要的是一种方法,能够不显式设置Id(因此它将自动生成)并能够显式设置Id(因此它将用于在数据库中创建记录)。

我试图将列定义为auto_increment自己并省略@GeneratedValue,但是validation将启动并且不允许我保存这样的实体。 如果我指定一个值!= 0和!=零,mysql会抱怨重复的主键。

我已经没有想法和选择了。 任何? (开始赏金)

如果使用TABLE排序,那么EclipseLink将允许您覆盖该值(如果您的数据库支持,则为SEQUENCE,而MySQL则不支持)。

对于IDENTITY,我甚至不确定MySQL是否允许您提供自己的ID,您可能需要validation这一点。 一般情况下,我绝不会建议使用IDENTITY,因为它不支持预分配。

允许IDENTITY提供或不提供ID存在一些问题。 一个是需要根据id值生成两个不同的插入SQL,对于IDENTITY,id根本不能在插入中生成。 您可能希望记录一个错误,让IDENTITY支持用户提供的ID。

您仍然可以使用自己的Sequence子类或MySQLPlatform子类。 您可以使用“eclipselink.target-database”持久性单元属性设置MySQLPlatform子类。

这适用于eclipselink。 它将为序列创建一个单独的表,但这不应该成为问题。

 @Id @GeneratedValue(strategy=GenerationType.AUTO) @Column(name="id", insertable=true, updatable=true, unique=true, nullable=false) private Long id; 

GenerationType.AUTO将选择理想的生成策略。 由于该字段被指定为可插入和可更新,因此将使用TABLE生成策略。 这意味着eclipselink将生成另一个包含当前序列值的表并生成序列本身,而不是将其委托给数据库。 由于列声明为可插入,如果持久化时id为null,则eclipselink将生成id。 否则将使用现有的id。

以数据库为中心的问题解决方案:

  1. 在表中创建一个辅助的可为空的列。 它将保存您手动分配的ID:

     CREATE TABLE `test_table` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `manual_id` bigint(20) NULL, `some_other_field` varchar(200) NOT NULL, PRIMARY KEY(id) ); 
  2. 将此列映射到实体中的普通字段:

     @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name="manual_id") private Integer manualId; 
  3. 创建一个触发器,如果​​它不为null,则将表id设置为手动分配的id:

     DELIMITER // CREATE TRIGGER `test_table_bi` BEFORE INSERT ON `test_table` FOR EACH ROW BEGIN IF NEW.`manual_id` IS NOT NULL THEN SET NEW.`id` = NEW.`manual_id`; END IF; END;// DELIMITER; 
  4. 需要分配自定义ID时,请始终使用manualId 。 触发器将为您带来魔力:

     testEntiy.setManualId(300); entityManager.persist(testEntity); 
  5. 在数据库导入阶段之后,简单地删除触发器,辅助列和它的映射。

     DROP TRIGGER `test_table_bi`; ALTER TABLE `test_table` DROP COLUMN `manual_id`; 

警告

如果手动指定的id大于当前的AUTO_INCREMENT值,则下一个生成的id将跳转到手动分配的id加1的值,例如:

 INSERT INTO `test_table` (manual_id, some_other_field) VALUES (50, 'Something'); INSERT INTO `test_table` (manual_id, some_other_field) VALUES (NULL, 'Something else'); INSERT INTO `test_table` (manual_id, some_other_field) VALUES (90, 'Something 2'); INSERT INTO `test_table` (manual_id, some_other_field) VALUES (NULL, 'Something else 2'); INSERT INTO `test_table` (manual_id, some_other_field) VALUES (40, 'Something 3'); INSERT INTO `test_table` (manual_id, some_other_field) VALUES (NULL, 'Something else 3'); 

将使用结果:

 +----+-----------+------------------+ | id | manual_id | some_other_field | +----+-----------+------------------+ | 50 | 50 | Something | | 51 | NULL | Something else | | 90 | 90 | Something 2 | | 91 | NULL | Something else 2 | | 40 | 40 | Something 3 | | 92 | NULL | Something else 3 | +----+-----------+------------------+ 

为避免出现问题,强烈建议将AUTO_INCREMENT列设置为以大于先前数据库中所有现有ID的数字开头,例如:

 ALTER TABLE `test_table` AUTO_INCREMENT = 100000; 

我可能会遗漏一些明显的东西,但为什么不用相同的字段定义具有相同@Table(name=".....")注释的另一个实体,但是不生成id? 然后,您可以将该实体用于将旧数据库中的数据复制到新数据库的代码,具有生成的ID的实体可用于需要生成ID的常规创建。

我不能告诉你它是否适用于EclipseLink,但是我们在这里使用Hibernate,它似乎并不介意。

使用GenerationType.SEQUENCE与PostgreSQL和EclipseLink为我工作。

1)改变

 @GeneratedValue(strategy = GenerationType.IDENTITY) 

通过

 @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="step_id_seq") @SequenceGenerator(name="step_id_seq", sequenceName="step_id_seq") 

现在,您可以使用NativeQuery调用序列:

 return ((Vector) em.createNativeQuery("select nextval('step_id_seq')::int").getSingleResult()).get(0); 

并在调用EntityManager.persist()方法之前将返回的Id设置为您的Entity。

希望现在还为时不晚!

寻找自定义ID生成器

http://blog.anorakgirl.co.uk/2009/01/custom-hibernate-sequence-generator-for-id-field/

也许这可能有所帮助。