mirror of https://github.com/apache/openjpa.git
OPENJPA-1397: Allow some columns of compound key to be null.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@888418 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8607e0b3b4
commit
b831319443
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.identity;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.IdClass;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JPA Entity with a compound Id of two fields, one simple and one an Entity.
|
||||||
|
*
|
||||||
|
* @author Michael Vorburger
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "test_complex")
|
||||||
|
@IdClass(ComplexCompoundIdTestEntityId.class)
|
||||||
|
public class ComplexCompoundIdTestEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH },
|
||||||
|
fetch = FetchType.LAZY, optional = true)
|
||||||
|
@JoinColumn(nullable = true, name = "type_id")
|
||||||
|
private TypeEntity type;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeEntity getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.identity;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JPA Id for ComplexCompoundIdTestEntity.
|
||||||
|
*
|
||||||
|
* @author Michael Vorburger
|
||||||
|
*/
|
||||||
|
public class ComplexCompoundIdTestEntityId {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private Long type;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||||
|
result = prime * result + ((type == null) ? 0 : type.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
ComplexCompoundIdTestEntityId other = (ComplexCompoundIdTestEntityId) obj;
|
||||||
|
if (id == null) {
|
||||||
|
if (other.id != null)
|
||||||
|
return false;
|
||||||
|
} else if (!id.equals(other.id))
|
||||||
|
return false;
|
||||||
|
if (type == null) {
|
||||||
|
if (other.type != null)
|
||||||
|
return false;
|
||||||
|
} else if (!type.equals(other.type))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.identity;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.IdClass;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JPA Entity with a compound Id of two fields, both simple.
|
||||||
|
*
|
||||||
|
* @author Michael Vorburger
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "test_simple")
|
||||||
|
@IdClass(SimpleCompoundIdTestEntityId.class)
|
||||||
|
public class SimpleCompoundIdTestEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Long firstId;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(nullable = true)
|
||||||
|
private Long secondId;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
public Long getFirstId() {
|
||||||
|
return firstId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getSecondId() {
|
||||||
|
return secondId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.identity;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JPA Id for SimpleCompoundIdTestEntity.
|
||||||
|
*
|
||||||
|
* @author Michael Vorburger
|
||||||
|
*/
|
||||||
|
public class SimpleCompoundIdTestEntityId {
|
||||||
|
|
||||||
|
public Long firstId;
|
||||||
|
|
||||||
|
public Long secondId;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((firstId == null) ? 0 : firstId.hashCode());
|
||||||
|
result = prime * result + ((secondId == null) ? 0 : secondId.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
SimpleCompoundIdTestEntityId other = (SimpleCompoundIdTestEntityId) obj;
|
||||||
|
if (firstId == null) {
|
||||||
|
if (other.firstId != null)
|
||||||
|
return false;
|
||||||
|
} else if (!firstId.equals(other.firstId))
|
||||||
|
return false;
|
||||||
|
if (secondId == null) {
|
||||||
|
if (other.secondId != null)
|
||||||
|
return false;
|
||||||
|
} else if (!secondId.equals(other.secondId))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.identity;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.OpenJPAEntityManager;
|
||||||
|
import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||||
|
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that compound identity can consists of null component column value.
|
||||||
|
*
|
||||||
|
* This test uses pre-defined database tables created by DDL explicitly.
|
||||||
|
* The tables have <em>logical</em> compound primary keys in the sense non-null
|
||||||
|
* constraint is <em>not</em> set on the primary columns. The tables are populated
|
||||||
|
* with SQL to contain null values in these columns.
|
||||||
|
* The test verifies that results are returned as par expectation.
|
||||||
|
* For more details, refer
|
||||||
|
* <A href="https://issues.apache.org/jira/browse/OPENJPA-1397">JIRA-1397</A>
|
||||||
|
*
|
||||||
|
* @author Pinaki Poddar
|
||||||
|
* @author Michael Vorburger
|
||||||
|
*/
|
||||||
|
public class TestCompundIdWithNull extends SingleEMFTestCase {
|
||||||
|
private static boolean tablesCreated = false;
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
// do not use CLEAR_TABLES or DROP_TABLES
|
||||||
|
super.setUp(SimpleCompoundIdTestEntity.class, ComplexCompoundIdTestEntity.class, TypeEntity.class);
|
||||||
|
if (!tablesCreated) {
|
||||||
|
createTables(emf.createEntityManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSimpleCompoundIdTestEntity() throws Exception {
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
String jpql = "SELECT o FROM SimpleCompoundIdTestEntity o ORDER BY o.secondId";
|
||||||
|
List<SimpleCompoundIdTestEntity> list = em.createQuery(jpql,SimpleCompoundIdTestEntity.class)
|
||||||
|
.getResultList();
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
assertEquals(Long.valueOf(123), list.get(0).getSecondId());
|
||||||
|
|
||||||
|
SimpleCompoundIdTestEntity secondResult = list.get(1);
|
||||||
|
assertNotNull("BUG (JIRA-1397)! Result list contains null in second element", secondResult);
|
||||||
|
assertNull(secondResult.getSecondId());
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testComplexCompoundIdTestEntity() throws Exception {
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
String jpql = "SELECT o FROM ComplexCompoundIdTestEntity o ORDER BY o.type";
|
||||||
|
List<ComplexCompoundIdTestEntity> list = em.createQuery(jpql,ComplexCompoundIdTestEntity.class)
|
||||||
|
.getResultList();
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
ComplexCompoundIdTestEntity secondResult = list.get(1);
|
||||||
|
assertNotNull("Result list contains null in second element", secondResult);
|
||||||
|
assertNull("Result list's second record secondId field was not null", secondResult.getType());
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create tables with logical compound keys without non-null constraint.
|
||||||
|
* Populate them with null values in some of the columns.
|
||||||
|
*/
|
||||||
|
private void createTables(EntityManager em) throws Exception {
|
||||||
|
em.getTransaction().begin();
|
||||||
|
OpenJPAEntityManager kem = OpenJPAPersistence.cast(em);
|
||||||
|
|
||||||
|
Connection conn = (Connection) kem.getConnection();
|
||||||
|
// NOTE that 'logically' test_simple has a ", CONSTRAINT test_simple_pk PRIMARY KEY (firstId, secondId)",
|
||||||
|
// but at least Derby doesn't permit NULL then.. in our real-world underlying schema that leads
|
||||||
|
// to this there are *NO* PRIMARY KEY on any tables, but there is a logical model expressed
|
||||||
|
// elsewhere stating that those two columns uniquely identify a row.
|
||||||
|
try {
|
||||||
|
conn.createStatement().execute("DROP TABLE test_type");
|
||||||
|
conn.createStatement().execute("DROP TABLE test_simple");
|
||||||
|
conn.createStatement().execute("DROP TABLE test_complex");
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.createStatement().execute("CREATE TABLE test_simple(firstId NUMERIC, secondId NUMERIC)");
|
||||||
|
conn.createStatement().execute("INSERT INTO test_simple(firstId, secondId) VALUES (1, 123)");
|
||||||
|
conn.createStatement().execute("INSERT INTO test_simple(firstId, secondId) VALUES (1, NULL)");
|
||||||
|
|
||||||
|
conn.createStatement().execute("CREATE TABLE test_type(id NUMERIC CONSTRAINT test_type_pk PRIMARY KEY, " +
|
||||||
|
"code VARCHAR(16))");
|
||||||
|
conn.createStatement().execute("INSERT INTO test_type(id, code) VALUES (987, 'ABC')");
|
||||||
|
|
||||||
|
conn.createStatement().execute("CREATE TABLE test_complex(id NUMERIC, type_id NUMERIC)");
|
||||||
|
conn.createStatement().execute("INSERT INTO test_complex(id, type_id) VALUES (1, 987)");
|
||||||
|
conn.createStatement().execute("INSERT INTO test_complex(id, type_id) VALUES (1, NULL)");
|
||||||
|
|
||||||
|
conn.close();
|
||||||
|
|
||||||
|
em.flush();
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.apache.openjpa.persistence.identity;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JPA Entity
|
||||||
|
*
|
||||||
|
* @author Michael Vorburger
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "test_type")
|
||||||
|
public class TypeEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
Loading…
Reference in New Issue