JPA保存列表的第二组眼睛,这会导致PK违反

时间:2019-04-09 23:01:54

标签: java spring-data-jpa

我有一个Oracle数据库表,其中的列位于我的FoobarId类中.FooarId中的所有3个字段都是我的Foobar表中的复合pk。

我正在第一次运行时插入数据。但是,当我再次运行它时,我得到了ORA-00001:唯一的约束,表明我的PK被违反。我在其他运行良好的项目中也有类似的代码。这个似乎不更新记录,而是尝试再次插入。我的EmbeddedId有问题吗?

我的实体类:

@Entity
@Table(name = "FOOBAR_TABLE")
public class Foobar implements Serializable {

    private static final long serialVersionUID = 4290109857781985996L;

    private FoobarId foobarId;

    private int containerId;
    private String containerType;
    private String action;

    public Foobar() { }

    public Foobar(String itemName, int itemId, Date itemDate) {
        foobarId = new FoobarId(itemName, itemId, itemDate);
    }

    @EmbeddedId
    public FoobarId getFoobarId() {
        return foobarId;
    }

    public void setFoobarId(FoobarId foobarId) {
        this.foobarId = foobarId;
    }

    // getters and settters

}

我的身份证班:

@Embeddable
public class FoobarId  implements Serializable{

    private static final long serialVersionUID = 7393136028821250311L;

    private int itemId;
    private String itemName;
    private Date itemDate;

    public FoobarId(){}

    public FoobarId(String itemName, int itemId, Date itemDate) {
        super();
        this.itemId = itemId;
        this.itemName = itemName;
        this.itemDate = itemDate;
    }

    // getters/setters

    @Override
 public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((itemDate == null) ? 0 : itemDate.hashCode());
    result = prime * result + ((itemName == null) ? 0 : itemName.hashCode());
    result = prime * result + itemId;
    return result;
}


@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    FoobarId other = (FoobarId) obj;
    if (itemDate == null) {
        if (other.itemDate != null) {
            return false;
        }
    } else if (!itemDate.equals(other.itemDate)) {
        return false;
    }
    if (itemName == null) {
        if (other.itemName != null) {
            return false;
        }
    } else if (!itemName.equals(other.itemName)) {
        return false;
    }
    if (itemId != other.itemId) {
        return false;
    }
    return true;
}
}

我具有扩展CrudRepository的必要Repository接口。

在服务类中,我保存了Foobar物品列表:

@Transactional
public void addList(List<Activity> itemList) {
    logger.info("Saving Activity List size of: " + itemList.size());
    activityRepository.save(itemList);
}

创建表sql:

  CREATE TABLE "FOOBAR" 
   (    
    "ITEM_NAME" VARCHAR2(50 CHAR) NOT NULL ENABLE, 
    "ITEM_ID" NUMBER NOT NULL ENABLE, 
    "ITEM_DATE" DATE NOT NULL ENABLE, 
    "CONTAINER_ID" NUMBER, 
    "CONTAINER_TYPE" VARCHAR2(50 CHAR), 
    "ACTION" VARCHAR2(50 CHAR), 
     CONSTRAINT "FOOBAR_PK" PRIMARY KEY ("ITEM_NAME", "ITEM_ID", "ITEM_DATE")
     )

2 个答案:

答案 0 :(得分:1)

使用数据库时,Java equals对象上的

Date不可靠(例如,参见this question)。

更改您的equals方法以比较每个日期的getTimeInMillis结果,或者最好不要将日期用作主键的一部分。

Spring-Data-Jpa还支持诸如LocalDateLocalDateTime之类的java.time类,它们也比Date更好。

答案 1 :(得分:1)

“日期”很棘手,因为语义取决于上下文。例如,java.util.Date包含以毫秒为单位的时间,但是Oracle的SQL DATE数据类型表示日期和时间(最高为一秒)。如果您的Java代码在具有SQL java.util.Date数据类型的PRIMARY KEY列上使用DATE的相等语义,则您会看到(从JPA的角度来看)持久存在的唯一值(从JPA的角度来看)如果只有不到一秒的时间不同,则任何时候数据库都会违反UNIQUE CONSTRAINT。

如您所见,将数据库中的列数据类型更改为TIMESTAMP会提高列的时间分辨率以及键空间,从而解决了问题。