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

5uzkadbs  于 12个月前  发布在  其他
关注(0)|答案(8)|浏览(136)

我有一个实体,它有一个主键/ ID字段,如下所示:

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

这个很好用。我正在使用DockseLink创建DDL-Schema,列被正确地创建如下:

`id` bigint(20) NOT NULL AUTO_INCREMENT

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

是否有方法手动指定具有@GeneratedValue的列的值?

以下是关于这个问题的一些想法:
我试图通过根本不使用@GeneratedValue来解决这个问题,而只是手动将列定义为INCREMENTed。然而,这迫使我手动提供一个ID always,因为MySeLink验证主键(所以它可能不是null,零或负数)。异常消息显示我应该指定eclipselink.id_validation,但是这似乎没有任何区别(我注解了@PrimaryKey(validation = IdValidation.NONE),但仍然得到相同的消息)。

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

编辑(对答案的回应):

**自定义排序:**我尝试实现自己的排序。我试着子类化DefaultSequence,但MyseLink会告诉我Internal Exception: org.eclipse.persistence.platform.database.MySQLPlatform could not be found。但我查过了类位于类路径上。

所以我子类化了另一个类,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)
    ...

(为清晰起见,缩短了堆栈跟踪)。这和我以前收到的信息是一样的。我不应该继承NativeSequence吗?如果是这样的话,我不知道该为Sequence或StandardSequence中的抽象方法实现什么。
还值得注意的是,简单地子类化(不覆盖任何方法)类就可以像预期的那样工作。然而,在shouldAlwaysOverrideExistingValue(...)中返回false根本不会生成一个值(我单步执行了这个程序,getGeneratedValue()没有被调用过一次)。
此外,当我在一个事务中插入8个特定类型的实体时,它会在数据库中产生11条记录(什么鬼?!).

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

我尝试自己将列定义为auto_increment并ommit @GeneratedValue,但是验证将启动,不允许我保存这样的实体。如果我指定一个值!= 0和!=零,mysql会抱怨主键重复。
我已经没有办法了。有吗?(开始悬赏)

w46czmvw

w46czmvw1#

这与eclipselink一起工作。它将为序列创建一个单独的表,但这应该不会造成问题。

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

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

mw3dktmi

mw3dktmi2#

如果你使用了TABLE排序,那么MySQL链接将允许你覆盖这个值(或者SEQUENCE,如果你的数据库支持这个,MySQL不支持)。
对于IDENTITY,我甚至不确定MySQL是否允许你提供自己的ID,你可能想验证一下。一般来说,我不推荐使用IDENTITY,因为它不支持预分配。
允许IDENTITY提供或不提供ID有一些问题。一个是需要根据id值生成两个不同的insert SQL,因为对于IDENTITY,id根本不能在insert中。您可能希望记录一个错误,以使IDENTITY支持用户提供的ID。
您应该仍然能够让它与您自己的Sequence子类或MySQLPlatform子类一起工作。您可以使用“eclipselink.target-database”持久性单元属性设置MySQLPlatform子类。

rqdpfwrv

rqdpfwrv3#

以数据库为中心解决您的问题:
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)
);

1.将此列Map到实体中的普通字段:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name="manual_id")
private Integer manualId;

1.创建一个触发器,如果表id不为null,则将其设置为手动分配的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;

1.当你需要分配一个自定义id时,总是使用manualId。触发器会为你带来魔力:

testEntiy.setManualId(300);
entityManager.persist(testEntity);

1.在数据库导入阶段之后,简单地删除触发器、辅助列及其Map。

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

警告

如果您手动指定一个大于当前AUTO_INCREMENT值的id,下一个生成的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;
50few1ms

50few1ms4#

我可能忽略了一些明显的东西,但是为什么不定义另一个具有相同@Table(name=".....")注解的实体,具有相同的字段,但是不生成id呢?然后,您可以将该Entity用于将数据从旧DB复制到新DB的代码,并且具有生成的ID的Entity可以用于需要ID生成的正常创建。
我不能告诉你它是否适用于Hibernate,但我们在这里使用Hibernate,它似乎并不介意。

kqlmhetl

kqlmhetl5#

使用GenerationType.SEQUENCE与PostgreSQL和MySQL Link对我来说很有效。
1)变化

@GeneratedValue(strategy = GenerationType.IDENTITY)

通过

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

现在,您可以使用NativeQuery调用sequence:

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

并在调用Entity. persist()方法之前将返回的ID设置为Entity。
希望不会太晚!

6gpjuf90

6gpjuf907#

我的方法(MySql)是停用GeneratedValue:

@Id
//@GeneratedValue
@Column(unique = true, nullable = false, columnDefinition ="BINARY(16)")
private UUID id;

并在实体中添加:

@PrePersist
protected void onCreation() {
    if (id == null) setId(UUID.randomUUID());
}

现在,在我的代码中,我可以做(例如在服务上):

String clientID = env.getParam("id");
Entity entity = entityRepository.findFirstById(UUID.fromString(clientID));
//is new?
if (entity==null){
  entity = new Entity();
  entity.setId(UUID.fromString(clientID));//set cumstom ID here
}

Entity entityNew = entityRepository.save(entity); //insert
if (entityNew.getId().equals(entity.getId()) ){
  Log.i("OK!")
}

👌

bvuwiixz

bvuwiixz8#

我的情况是
我注解掉了Entity的@Id字段中的**@GeneratedValue**注解行
这让我可以设置要持久化的对象中的主键

相关问题