2013-03-28 116 views
9

我有一个包含oracle数据库中的客户数据的表。下面是一个简化的定义:具有空值的JPA复合主键

CUSTOMER (CUSTOMER_ID NUMBER NOT NULL, 
      SOURCE_SYSTEM VARCHAR2(30), 
      FULL_NAME VARCHAR2(360), 
      PHONE_NUMBER VARCHAR2(240) 
     ) 

此表的主键是(CUSTOMER_ID, SOURCE_SYSTEM)

该表有许多行,其中SOURCE_SYSTEM为空。在数据库级别,没有问题,但是当我尝试访问任何通过JPA实体这些行的,它会导致一些问题:

1:使用em.find()用空SOURCE_SYSTEM总是导致中读取行返回null。

2:使用em.merge()到UPSERT一排空SOURCE_SYSTEM成功,如果记录不存在于表中,但在后续的更新失败,因为合并总是导致插入正在运行。

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))") 

不幸的是,“主键不能包含空”似乎相当决赛。我无法找到有关此错误的解决方法的太多信息,这看起来好像没有解决方案。

问题:我想知道如果任何人有任何基于Java代码的解决方案不涉及更改数据库。我目前的解决方法是使用ROW_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_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 
    ... 
} 
+0

你为什么使用null作为customer_id?它看起来像应该使用排序的东西,如果它不为空,应该允许source_system字段为空。您可以尝试指定要使用的验证,如http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_primarykey.htm中所述,但我不确定为什么您的操作不起作用。您正在映射customer_id字段两次,因此请确保您正确地设置了这两个字段。 – Chris

+0

还打开日志记录以查看映射或生成的SQL是否存在问题。对空字段的查询通常必须略有不同,这可能不会发生,因为它的pk检查。 HTTP://wiki.eclipse。org/EclipseLink/Examples/JPA/Logging描述了Eclipselink日志记录 – Chris

+0

'CUSTOMER_ID'永远不会为空,并且在数据库中的类和NOT NULL标记为'nullable = false',但它不基于一个序列。 很可能''em.find()'的sql是用'SOURCE_SYSTEM ='''生成的,而不是'SOURCE_SYSTEM IS NULL',这就是为什么记录没有被正确返回的原因。 –

回答

0

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

customer_id是否是唯一的?如果是这样,那么只需从Id中删除sourceSystem。

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

+0

'CUSTOMER_ID'不是唯一的,我不能对表格进行更改,因为它已被其他应用程序使用。不幸的是,[Oracle将空字符串视为空值](http://www.techonthenet.com/oracle/questions/empty_null.php),而且,我无法更改表中现有的空值以适合我自己的需求,因为它会影响当前期望'SOURCE_SYSTEM'中有空值的任何其他应用程序。 –