IMO using attached entities in RF is dangerous because in hibernate any 
committed transaction will persist any previous change in the current 
context. So, if you are using per-request entityManager, whenever you call 
transaction.commit you will persist any modification sent from the client 
(which has been set to an attached entity). And, because all our entities 
were detached after the validation phase, we did use the default optimistic 
lock (no forceLocked). 

On Tuesday, June 9, 2015 at 9:34:19 PM UTC+2, Anton Mityagin wrote:
>
>
> Thanks for your comment
>
> On Tuesday, June 9, 2015 at 10:12:10 PM UTC+3, Ignacio Baca Moreno-Torres 
> wrote:
>>
>> We made a similar approach using the client/server version variables. But 
>> we call it version/expectedVersion.
>>
>> As explained in stackoverflow 
>> <http://www.google.com/url?q=http%3A%2F%2Fstackoverflow.com%2Fquestions%2F7696764%2Fdoes-gwt-requestfactory-support-implementation-of-optimistic-concurrency-control%2F7697307%237697307&sa=D&sntz=1&usg=AFQjCNEjQwYbtnsepzEPUD0X1zeqCT3jAg>
>>  RF 
>> only sent changes, this forces to have an always-null expectedVersion and 
>> manually copy the getVersion to setExpectedVersion to enable optimistic 
>> locking. BUT! it's critical that the entity be detached when you set the 
>> version (or you detach and merge) because if you set the version on an 
>> attached entity the version is not checked (at least in hibernate).
>>
>> Yes, I know about detached entity and setting version to it, in the end 
> of my post I pointed this thing, and I pointed how to set version to 
> attached entity (managed by hibernate).
>  
>
>> OTOH, RF only sent changes to the server so optimistic locking is not 
>> required most of the time. Just for curiosity, why you need optimistic 
>> locking?
>>
>
> Because there is a possibility conflict while saving data and users dont 
> want to lose their data or overrite someone else's data.
>
>>
>> Usually only some operation requires fully transactional/optimistic 
>> locking and in our experience, this operations are better handled using 
>> some kind of command pattern. I.e. sent how-to-update instead of 
>> what-to-set. We used the optimistic locking approach initially because we 
>> try to use RF/Entities/Proxies for everything, but now we use commands for 
>> this situations and versions are not required anymore. Moreover, RF has a 
>> problem when you try to repeat an operation (which is a good idea in 
>> optimistic locking), because if the server side fails in the operations 
>> phase (after validation phase) the client entity gets invalidated, and you 
>> can't sent the entity again.
>>
>> Yes, I know about this problem.
> I have workaround it.
> Now I test it and I you wish I'll post it here
>
> In short, I send to server clone of proxy and always create a new request 
> context which will be fired.
> 1. get immutable proxy A1 from DB
> 2. driver.display(A1)
> 3. user clicks save button
> 4. make a clone of A1 - A2
> 5. driver.edit(A2, requestContext)
> 6. driver.flush()
> 7. validate(A2) on client
> 8. requestContext.getRequest().save(A2).fire()
> 9. in case of exception or server validation failure driver.display(A1)
> 10. user may fix a problem and try save proxy again
>
>>
>>
>>
>>  
>>
>> On Monday, June 8, 2015 at 11:11:41 PM UTC+2, Anton Mityagin wrote:
>>>
>>>
>>> As all you know RequestFactory does not support optimistic locking.
>>>
>>> see details 
>>> https://code.google.com/p/google-web-toolkit/issues/detail?id=6046
>>>
>>> I tried to implement it для RequestFactory + Spring + JPA + Hibernate
>>>
>>> I took as a basis the idea proposed by Thomas (
>>> http://stackoverflow.com/questions/7696764/does-gwt-requestfactory-support-implementation-of-optimistic-concurrency-control/7697307#7697307
>>> ).
>>>
>>> As he wrote his idea - pure theory.
>>>
>>> @ProxyFor(MyEntity.class)
>>> interface MyEntityProxy extends EntityProxy {
>>>    String getServerVersion();
>>>    String getClientVersion();
>>>    void setClientVersion(String clientVersion);
>>>    …
>>> }
>>>
>>> @Entity
>>> class MyEntity {
>>>    private String clientVersion;
>>>    @Version private String serverVersion;
>>>
>>>    public String getServerVersion() { return serverVersion; }
>>>    public String getClientVersion() { return null; }
>>>    public void setClientVersion(String clientVersion) {
>>>       this.clientVersion = clientVersion;
>>>    }
>>>    
>>>    public void patchVersion() {
>>>       serverVersion = clientVersion;
>>>    }
>>>
>>>    public void shouldPatchVersion() {
>>>       return Objects.equal(serverVersion, clientVersion);
>>>    }
>>> }
>>>
>>> On the server-side we need to use somthing like this to edit 
>>> MyEntityProxy 
>>> public <P extends BaseProxy> P edit(P proxy, RequestContext request)
>>> {
>>>   P mutableProxy = request.edit(proxy);
>>>   
>>>   if (mutableProxy instanceof MyEntityProxy)
>>>   {
>>>     MyEntityProxy myProxy = (MyEntityProxy)mutableProxy;
>>>     myProxy.setClientVersion(myProxy.getServerVersion());
>>>   }
>>> }
>>>
>>> On the server-side we need to handle case when clientVersion not equals 
>>> to serverVersion.
>>>
>>> I think that if we use EntityManager, we do not have to manually throw 
>>> an exception when the versions are not equal.
>>> It has to do EntityManager when it tries to save domain object in the 
>>> database. Otherwise a situation may arise when 
>>> an object has been checked, but has not been saved to the database, and 
>>> someone else has save the same object.
>>>
>>> I think a good place to make check and patch server version right before 
>>> validating the domain object.
>>> ServiceLayerDecorator
>>> public <T> Set<ConstraintViolation<T>> validate(T domainObject)
>>>
>>> Unfortunately, not enough simply call MyEntity.patchVersion()
>>> JPA provider uses internal structures to keep current version of maanged 
>>> object.
>>> And value of serverVersion does not actually play any role.
>>>
>>> We need to use specific JPA provider's API to change a version of 
>>> managed object.
>>>
>>> In my case it's Hibernate:
>>>
>>> @Override
>>> public <T> Set<ConstraintViolation<T>> validate(T domainObject)
>>> {
>>>   
>>>   if (domainObject != null && domainObject instanceof HasVersion<?>)
>>>   {
>>>     MyEntity version = (MyEntity)domainObject;
>>>     
>>>     if (!version.*shouldPatchVersion*())
>>>     {
>>>       ApplicationContext context = 
>>> ApplicationContextHolderLocator.getHolder().getApplicationContext();
>>>       EntityManager entityManager = context.getBean(EntityManager.class);
>>>       
>>>       if (entityManager.getDelegate() instanceof SessionImplementor)
>>>           {
>>>             SessionImplementor sess = 
>>> (SessionImplementor)entityManager.getDelegate();
>>>             EntityEntry entry = 
>>> sess.getPersistenceContext().getEntry(domainObject);
>>>             if (entry != null)
>>>             {
>>>               version.*patchVersion*();
>>>               LockMode lockMode = entry.getLockMode();
>>>               *entry.forceLocked(domainObject, 
>>> version.getServerVersion());*
>>>               entry.setLockMode(lockMode);
>>>             }
>>>           }
>>>     }
>>>   }
>>>   
>>>   return super.validate(domainObject);
>>> }
>>>
>>> after this manipulation Hibernate will throws StaleObjectStateException 
>>> as expected if client has edited old version of domain object.
>>>
>>>
>>> There is another way to patch version in domain object:
>>>
>>> in the method T find(Class<? extends T> clazz, I id); of entities 
>>> locator detach from persistent context the found domain object
>>> in the method validate(T domainObject) call method *patchVersion*() and 
>>> try to attach domain object to the persistent context by calling
>>> entityManger.merge.
>>> No hibernate dependency, but one more database hit while merging object 
>>> and exceptions may be thrown.
>>>
>>>
>>> Please express an opinion on this implementation.
>>>
>>> What are the disadvantages?
>>>
>>>

-- 
You received this message because you are subscribed to the Google Groups 
"Google Web Toolkit" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.

Reply via email to