My favorites | Sign in
Project Home Downloads Wiki Issues Source
New issue   Search
for
  Advanced search   Search tips   Subscriptions
Issue 91: Exception: {Primary keys are immutable} thrown when cascadingly deleting of chilren with "gae.pk-name" field
12 people starred this issue and may be notified of changes. Back to list
Status:  Verified
Owner:  ----
Closed:  Jul 2009


Sign in to add a comment
 
Reported by yongli2...@gmail.com, Jul 10, 2009
What steps will reproduce the problem?
(See attachment for full sourcecode)
1.
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Parent3 {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
	private String id;
	
    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-name", value="true")
    private String keyName;
    
    @Persistent
    private String name;

    @Persistent(mappedBy="parent3")
    private Set<Child3> child3s = new HashSet<Child3>();

2.
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Child3 {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
	private String id;
	
    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-name", value="true")
    private String keyName;
    
    @Persistent
    private Parent3 parent3;
    
    @Persistent
    private String name;

3.
	// prepare 
	pm.currentTransaction().begin();
	Parent3 p = new Parent3();
	p.setKeyName("p1");
	p.setName("xxx");
	
	Parent3 p1 = pm.makePersistent(p);
	
	pm.currentTransaction().commit();
	System.out.println("id="+p1.getId());
	
	pm.currentTransaction().begin();
	Child3 c = new Child3();
	c.setKeyName("c1");
	c.setName("xxx");
	c.setParent3(p);
	Child3 c1 = pm.makePersistent(c);
	
	pm.currentTransaction().commit();
	System.out.println("id="+c1.getId());
	

	// query
	Query query = pm.newQuery(Parent3.class);
	List<Parent3> parent3s = (List<Parent3>)query.execute();
	System.out.println("size="+parent3s.size());
	for(Parent3 parent3:parent3s) {
		System.out.println("id:"+parent3.getId()+", name: "+parent3.getName());
		Set<Child3> set = parent3.getChild3s();
		System.out.println("child size: "+set.size());
		assertTrue(1==set.size());
		pm.currentTransaction().begin();
                // NOTE!!!!  exception thrown here
		pm.deletePersistent(parent3);
		pm.currentTransaction().commit();
		
	}


What is the expected output? What do you see instead?
INFO: Exception thrown
Attempt was made to modify the primary key of an object of type
com.jcommerce.core.test.case3.Child3 identified by key
Parent3(p1)/Child3(c1).  Primary keys are immutable.
org.datanucleus.exceptions.NucleusUserException: Attempt was made to modify
the primary key of an object of type com.jcommerce.core.test.case3.Child3
identified by key Parent3(p1)/Child3(c1).  Primary keys are immutable.
	at
org.datanucleus.store.appengine.DatastoreFieldManager.storeKeyPK(DatastoreFieldManager.java:641)
	at
org.datanucleus.store.appengine.DatastoreFieldManager.storePKNameField(DatastoreFieldManager.java:463)
	at
org.datanucleus.store.appengine.DatastoreFieldManager.storeStringField(DatastoreFieldManager.java:424)
	at
org.datanucleus.state.AbstractStateManager.providedStringField(AbstractStateManager.java:997)
	at com.jcommerce.core.test.case3.Child3.jdoProvideField(Child3.java)
	at com.jcommerce.core.test.case3.Child3.jdoProvideFields(Child3.java)
	at
org.datanucleus.state.JDOStateManagerImpl.provideFields(JDOStateManagerImpl.java:2597)
	at
org.datanucleus.store.appengine.DatastorePersistenceHandler.deleteObject(DatastorePersistenceHandler.java:446)
	at
org.datanucleus.state.JDOStateManagerImpl.internalDeletePersistent(JDOStateManagerImpl.java:4080)
	at
org.datanucleus.state.JDOStateManagerImpl.deletePersistent(JDOStateManagerImpl.java:4048)
	at
org.datanucleus.ObjectManagerImpl.deleteObjectInternal(ObjectManagerImpl.java:1430)
	at org.datanucleus.store.mapped.scostore.FKSetStore.clear(FKSetStore.java:672)
	at org.datanucleus.sco.backed.Set.clear(Set.java:763)
	at
org.datanucleus.store.mapped.mapping.CollectionMapping.preDelete(CollectionMapping.java:299)
	at
org.datanucleus.store.appengine.DependentDeleteRequest.execute(DependentDeleteRequest.java:71)
	at
org.datanucleus.store.appengine.DatastorePersistenceHandler.deleteObject(DatastorePersistenceHandler.java:439)
	at
org.datanucleus.state.JDOStateManagerImpl.internalDeletePersistent(JDOStateManagerImpl.java:4080)
	at
org.datanucleus.state.JDOStateManagerImpl.deletePersistent(JDOStateManagerImpl.java:4048)
	at
org.datanucleus.ObjectManagerImpl.deleteObjectInternal(ObjectManagerImpl.java:1430)
	at org.datanucleus.ObjectManagerImpl.deleteObject(ObjectManagerImpl.java:1355)

What version of the product are you using? On what operating system?


Please provide any additional information below.
by looking at the source code under:
  private void storePKNameField(int fieldNumber, String value) {
    // TODO(maxr) make sure the pk is an encoded string
    if (!fieldIsOfTypeString(fieldNumber)) {
      throw new NucleusUserException(
          "Field with \"" + DatastoreManager.PK_ID + "\" extension must be
of type String").setFatal();
    }
    Key key = null;
    if (value != null) {
      key = KeyFactory.createKey(datastoreEntity.getKind(), value);
    }
    storeKeyPK(key);
  }

I found it try to recreate the key with the keyName only, however it's not
correct if the entity being deleted is a child, whose ID should be
constructed as: 
String pkn = p.getKeyName();
String akn = a.getKeyName();
Key aKey = new KeyFactory.Builder("Parents3", pkn).addChild("Child3",
akn).getKey();

I think it is a bug.  Or anybody can tell me what's wrong in my testcase?
Shouldn't I declare parents3 and Child3 and assign KeyNames as that? 


Test3.java
4.1 KB   View   Download
Jul 10, 2009
#1 yongli2...@gmail.com
Sorry, the GAE source code where I found problem is
org.datanucleus.store.appengine.DatastoreFieldManager

  private void storePKNameField(int fieldNumber, String value) {
    // TODO(maxr) make sure the pk is an encoded string
    if (!fieldIsOfTypeString(fieldNumber)) {
      throw new NucleusUserException(
          "Field with \"" + DatastoreManager.PK_ID + "\" extension must be of type
String").setFatal();
    }
    Key key = null;
    if (value != null) {
      key = KeyFactory.createKey(datastoreEntity.getKind(), value);
    }
    storeKeyPK(key);
  }

and I am using GAE-JAVA 1.2.1
Jul 11, 2009
#2 yongli2...@gmail.com
In fact seems even delete of Child3 alone will fail. 

I do think it's a serious problem and seems I don't find workaround if I need to
specify "gae.pk-name" field for entities of owned one-to-one and one-to-many relationship
Jul 13, 2009
#3 vitoe...@gmail.com
I have the same error
Jul 13, 2009
#4 lpin...@gmail.com
I have the same error too. Waiting for the solution.
Jul 13, 2009
#5 liu_zhic...@vanceinfo.com
I have the same error too.

Jul 13, 2009
#6 xul...@gmail.com
I hit the same issue
Jul 13, 2009
#7 vito...@gmail.com
any response for it?
Jul 14, 2009
Project Member #8 max.r...@gmail.com
(No comment was entered for this change.)
Status: Accepted
Labels: FoundIn-1.0.1 TargetRelease-1.0.3
Jul 14, 2009
Project Member #9 max.r...@gmail.com
(No comment was entered for this change.)
Status: Fixed
Jul 15, 2009
#10 liu_zhic...@vanceinfo.com
Jul 24, 2009
#11 villa...@gmail.com
When is going to be released this fix?? I've tried to download the latest source code 
from subversion but it doesn't work
Nov 6, 2009
Project Member #12 max.r...@gmail.com
(No comment was entered for this change.)
Status: Verified
Sign in to add a comment

Powered by Google Project Hosting