Hi all,
I'm receiving below exception when I try to save more than one entity
(of "Clazz" type) in one transaction using JPA. I sthis a bug or it is
by design? How to save more than one entities in one transaction? Is
there any example for this case? Thank!
Caused by: java.lang.IllegalArgumentException: can't operate on
multiple entity
groups in a single transaction. found both Element {
type: "Clazz"
id: 16
}
and Element {
type: "Clazz"
id: 17
}
at
com.google.appengine.api.datastore.DatastoreApiHelper.translateError(
DatastoreApiHelper.java:28)
at
com.google.appengine.api.datastore.DatastoreApiHelper.makeSyncCall(Da
tastoreApiHelper.java:55)
at com.google.appengine.api.datastore.DatastoreServiceImpl
$2.run(Datasto
reServiceImpl.java:169)
at
com.google.appengine.api.datastore.TransactionRunner.runInTransaction
(TransactionRunner.java:29)
at com.google.appengine.api.datastore.DatastoreServiceImpl.put
(Datastore
ServiceImpl.java:157)
at com.google.appengine.api.datastore.DatastoreServiceImpl.put
(Datastore
ServiceImpl.java:137)
at com.google.appengine.api.datastore.DatastoreServiceImpl.put
(Datastore
ServiceImpl.java:133)
at
org.datanucleus.store.appengine.RuntimeExceptionWrappingDatastoreServ
ice.put(RuntimeExceptionWrappingDatastoreService.java:104)
at
org.datanucleus.store.appengine.DatastorePersistenceHandler.put(Datas
torePersistenceHandler.java:125)
at
org.datanucleus.store.appengine.DatastorePersistenceHandler.put(Datas
torePersistenceHandler.java:94)
at
org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObj
ect(DatastorePersistenceHandler.java:195)
at
org.datanucleus.state.JDOStateManagerImpl.internalMakePersistent(JDOS
tateManagerImpl.java:3067)
at org.datanucleus.state.JDOStateManagerImpl.flush
(JDOStateManagerImpl.j
ava:4395)
at org.datanucleus.ObjectManagerImpl.flushInternal
(ObjectManagerImpl.jav
a:2807)
at org.datanucleus.ObjectManagerImpl.flush
(ObjectManagerImpl.java:2747)
at org.datanucleus.ObjectManagerImpl.preCommit
(ObjectManagerImpl.java:28
86)
at org.datanucleus.TransactionImpl.internalPreCommit
(TransactionImpl.jav
a:348)
at org.datanucleus.TransactionImpl.commit(TransactionImpl.java:
235)
at org.datanucleus.jpa.EntityTransactionImpl.commit
(EntityTransactionImp
l.java:104)
This is the expected behavior. I'd recommend reading
http://code.google.com/appengine/docs/java/datastore/transactions.html if
you haven't already since it explains the issue pretty well, but in short,
you can only save multiple objects in a single transaction if they belong to
the same entity group. You can't save multiple objects in a single
transaction if they are all root entities, and I believe that's what you're
trying to do here. You'll need to save each entity in a separate
transaction or just not use transactions in this specific use case.
On Mon, Apr 27, 2009 at 1:58 AM, tha <hathanht...@gmail.com> wrote:
> Hi all,
> I'm receiving below exception when I try to save more than one entity
> (of "Clazz" type) in one transaction using JPA. I sthis a bug or it is
> by design? How to save more than one entities in one transaction? Is
> there any example for this case? Thank!
> Caused by: java.lang.IllegalArgumentException: can't operate on
> multiple entity
> groups in a single transaction. found both Element {
> type: "Clazz"
> id: 16
> }
> and Element {
> type: "Clazz"
> id: 17
> }
> at
> com.google.appengine.api.datastore.DatastoreApiHelper.translateError(
> DatastoreApiHelper.java:28)
> at
> com.google.appengine.api.datastore.DatastoreApiHelper.makeSyncCall(Da
> tastoreApiHelper.java:55)
> at com.google.appengine.api.datastore.DatastoreServiceImpl
> $2.run(Datasto
> reServiceImpl.java:169)
> at
> com.google.appengine.api.datastore.TransactionRunner.runInTransaction
> (TransactionRunner.java:29)
> at com.google.appengine.api.datastore.DatastoreServiceImpl.put
> (Datastore
> ServiceImpl.java:157)
> at com.google.appengine.api.datastore.DatastoreServiceImpl.put
> (Datastore
> ServiceImpl.java:137)
> at com.google.appengine.api.datastore.DatastoreServiceImpl.put
> (Datastore
> ServiceImpl.java:133)
> at
> org.datanucleus.store.appengine.RuntimeExceptionWrappingDatastoreServ
> ice.put(RuntimeExceptionWrappingDatastoreService.java:104)
> at
> org.datanucleus.store.appengine.DatastorePersistenceHandler.put(Datas
> torePersistenceHandler.java:125)
> at
> org.datanucleus.store.appengine.DatastorePersistenceHandler.put(Datas
> torePersistenceHandler.java:94)
> at
> org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObj
> ect(DatastorePersistenceHandler.java:195)
> at
> org.datanucleus.state.JDOStateManagerImpl.internalMakePersistent(JDOS
> tateManagerImpl.java:3067)
> at org.datanucleus.state.JDOStateManagerImpl.flush
> (JDOStateManagerImpl.j
> ava:4395)
> at org.datanucleus.ObjectManagerImpl.flushInternal
> (ObjectManagerImpl.jav
> a:2807)
> at org.datanucleus.ObjectManagerImpl.flush
> (ObjectManagerImpl.java:2747)
> at org.datanucleus.ObjectManagerImpl.preCommit
> (ObjectManagerImpl.java:28
> 86)
> at org.datanucleus.TransactionImpl.internalPreCommit
> (TransactionImpl.jav
> a:348)
> at org.datanucleus.TransactionImpl.commit(TransactionImpl.java:
> 235)
> at org.datanucleus.jpa.EntityTransactionImpl.commit
> (EntityTransactionImp
> l.java:104)
Thanks Max. You are correct. The code worked well when I removed the
transaction from my Spring context file. But I don't think my code is
incorrect just because it is inserting many objects in one
transaction. It will work with other JPA providers. So GAE's JPA
provider is not compliant with JPA spec, how can we say GAE supports
JPA 1.0?
Now without transaction, I have another issue in ANOTHER method. Let
me explain the situation in details .
My application is using Spring-JPA. In one method I want to remove
some entities from datastore using a removeAll() method. I implemented
it like this:
MyDAO class:
public void removeAll(Set<String> codes) {
Query q = entityManager.createQuery("select o from " +
getPersistentClass().getName());
List<Clazz> found = q.getResultList();
found.size(); //workaround for GAE bug !!!!
for (Clazz c : found) {
if (classCodes.contains(c.getCode())) {
entityManager.remove(c);
}
}
}
This code looks ugly because I cannot use DELETE FROM .. WHERE ..
IN .. query to remove entities. GAE said something about IN operator
is not implemented. So I have to query all entities then remove the
some entities. This method used to work with Spring declarative
transaction. Now without transaction, it stops working because the
found.size() call throws some error about closed entity manager (it
should be in the same transaction with q.getResultList()).
It looks like that to write a GAE application, my code has to be
modified a lot: to use transaction manually, to workaround for GAE
limitations, etc. In some cases, I will use transactions, and in other
cases, I will not to. It is so confusing, and the code doesn't look
like "typical Spring-JPA" code at all. I believe that declarative
transaction is the most used way nowadays. Why do I have to use manual
transaction? :(
I guess I will not be able to implement a "typical Spring application"
which can run on Tomcat and GAE. I might have to switch back to work
on Tomcat only.
Regards,
Thai
On Apr 28, 1:49 am, Max Ross <maxr+appeng...@google.com> wrote:
> This is the expected behavior. I'd recommend readinghttp://code.google.com/appengine/docs/java/datastore/transactions.htmlif > you haven't already since it explains the issue pretty well, but in short,
> you can only save multiple objects in a single transaction if they belong to
> the same entity group. You can't save multiple objects in a single
> transaction if they are all root entities, and I believe that's what you're
> trying to do here. You'll need to save each entity in a separate
> transaction or just not use transactions in this specific use case.
> Max
> On Mon, Apr 27, 2009 at 1:58 AM, tha <hathanht...@gmail.com> wrote:
> > Hi all,
> > I'm receiving below exception when I try to save more than one entity
> > (of "Clazz" type) in one transaction using JPA. I sthis a bug or it is
> > by design? How to save more than one entities in one transaction? Is
> > there any example for this case? Thank!
> > Caused by: java.lang.IllegalArgumentException: can't operate on
> > multiple entity
> > groups in a single transaction. found both Element {
> > type: "Clazz"
> > id: 16
> > }
> > and Element {
> > type: "Clazz"
> > id: 17
> > }
> > at
> > com.google.appengine.api.datastore.DatastoreApiHelper.translateError(
> > DatastoreApiHelper.java:28)
> > at
> > com.google.appengine.api.datastore.DatastoreApiHelper.makeSyncCall(Da
> > tastoreApiHelper.java:55)
> > at com.google.appengine.api.datastore.DatastoreServiceImpl
> > $2.run(Datasto
> > reServiceImpl.java:169)
> > at
> > com.google.appengine.api.datastore.TransactionRunner.runInTransaction
> > (TransactionRunner.java:29)
> > at com.google.appengine.api.datastore.DatastoreServiceImpl.put
> > (Datastore
> > ServiceImpl.java:157)
> > at com.google.appengine.api.datastore.DatastoreServiceImpl.put
> > (Datastore
> > ServiceImpl.java:137)
> > at com.google.appengine.api.datastore.DatastoreServiceImpl.put
> > (Datastore
> > ServiceImpl.java:133)
> > at
> > org.datanucleus.store.appengine.RuntimeExceptionWrappingDatastoreServ
> > ice.put(RuntimeExceptionWrappingDatastoreService.java:104)
> > at
> > org.datanucleus.store.appengine.DatastorePersistenceHandler.put(Datas
> > torePersistenceHandler.java:125)
> > at
> > org.datanucleus.store.appengine.DatastorePersistenceHandler.put(Datas
> > torePersistenceHandler.java:94)
> > at
> > org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObj
> > ect(DatastorePersistenceHandler.java:195)
> > at
> > org.datanucleus.state.JDOStateManagerImpl.internalMakePersistent(JDOS
> > tateManagerImpl.java:3067)
> > at org.datanucleus.state.JDOStateManagerImpl.flush
> > (JDOStateManagerImpl.j
> > ava:4395)
> > at org.datanucleus.ObjectManagerImpl.flushInternal
> > (ObjectManagerImpl.jav
> > a:2807)
> > at org.datanucleus.ObjectManagerImpl.flush
> > (ObjectManagerImpl.java:2747)
> > at org.datanucleus.ObjectManagerImpl.preCommit
> > (ObjectManagerImpl.java:28
> > 86)
> > at org.datanucleus.TransactionImpl.internalPreCommit
> > (TransactionImpl.jav
> > a:348)
> > at org.datanucleus.TransactionImpl.commit(TransactionImpl.java:
> > 235)
> > at org.datanucleus.jpa.EntityTransactionImpl.commit
> > (EntityTransactionImp
> > l.java:104)
On Mon, Apr 27, 2009 at 5:28 PM, tha <hathanht...@gmail.com> wrote:
> Thanks Max. You are correct. The code worked well when I removed the
> transaction from my Spring context file. But I don't think my code is
> incorrect just because it is inserting many objects in one
> transaction. It will work with other JPA providers. So GAE's JPA
> provider is not compliant with JPA spec, how can we say GAE supports
> JPA 1.0?
Due to the nature of the datastore there are certain aspects of JPA (and
JDO) that it's unlikely we will be able to support. Global transactions are
one such aspect. Now, there's nothing stopping you from configuring a
datasource that effectively turns the transaction management declarations
into no-ops:
(The example is JDO but it applies to JPA as well)
This will let you create whatever you want inside a Spring transaction,
although your inserts are not going to succeed or fail atomically and your
code will need to account for that. If portability is your primary concern
then this approach may make sense for you, although I don't believe there's
anything non-portable about creating one entity per transaction - it's just
an atypical idiom.
> Now without transaction, I have another issue in ANOTHER method. Let
> me explain the situation in details .
> My application is using Spring-JPA. In one method I want to remove
> some entities from datastore using a removeAll() method. I implemented
> it like this:
> MyDAO class:
> public void removeAll(Set<String> codes) {
> Query q = entityManager.createQuery("select o from " +
> getPersistentClass().getName());
> List<Clazz> found = q.getResultList();
> found.size(); //workaround for GAE bug !!!!
> for (Clazz c : found) {
> if (classCodes.contains(c.getCode())) {
> entityManager.remove(c);
> }
> }
> }
> This code looks ugly because I cannot use DELETE FROM .. WHERE ..
> IN .. query to remove entities. GAE said something about IN operator
> is not implemented. So I have to query all entities then remove the
> some entities. This method used to work with Spring declarative
> transaction. Now without transaction, it stops working because the
> found.size() call throws some error about closed entity manager (it
> should be in the same transaction with q.getResultList()).
In the above code I don't see the entity manager being closed so it's hard
for me to say exactly what is going on, but I can tell you that the entity
manager needs to be open in order for found.size() to succeed.
> It looks like that to write a GAE application, my code has to be
> modified a lot: to use transaction manually, to workaround for GAE
> limitations, etc. In some cases, I will use transactions, and in other
> cases, I will not to. It is so confusing, and the code doesn't look
> like "typical Spring-JPA" code at all. I believe that declarative
> transaction is the most used way nowadays. Why do I have to use manual
> transaction? :(
Again, if portability is your primary goal then the config change I
suggested above may be your best option. The datastore is not a drop-in
replacement for a relational database, and if our docs have given you that
impression then that's a serious error on our part and one that we'd be very
anxious to fix. Is there something specific that led you to this
conclusion?
> I guess I will not be able to implement a "typical Spring application"
> which can run on Tomcat and GAE. I might have to switch back to work
> on Tomcat only.
> Regards,
> Thai
> On Apr 28, 1:49 am, Max Ross <maxr+appeng...@google.com<maxr%2Bappeng...@google.com>>
> wrote:
> > This is the expected behavior. I'd recommend readinghttp://
> code.google.com/appengine/docs/java/datastore/transactions.htmlif
> > you haven't already since it explains the issue pretty well, but in
> short,
> > you can only save multiple objects in a single transaction if they belong
> to
> > the same entity group. You can't save multiple objects in a single
> > transaction if they are all root entities, and I believe that's what
> you're
> > trying to do here. You'll need to save each entity in a separate
> > transaction or just not use transactions in this specific use case.
> > Max
> > On Mon, Apr 27, 2009 at 1:58 AM, tha <hathanht...@gmail.com> wrote:
> > > Hi all,
> > > I'm receiving below exception when I try to save more than one entity
> > > (of "Clazz" type) in one transaction using JPA. I sthis a bug or it is
> > > by design? How to save more than one entities in one transaction? Is
> > > there any example for this case? Thank!
> > > Caused by: java.lang.IllegalArgumentException: can't operate on
> > > multiple entity
> > > groups in a single transaction. found both Element {
> > > type: "Clazz"
> > > id: 16
> > > }
> > > and Element {
> > > type: "Clazz"
> > > id: 17
> > > }