Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BeanUtilでMapからBeanへ移送する際、ネストしたオブジェクト数が多い場合に処理が遅くなる #40

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from

Conversation

morioka-homare
Copy link

不具合内容

  • BeanUtilでMapからBeanへ移送する際、ネストしたオブジェクト数が多い場合に処理が遅くなる

修正内容

  • BeanUtil.copy メソッドを修正
    • 全エントリでループしている箇所を、まずネストしていないプロパティだけを処理し、ネストしているエントリは変数に保持しておく。この時にネストのルート部分をキー、それに紐づくエントリーのリストをバリューとしてMap形式で保存する。
    • 上記のMapでキーでループしつつ、さらにバリューのリストでループして処理を行うようにする。

テスト

  • 以下のテストコードを追加
    • 移送先beanのプロパティの種類、ネストの数に応じた各ケース
      • プロパティの種類は、プリミティブ型、クラス、クラスの配列、クラスのリスト、レコード、レコードの配列、レコードのリスト
    • 大量のオブジェクトがある場合のケース
    • 今回修正したprivateメソッドを使用しているBeanUtil.setPropertyメソッドの動作確認


PropertyExpression expression = new PropertyExpression(entry.getKey());

if (expression.isSimpleProperty() && expression.isNode()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

実装の話ですが、
まずexpression.isNode()で分岐させて、その中で expression.isSimpleProperty() かどうかを見たほうがすっきりするかなと
(261行目でも expression.isNode() を参照しているので)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全体的に構成を修正しました。

Object destObject;

Class<?> propertyType = getPropertyType(bean.getClass(), propertyName);
if (propertyType.isArray()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この部分、 setArrayProperty() と同様にメソッドに切り出した方が良いかなと思いました。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらと上の方とで、同じような抽象度になるはずなので

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全体的に構成を修正しました。

Array.set(propertyObject, propertyExpression.getListIndex(), destObject);
}
}
} else if (List.class.isAssignableFrom(propertyType)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらも同様に、setListProperty()のようなメソッドになりそうですね

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全体的に構成を修正しました。

for (Map.Entry<String, Map<String, Object>> entry : groupMap.entrySet()) {
try {
PropertyExpression nestedPropertyExpression = new PropertyExpression(entry.getKey());
setProperty(destObject, nestedPropertyExpression, entry.getValue(), copyOptions.reduce(nestedPropertyExpression.getRoot()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

439行目や417行目でConversionUtil.convertを使っていますが、こちらでは使わなくて大丈夫でしょうか。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

既存の処理と同様にしています

}
} catch (BeansException bex) {
LOGGER.logDebug(
"An error occurred while writing to the property :" + entry.getKey());
}
}

for (Map.Entry<String, Map<String, Object>> entry : groupedMap.entrySet()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この部分、setProperty でも同じような処理が出てきたと思いますが、まとめるのは難しいでしょうか?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これはほぼ同じことを思います。

Mapの組み方は2種類、その後のループの内容が若干異なるのが差分のようなので、集約できそうに見えます。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全体的に構成を修正しました。

@@ -857,43 +959,100 @@ static Map<String, Object> getReducedMap(String rootProperty, Map<String, ?> map
} else if (expression.isListOrArray()) {
Class<?> propertyType = getPropertyType(beanClass, expression.getListPropertyName());
if (propertyType.isArray()) {
setArrayPropertyToMap(beanClass, expression, propertyMap, map, copyOptions);
if (expression.isNode()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここも、 expression.isNode() の判定は外側で実施するのが良い気がします。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全体的に構成を修正しました。

Map<String, Object> nodeMap = new HashMap<>();
Map<String, Object> nestedMap = new HashMap<>();

for(Map.Entry<String, ?> entry : map.entrySet()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

処理自体の話ではないですが、時々for(Map.Entryのように空白が抜けたりしているので、修正箇所についてはフォーマッタをかけておいてください。

Comment on lines +226 to +230
List<Map<String,Object>> separatedList = getSeparatedlist(map);

for (int i = 0; i< 2; i++) {
boolean isNode = i == 0;
Map<String, Object> srcMap = separatedList.get(i);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

第三者から見ると「iが0だとどうしてノードなのか?」と不思議に思います。

引数のmapをそのまま使うのではなく、getSeparatedlistを必要とする理由は以降の処理で不要なループを増やさないためですよね?

メソッド名はgetSeparatedlistではなくgroupByNodeTypeなどにしてはどうでしょうか。

また返すのをListではなくMapにして、Mapのキーで意味を識別させた方がよいような気がします。

どうでしょうか?

if (srcMap.isEmpty()) {
continue;
}
if (expression.isSimpleProperty() && isNode) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この箇所ですが、呼び出される経路によってはisNodeexpression.isNodeが異なる結果になることがありますか?

for (Map.Entry<String, ?> mapEntry : map.entrySet()) {
PropertyExpression keyAbsoluteExpression = expression.sibling(mapEntry.getKey());
PropertyExpression restExpression = keyAbsoluteExpression.rest();
groupMap.computeIfAbsent(restExpression.getParentKey() + "." + restExpression.getRoot(),
Copy link

@charon-r13b charon-r13b Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restExpression.getParentKey() + "." + restExpression.getRoot()というパターンが頻出しますが、PropertyExpressionのメソッドにしてはどうでしょうか?

PropertyExpression#getAbsoluteRootなどでよい気がします。

別コメントで書いたように、集約されるのならこのままでもよいかもですが。

*
* @return リーフ要素のPropertyExpression
*/
PropertyExpression leaf() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このメソッド、使用箇所がないように見えますが意図的に残されていますか?

* @param propertyName プロパティ名
* @return 同一親をもつPropertyExpression
*/
PropertyExpression sibling(String propertyName) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このメソッド、使用箇所がないように見えますが意図的に残されていますか?

return parentKey;
}

String getAbsoluteRawKey() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このメソッド、使用箇所がないように見えますが意図的に残されていますか?

Comment on lines +290 to +291
LOGGER.logDebug(
"An error occurred while writing to the property :" + nestedExpression.getAbsoluteRawKey());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

テストケースに、ここでの例外ハンドリング時にnestedExpression.getAbsoluteRawKey()の結果が期待通りか確認するテストが必要ではないでしょうか?

@charon-r13b
Copy link

いくつかのメソッドの引数に出てくるisNodeを、できればなくしたいです。

既存のメソッドで、単一の値を設定するものはsetPropertyValueになっているので同じように接尾語をValueにしてメソッドを呼び分けてはどうでしょうか。

@charon-r13b
Copy link

この修正の発端は、タイトルに書いてあるとおり「BeanUtilでMapからBeanへ移送する際、ネストしたオブジェクト数が多い場合に処理が遅くなる」なので、その対応結果が必要です。

目安として10,000件程度での修正前後の性能比較が上がっていたと思いますが、その結果を概要に追記しておいてください。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants