JPA на Google App Engine: всегда ли это происходит в транзакции при вызове persist(...)?

В моем заявлении я сохраняю сущность, у которой есть несколько детей, которым не принадлежит.

Кажется, что призыв к persist что делает сохранение сущности и всех дочерних элементов происходит внутри транзакции, потому что я получаю сообщение об ошибке, когда не включаю межгрупповые транзакции (и дочерние элементы живут в разных группах сущностей в качестве родительской сущности).

Возможно ли, чтобы сохранение продолжалось без транзакций?

(В случае, если эта информация необходима: я использую Guice для ввода области запроса Provider<EntityManager> в мой сервисный объект.)

ДОБАВЛЕНО:

Вот простой тестовый пример:

@Entity public class Department {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; 

    @OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
    @Unowned
    Collection<Employee> employees = new ArrayList<Employee>();

    public Collection<Employee> getEmployees() {
        return employees;
    }

}

@Entity public class Employee {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;        @ManyToOne  private Department department;      private String name;

    public Employee(String name) {      this.name = name;   }       public String getName() {       return name;    }

    public void setName(String name) {      this.name = name;   }    }

Теперь, когда я делаю:

EntityManager em = EMF.get().createEntityManager();
try {
    Department department = new Department();
    department.getEmployees().add(new Employee("Joe"));
    em.persist(department);
} finally {
    em.close();
}

Я получил:

Illegal argument

Вызванный:

javax.persistence.PersistenceException: недопустимый аргумент в org.datanucleus.api.jpa.NucleusJPAHelper.getJPAExceptionForNucleusException(NucleusJPAHelper.java:298) в org.datanucleus.api.jpa.Jan.janity.JAntE.jpa.JpaServlet.doGet(JpaServlet.java:23) в javax.servlet.http.HttpServlet.service(HttpServlet.java:617) в javax.servlet.http.HttpServlet.service (HttpServlet.java) jj.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) в org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) в com.google.appengine.api.socket DevSocketFilter. 123) в org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) в com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34) по адресу org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) по адресу com.google.appengine.api.FilterFloterblobs ServeBlobFilter.java:61) в org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) в com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter: TransilClang.mortbay.jetty.servlet.ServletHandler $ CachedChain.doFilter (ServletHandler.java:1157) в com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125) в org.mortv.let.j. $ CachedChain.doFilter (ServletHandler.java:1157) в com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97) в org.mortbay.jetty.servlet.ServletHandlerjF: 1157) в org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388) в org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) в org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) в org.mortbay.jetty.handler.ContextHandler Ручной org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) в com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:409) в org.mortbay.jet. HandlerWrapper.handle (HandlerWrapper.java:152) в org.mortbay.jetty.Server.handle(Server.java:326) в org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) в org.mortbay.jet.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923) в org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547) в org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212) в org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) в org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.javagort:40).thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582) Причина: java.lang.IllegalArgumentException: необходимо явно указать межгрупповую транзакцию, см. TransactionOptions.Builder.withXG обнаружил оба элемента {тип: "отдел" id: 17 } и Элемент { type: "Employee" id: 18 }

на com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:33) на com.google.appengine.api.datastore.DatastoreApiHelper$1.convertException(DatastoreApiHelper.java:70 at.api.utils.FutureWrapper.get(FutureWrapper.java:94) в com.google.appengine.api.datastore.FutureHelper$CumulativeAggregateFuture.get(FutureHelper.java:142) в com.google.appengine.api.datastore.FutureHelper$TxnAwareFuture.get(FutureHelper.java:218) на com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:86) на com.google.appengine.api.datastore.FutureHelper.getInternal(FutureHelper.java:71) на com.google.appengine.api.datastore.FutureHelper.quietGet(FutureHelper.java:32) на com.google.appengine.api.datastore.DatastoreServiceImpl.put(DatastoreServiceImpl.java:85) на com.google.appengine..datanucleus.WrappedDatastoreService.put(WrappedDatastoreService.java:112) в com.google.appengine.datanucleus.EntityUtils.putEntitiesIntoDatastore(EntityUtils.java:766) по адресу com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:314) по адресу com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistence.anDataDategMateMate.Hatler.Hatler.Ru.WD_Med_Man.WD) (JDOStateManager.java:2381) по адресу org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) по адресу org.datanucleus.store.types.sco.SCOUtils.validateObjectForWriting(SCO.U). appengine.datanucleus.scostore. internalAdd (FKListStore.java:195) в com.google.appengine.datanucleus.scostore.FKListStore.addAll (FKListStore.java:114) в org.datanucleus.store.mapped.mapping.CollectionMapping.postInsert): CollectionMapping. на com.goo gle.appengine.datanucleus.StoreFieldManager. java: 218) в org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) в org.datanucleus.state.JDOStateManager.flush (JDOStateManager.java:3anj.ImpI:3888) по адресу org.datanucleus.ObjectManagerImpl.flushInternal(ObjectManagerImpl.java:3811) по адресу org.datanucleus.ObjectManagerImpl.flush(ObjectManagerImpl.java:3751) по адресу org.datanucleager.MiPImpImpImpImImpImp4.datanucleus.ObjectManagerImpl.transactionPreCommit(ObjectManagerImpl.java:428) в org.datanucleus.TransactionImpl.internalPreCommit(TransactionImpl.java:398) в org.datanucleus.TransactionImpl.commit(TransactionImpl.java:287) в org.datanucleus.ObjectManagerImpl.close(ObjectManagerImpl.java:1090) в org.datanucleus.api.jpa.JPAEntityManager.close(JPAEntityM19ager)

Вызванный:

java.lang.IllegalArgumentException: необходимо явно указать межгрупповую транзакцию, см. TransactionOptions.Builder.withXGfound для обоих Элемент {тип: "Отдел" id: 17 } и Элемент {тип: "Сотрудник" id: 18}

на com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:33) на com.google.appengine.api.datastore.DatastoreApiHelper$1.convertException(DatastoreApiHelper.java:70 at.api.utils.FutureWrapper.get(FutureWrapper.java:94) в com.google.appengine.api.datastore.FutureHelper$CumulativeAggregateFuture.get(FutureHelper.java:142) в com.google.appengine.api.datastore.FutureHelper$TxnAwareFuture.get(FutureHelper.java:218) на com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:86) на com.google.appengine.api.datastore.FutureHelper.getInternal(FutureHelper.java:71) на com.google.appengine.api.datastore.FutureHelper.quietGet(FutureHelper.java:32) на com.google.appengine.api.datastore.DatastoreServiceImpl.put(DatastoreServiceImpl.java:85) на com.google.appengine..datanucleus.WrappedDatastoreService.put(WrappedDatastoreService.java:112) в com.google.appengine.datanucleus.EntityUtils.putEntitiesIntoDatastore(EntityUtils.java:766) по адресу com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:314) по адресу com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistence.anDataDategMateMate.Hatler.Hatler.Ru.WD_Med_Man.WD) (JDOStateManager.java:2381) по адресу org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) по адресу org.datanucleus.store.types.sco.SCOUtils.validateObjectForWriting(SCO.U). appengine.datanucleus.scostore. internalAdd (FKListStore.java:195) в com.google.appengine.datanucleus.scostore.FKListStore.addAll (FKListStore.java:114) в org.datanucleus.store.mapped.mapping.CollectionMapping.postInsert): CollectionMapping. на com.goo gle.appengine.datanucleus.StoreFieldManager. java: 218) в org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) в org.datanucleus.state.JDOStateManager.flush (JDOStateManager.java:3anj.ImpI:3888) по адресу org.datanucleus.ObjectManagerImpl.flushInternal(ObjectManagerImpl.java:3811) по адресу org.datanucleus.ObjectManagerImpl.flush(ObjectManagerImpl.java:3751) по адресу org.datanucleager.MiPImpImpImpImImpImp4.datanucleus.ObjectManagerImpl.transactionPreCommit(ObjectManagerImpl.java:428) в org.datanucleus.TransactionImpl.internalPreCommit(TransactionImpl.java:398) в org.datanucleus.TransactionImpl.commit(TransactionImpl.java:287) в org.datanucleus.ObjectManagerImpl.close(ObjectManagerImpl.java:1090) в org.datanucleus.api.jpa.JPAEntityManager.close(JPAEntityMex.jj) jj.JpaServlet.doGet (JpaServlet.java:23) в javax.servlet.http.HttpServlet.service(HttpServlet.java:617) в javax.servlet.http.HttpServlet.service(HttpServlet.java:7:7: 7: 7): 7 jetty.servlet.ServletHolder.handle (ServletHolder.java:511) в org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) в com.google.appengine.api.socket.Fil.doFilter(DevSocketFilter.java:74) по адресу org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) по адресу com.google.appengine.tools.development.ResponseRewriterFilter.seerjererererjerter (ответ) в org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) в com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(руководитель erVerificationFilter.java:34) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) в com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilb61 (ServeBlobFilter.doFilb61) по адресу org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) по адресу com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43bserv.java:43b.set.jort.jort.jort.mj.tort.mjort.jort.jort.jort.jort.jort.jort.jort.mj.ort.j..mort.jet..ServletHandler $ CachedChain.doFilter (ServletHandler.java:1157) в com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125) в org.mortbay.jetty.servlet.ServF.java: 1157) в com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97) в org.mortbay.jetty.servlet.ServletHandler $ CachedChain.doFilter (ServletHandler.j::. mortbay.jetty.servlet.ServletHandler.handle (ServletHandler.java:388) в org.mortbay.jetty.security.SecurityHandl er.handle (SecurityHandler.java:216) в org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) в org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) в организации.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) при com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94) при org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) в com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:409) в org.mortbay.jetty.handler.HandlerWrapper.handle (HandlerWrapper.15). org.mortbay.jetty.Server.handle(Server.java:326) в org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) в org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(Hortler.headerComplete: 923) на org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547) на org.mortbay.jetty.HttpParser.parseAvailable (HttpParser.java:212) на org.mo rtbay.jetty.HttpConnection.handle (HttpConnection.java:404) в org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409) в org.mortbay.thread.QueuedThreadPool$PoolThuj. 582)

Вот почему я думал, что persist происходит внутри транзакции, хотя я не начал явно.

Вот соответствующие сообщения отладки от DataNucleus:

08:38:17 587 ОТЛАДКА [DataNucleus.Persistence] - ================= Конфигурация постоянства =============== 08:38:17 587 DEBUG [DataNucleus.Persistence] - Фабрика персистентности DataNucleus - Поставщик: "DataNucleus" Версия: "3.1.3" 08:38:17 588 DEBUG [DataNucleus.Persistence] - Фабрика персистентности DataNucleus инициализирована для URL хранилища данных = "appengine" driver = " "userName =" " 08:38:17,588 DEBUG [DataNucleus.Persistence] - JDK: 1.6.0_26 в Linux 08:38:17,588 DEBUG [DataNucleus.Persistence] - API постоянства: JPA 08:38:17,588 DEBUG [DataNucleus.Persistence ] - Реестр плагинов: org.datanucleus.plugin.NonManagedPluginRegistry 08:38:17,588 DEBUG [DataNucleus.Persistence] - Блок постоянства: транзакции-необязательный 08:38:17,588 DEBUG [DataNucleus.Persistence] - Стандартные параметры: pm-singlethreaded, сохраняемые значения, нетранзакционное чтение, нетранзакционная запись, serverTimeZone=UTC 08:38:17,589 DEBUG [DataNucleus.Persistence] - Параметры сохранения: политика удаления при отсоединении при закрытии =JDO2 08:38:17,589 DEBUG [DataNucleus.Persistence] - Транзакции: type=RESOURCE_LOCAL mode= оптимистическая изоляция = фиксация для чтения 08:38:17,589 DEBUG [DataNucleus.Persistence] - Генерация значения: txn-изоляция = соединение для фиксации чтения = новое 08:38:17,589 DEBUG [DataNucleus.Persistence] - Загрузка классов: jdo 08:38:17,589 DEBUG [DataNucleus.Persistence] - Кэш: Уровень 1 (мягкий), Уровень 2 (мягкий, режим = НЕ УКАЗАННЫЙ), QueryResults (мягкий), Коллекции / Карты 08:38: 17 589 DEBUG [DataNucleus.Persistence] - ================================================================= 08:38:17,657 DEBUG [DataNucleus.Persistence] - Диспетчер объектов "org.datanucleus.ObjectManagerImpl@e4eb585" открыт для хранилища данных "com.google.appengine.datanucleus.DatastoreManager@516f3619" with txn="org.datanucleus.TransactionImpl@5c48cd13" 08:38:17,688 DEBUG [DataNucleus.Persistence] - Делаем объект постоянным:" com.example.jpa.Department@55b7 38f86 ": DEBUG [DataNucleus.Persistence] - объект "com.example.jpa.Department@55b7bf86" помечен для постоянного но его фактическое сохранение в хранилище данных будет отложено из-за использования оптимистических транзакций или "delayDatastoreOperationsUntilCommit" 08:38:17,741 DEBUG [DataNucleus.Persistence] - сохранение объекта постоянным: "com.example.jpa.Employee@95d0a50" 08:38:17,742 DEBUG [DataNucleus.Persistence] - Объект "com.example.jpa.Employee@95d0a50" был помечен для постоянного хранения, но его фактическое сохранение в хранилище данных будет отложено из-за использования оптимистических транзакций или "delayDatastoreOperationsUntilCommit" 08:38:17,747 DEBUG [DataNucleus.Transaction] - транзакция создана [DataNucleus Transaction, ID=Xid= 08:38:17,748 DEBUG [DataNucleus.Transaction] - транзакция начата для ObjectManager org.datanucleus.ObjectManagerImpl@e4eb585 (оптимистический =):17,757 DEBUG [DataNucleus.Transaction] - Выполнение операции зачисления на ресурсе: com.google.appengine.datanucleus.DatastoreXAResource@7d7082d8, код ошибки TMNOFLAGS и транзакция: [транзакция DataNucleus, ID=Xid= 08:38:18,024 DEBUG [да taNucleus.Transaction] - Запущена транзакция хранилища данных: 0 08:38:18,026 DEBUG [DataNucleus.Transaction] - Подтверждение транзакции для ObjectManager org.datanucleus.ObjectManagerImpl@e4eb585 08:38:18,027 DEBUG [DataNucleusManager.Persistence (DataNucleus.Fersistence)) процесс запущен с использованием упорядоченного сброса - 2 грязных объекта 08:38:18,105 DEBUG [DataNucleus.Persistence] - Управление постоянством класса: com.example.jpa.Employee [Таблица: com.example.jpa.Employee, InheritanceStrategy: new-table] 08:38:18,136 DEBUG [DataNucleus.Persistence] - Управление постоянством класса: com.example.jpa.Department [Таблица: com.example.jpa.Department, InheritanceStrategy: new-table] 08:38:18,316 DEBUG [DataNucleus. Постоянство] - поле "com.example.jpa.Department.employees" сохраняется для "каскадного сохранения". 08:38:18,331 ИНФОРМАЦИЯ [DataNucleus.Persistence] - У объекта "com.example.jpa.Department@55b7bf86" есть коллекция "com.example.jpa.Department.employees" все еще элемент "com.example.jpa.Employee@95d0a50 "не установлен владелец. Управление отношениями и настройка владельца. 08:38:18,333 WARN [DataNucleus.MetaData] - предупреждение о метаданных для com.example.jpa.Employee.department: ошибка в метаданных для com.example.jpa.Employee.department: хранилище данных не поддерживает объединения и поэтому не может обрабатывать запросы на размещение связанных объектов в группе извлечения по умолчанию. Поле будет извлечено лениво при первом доступе. Вы можете изменить это предупреждение, установив свойство datanucleus.appengine.ignorableMetaDataBehavior в своей конфигурации. Значение NONE отключит предупреждение. Значение ERROR превратит предупреждение в исключение. 08:38:18,354 DEBUG [DataNucleus.Persistence] - процесс ObjectManager.internalFlush() завершен 08:38:18,355 DEBUG [DataNucleus.Transaction] - недопустимый аргумент org.datanucleus.exceptions.NucleusFatalUserException: недопустимый аргумент

4 ответа

Решение

Просто столкнулся с той же проблемой и могу с уверенностью сказать, что транзакция совершена при звонке

EntityManager.close();

Вот некоторые доказательства из реализации DataNucleus JPA

Немного поздно, но я подумал, что это будет полезно для других. Я столкнулся с той же проблемой при использовании JPA с Google App Engine. Но следующий код работал для меня. Надеюсь, поможет-

try{
    EntityTransaction t = em.getTransaction();
    try{
        t.begin();
        //Do stuff
        em.persist(persistObject);
        t.commit();
    }finally {
        if (t.isActive())
            t.rollback();
    }
} finally {
    em.close();
}

Вам необходимо указать, что ваша транзакция является межгрупповой транзакцией для jpa. Прочтите ниже документ, специально кросс-групповые транзакции для jpa / jdo

https://cloud.google.com/appengine/docs/java/datastore/transactions?csw=1

для моего случая добавление ниже строки в persistance.xml решило эту проблему.

<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true"/>

Ясно из исключения

java.lang.IllegalArgumentException: cross-group transaction need to be explicitly specified, see TransactionOptions.Builder.withXGfound both Element { type: "Department" id: 17 } and Element { type: "Employee" id: 18 }

at com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:33) 

хранилище данных выполняется в (низкоуровневой) транзакции (также показанной в журнале), и для этого требуется, чтобы несколько групп сущностей были включены для выполнения того, что вы хотите там.

Другие вопросы по тегам