Field does not update data

35 views
Skip to first unread message

stackp...@gmail.com

unread,
Oct 23, 2024, 7:08:20 AM10/23/24
to Ebean ORM
Hello everyone!

I am using Ebean 15.1.0. 

I am trying to use Field in Java to update a field. For example:

public static Boolean incrementNbr(Integer type, Integer nextNbr) {
...
Field field = numberConf.getClass().getDeclaredField("nextProductNbr"+type);            field.setAccessible(true); 
field.set(numberConf, nextNbr);  
numberConf.update();
...
}

When using the inspector, the value of numberConf is updated but the database does not contain this change. I also put numberConf.markAsDirty(); before update() but same result.

I tried to update with Field others entity fields but the same result.

Is this a bug or do I need to add something else?

Thank you in advance!

Rob Bygrave

unread,
Oct 23, 2024, 3:05:29 PM10/23/24
to eb...@googlegroups.com

Is this a "stateless update"? 

If the numberConf instance has come from a query then it's a "normal update" but if the numberConf instance was just "new'ed up" then it is a "stateless update".

There is a difference on how to handle this depending on if it's a stateless update or normal update.

For the "stateless update" case we need to get the BeanState and set the "loaded state" for the property that is being changed via reflection. That is, for "stateless update" it is the "loaded" properties that are included in the update. [For a normal update it is the "changed" properties that are included in the update].

 In the example code below, it is a stateless update and the property being reflectively changed is "email" [and for the above it is "nextProductNbr"+type]. 


@Test
void statelessUpdate_via_reflection() throws NoSuchFieldException, IllegalAccessException {
// populate db with a user
User seed = new User();
seed.setName("someName");
seed.setEmail("so...@junk.com");
seed.save();

Field field = User.class.getDeclaredField("email");
field.setAccessible(true);

// our bean to perform stateless update
User user = new User();
user.setId(seed.getId());
user.setName("mod");

field.set(user, "cha...@junk.com");

// need to set property loaded state to include in the stateless update
BeanState beanState = DB.beanState(user);
beanState.setPropertyLoaded("email", true);

LoggedSql.start();
user.update();

List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("update c_user set name=?, email=?, when_modified=? where id=?");
}

--

---
You received this message because you are subscribed to the Google Groups "Ebean ORM" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ebean+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ebean/fe6741da-0b91-4072-83e0-61a4e70e5741n%40googlegroups.com.

Rob Bygrave

unread,
Oct 23, 2024, 3:13:32 PM10/23/24
to eb...@googlegroups.com
For the "Normal update" case ...

It isn't supported as well because we should be able to use BeanState to mark the property as changed but that isn't there in the API (we should add it). Instead, we need to cast and get the underlying EntityBeanIntercept.

Either use markPropertyAsChanged() or more ideal use setChangedProperty() with the original/before value [which then supports any change listeners that desire the orig and new value etc].

Note: This really isn't ideal because we don't want application code casting and using EntityBeanIntercept directly. We really want application code to use BeanState instead [so really BeanState should have these options added to it].

Example:

@Test
void normalUpdate_via_reflection() throws NoSuchFieldException, IllegalAccessException {

// populate db with a user
User seed = new User();
seed.setName("someName");
seed.setEmail("so...@junk.com");
seed.save();

Field field = User.class.getDeclaredField("email");
field.setAccessible(true);

  // fetching the bean from database, so a "normal update"
User user = DB.find(User.class, seed.getId());
user.setName("mod");

// ideally we get the old value first (if there are change listeners etc)
Object oldValue = field.get(user);

// reflectively modify
field.set(user, "cha...@junk.com");

// BeanState beanState = DB.beanState(user);
// need to use EntityBeanIntercept rather than BeanState
// so this isn't great !!
EntityBean eb = (EntityBean) user;
EntityBeanIntercept ebi = eb._ebean_getIntercept();
int pos = ebi.findProperty("email");

// EITHER ideally mark as dirty with the original value
//ebi.setChangedPropertyValue(pos, true, oldValue);

// OR just mark as changed
ebi.markPropertyAsChanged(pos);


LoggedSql.start();
user.update();

List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
  assertThat(sql.get(0)).contains("update c_user set name=?, email=?, version=?, when_modified=? where id=? and version=?");
}

Rob Bygrave

unread,
Oct 23, 2024, 9:01:08 PM10/23/24
to eb...@googlegroups.com
The better alternative to using Field access here would be to use the setter method instead. Invoke the setter method reflectively rather than setting the field value reflectively.

That would be the better alternative, uniform approach for both stateless update and normal update.

stackp...@gmail.com

unread,
Oct 24, 2024, 4:21:29 AM10/24/24
to Ebean ORM
Thank you very much, Rob.

It is a "normal update". I get the record from the database and update the corresponding property depending on the value passed in the type parameter.

I agree with you and I also think that the best idea is to use setters in a reflexive way. I tried and it is working fine.
I'll give an example in case someone has the same problem.

Product product = getProduct(...);
Method setterMethod = product.getClass().getMethod("setProductNbr"+type, String.class); //Setter
setterMethod.invoke(product, incrementProductNbrString(nextNbr)); //1 parameter
product.update();

Thanks
Reply all
Reply to author
Forward
0 new messages