使用@EmbeddedId在复合键上通过@GeneratedValue生成的NULL ID

时间:2014-06-06 18:51:21

标签: jpa jpa-2.0 spring-data spring-data-jpa composite-key

尝试通过Spring Data JPA在具有复合键(Long& Date)的表中保存行。复合键的长部分是@GeneratedValue。但在进行基本的 save()调用时,我收到以下错误:

org.hibernate.id.IdentifierGenerationException: null id generated for:class com.bts.billing.domain.CashBatchPaymentHistoryDto

save()之前手动设置日期,并且我已经验证了数据库中存在的顺控程序并且可以访问。

实体

@Entity
@Table(name = "CASH_BATCH_PMT_H")
public class CashBatchPaymentHistoryDto implements Serializable {
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    private CashBatchPaymentHistoryPK pk;

    private Date        cashBatchProcDt;
    @Column(name = "C_CBP_STS", nullable = false)
    private String      cashBatchStatus;
    @Column(name = "C_CBP_TYP", nullable = false)
    private String      cashBatchType;

    //Removed Getters & Setters To Save Space
}

@Embeddable Class

@Embeddable    
@SequenceGenerator(name = "CBPMT", sequenceName = "CBPMT", allocationSize = 1)
public class CashBatchPaymentHistoryPK implements Serializable {
    private static final long serialVersionUID = 1L;

    @Column(name = "N_CBP_OID", nullable=false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CBPMT")
    private Long cashBatchID;
    @Column(name = "D_CBP_PROC", nullable=false)
    private Date cashBatchProcDt;

    public CashBatchPaymentHistoryPK() {}

    public CashBatchPaymentHistoryPK(long cashBatchID, Date cashBatchProcDt) {
        this.cashBatchID = cashBatchID;
        this.cashBatchProcDt = cashBatchProcDt;
    }

    //Removed Getters & Setters For Space

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (int) (cashBatchID ^ (cashBatchID >>> 32));
        result = prime * result + (int) (cashBatchProcDt.hashCode() ^ (cashBatchProcDt.hashCode() >>> 32));
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        CashBatchPaymentHistoryPK other = (CashBatchPaymentHistoryPK) obj;
        if (cashBatchID != other.cashBatchID) {
            return false;
        }
        if (cashBatchProcDt != other.cashBatchProcDt) {
            return false;
        }
        return true;
    }
}

存储库类

public interface CashBatchPaymentHistoryRepository extends CrudRepository<CashBatchPaymentHistoryDto, CashBatchPaymentHistoryPK> {
}

有关此问题的任何想法?谢谢!

1 个答案:

答案 0 :(得分:4)

看起来您正在将ID值设置为半准备状态(Date正在设置但long字段要由持久性提供程序初始化,对吧?这意味着,{{1当pk实例交给null时,实际上是非CashBatchPaymentHistoryDto。如果是这样,这将导致repository.save(…)被触发,可以想象会导致异常因为它不会期望生成ID。

一般来说,如果您手动维护id值(甚至只是部分值),则必须明确确定实体的is-new-state(因为这将有效地决定是否调用EntityManager.merge(…)或JPA案例中的persist(…)。默认情况下,这可以通过查看id字段并将merge(…)值解释为新的非null而不是新的来实现。

在您目前的情况下,有两种选择:

  1. 使用null带注释的字段,并在第一次调用@Version之前保持未初始化状态。
  2. 实施save(…)及其Persistable方法检查您的对象状态,以正确判断它是否是新的(在您的情况下可能通过检查isNew()pk.cashBatchID )。