Issue with updating a bean

88 views
Skip to first unread message

Ole Hornischer

unread,
Jan 9, 2011, 2:09:34 PM1/9/11
to Ebean ORM
HI everyone!
I recently started using Ebean for my Swing desktop app and am very
happy with it. However I ran into a little problem with updating one
of my beans.

I want to save/update the existing bean (it has an ID and it exists in
the database, i compared right before the save command). However when
saving I receive an OptimisticLockException (see below). When
debugging to the source of the exception i see it is being thrown
cause the row count is != 1 (I assume when loading the bean from the
DB prior to the update). However the bean is apparently loaded by a
query that includes all nun-null elements (of the bean to be saved)
which of course will not return anything, because the changed values
are not yet in the database. What am I doing wrong?

javax.persistence.OptimisticLockException: Data has changed. updated
[0] rows
at
com.avaje.ebeaninternal.server.core.PersistRequestBean.checkRowCount(PersistRequestBean.java:
509)
at
com.avaje.ebeaninternal.server.persist.dml.UpdateHandler.execute(UpdateHandler.java:
104)
at
com.avaje.ebeaninternal.server.persist.dml.DmlBeanPersister.execute(DmlBeanPersister.java:
105)
at
com.avaje.ebeaninternal.server.persist.dml.DmlBeanPersister.update(DmlBeanPersister.java:
85)
at
com.avaje.ebeaninternal.server.persist.DefaultPersistExecute.executeUpdateBean(DefaultPersistExecute.java:
110)
at
com.avaje.ebeaninternal.server.core.PersistRequestBean.executeNow(PersistRequestBean.java:
449)

If you need more information or code samples, please tell me which.
The bean and the saving method are attached below.
I hope I just oversaw something stupid and this can be fixed easily.
Personally I would expect that the query to retrieve the existing copy
of the bean from the DB would use the ID only, but maybe that has
other implications.

Thanks for your help,
Cheers Ole

Bean:
@Data
@Entity
public class Substitution {

@Id
private long id;
private Date date;
@ManyToOne
private StudyUnit studyUnit;
@ManyToOne
private Room altRoom;
@ManyToOne
private Teacher substitute;
@ManyToOne(cascade=CascadeType.ALL)
private Substitution insteadOf;
@ManyToOne
private SubstitutionPlanComment substitutionPlanComment;
private boolean locked = false;
private boolean moved = false;

@Version
private long version;

private String comment;
@Transient
private boolean conflicted = false;
@Transient
private Substitution isFor;


/**
* The plain constructor
*
*/
public Substitution() {
}

/**
* @param date
* @param studyUnit
*/
public Substitution(Date date,
StudyUnit studyUnit) {
super();
this.date = date;
this.studyUnit = studyUnit;
}

public void setInsteadOf(Substitution instead) {
insteadOf = instead;
if (insteadOf != null
&& !insteadOf.equals(instead)) {
DataRepository.instance().delete(insteadOf);
}
if (instead != null) {
instead.setIsFor(this);
}
}

@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof Substitution)) return false;
return getId() == ((Substitution)o).getId();
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[").append(getId()).append(" / ").append(getDate())
.append(" / ").append(getStudyUnit()).append(" / ")
.append(getSubstitute());
return sb.toString();
}
}



update method (the transaction handling is done by the calling methods
so that should be no issue, if I understand it correctly):
private static List<SubstitutionEvent> updateInternal(Substitution
sub, boolean silent) {
List<SubstitutionEvent> events = new
ArrayList<SubstitutionEvent>();

Substitution s = Ebean.find(Substitution.class, sub.getId());
if (s != null) {
if (s.getInsteadOf() != null
&& sub.getInsteadOf() == null) {
events.add(deleteInternal(s.getInsteadOf(), silent));
} else if (s.getInsteadOf() == null
&& sub.getInsteadOf() != null
&& !silent) {
events.add(new SubstitutionEvent(Type.CREATED,
sub.getInsteadOf()));
} else if (s.getInsteadOf() != null
&& sub.getInsteadOf() != null
&& !s.getInsteadOf().equals(sub.getInsteadOf())) {
events.add(deleteInternal(s.getInsteadOf(), silent));
if (!silent) {
events.add(new SubstitutionEvent(Type.CREATED,
sub.getInsteadOf()));
}
}
Ebean.save(sub); // Here is where the exception is initially
triggered. I already tried to use Ebean.update() but with the same
result
if (!silent) {
events.add(new SubstitutionEvent(Type.MODIFIED, sub));
}
} else {
events.add(saveInternal(sub, silent));

}
return events;
}

Rob Bygrave

unread,
Jan 9, 2011, 4:26:53 PM1/9/11
to eb...@googlegroups.com
I'd suggest you look at the transaction logs which log the sql being executed with the bind values.

The logic is not obvious to me in that you fetch "s", don't seem to modify "sub" ... and save "sub" ... so sub doesn't even look to have been changed? Anyway, the transaction logs might help identify the issue.

Ole Hornischer

unread,
Jan 10, 2011, 4:50:28 AM1/10/11
to eb...@googlegroups.com
Hi Rob!
Thanks for your quick reply.
*sub* is part of the signature of the method and is therefore change before calling the method. 
Anyways, I figured out my problem: One of the references of sub was causing the issue. It was a bean stuck in one of the calculation caches. After cleaning the id and version of the bean in said cache it works fine now.

I really enjoy the ease of use of Ebean. Great library!
Cheers Ole

Ole Hornischer

unread,
Jan 10, 2011, 10:41:45 AM1/10/11
to eb...@googlegroups.com
Hi Rob!
I do however have another question: How do I save a null-value? When updating Ebean seems to look for all non-null elements in the bean and update all of them. However, if a property turned null and is supposed to be updated accordingly it is ignored. Do I always have to specifically name the property in the update set or is there another way I did not find yet?
Cheers Ole

On 9 January 2011 22:26, Rob Bygrave <robin....@gmail.com> wrote:

Rob Bygrave

unread,
Jan 10, 2011, 3:14:33 PM1/10/11
to eb...@googlegroups.com
So to clarify we are talking about "stateless updates" that are occur via ebeanServer.update() ... rather than updates that naturally occur from a ebeanServer.save().

See http://www.avaje.org/bugdetail-346.html - ENHANCEMENT - Add "updateNullProperties" option for stateless update ... fixed in version 2.7.2

... so in 2.7.2 you'd use the new additional update() method that allows you to specify boolean updateNullProperties ... or you change the default behaviour for all stateless updates via ebean.defaultUpdateNullProperties=true.


Cheers, Rob.

Ole Hornischer

unread,
Jan 11, 2011, 5:22:40 AM1/11/11
to eb...@googlegroups.com
Hey Rob!
That is exactly what I was looking for. Thanks a lot!
Cheers Ole
Reply all
Reply to author
Forward
0 new messages