Community
Participate
Working Groups
If caching is enabled (selectively) in the eclipslink.jpa.wdf.testsuite (using patch https://bugs.eclipse.org/bugs/attachment.cgi?id=188601), some tests fail with the following stack: Local Exception Stack: Exception [EclipseLink-6094] (Eclipse Persistence Services - @VERSION@.@QUALIFIER@): org.eclipse.persistence.exceptions.QueryException Exception Description: The parameter name [FLOOR] in the query's selection criteria does not match any parameter name defined in the query. Query: ReadObjectQuery(name="employee" referenceClass=Employee sql="SELECT ID, FIRSTNAME, LASTNAME, SALARY, EMP_END, EMP_START, COSTCENTER, DEPARTMENT, BROKERAGE_ACCOUNT, CUBICLE_PLACE, CUBICLE_FLOOR, SAMPLE_ACCOUNT, PROFILE_GUID FROM TMP_EMP WHERE ((CUBICLE_FLOOR = ?) AND (CUBICLE_PLACE = ?))") at org.eclipse.persistence.exceptions.QueryException.parameterNameMismatch(QueryException.java:1051) at org.eclipse.persistence.internal.expressions.ParameterExpression.getValue(ParameterExpression.java:246) at org.eclipse.persistence.internal.databaseaccess.DatabaseCall.translate(DatabaseCall.java:970) at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:206) at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:193) The following tests are affected: eclipselink.jpa.wdf.test - TestBidirectionalOneToOne - testUnchanged - testMoveCubicle - TestUpdate - testUpdateRelationWithCompositeKey - TestEmployeeCubicle - testRelationToCompositeKey It appears that the relationship Ecployee->Cubicle is causing trouble. It is uses a compound FK and is mapped as follows: @JoinColumns( { @JoinColumn(name = "CUBICLE_FLOOR", referencedColumnName = "FLOOR"), @JoinColumn(name = "CUBICLE_PLACE", referencedColumnName = "PLACE") }) protected Cubicle cubicle;
Using Glassfish 3.1 final (which uses EclipseLink 2.1.2), the bug is present. As per discussion of the same defect at http://www.eclipse.org/forums/index.php?S=330acc96b116ea4b34a2324004f106e1&t=msg&th=205504 , the defect is not present in 2.1.1, therefore the bug must have appeared in 2.1.2 Hope this helps you hunt it down. Cheers.
(In reply to comment #1) > Using Glassfish 3.1 final (which uses EclipseLink 2.1.2), the bug is present. > > > As per discussion of the same defect at > http://www.eclipse.org/forums/index.php?S=330acc96b116ea4b34a2324004f106e1&t=msg&th=205504 > , the defect is not present in 2.1.1, therefore the bug must have appeared in > 2.1.2 > > Hope this helps you hunt it down. > > Cheers. It appears that this bug occurs when caching is disabled, rather than when it is enabled. We have been experimenting with the enabling and disabling the 2nd level cache, and this error only occurred when eclipselink.cache.shared.default was set to false. I have not experimented with selective caching, only the global shared setting.
Have also run into this on a client site where their caching is disabled. In our case, it's occurring when merging an in-memory/non-persisted entity for deletion. During the merge, the object (and its relations) need to be locked to prevent modifications mid-merge. Here is the behaviour I've witnessed: - Before removing Entity A (which exists in memory, not persisted), we call merge on it - Internally, this creates a RepeatableUnitOfWork context and a MergeManager to merge a clone of Entity A, with its references (EntityManager:452, UnitOfWorkImpl:3427) - The ClassDescriptor of Entity A is loaded, which maps Java class to database table (UnitOfWorkImpl:3453) - The MergeManager attempts to merge the changes of the clone into the working copy (MergeManager:263) - The MergeManager attempts to register the clone of Entity A, recognizes it as a new object (because of its lack of primary key) (MergeManager:932) - The UnitOfWorkImpl also checks for the existence of Entity A, and returns null as it has not been persisted (UnitOfWorkImpl:2879) and therefore attempts to clone and register this new object (UnitOfWorkImpl:2883) - The RepeatableWriteUnitOfWork makes a shallow clone of Entity A, then populates its attributes (RepeatableWriteUnitOfWork:571); *WHEN IT DOES THIS, IT PASSES A NULL CacheKey OBJECT INTO THE METHOD* - The first mapping in Entity A refers to Entity B, which is an entity that does not exist in the cache (cache disabled) and which does exist in the database - The ObjectBuilder attempts to build a clone of Entity B (ObjectBuilder:3285) - In the ForeignReferenceMapping, the original object (Entity A) has a null cacheKey, so it gets the attribute value directly from the object instead of from a ReadObject query (ForeignReferenceMapping:243, 248) - It then attempts to clone that attribute (ForeignReferenceMapping:250) - The ObjectReferenceMapping tries to register the attribute (Entity B) with the unit of work (ObjectReferenceMapping:99) - The UnitOfWork checks to see if Entity B already exists (UnitOfWorkImpl:2879), and determines that it does (UnitOfWork:780) - However, Entity B does NOT exist in the cache (caching is disabled), so it creates a new CacheKey for the entity (UnitOfWorkImpl:799); *THIS NEW CacheKey OBJECT HAS A PRIMARY KEY ATTRIBUTE AND A READ-TIME ATTRIBUTE, BUT NO OTHER INFORMATION* - The UnitOfWork attempts to clone and register Entity B (UnitOfWorkImpl:801) - To do so, the UnitOfWork performs a shallow clone on Entity B (UnitOfWorkImpl:968) - Then it attempts to populate and register that working clone (UnitOfWorkImpl:987) - When the ObjectBuilder attempts to populate Entity B's attributes in the clone (UnitOfWorkImpl:3622), the first mapping is a one-to-one mapping to Entity C, which is another entity that does not exist in the cache (cache is disabled) and which does exist in the database - This time, when the ForeignReferenceMapping attempts to build the clone, it determines that it must read the entity from the database because it does have a CacheKey is not isolated (ForeignReferenceMapping:243) - The CacheKey is responsible for holding a DatabaseRecord with protected foreign keys (CacheKey:350); *THIS MINIMALISTIC CacheKey ENDS UP WITH AN EMPTY DatabaseRecord, DESPITE WHATEVER FK RELATIONSHIPS MAY EXIST* - As the database query is being prepared, it checks for query argument values (DatabaseQuery:786); however, the basic ReadObjectQuery created by the ForeignReferenceMapping (ForeignReferenceMapping:244) has a null for its argumentValues member, so creating a new DatabaseRecord from the query's arguments is skipped (and impossible) - At this point, the underlying SQLCall looks something like: SELECT ID, ATTR1, ATTR2, ... ENTITY_CLASS_B_ID FROM ENTITY_CLASS_C WHERE (ENTITY_CLASS_B_ID = ?) - As the DatabaseCall is attempting to populate the query's parameters with mapped values from the DatabaseRecord (DatabaseCall:949), it finds that the DatabaseRecord is empty and the required value cannot be found - When the ParameterExpression discovers that the field is not mapped, it assumes the name has been mismatched (as opposed to omitted altogether) and it throws the exception we've seen above (ParameterExpression:246) I'm still working on the "proper" way to address this issue, but perhaps this diagnosis work can help someone more familiar with the EclipseLink packages hit resolution quicker than me. And the summary of this bug should be updated: it's present when caching is DISABLED, not enabled. If enabled, the code in ForeignReferenceMapping.buildClone() which exposes this error would never be called.
Can you clarify what you mean by "caching disabled"?
(In reply to comment #4) > Can you clarify what you mean by "caching disabled"? In persistence.xml, setting most of the entity types like so: <property name="eclipselink.cache.shared.EntityClassA" value="false" /> <property name="eclipselink.cache.shared.EntityClassB" value="false" /> <property name="eclipselink.cache.shared.EntityClassC" value="false" />
I have a minimal reproduction now and have verified that this worked in 2.1.2 and has since broken. The issue appears to be related to cascade-merge of relationships from a detached entity which exists in the database when combined with isolated caching. Model: @Entity public class A { @Id private int id; @OneToMany(mappedBy="a", cascade=CascadeType.MERGE) private List<B> bs = new ArrayList<B>(); @Entity public class B { @Id private int id; @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.MERGE) private A a; TEST CASE: // STEP 1: Create A EntityManager em = emf.createEntityManager(); A a = new A(); a.setId(1); em.getTransaction().begin(); em.persist(a); em.getTransaction().commit(); em.close(); // STEP 2: Create new A referencing B em = emf.createEntityManager(); B b = new B(); b.setId(1); b.setA(a); em.getTransaction().begin(); em.merge(b); Work-arounds: 1. In the second em use persist instead of merge for the new B 2. Use a managed A (em.find) instead of the detached in the second em 3. call b.setA(a) after em.merge(b)
Created attachment 193497 [details] Simple A-B reproduction
Option #3 did pass my test but inserted null for the A_ID FK column on B. I would not recommend using this approach. Stick to #1 or # 2.
My issue with #3 (merge before setting reference) is that I did not use the managed entity returned from the merge. The code that works appears as: // STEP 1: Create A EntityManager em = emf.createEntityManager(); A a = new A(); a.setId(1); em.getTransaction().begin(); em.persist(a); em.getTransaction().commit(); em.close(); // STEP 2: Create new A referencing B em = emf.createEntityManager(); B b = new B(); b.setId(1); em.getTransaction().begin(); B mergedB = em.merge(b); mergedB.setA(a); em.getTransaction().commit(); em.close();
potential for 2.3
The issue occurs because during checkExistence's cloning of the existing entity the detached Entity 'B' instance is used as the original for the UOW and no original exists in the Cache. This will also cause any changes in B that are being merged to be ignored as EclipseLink sees the detached B as the original. The exception results from the cacheKey instance that is created from the detached instance. Farther down the code execution it is assumed this cacheKey came from the cache but that is not the case because the detached instance was used as the original. This issue has likely snuck by in the past because it is an unlikely case for the client to have an entity that is no longer in the cache. Now with the UOW code that isolates Entities to the UOW this could happen more often. We should be changing the code so that we do not use the detached instance as the original but load an original from the database when one can not be found in the cache or the UOW. A quick solution to the exception is to update UOW.checkExisted as follows but this will not resolve the underlying issues. CacheKey cacheKey = new CacheKey(primaryKey); cacheKey.setReadTime(System.currentTimeMillis()); --> cacheKey.setIsolated(true); // if the cache does not have a version then this must be built from the supplied version return cloneAndRegisterObject(object, cacheKey, descriptor);
In the instances that I ran into this issue, the entities which were not in the cache were newly created entities, and the exception was triggered as a result of a cascaded PERSIST operation. The solution was to turn off the cache altogether (which is what we opted for) or explicitly persist the offending entity prior to persisting the other entities from which the cascade would be propagated. Therefore, it seems to me that forcing a load from the DB wouldn't solve all the cases since, obviously, in this particular scenario there wouldn't be anything in the DB to load into the cache to begin with (i.e. new instance being persisted). In this case, the new instance is created using the "new" operator, vs. using JPA "from Entity" creation. This is because there are strict restrictions and requirements for entity creation (i.e. entities are not allowed to be created unless all dependent information for them already exists). This, the model of use (ideal or otherwise) is: // note that "b" would be declared as cascading PERSIST operations for "a" Entity a = new Entity(dependentInfo1, dependentInfo2, ...); CascaderEntity b = new CascaderEntity(a, ....); entityManager.persist(b); // boom! Exception raised. Hope this info makes sense, and helps.
Ooooops... sorry - mis-stated the case. The exception is raised as a result of a persist-merge operation. i.e. EntityB returnNew(...) { EntityA a = new EntityA(...); EntityB b = new EntityB(a, ...); em.persist(b); return em.merge(b); // <- this call triggers the exception } Sorry for the confusion - I thought I could describe it from memory 100% but turns out I could not. Hope this helps.
Did not get addressed for 2.3.0 as planned. Still very important so I am assigning to 2.3.1
Setting as duplicate for 345478 - symptoms look the same - a fix will be available for that bug in the next few days. If the fix for that problem does not solve this issue when it is closed, please feel free to remove the duplicate setting. *** This bug has been marked as a duplicate of bug 345478 ***
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink