2015-07-08 4 views
0

Я вынужден задать свой вопрос на этом форуме, потратив около 8 часов, поиск по всему Интернету (включая SO), безрезультатно. Мой вопрос связан с JPA @Version field doesn't get incremented, но есть некоторые отличия.JPA 2.0 колонка @Version не получает приращение

Моя настройка: Java, JPA 2.0, DataNucleus 3.1.3, SDK приложения Google App Engine 1.9.17.

У меня есть модели данных, пользователь, который имеет поле с аннотацией @version следующим образом:

import javax.persistence.Table; 
import javax.persistence.Version; 
import org.datanucleus.api.jpa.annotations.Extension; 

@Entity(name="User") 
@Table(name="User") 
public class  User 
    implements Serializable { 

    private static final long serialVersionUID = -6706180854431454626L; 

    public User() { 
    } 

    @Id 
    @Column (name="USER_IDEN_PK",nullable=false) 
    .... 
    .... 
    @Basic (fetch = FetchType.EAGER) 
    @Column (name="USER_LAST_LOGOUT_TIMESTAMP",nullable=false) 
    private Long userLastLogoutTimestamp = null; 

    @Version 
    private Integer version; 

    public void setUserLastLogoutTimestamp(Long userLastLogoutTimestamp) { 
     this.userLastLogoutTimestamp = userLastLogoutTimestamp; 
    } 

    public Integer getVersion() { 
     return version; 
    } 

    public void setVersion(Integer version) { 
     this.version = version; 
    } 

Ниже приведен код, в котором объект пользователя обновляется. Параметр paramUser - это обновленный пользовательский объект, который должен сохраняться в хранилище данных с высокой репликацией Google App Engine.

public synchronized User updateUser (User paramUser) 
          throws Exception { 
     User existingUser = null; 

     existingUser = getUserByUserIden(paramUser.getUserIden()); 
     logger.info("exst ver = " + existingUser.getVersion().intValue()); 
     logger.info("param ver = " + paramUser.getVersion().intValue()); 
     if(null != existingUser) { 
      modifiedUser = tu.getEM().merge(paramUser); 
      tu.getEM().flush(); 
     } 
     logger.info("new ver = " + modifiedUser.getVersion().intValue()); 
     return modifiedUser; 
    } 

Вот что происходит:

  • Первоначально поле «версия» имеет значение = 1. объект пользователя считывается из базы данных и отображаются на экране.
  • Некоторые атрибуты настоящего Пользовательского объекта изменены.
  • Попытка сохранить этот объект .
  • «paramUser» имеет все немодифицированные, а также измененные поля .
  • Код сначала «нахожу» сущность, и, согласно моему ожиданию , я получаю «1», напечатанный против «exst ver».
  • Затем код печатает «1» против «param ver», что тоже соответствует моим ожиданиям, потому что я НЕ должен изменять это значение.
  • Код, затем «слияние» с объектом «paramUser». Этот метод возвращает обновленный объект пользователя, который я сохраняю в «modifiedUser».
  • Наконец, я очищаю диспетчер сущностей.

Вот вопрос: против «новой веры» я все еще получаю «1», тогда как я ожидал, что получу «2». Даже в базовом хранилище данных GAE значение «версии» остается «1».

Что мне нужно сделать, чтобы сделать хранилище данных JPA/DataNucleus/GAE/High-Replication увеличивающим поле «версия» на 1, когда я называю API «слияния»?

Любая помощь/идеи/предложения будут высоко оценены.

Вот лог:

2015-07-08 21:57:33.147 

com.applix.imedipro.serverapp.controller.UserController update: paramJSONUser = {"userIden":"someUserIden","userPassword":"someUserPassword","userName":"someUserName","userMobile":2222222222,"userStreet":"someUserStreet","userArea":"someUserArea","userTown":"someUserTown","userDistrict":"someUserDistrict","userState":"someUserState","userCountry":"someUserCountry","userListOfRoleNames":["SuperUser"],"userLastLoginTimestamp":1436372833064,"userLastLogoutTimestamp":0,"version":1} 

I 2015-07-08 21:57:33.147 

com.applix.imedipro.serverapp.dao.DaoUser <init>: This object is  [email protected] 

D 2015-07-08 21:57:33.148 

org.datanucleus.ObjectManagerImpl initialiseLevel1Cache: Level 1 Cache of type "soft" initialised 

D 2015-07-08 21:57:33.148 

org.datanucleus.ObjectManagerImpl <init>: Object Manager "[email protected]" opened for datastore "[email protected]" with txn="[email protected]" 

D 2015-07-08 21:57:33.148 

org.datanucleus.transaction.Transaction <init>: Transaction created [DataNucleus Transaction, ID=Xid= 

D 2015-07-08 21:57:33.148 

org.datanucleus.TransactionImpl internalBegin: Transaction begun for ObjectManager [email protected] (optimistic=true) 

D 2015-07-08 21:57:33.148 

com.google.appengine.datanucleus.DatastoreConnectionFactoryImpl$DatastoreManagedConnection <init>: Created ManagedConnection using DatastoreService = [email protected] 

D 2015-07-08 21:57:33.148 

org.datanucleus.transaction.Transaction enlistResource: Running enlist operation on resource: [email protected], error code TMNOFLAGS and transaction: [DataNucleus Transaction, ID=Xid= 

D 2015-07-08 21:57:33.151 

com.google.appengine.datanucleus.DatastoreXAResource start: Started datastore transaction: 4261602282148180867 

D 2015-07-08 21:57:33.152 

org.datanucleus.store.connection.ConnectionManagerImpl allocateConnection: Connection added to the pool : com.google.appengine.datanu[email protected]4445f6 for [email protected] in factory=ConnectionFactory:tx[[email protected]842f22] 

D 2015-07-08 21:57:33.157 

org.datanucleus.ObjectManagerImpl getObjectFromLevel1Cache: Object with id "com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM" not found in Level 1 cache [cache size = 0] 

D 2015-07-08 21:57:33.157 

org.datanucleus.ObjectManagerImpl putObjectIntoLevel1Cache: Object "[email protected]" (id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") added to Level 1 cache (loadedFlags="[NNNNYNNNNNNNNNN]") 

D 2015-07-08 21:57:33.158 

org.datanucleus.state.JDOStateManager wrapSCOField: Object "[email protected]" (id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") is having the value in field "userListOfRoleNames" replaced by a SCO wrapper 

D 2015-07-08 21:57:33.158 

org.datanucleus.store.types.sco.simple.ArrayList initialise: Created SCO wrapper for object "[email protected]" field "userListOfRoleNames" with 1 entries, using options="cached,allowNulls" 

D 2015-07-08 21:57:33.158 

org.datanucleus.ObjectManagerImpl enlistInTransaction: Object "[email protected]" (id="ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") enlisted in transactional cache 

D 2015-07-08 21:57:33.158 

org.datanucleus.ObjectManagerImpl getObjectFromLevel2Cache: Object with id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM" taken from Level 2 cache (fields="[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]", version="") - represented as "[email protected]" 

D 2015-07-08 21:57:33.158 

org.datanucleus.state.LifeCycleState changeState: Object "[email protected]" (id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") has a lifecycle change : "P_CLEAN"->"P_NONTRANS" 

D 2015-07-08 21:57:33.158 

org.datanucleus.ObjectManagerImpl evictFromTransaction: Object "[email protected]" (id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") being evicted from transactional cache 

D 2015-07-08 21:57:33.159 

org.datanucleus.ObjectManagerImpl removeObjectFromLevel2Cache: Object with id="ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM" removed from Level 2 cache 

D 2015-07-08 21:57:33.159 

org.datanucleus.ObjectManagerImpl persistObjectInternal: Making object persistent : "[email protected]" 

D 2015-07-08 21:57:33.159 

org.datanucleus.ObjectManagerImpl enlistInTransaction: Object "[email protected]" (id="[email protected]") enlisted in transactional cache 

D 2015-07-08 21:57:33.159 

org.datanucleus.state.JDOStateManager makePersistent: Object "[email protected]" has been marked for persistence but its actual persistence to the datastore will be delayed due to use of optimistic transactions or "delayDatastoreOperationsUntilCommit" 

D 2015-07-08 21:57:33.159 

org.datanucleus.ObjectManagerImpl putObjectIntoLevel1Cache: Object "[email protected]" (id="[email protected]") added to Level 1 cache (loadedFlags="[YYYYYYYYYYYYYYY]") 

D 2015-07-08 21:57:33.159 

org.datanucleus.ObjectManagerImpl flushInternalWithOrdering: ObjectManager.internalFlush() process started using ordered flush - 1 dirty objects 

I 2015-07-08 21:57:33.159 

com.applix.imedipro.serverapp.dao.DaoUser updateUser: existing version = 1 

I 2015-07-08 21:57:33.159 

com.applix.imedipro.serverapp.dao.DaoUser updateUser: param version = 1 

D 2015-07-08 21:57:33.160 

org.datanucleus.store.connection.ConnectionManagerImpl allocateConnection: Connection found in the pool : com.google.appengine.datanu[email protected]4445f6 for [email protected] in factory=ConnectionFactory:tx[[email protected]842f22] 

D 2015-07-08 21:57:33.160 

org.datanucleus.store.connection.ConnectionManagerImpl allocateConnection: Connection found in the pool : com.google.appengine.datanu[email protected]4445f6 for [email protected] in factory=ConnectionFactory:tx[[email protected]842f22] 

D 2015-07-08 21:57:33.160 

org.datanucleus.store.connection.ConnectionManagerImpl allocateConnection: Connection found in the pool : com.google.appengine.datanu[email protected]4445f6 for [email protected] in factory=ConnectionFactory:tx[[email protected]842f22] 

D 2015-07-08 21:57:33.160 

org.datanucleus.store.connection.ConnectionManagerImpl allocateConnection: Connection found in the pool : com.google.appengine.datanu[email protected]4445f6 for [email protected] in factory=ConnectionFactory:tx[[email protected]842f22] 

D 2015-07-08 21:57:33.160 

org.datanucleus.store.connection.ConnectionManagerImpl allocateConnection: Connection found in the pool : com.google.appengine.datanu[email protected]4445f6 for [email protected] in factory=ConnectionFactory:tx[[email protected]842f22] 

D 2015-07-08 21:57:33.160 

com.google.appengine.datanucleus.EntityUtils putEntitiesIntoDatastore: Putting entity of kind User with key User("someUserIden") as {USER_TOWN[someUserTown], USER_AREA[someUserArea], USER_DISTRICT[someUserDistrict], VERSION[1], USER_Street[someUserStreet], USER_NAME[someUserName], USER_MOBILE[2222222222], userListOfRoleNames[[SuperUser]], USER_LAST_LOGOUT_TIMESTAMP[0], USER_COUNTRY[someUserCountry], USER_STATE[someUserState], USER_PASSWORD[someUserPassword], USER_LAST_LOGIN_TIMESTAMP[1436372833064], } 

D 2015-07-08 21:57:33.164 

org.datanucleus.ObjectManagerImpl replaceObjectId: Object "[email protected]" (id="org.datanucleus.identity.Iden[email protected]") being changed to be referenced by id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM" in Level 1 cache 

D 2015-07-08 21:57:33.164 

org.datanucleus.ObjectManagerImpl replaceObjectId: Object "[email protected]" (id="[email protected]") enlisted in transactional cache is now enlisted using id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM" 

D 2015-07-08 21:57:33.164 

org.datanucleus.store.connection.ConnectionManagerImpl allocateConnection: Connection found in the pool : com.google.appengine.datanu[email protected]4445f6 for [email protected] in factory=ConnectionFactory:tx[[email protected]842f22] 

D 2015-07-08 21:57:33.164 

org.datanucleus.state.JDOStateManager wrapSCOField: Object "[email protected]" (id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") is having the value in field "userListOfRoleNames" replaced by a SCO wrapper 

D 2015-07-08 21:57:33.164 

org.datanucleus.store.types.sco.simple.ArrayList initialise: Created SCO wrapper for object "[email protected]" field "userListOfRoleNames" with 1 entries, using options="cached,allowNulls" 

D 2015-07-08 21:57:33.164 

org.datanucleus.ObjectManagerImpl flushInternal: ObjectManager.internalFlush() process finished 

I 2015-07-08 21:57:33.164 

com.applix.imedipro.serverapp.dao.DaoUser updateUser: new version = 1 

I 2015-07-08 21:57:33.164 

com.applix.transactions.TranxUtility postTransaction: EM is open 

D 2015-07-08 21:57:33.165 

org.datanucleus.TransactionImpl internalPreCommit: Transaction committing for ObjectManager [email protected] 

D 2015-07-08 21:57:33.165 

org.datanucleus.ObjectManagerImpl flushInternalWithOrdering: ObjectManager.internalFlush() process started using ordered flush - 0 dirty objects 

D 2015-07-08 21:57:33.165 

org.datanucleus.ObjectManagerImpl flushInternal: ObjectManager.internalFlush() process finished 

D 2015-07-08 21:57:33.165 

org.datanucleus.ObjectManagerImpl putObjectsIntoLevel2Cache: Object "[email protected]" (id="ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") added to Level 2 cache (fields="[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]", version="1") 

D 2015-07-08 21:57:33.165 

org.datanucleus.transaction.Transaction commit: Committing [DataNucleus Transaction, ID=Xid= 

I 2015-07-08 21:57:33.165 

com.applix.transactions.TranxUtility postTransaction: transaction is active 

I 2015-07-08 21:57:33.165 

com.applix.transactions.TranxUtility postTransaction: About to start committing the transaction 

D 2015-07-08 21:57:33.190 

com.google.appengine.datanucleus.DatastoreXAResource commit: Committed datastore transaction: 4261602282148180867 

D 2015-07-08 21:57:33.191 

org.datanucleus.store.connection.ConnectionManagerImpl$1 managedConnectionPostClose: Connection removed from the pool : com.google.appengine.datanu[email protected]4445f6 for [email protected] in factory=ConnectionFactory:tx[[email protected]842f22] 

D 2015-07-08 21:57:33.191 

org.datanucleus.state.LifeCycleState changeState: Object "[email protected]" (id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") has a lifecycle change : "P_NEW"->"P_NONTRANS" 

D 2015-07-08 21:57:33.191 

org.datanucleus.ObjectManagerImpl evictFromTransaction: Object "[email protected]" (id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") being evicted from transactional cache 

D 2015-07-08 21:57:33.191 

org.datanucleus.TransactionImpl commit: Transaction committed in 26 ms 

D 2015-07-08 21:57:33.191 

org.datanucleus.state.JDOStateManager detach: Detaching object from persistence : "[email protected]" (depth=0) 

I 2015-07-08 21:57:33.191 

com.applix.transactions.TranxUtility postTransaction: Transaction committed 

D 2015-07-08 21:57:33.192 

org.datanucleus.state.JDOStateManager unwrapSCOField: Object "[email protected]" (id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") is having the SCO wrapper in field "userListOfRoleNames" replaced by the unwrapped value 

D 2015-07-08 21:57:33.192 

org.datanucleus.state.LifeCycleState changeState: Object "[email protected]" (id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM") has a lifecycle change : "P_NONTRANS"->"DETACHED_CLEAN" 

D 2015-07-08 21:57:33.192 

org.datanucleus.state.JDOStateManager disconnect: Disconnecting [email protected] from StateManager[[email protected], lifecycle=DETACHED_CLEAN] 

D 2015-07-08 21:57:33.192 

org.datanucleus.ObjectManagerImpl removeObjectFromLevel1Cache: Object with id="com.applix.imedipro.entity.User:ahJzfmF0LWltZWRpcHJvLWJldGFyFwsSBFVzZXIiDW5pbWVzaF9wYXJtYXIM" being removed from Level 1 cache [current cache size = 1] 

D 2015-07-08 21:57:33.192 

org.datanucleus.ObjectManagerImpl disconnectObjectProvidersFromCache: Level 1 Cache cleared 

D 2015-07-08 21:57:33.192 

org.datanucleus.ObjectManagerImpl close: Object Manager "[email protected]" closed 

I 2015-07-08 21:57:33.192 

com.applix.transactions.TranxUtility postTransaction: Entity Manager cleared 
+1

и журнал говорит? –

+0

новый журнал с .level = ALL добавлен. Извините за форматирование. –

+0

Журнал обрезается по ширине столбца. Также укажите WHERE в этом журнале - это вызванное слияние, потому что вы можете легко выгрузить оператор в журнал непосредственно перед слиянием и, следовательно, посмотреть, какие утверждения относятся к вашему коду персистентности. И посмотрите состояние объекта, в котором вы проходите (который также будет регистрироваться). Возможно, он не отсоединен ... –

ответ

0

Наконец, после 12 часов, нашел, как ответ.

Вот код, который DID работы:

public synchronized User updateUser (User paramUser) 
         throws Exception { 
    User user = null; 

    user = getUserByUserIden(paramUser.getUserIden()); 
    user.setUserPassword(paramUser.getUserPassword()); 
    user.setUserName(paramUser.getUserName()); 
    user.setUserMobile(paramUser.getUserMobile()); 
    user.setUserStreet(paramUser.getUserStreet()); 
    user.setUserArea(paramUser.getUserArea()); 
    user.setUserTown(paramUser.getUserTown()); 
    user.setUserDistrict(paramUser.getUserDistrict()); 
    user.setUserState(paramUser.getUserState()); 
    user.setUserCountry(paramUser.getUserCountry()); 
    user.setUserListOfRoleNames(paramUser.getUserListOfRoleNames()); 
    user.setUserLastLoginTimestamp(paramUser.getUserLastLoginTimestamp()); 
    user.setUserLastLogoutTimestamp(paramUser.getUserLastLogoutTimestamp()); 

    user = tu.getEM().merge(user); 

    return user; 
} 

Другими словами, Ниже приводится последовательность, которая работает:

  • Читать «Пользователь» объект в «пользователь», основанный на ключ значение, переданное «paramUser»
  • Скопировать все поля из «paramUser» в «пользователь»
  • объединения «пользователь»
+0

или, в качестве альтернативы, отсоединить объект и передать его другим частям вашего приложения, обновив поля по мере их перехода, а затем передать их на уровень сохранения и вызвать em.merge. –