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(); }