Ebeanのデフォルトの動きでは、nullがセットされている値はupdateの対象にはならないです。
通常はそれを踏まえて、主キーと更新したいプロパティの値をセットしてsaveメソッドするという使い方です。
場合によっては、DBの値をnullにセットしたいこともあります。
Ebean#updateメソッドには、第二引数にSetをとるものがあります。これはSetにあるプロパティのみ更新の対象とし、nullへの上書きもできます。
あらかじめ、nullに更新したいプロパティのSetを作成し、それとBeanでnon-nullの値をもつプロパティのSetを合わせたものを渡すことで、特定項目はnullにできる更新になります。
それの結果が下記です。
try {
Ebean.update(bean, getUpdateProperties(bean));
Ebean.refresh(bean)
} catch (Exception e) {
logger.error("update failed.", e);
}
logger.debug("END update");
}
/**
* null更新可能なプロパティ名のセット.
*/
private final static HashSet<String> NULLABLE_PROPERTIES =
Sets.newHashSet(
"passwordExpirationDate",
"tokenExpirationDate"
);
/**
* 値を持つか、null更新可能なプロパティ名のセットを取得.
* @param bean
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
private Set<String> getUpdateProperties(Object bean) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Map<String,String> beanMap = BeanUtils.describe(bean);
Predicate<Map.Entry<String,String>> hasNotNullValueOrIsNullableKey = new Predicate<Map.Entry<String,String>>(){
public boolean apply(Map.Entry<String, String> entry) {
return (entry.getValue() != null) || NULLABLE_PROPERTIES.contains(entry.getKey());
}
};
Map<String,String> filteredMap = Maps.filterEntries(beanMap, hasNotNullValueOrIsNullableKey);
return filteredMap.keySet();
}
non-nullなプロパティを求めるのに、commons-beanのBeanUtils#describeを使ってみました。MapのフィルタリングはGuavaを利用しています。
問題点
Ebean#updateのあとにEbean#refreshをしています。refreshはselect文を発行してbeanの値を再度詰め直すものです。
これをやらないと、@Entityと紐付けているテーブルとは対応していないプロパティを参照するとnullpoが発生しました・・・ 更新するプロパティを制限しているせいで、オブジェクトの状態がうまくいってないのか。
6.x 系でのやり方
6.xではEbean#update(Object bean, Set<String> properties)
がなくなっているので、別のやり方が必要。
検証中だが、次でいけそう。
- BeanPersistAdapterを継承し、preUpdateのみをoverride
- BeanPersistRequest#getUpdatedValues()を使って、nullに更新したいkeyのValuePairを取得
- ValuePairは更新前後の値を持つ、setNewValue(null)で更新後の値をnullに指定
あるいは、
ServerConfig.setUpdateChangesOnly(false)
として、毎回全項目を更新することで、nullへの変更をできるようにするか。(もはや項目を限定しないやり方...)