简体   繁体   English

如何使用一个自动生成的值创建复合主键?

[英]How to create composite primary key with one auto generated value?

I am trying to map entity with my existing database table which is having two primary keys.

Out of two keys, one primary key is auto generated.

I am using `Spring Boot`, `JPA`, `Hibernate` and `MySQL`. I have used `@IdClass` to map with the composite primary class (with public constructor, setter/getters, same property names, equals and hash code methods).

`org.springframework.data.repository.CrudRepository` save method to save the entity.  

Code snippet below.下面的代码片段。

@Entity
@Table(name = "data")
@IdClass(DataKey.class)
public class DeviceData {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id")
    private BigInteger id;
    @Column(name="name")
    private String name;
    @Id
    @Column(name="device_id")
    private int deviceId;
    getters/setters
}
public class DataKey implements Serializable {
    private static final long serialVersionUID = 1L;
    private BigInteger id;
    private int deviceId;
    //setter/getters
    public DataKey() {
    }
    public DataKey(BigInteger id, int deviceId) {
        this.id = id;
        this.deviceId = deviceId;
    }
    public int hashCode() {
        return Objects.hash(id, deviceId);
    }
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof DataKey))
            return false;
        DataKey dk = (DataKey) obj;
        return dk.id.equals(this.id) && dk.deviceId == (this.deviceId);}
}
I am using org.springframework.data.repository.CrudRepository save method for persisting the entity.

DeviceData data=new DeviceData();设备数据数据=新设备数据();

data.setName("device1");数据集名称(“设备1”); data.setDeviceId("1123"); data.setDeviceId("1123");

dao.save(data); dao.save(数据); //dao extending crudrepository interface. //dao 扩展 crudrepository 接口。

But I am getting below errors :但我收到以下错误:

    org.springframework.orm.jpa.JpaSystemException: Could not set field 
    value [POST_INSERT_INDICATOR] value by reflection.[class DataKey.id]
    setter of DataKey.id; nested exception is
    org.hibernate.PropertyAccessException: Could not set field value
    [POST_INSERT_INDICATOR] value by reflection : [class class DataKey.id]
    setter of class DataKey.id.

Caused by: java.lang.IllegalArgumentException: Can not set java.math.BigInteger field DataKey.id to org.hibernate.id.IdentifierGeneratorHelper$2引起:java.lang.IllegalArgumentException:无法将 java.math.BigInteger 字段 DataKey.id 设置为 org.hibernate.id.IdentifierGeneratorHelper$2

I had also tried the same in one of my projects but eventually I found out that it is not possible to auto generate one of the key attributes(in case of composite primary key) in JPA.我也曾在我的一个项目中尝试过相同的方法,但最终我发现无法在 JPA 中自动生成其中一个关键属性(如果是复合主键)。 So, I did it separately by invoking the sequence with the help of query.所以,我通过在查询的帮助下调用序列来单独完成它。

I have achieved this by creating a one orderId class with parameters -- Mysql
@Getter
@Setter
public class OrderId implements Serializable {

    private int oId;
    private int customerId;
    private int productId;

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        OrderId orderId = (OrderId) o;
        return oId == orderId.oId && customerId == orderId.customerId && productId == orderId.productId;
    }

    @Override
    public int hashCode() {
        return Objects.hash(oId, customerId, productId);

    }

the class OrderData with orderid as auto generated field in mysql
@Entity
@Table(name = "OrderData")
@Getter
@Setter
@NoArgsConstructor
@IdClass(OrderId.class)
public class OrderData{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int oId;
    @Id
    private int customerId;
    @Id
    private int productId;
    private int quanity;
    private String paymentMode;
    private String orderStatus;

}

I was able to solve it using a sequence generator.我能够使用序列生成器解决它。

    @Id
    @SequenceGenerator(name="my_seq", sequenceName="my_db_table_seq", allocationSize=1)
    @GeneratedValue(generator = "my_seq")
    @Column(name = "id", updatable = false, nullable = false)
    private BigInteger id

In my case I'm using Postgres, and my tables that have SERIAL as a column data type also have a corresponding entry in the pg_catalog.pg_sequences table (eg my_db_table_seq in my example above).在我的例子中,我使用的是 Postgres,并且我的以SERIAL作为列数据类型的表在pg_catalog.pg_sequences表中也有一个相应的条目(例如,在我上面的例子中是my_db_table_seq )。

If you don't specify a particular named sequence generator (and just use @GeneratedValue(strategy = GenerationType.SEQUENCE) on your @Id column), it looks like Hibernate defaults to one called hibernate_sequence , and I have seen some suggestions to run the db command CREATE SEQUENCE hibernate_sequence START 1 to get that created in the database.如果你没有指定一个特定的命名序列生成器(并且只在你的@Id列上使用@GeneratedValue(strategy = GenerationType.SEQUENCE) ),看起来 Hibernate 默认为一个叫做hibernate_sequence ,我已经看到了一些建议来运行db 命令CREATE SEQUENCE hibernate_sequence START 1以获取在数据库中创建的内容。 But this would use the same sequence for every table that inserts rows via Hibernate, so probably not a good idea.但这将对通过 Hibernate 插入行的每个表使用相同的序列,因此可能不是一个好主意。 Best to use the sequence that gets created in the database for that table and then be explicit with the @SequenceGenerator annotation.最好使用在数据库中为该表创建的序列,然后使用@SequenceGenerator注释显式。

There does seem to be a problem using @GeneratedValue(strategy = GenerationType.IDENTITY) in a composite key on an entity that has @IdClass , so would be nice if Hibernate could support this.在具有@IdClass的实体上的复合键中使用@GeneratedValue(strategy = GenerationType.IDENTITY)似乎确实存在问题,因此如果 Hibernate 可以支持这@IdClass ,那就太好了。

But looks like the sequence generator is a valid workaround, so that's what I'm going with.但看起来序列生成器是一种有效的解决方法,所以这就是我要使用的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM