Java 具有空值的JPA复合主键

Java 具有空值的JPA复合主键,java,jpa,eclipselink,jpa-2.0,ejb-3.0,Java,Jpa,Eclipselink,Jpa 2.0,Ejb 3.0,我在oracle数据库中有一个包含客户数据的表。以下是一个简化的定义: CUSTOMER (CUSTOMER_ID NUMBER NOT NULL, SOURCE_SYSTEM VARCHAR2(30), FULL_NAME VARCHAR2(360), PHONE_NUMBER VARCHAR2(240) ) 此表的主键是(客户ID,源系统) 该表有许多行,SOURCE\u SYSTEM为空。在数据库级别,没有问

我在oracle数据库中有一个包含客户数据的表。以下是一个简化的定义:

CUSTOMER (CUSTOMER_ID NUMBER NOT NULL,
          SOURCE_SYSTEM VARCHAR2(30),
          FULL_NAME VARCHAR2(360),
          PHONE_NUMBER VARCHAR2(240)
          )
此表的主键是
(客户ID,源系统)

该表有许多行,
SOURCE\u SYSTEM
为空。在数据库级别,没有问题,但是,当我尝试通过JPA实体访问这些行时,它会导致许多问题:

1:使用
em.find()

2:如果记录在表中不存在,则使用
em.merge()
向上插入源系统为空的行
成功,但在后续更新时失败,因为合并总是导致运行插入

3:使用
em.createQuery()
显式查询空值行会导致以下异常:

Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.3.1.v20111018-r10243):
   org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [ArrayRecord(
CUSTOMER.CUSTOMER_ID => 1
CUSTOMER.FULL_NAME => GUY PERSON
CUSTOMER.PHONE_NUMBER => 555-555-1234
CUSTOMER.SOURCE_SYSTEM => null)] during the execution of the query was detected to be null.
   Primary keys must not contain null.
Query: ReadAllQuery(referenceClass=Customer sql="SELECT CUSTOMER_ID, FULL_NAME, PHONE_NUMBER, SOURCE_SYSTEM FROM CUSTOMER WHERE ((CUSTOMER_ID = ?) AND (SOURCE_SYSTEM IS NULL))")
不幸的是,“主键不能包含null”似乎是最后一句话。我找不到关于这个错误的解决方法的太多信息,这使得看起来似乎没有解决方案

问题:我想知道是否有人有任何基于Java代码的解决方案不涉及对数据库进行更改。我目前的解决方法是使用
行ID
作为表的
@ID
,但这意味着我不能再使用
em.merge()
em.find()

以下是我的Java类:

Customer.java:

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

  @EmbeddedId
  private Customer_Id key;

  @Column(name = "CUSTOMER_ID", nullable = false, insertable = false, updatable = false)
  private Long customerId;
  @Column(name = "SOURCE_SYSTEM", length = 30, insertable = false, updatable = false)
  private String sourceSystem;

  @Column(name = "FULL_NAME", length = 360)
  private String fullName;
  @Column(name = "PHONE_NUMBER", length = 240)
  private String phoneNumber;

  //Setters, Getters, etc
  ...
}
Customer\u Id.java

@Embeddable
public class Customer_Id implements Serializable {
  private static final long serialVersionUID = 1L;

  @Column(name = "CUSTOMER_ID", nullable = false)
  private Long customerId;
  @Column(name = "SOURCE_SYSTEM", length = 30)
  private String sourceSystem;

  //Setters, Getters, etc
  ...
}

主键不能包含null(在JPA或数据库中)。使用不同的值,如“或”

客户id是否唯一?如果是这样,那么只需从Id中删除sourceSystem


否则,您可以尝试记录错误以添加对空id的支持。

为什么要将空id用于客户id?它看起来应该使用排序,如果它不为null,则应该允许source_系统字段为null。您可以尝试按照此处所述指定要使用的验证,但我不确定您所使用的验证为什么不起作用。但是您要映射customer_id字段两次,因此请确保您正确设置了这两个字段,并打开日志记录,查看您的映射或生成的SQL是否存在问题。对空字段的查询通常必须略有不同,这可能不会发生,因为它是pk检查。描述Eclipselink日志记录
CUSTOMER\u ID
将永远不会为空,并在类中标记为
nullable=false
,在数据库中标记为
NOT null
,尽管它不基于序列。
em.find()
的sql很可能是使用
SOURCE\u SYSTEM=''
生成的,而不是
SOURCE\u SYSTEM为NULL
,这就是记录未正确返回的原因。我已尝试使用EclipseLink特定的
@PrimaryKey
注释和
validation=IdValidation.NONE
,,但是最终行为与上面显示的
@EmbeddedId
注释完全相同。您确实应该实现equals和hashCode以考虑可空字段。。另请参见
CUSTOMER\u ID
不是唯一的,我无法对该表进行更改,因为它已被其他应用程序使用。不幸的是,我无法更改表中现有的空值以满足我自己的需要,因为它会影响当前在
SOURCE\u SYSTEM
中预期为空的任何其他应用程序。您能否定义一个二级索引,用合适的占位符替换空字符串?