-   Programming (
-   -   Ebean: why is Ebean trying to do an update by doing where with all fields? (

eantoranz 08-29-2012 06:03 PM

Ebean: why is Ebean trying to do an update by doing where with all fields?

I have this entity (associated with Ebean) that I want to save after having performed some changes on some of the fields.

I get a javax.persistence.OptimisticLockException: MissingResource exception in DmlHandler.checkRowCount().

Now, I just noticed that in the update query, ebean is using all fields of the entity in the "where" clause instead of using the field I had marked as @Id in the entity. Why is that?


@Column(name = FIELD_ID)
private int id;

I suppose that it's checking that a row was actually modified and if it's using all the new values in the where clause, the fields that I changed won't match and so there's a Missing Resource, of course.

Thanks in advance.

eantoranz 08-30-2012 12:00 PM

I've been following the code and I found something about Concurrency Mode in UpdateMeta.getDynamicUpdatePlan(ConcurrencyMode, PersistRequestBean<?>).


private SpiUpdatePlan getDynamicUpdatePlan(ConcurrencyMode mode, PersistRequestBean<?> persistRequest) {

        Set<String> updatedProps = persistRequest.getUpdatedProperties();
        if (ConcurrencyMode.ALL.equals(mode)){
                // due to is null in where clause we won't bother trying to
                // cache plans for ConcurrencyMode.ALL
                String sql = genSql(mode, persistRequest, null);
                if (sql == null){
                    // changed properties must have been updatable=false
                    return UpdatePlan.EMPTY_SET_CLAUSE;
                } else {
                    return new UpdatePlan(null, mode, sql, set, updatedProps);
        // we can use a cached UpdatePlan for the changed properties
        int hash =  mode.hashCode();
        hash = hash * 31 + (updatedProps == null ? 0 : updatedProps.hashCode());
        Integer key = Integer.valueOf(hash);
        BeanDescriptor<?> beanDescriptor = persistRequest.getBeanDescriptor();
        SpiUpdatePlan updatePlan = beanDescriptor.getUpdatePlan(key);
        if (updatePlan != null){
                return updatePlan;
        // build a new UpdatePlan and cache it
        // build a bindableList that only contains the changed properties
        List<Bindable> list = new ArrayList<Bindable>();
        set.addChanged(persistRequest, list);
        BindableList bindableList = new BindableList(list);
        // build the SQL for this update statement
        String sql = genSql(mode, persistRequest, bindableList);
        updatePlan = new UpdatePlan(key, mode, sql, bindableList, null);
        // add the UpdatePlan to the cache
        beanDescriptor.putUpdatePlan(key, updatePlan);
        return updatePlan;

ConcurrencyMode has been set to "ALL" and I think that's why it's using all the fields. Now, the mode has been determined by PersistRequestBean.determineConcurrencyMode().


 * Determine the concurrency mode depending on fully/partially populated
 * bean.
 * <p>
 * Specifically with version concurrency we want to check that the version
 * property was one of the loaded properties.
 * </p>
public ConcurrencyMode determineConcurrencyMode() {
        if (loadedProps != null) {
                // 'partial bean' update/delete...
                if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
                        // check the version property was loaded
                        BeanProperty prop = beanDescriptor.firstVersionProperty();
                        if (prop != null && loadedProps.contains(prop.getName())) {
                                // OK to use version property
                        } else {
                                concurrencyMode = ConcurrencyMode.ALL;
        return concurrencyMode;

Is there something I can do to change the ConcurrencyMode?

PS I don't know if this is related but I have done a getX from the entity so shouldn't that property be saved in loadedProps at least?

eantoranz 08-30-2012 01:25 PM

Seems like concurrencyMode is the one to blame here.

Here's this code from UpdateMeta.genSql(ConcurrencyMode, PersistRequestBean<?>, BindableList):


if (ConcurrencyMode.VERSION.equals(conMode)) {
        if (version == null){
                return null;
        version.dmlAppend(request, false);
} else if (ConcurrencyMode.ALL.equals(conMode)) {
        all.dmlWhere(request, true, request.getOldValues());

first equals() call is false, then second equals() is true. I think I have to hack the concurrency mode. How is that done? ANd hasn't it bothered me before? Weird.

eantoranz 08-30-2012 02:33 PM

Adding a @Version field to the entity seems to have solved the whole thing.

All times are GMT -5. The time now is 09:56 AM.