If I have an Entity which has a primary key which is derived and I am implementing it using @EmbeddedId, am I responsible for maintaining the derived fields of the @EmbeddedId?
I could not find anything in the JPA 2.0 spec https://download.oracle.com/auth/otn-pub/jcp/persistence-2.0-fr-oth-JSpec/persistence-2_0-final-spec.pdf?e=1317047713&h=54831c176e81a244a4c309e13eba0b27 which tells me what to expect.
I have a container class EContainer that is reponsible for maintaining a "map" of items, of type ECompoundKeyMapItem, identified by their "key" within the EContainer class.
The items (ECompoundKeyMapItem) are responsible similarly for maintaining a "map" of subitems, of type "ECompoundKeyMapSubItem", identified by their keys.
The embeddedId for the item class, ECompoundKeyMapItemId, contains a field, "cont", that is derived from ECompoundKeyMapItem's container field via the @MapsId("cont") annotation.
Likewise the ECompoundKeyMapSubItem class has a PK which is an embeddedId "ECompoundKeyMapSubItemId" class, which contains a field "prnt" which is derived from ECompoundKeyMapSubItem's field parent.
In the following as the code stands I do NOT maintain the derived fields in the embeddedIds explicitly and the test fails where it attempts to find an item with an @EmbeddedId ...
ECompoundKeyMapItem item = container.getMapItem(1, em);
if(item == null) {
throw new TestException("cannot find ECompoundKeyMapItem 1");
}
However it does not fail on Eclipselink.
If I alter the code so that I do maintain the EmbeddedId explicitly it works;
class ECompoundKeyMapItem ....
public void setSingleton(EContainer singleton) {
this.id.cont = singleton.id;
this.container = singleton;
}
But if I use the principle that the @EmbeddedId must be maintained for all derived fields I get an error with the ECompoundKeyMapSubItem class
class ECompoundKeyMapSubItem .... public void setParent(ECompoundKeyMapItem parent) { this.id.prnt = parent.id; this.parent = parent; } [ObjectDB 2.3.0_01] Unexpected exception (Error 990) Generated by Java HotSpot(TM) Client VM 1.6.0_27 (on Windows Server 2008 6.0). Please report this error on http://www.objectdb.com/database/issue/new com.objectdb.o.InternalException: null com.objectdb.o.InternalException at com.objectdb.o.VUT.e(VUT.java:220) at com.objectdb.o.VUT.e(VUT.java:147) at com.objectdb.o.UMR.s(UMR.java:418) at com.objectdb.o.UMR.q(UMR.java:374) at com.objectdb.o.UML.s(UML.java:486) at com.objectdb.o.MMM.X(MMM.java:793) at com.objectdb.o.OBM.bx(OBM.java:384) at com.objectdb.o.OBM.bx(OBM.java:249) at com.objectdb.jpa.EMImpl.persist(EMImpl.java:375) at uk.co.his.test.embeddedCompoundKeys.ECompoundKeyMapItem.mapSubItemsPutNew(ECompoundKeyMapItem.java:125) at uk.co.his.test.embeddedCompoundKeys.ECompoundKeyMapItem.addSubItem(ECompoundKeyMapItem.java:115) at uk.co.his.test.embeddedCompoundKeys.EContainer.generateMapItems(EContainer.java:57) at uk.co.his.test.MainCompoundKeys.runEmbeddedContainTest(MainCompoundKeys.java:92) at uk.co.his.test.MainCompoundKeys.main(MainCompoundKeys.java:148) Unexpetced error com.objectdb.o.InternalException
//Code without manual maintenance of derived fields in @EmbeddedId classes...
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.FlushModeType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Query;
import javax.persistence.Version;
import uk.co.his.test.TestException;
@Entity
@NamedQueries({
@NamedQuery(name = "numberOfMapItems", query = "SELECT COUNT(x) FROM ECompoundKeyMapItem x WHERE x.container = :container") })
public class EContainer {
public static EContainer getInstance(EntityManager em) {
EContainer c = em.find(EContainer.class, "1");
if (c == null) {
c = new EContainer();
em.persist(c);
}
return c;
}
@Id
protected String id = "1";
@SuppressWarnings("unused")
// used by JPA
@Version
private int version;
@Basic
protected java.lang.Integer lastInstanceInMap;
protected EContainer() { // Empty constructor for JPA
}
public void generateMapItems(Integer numberToGen, boolean subItems, EntityManager em)
throws TestException {
if (lastInstanceInMap == null) {
lastInstanceInMap = 0;
}
int endCnt = lastInstanceInMap + numberToGen;
int startCnt = lastInstanceInMap;
long timing = System.currentTimeMillis();
while (endCnt > startCnt) {
startCnt++;
String key = "" + startCnt;
ECompoundKeyMapItem item = mapItemsPutNew(key, new ECompoundKeyMapItem(startCnt), em);
ECompoundKeyMapSubItem subItem = item.addSubItem(em, key);
put(key, subItem);
lastInstanceInMap = startCnt;
if(startCnt % 1000 == 0) {
System.out.println("Created 1000 items " + startCnt + " in " + (System.currentTimeMillis() - timing) + "ms");
timing = System.currentTimeMillis();
}
}
}
private ECompoundKeyMapItem mapItemsPutNew(java.lang.String key,
ECompoundKeyMapItem newObj, EntityManager em) throws TestException {
newObj.setUUID();
newObj.setSingleton(this);
newObj.setCachedKey(key);
em.persist(newObj);
return newObj;
}
private void put(java.lang.String key, ECompoundKeyMapSubItem o) {
EContainer oldReferer = o.getOtherContainerRelationship();
if (oldReferer != null) {
oldReferer.remove(o);
}
o.setOtherContainerRelationship(this);
o.setOtherRelationshipKey(key);
}
private void remove(ECompoundKeyMapSubItem o) {
o.setOtherContainerRelationship(null);
o.setOtherRelationshipKey(null);
}
public ECompoundKeyMapItem getMapItem(Integer cachedKey, EntityManager em) {
return em.find(ECompoundKeyMapItem.class, new ECompoundKeyMapItem.ECompoundKeyMapItemId(cachedKey.toString(), id));
}
public Integer getLastMapInstanceKey() {
return lastInstanceInMap;
}
public int getNumberOfMapItems(EntityManager em) {
Query q = em
.createNamedQuery("numberOfMapItems");
q.setParameter("container", this);
q.setFlushMode(FlushModeType.AUTO);
try {
long l = (Long) q.getSingleResult();
if (l > Integer.MAX_VALUE)
return -1;
int i = (int) l;
return i;
} catch (javax.persistence.NoResultException ex) {
return -1;
}
}
}
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.Version;
@Entity
public class ECompoundKeyMapItem {
@Embeddable
public static class ECompoundKeyMapItemId implements Serializable {
private static final long serialVersionUID = 0L;
private String cachedKey;
private String cont;
public ECompoundKeyMapItemId() {}
public ECompoundKeyMapItemId(String id, String parent) {
this.cachedKey = id;
this.cont = parent;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof ECompoundKeyMapItemId)) return false;
ECompoundKeyMapItemId other = (ECompoundKeyMapItemId) o;
return cont.equals(other.cont) && cachedKey.equals(other.cachedKey);
}
@Override
public int hashCode() {
//Follow bloch Item 8.
int result = 17;
result = 37*result + cachedKey.hashCode();
result = 37*result + cont.hashCode();
return result;
}
public String getCachedKey() {
return cachedKey;
}
public String getSingleton() {
return cont;
}
}
@EmbeddedId public ECompoundKeyMapItemId id;
@ManyToOne(fetch=FetchType.LAZY)
@MapsId("cont")
private EContainer container;
public EContainer getSingleton() {
return container;
}
@Version
private int version;
@Basic
private Integer property;
@SuppressWarnings("unused") //JPA
private ECompoundKeyMapItem() {
}
public ECompoundKeyMapItem(int startCnt) {
id = new ECompoundKeyMapItemId();
property = startCnt;
}
public void setUUID() {
//Do nothing we do not have a separate ID
}
public Integer getJPAVersion() {
return version;
}
public void setSingleton(EContainer singleton) {
this.container = singleton;
}
public void setCachedKey(String key) {
this.id.cachedKey = key;
}
public String getCachedKey() {
return this.id.cachedKey;
}
public void setProperty(Integer newValue) {
property = newValue;
}
public Integer getProperty() {
return property;
}
public String getInfo() {
return "ECompoundKeyMapItem: id=" + getId() + ", version=" + version + ", key=" + id.cachedKey + ", property=" + property;
}
public String getId() {
return container.id + "/" + id.cachedKey;
}
public ECompoundKeyMapSubItem addSubItem(EntityManager em, String key) {
ECompoundKeyMapSubItem newObj = mapSubItemsPutNew(key, em);
return newObj;
}
private ECompoundKeyMapSubItem mapSubItemsPutNew(String key, EntityManager em) {
ECompoundKeyMapSubItem newObj = new ECompoundKeyMapSubItem(key);
newObj.setParent(this);
newObj.setSubKey(key);
//em.persist(this);
em.persist(newObj);
//em.flush();
return newObj;
}
}
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.Version;
import uk.co.his.test.embeddedCompoundKeys.ECompoundKeyMapItem.ECompoundKeyMapItemId;
@Entity
public class ECompoundKeyMapSubItem {
@Embeddable
public static class ECompoundKeyMapSubItemId implements Serializable {
private static final long serialVersionUID = 0L;
private String subKey;
private ECompoundKeyMapItemId prnt;
public ECompoundKeyMapSubItemId() {}
public ECompoundKeyMapSubItemId(String id, ECompoundKeyMapItemId parent) {
this.subKey = id;
this.prnt = parent;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof ECompoundKeyMapSubItemId)) return false;
ECompoundKeyMapSubItemId other = (ECompoundKeyMapSubItemId) o;
return prnt.equals(other.prnt) && subKey.equals(other.subKey);
}
@Override
public int hashCode() {
//Follow bloch Item 8.
int result = 17;
result = 37*result + subKey.hashCode();
result = 37*result + prnt.hashCode();
return result;
}
public String getSubKey() {
return subKey;
}
public ECompoundKeyMapItemId getParent() {
return prnt;
}
}
@EmbeddedId
private ECompoundKeyMapSubItemId id;
@ManyToOne(fetch=FetchType.LAZY)
@MapsId("prnt")
private ECompoundKeyMapItem parent;
@Version
private int version;
@Basic
private String property;
// Attributes which mark another non containment relationship
@ManyToOne(fetch=FetchType.LAZY)
private EContainer otherContainerRelationship;
@Basic
private String otherRelationshipKey;
@SuppressWarnings("unused")//JPAs
private ECompoundKeyMapSubItem() {}
public ECompoundKeyMapSubItem(String property) {
id = new ECompoundKeyMapSubItemId();
this.property = property;
}
public void setUUID() {
//Do nothing we do not have a separate ID
}
public Integer getJPAVersion() {
return version;
}
public ECompoundKeyMapItem getParent() {
return parent;
}
public void setParent(ECompoundKeyMapItem parent) {
this.parent = parent;
}
public String getSubKey() {
return id.subKey;
}
public void setSubKey(String subKey) {
this.id.subKey = subKey;
}
public void setProperty(String newValue) {
property = newValue;
}
public String getProperty() {
return property;
}
public EContainer getOtherContainerRelationship() {
return otherContainerRelationship;
}
public void setOtherContainerRelationship(EContainer otherContainerRelationship) {
this.otherContainerRelationship = otherContainerRelationship;
}
public String getOtherRelationshipKey() {
return otherRelationshipKey;
}
public void setOtherRelationshipKey(String otherRelationshipKey) {
this.otherRelationshipKey = otherRelationshipKey;
}
public String getInfo() {
return "ECompoundKeyMapItem: id=" + parent.getId() + "/" + id.subKey + ", version=" + version + ", key=" + id.subKey + ", property=" + property;
}
}
Here is the test...
public static void runEmbeddedContainTest(String puName, int numItems) throws TestException {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(puName);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
EContainer container = EContainer.getInstance(em);
long begin = System.currentTimeMillis();
container.generateMapItems(numItems, false, em);
long beginCommit = System.currentTimeMillis();
em.getTransaction().commit();
long end = System.currentTimeMillis();
System.out.println("Commit " + numItems + " in " + (end - beginCommit) + " milliseconds");
System.out.println("Created " + numItems + " in " + (end - begin) + " milliseconds");
System.err.flush();
System.out.flush();
int numberOfMapItems = container.getNumberOfMapItems(em);
if(numberOfMapItems != numItems) {
throw new TestException("Created " + numberOfMapItems + " not " + numItems + " items");
}
ECompoundKeyMapItem item = container.getMapItem(1, em);
if(item == null) {
throw new TestException("cannot find ECompoundKeyMapItem 1");
}
if(item.getSingleton() != container) {
throw new TestException("Container is not as expected: " + container);
}
if(!"1".equals(item.getCachedKey())){
throw new TestException("Item key is not as expected: 1");
}
item = container.getMapItem(numItems, em);
if(item == null) {
throw new TestException("cannot find ECompoundKeyMapItem " + numItems);
}
if(item.getSingleton() != container) {
throw new TestException("Container is not as expected: " + container);
}
if(!(numItems+"").equals(item.getCachedKey())){
throw new TestException("Item key is not as expected: " + numItems);
}
System.out.println("Done Checks");
System.err.flush();
System.out.flush();
}