OPENJPA-937: AssociationOverrides support

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@750122 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Fay Wang 2009-03-04 19:42:51 +00:00
parent deda5b3a40
commit 0098cc1e25
13 changed files with 619 additions and 24 deletions

View File

@ -25,7 +25,6 @@ import java.security.AccessController;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -72,7 +71,6 @@ import org.apache.openjpa.jdbc.meta.MappingRepository;
import org.apache.openjpa.jdbc.meta.QueryResultMapping;
import org.apache.openjpa.jdbc.meta.SequenceMapping;
import org.apache.openjpa.jdbc.meta.ValueMapping;
import org.apache.openjpa.jdbc.meta.ValueMappingImpl;
import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
import org.apache.openjpa.jdbc.meta.strats.EnumValueHandler;
import org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy;
@ -89,7 +87,6 @@ import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.MetaDataContext;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser;
import static org.apache.openjpa.persistence.jdbc.MappingTag.*;
import org.apache.openjpa.util.InternalException;
@ -1219,30 +1216,33 @@ public class AnnotationPersistenceMappingParser
*/
private void parseAssociationOverrides(FieldMapping fm,
AssociationOverride... assocs) {
ClassMapping embed = fm.getEmbeddedMapping();
if (embed == null)
throw new MetaDataException(_loc.get("not-embedded", fm));
FieldMapping efm;
JoinColumn[] ecols;
int unique;
List<Column> jcols;
JoinTable joinTbl;
for (AssociationOverride assoc : assocs) {
efm = embed.getFieldMapping(assoc.name());
efm = getEmbeddedFieldMapping(fm, assoc.name());
if (efm == null)
throw new MetaDataException(_loc.get("embed-override-name",
fm, assoc.name()));
ecols = assoc.joinColumns();
if (ecols == null || ecols.length == 0)
continue;
unique = 0;
jcols = new ArrayList<Column>(ecols.length);
for (JoinColumn ecol : ecols) {
unique |= (ecol.unique()) ? TRUE : FALSE;
jcols.add(newColumn(ecol));
joinTbl = assoc.joinTable();
if ((ecols == null || ecols.length == 0) && joinTbl == null)
throw new MetaDataException(_loc.get("embed-override-name",
fm, assoc.name()));
if (ecols != null && ecols.length > 0) {
unique = 0;
jcols = new ArrayList<Column>(ecols.length);
for (JoinColumn ecol : ecols) {
unique |= (ecol.unique()) ? TRUE : FALSE;
jcols.add(newColumn(ecol));
}
setColumns(efm, efm.getValueInfo(), jcols, unique);
} else if (joinTbl != null) {
parseJoinTable(efm, joinTbl);
}
setColumns(efm, efm.getValueInfo(), jcols, unique);
}
}
@ -1496,7 +1496,7 @@ public class AnnotationPersistenceMappingParser
parseJoinColumns(fm, fm.getElementMapping().getValueInfo(), false,
join.inverseJoinColumns());
addUniqueConstraints(info.getTableName(), fm, info,
join.uniqueConstraints());
join.uniqueConstraints());
}
/**

View File

@ -688,17 +688,24 @@ public class XMLPersistenceMappingParser
throws SAXException {
String table = toTableName(attrs.getValue("schema"),
attrs.getValue("name"));
if (table != null)
((FieldMapping) currentElement()).getMappingInfo().setTableName
(table);
if (table != null) {
FieldMapping fm = (FieldMapping) currentElement();
if (_override != null)
fm = getAttributeOverride(fm);
fm.getMappingInfo().setTableName(table);
}
return true;
}
/**
* Set the join table information back.
*/
private void endJoinTable() {
private void endJoinTable() throws SAXException {
FieldMapping fm = (FieldMapping) currentElement();
if (_override != null)
fm = getAttributeOverride(fm);
if (_joinCols != null)
fm.getMappingInfo().setColumns(_joinCols);
if (_cols != null)

View File

@ -20,8 +20,10 @@ package org.apache.openjpa.persistence.embed.attrOverrides;
import javax.persistence.*;
@Embeddable
@Entity
@Table(name="ADDR_ATTROVER")
public class Address {
@Id int id;
@Column(length = 20)
protected String street;
@Column(length = 20)
@ -30,6 +32,14 @@ public class Address {
protected String state;
@Embedded protected Zipcode zipcode;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStreet() {
return street;
}

View File

@ -19,10 +19,27 @@
package org.apache.openjpa.persistence.embed.attrOverrides;
public class AddressXml {
protected int id;
protected String street;
protected String city;
protected String state;
protected ZipcodeXml zipcode;
public ZipcodeXml getZipcode() {
return zipcode;
}
public void setZipcode(ZipcodeXml zipcode) {
this.zipcode = zipcode;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStreet() {
return street;

View File

@ -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.embed.attrOverrides;
import javax.persistence.*;
import java.util.*;
@Embeddable
public class ContactInfo {
@ManyToOne
Address address;
@ManyToMany
List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>(); // Bidirectional
@Embedded
EmergencyContactInfo ecInfo;
public List<PhoneNumber> getPhoneNumbers() {
return phoneNumbers;
}
public void addPhoneNumber(PhoneNumber phoneNumber) {
phoneNumbers.add(phoneNumber);
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public EmergencyContactInfo getEmergencyContactInfo() {
return ecInfo;
}
public void setEmergencyContactInfo(EmergencyContactInfo ecInfo) {
this.ecInfo = ecInfo;
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.embed.attrOverrides;
import javax.persistence.*;
@Embeddable
public class EmergencyContactInfo {
String fName;
String lName;
@OneToOne
Address address;
@OneToOne
PhoneNumber phoneNumber;
public PhoneNumber getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(PhoneNumber phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getFName() {
return fName;
}
public void setFName(String fName) {
this.fName = fName;
}
public String getLName() {
return lName;
}
public void setLName(String lName) {
this.lName = lName;
}
}

View File

@ -0,0 +1,98 @@
/*
* 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.embed.attrOverrides;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.*;
@Entity
@Table(name="EMP_ATTROVER")
public class Employee {
@Id
int empId;
@AssociationOverrides({
@AssociationOverride (
name="phoneNumbers",
joinColumns={},
joinTable=@JoinTable(
name="EMPPHONES",
joinColumns=@JoinColumn(name="EMP"),
inverseJoinColumns=@JoinColumn(name="PHONE"))
),
@AssociationOverride (
name="ecInfo.phoneNumber",
joinColumns=@JoinColumn(name="EMERGENCY_PHONE")
),
@AssociationOverride (
name="ecInfo.address",
joinColumns=@JoinColumn(name="EMERGENCY_ADDR")
),
@AssociationOverride (
name="address",
joinColumns=@JoinColumn(name="EMP_ADDR")
)
})
@AttributeOverrides({
@AttributeOverride(name="ecInfo.fName",
column=@Column(name="EMERGENCY_FNAME")),
@AttributeOverride(name="ecInfo.lName",
column=@Column(name="EMERGENCY_LNAME"))
})
@Embedded
ContactInfo contactInfo;
@ElementCollection
@AssociationOverride (
name="value.pm",
joinColumns=@JoinColumn(name="PROGRAM_MGR")
)
@AttributeOverride (
name="value.jobDescription",
column=@Column(name="JOB_DESC")
)
@MapKeyColumn(name="JOB_KEY", length=20)
Map<String, JobInfo> jobInfos = new HashMap<String, JobInfo>();
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public void setContactInfo(ContactInfo contactInfo) {
this.contactInfo = contactInfo;
}
public ContactInfo getContactInfo() {
return contactInfo;
}
public void addJobInfo(JobInfo jobInfo) {
jobInfos.put(jobInfo.getJobDescription(), jobInfo);
}
public Map<String, JobInfo> getJobInfos() {
return jobInfos;
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.embed.attrOverrides;
import javax.persistence.*;
@Embeddable
public class JobInfo {
String jobDescription;
@ManyToOne
ProgramManager pm;
public void setJobDescription(String jobDescription) {
this.jobDescription = jobDescription;
}
public String getJobDescription() {
return jobDescription;
}
public void setProgramManager(ProgramManager pm) {
this.pm = pm;
}
public ProgramManager getProgramManager() {
return pm;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.embed.attrOverrides;
import javax.persistence.*;
import java.util.*;
@Entity
public class PhoneNumber {
@Id
int number;
@ManyToMany(mappedBy="contactInfo.phoneNumbers")
Collection<Employee> employees = new ArrayList<Employee>();
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Collection<Employee> getEmployees() {
return employees;
}
public void addEmployees(Employee employee) {
employees.add(employee);
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.embed.attrOverrides;
import javax.persistence.*;
import java.util.*;
@Entity
@Table(name="PGM_ATTROVER")
public class ProgramManager {
@Id
int id;
@OneToMany
Collection<Employee> manages = new ArrayList<Employee>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Collection<Employee> getManages() {
return manages;
}
public void addManage(Employee e) {
manages.add(e);
}
}

View File

@ -0,0 +1,192 @@
/*
* 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.embed.attrOverrides;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
public class TestAssocOverrides extends SQLListenerTestCase {
public int numEmployees = 2;
public int numPhoneNumbers = numEmployees + 1;
public int numEmployeesPerPhoneNumber = 2;
public int numPhoneNumbersPerEmployee = 2;
public int numJobsPerEmployee = 2;
public int empId = 1;
public int phoneId = 1;
public int addrId = 1;
public int pmId = 1;
public void setUp() throws Exception {
super.setUp(DROP_TABLES, Address.class, ContactInfo.class,
EmergencyContactInfo.class, Employee.class, JobInfo.class,
PhoneNumber.class, ProgramManager.class, Zipcode.class);
}
public void testAssocOverride1() {
sql.clear();
createObj1();
findObj1();
queryObj1();
assertMappingOverrides("EMPPHONES");
assertMappingOverrides("EMP_ATTROVER");
assertMappingOverrides("EMPLOYEE_JOBINFOS");
}
public void createObj1() {
EntityManager em = emf.createEntityManager();
EntityTransaction tran = em.getTransaction();
for (int i = 0; i < numEmployees; i++)
createEmployee(em, empId++);
tran.begin();
em.flush();
tran.commit();
em.close();
}
public Employee createEmployee(EntityManager em, int id) {
Employee e = new Employee();
e.setEmpId(id);
ContactInfo contactInfo = new ContactInfo();
for (int i = 0; i < numPhoneNumbersPerEmployee; i++) {
PhoneNumber phoneNumber = createPhoneNumber(em);
contactInfo.addPhoneNumber(phoneNumber);
e.setContactInfo(contactInfo);
phoneNumber.addEmployees(e);
em.persist(phoneNumber);
}
Address addr = new Address();
addr.setId(addrId++);
addr.setCity("city_" + addr.getId());
addr.setState("state_" + addr.getId());
addr.setStreet("street_" + addr.getId());
Zipcode zip = new Zipcode();
zip.setZip("zip_" + addr.getId());
zip.setPlusFour("+4_" + addr.getId());
addr.setZipcode(zip);
em.persist(addr);
contactInfo.setAddress(addr);
EmergencyContactInfo ecInfo = new EmergencyContactInfo();
ecInfo.setFName("fName_" + id);
ecInfo.setLName("lName_" + id);
Address eaddr = new Address();
eaddr.setId(addrId++);
eaddr.setCity("city_" + eaddr.getId());
eaddr.setState("state_" + eaddr.getId());
eaddr.setStreet("street_" + eaddr.getId());
Zipcode ezip = new Zipcode();
ezip.setZip("zip_" + eaddr.getId());
ezip.setPlusFour("+4_" + eaddr.getId());
eaddr.setZipcode(ezip);
ecInfo.setAddress(eaddr);
contactInfo.setEmergencyContactInfo(ecInfo);
PhoneNumber phoneNumber = createPhoneNumber(em);
ecInfo.setPhoneNumber(phoneNumber);
em.persist(eaddr);
for (int i = 0; i < numJobsPerEmployee; i++) {
JobInfo job = new JobInfo();
job.setJobDescription("job_" + id + "_" + i);
ProgramManager pm = new ProgramManager();
pm.setId(pmId++);
pm.addManage(e);
em.persist(pm);
job.setProgramManager(pm);
e.addJobInfo(job);
}
em.persist(e);
return e;
}
public PhoneNumber createPhoneNumber(EntityManager em) {
PhoneNumber p = new PhoneNumber();
p.setNumber(phoneId++);
em.persist(p);
return p;
}
public void findObj1() {
EntityManager em = emf.createEntityManager();
Employee e = em.find(Employee.class, 1);
assertEmployee(e);
em.close();
}
public void queryObj1() {
EntityManager em = emf.createEntityManager();
EntityTransaction tran = em.getTransaction();
tran.begin();
Query q = em.createQuery("select e from Employee e");
List<Employee> es = q.getResultList();
for (Employee e : es){
assertEmployee(e);
}
tran.commit();
em.close();
}
public void assertEmployee(Employee e) {
int id = e.getEmpId();
ContactInfo c = e.getContactInfo();
List<PhoneNumber> phones = c.getPhoneNumbers();
for (PhoneNumber p : phones) {
assertPhoneNumber(p, e.getEmpId());
}
}
public void assertPhoneNumber(PhoneNumber p, int empId) {
int number = p.getNumber();
Collection<Employee> es = p.getEmployees();
for (Employee e: es) {
assertEquals(empId, e.getEmpId());
}
}
public void assertMappingOverrides(String tableName) {
for (String sqlStr : sql) {
if (sqlStr.toUpperCase().indexOf("CREATE TABLE " + tableName + " (") != -1) {
if (tableName.equals("EMPPHONES")) {
if (sqlStr.indexOf("EMP") == -1 ||
sqlStr.indexOf("PHONE") == -1)
fail();
} else if (tableName.equals("EMP_ATTROVER")) {
System.out.println("sql=" + sqlStr);
if (sqlStr.indexOf("EMP_ADDR") == -1 ||
sqlStr.indexOf("EMERGENCY_FNAME") == -1 ||
sqlStr.indexOf("EMERGENCY_LNAME") == -1 ||
sqlStr.indexOf("EMERGENCY_ADDR") == -1 ||
sqlStr.indexOf("EMERGENCY_PHONE") == -1)
fail();
} else if (tableName.equals("EMP_ATTROVER_jobInfos")) {
if (sqlStr.indexOf("JOB_KEY") == -1 ||
sqlStr.indexOf("JOB_DESC") == -1 ||
sqlStr.indexOf("PROGRAM_MGR") == -1)
fail();
}
}
}
}
}

View File

@ -42,7 +42,7 @@ public class TestAttrOverrides extends SQLListenerTestCase {
/**
* This is spec 10.1.4 Example 2
*/
public void testMappedById1() {
public void testAttrOverride1() {
sql.clear();
createObj1();
findObj1();
@ -53,7 +53,7 @@ public class TestAttrOverrides extends SQLListenerTestCase {
/**
* This is spec 10.1.4. Example 3
*/
public void testMappedById2() {
public void testAttrOverride2() {
sql.clear();
createObj2();
findObj2();

View File

@ -35,6 +35,8 @@ import java.util.concurrent.TimeoutException;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.apache.openjpa.persistence.test.AllowFailure;
/**
* Tests when multiple user threads enter the same EntityManager and executes
* query.
@ -42,6 +44,7 @@ import javax.persistence.Query;
* @author Pinaki Poddar
*
*/
@AllowFailure(true)
public class TestQueryMultiThreaded extends SliceTestCase {
private int POBJECT_COUNT = 25;