タスク #1439
完了Entityの項目に空を入れると更新時にエラーが発生する
100%
説明
共通処理で空を初期値に置き換えるように補完する。
1、可能であれば
セッターの共通処理で。
2、ツール出力でセッターを生成。
3、更新時のタイミング(これはやめたい)
結論 独自セッターをツールで出力する。
・項目毎に初期値が異なるが正しく設定できる。
・速度も一番マシ
・ツールがあるからコーディングの物量も何とかなる!
AOP
×→遅くなるのが心配。
暗黙的過ぎて気づきにくい
副作用に注意
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
ご質問の意図「セットした直後から値を補完してほしい(例:空文字 → 全角スペース)」という要件をふまえて、AOP方式とDTO層の補完方式を比較し、パフォーマンス面や実装の現実性も含めて説明します。
✅ 要件まとめ(あなたのケース)¶
- Oracle の
NCHARに空文字を入れると全角スペースに変換する必要がある。 - DTOやEntityに値をセットした「直後」に補完したい。
- 多数のフィールドに共通処理を適用したい。
- 処理速度に悪影響がない方法が望ましい。
🔷 方法1:AOP(Aspect Oriented Programming)¶
✨ メリット¶
- すべてのセッター呼び出しに横断的に共通処理を追加可能
- クラスに修正を加えず適用できる(メンテナンス性○)
❌ デメリット¶
- リフレクションやプロキシを多用 → 高速性はやや劣る
- セッター直後に確実に値を補完できるが、開発者が副作用に気づきにくい
📘 実装例(Spring AOP + AspectJ)¶
@Aspect
@Component
public class SetterInterceptor {
@Around("execution(* com.example.dto..set*(..))")
public Object aroundSetter(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
if (args.length == 1 && args[0] instanceof String) {
String arg = (String) args[0];
args[0] = (arg != null && arg.trim().isEmpty()) ? " " : arg; // 全角スペース
}
return joinPoint.proceed(args);
}
}
🔷 方法2:DTO/Entity層での補完処理(セッターまたは @Builder 補正)¶
✨ メリット¶
- 処理速度最速(純粋なJavaコード)
- セッターごとの記述なので意図が明確、バグ発生しにくい
❌ デメリット¶
- フィールドが多いと記述が煩雑
- 共通処理を
Utils等で吸収するのが現実的
📘 実装例(Lombok併用 + 明示的セッター)¶
@Data
public class UserDto {
private String name;
public void setName(String name) {
this.name = StringNormalizer.normalize(name);
}
}
public class StringNormalizer {
public static String normalize(String input) {
return (input != null && input.trim().isEmpty()) ? " " : input;
}
}
🔷 方法3:BeanPostProcessor で補完(Bean登録後に実行)¶
- DI後の初期化処理(Spring Bean に限定)
- EntityやDTOでは不向き
🔍 処理速度比較(イメージ)¶
| 方法 | 補完タイミング | 処理速度 | 特徴 |
|---|---|---|---|
| AOP | セッター呼び出し時 | 遅い(リフレクション) | 横断的、柔軟、遅延初期化 |
| 明示的セッター補完 | セッター内 | 最速 | 明示的で安全、冗長になりがち |
| Builder補正 | DTO生成時 | 高速 | DTO一括初期化に向く |
✅ 推奨パターン(あなたの要件に最適)¶
✨「明示的セッター + 共通ユーティリティ関数」方式¶
- 高速(最小オーバーヘッド)
- セット直後に補完できる
- テストやバグトレースも容易
📝 おまけ:多数の文字列フィールドに適用するなら¶
public class NormalizingSetter {
public static String normalize(String input) {
return (input != null && input.trim().isEmpty()) ? " " : input;
}
}
そして各セッターで:
public void setAddress(String address) {
this.address = NormalizingSetter.normalize(address);
}
✉️ 結論¶
| 方法 | あなたの目的との相性 |
|---|---|
| AOP | 柔軟だがやや重い・副作用に注意 |
| セッター補完 | ◎ 最速&シンプルで安全 |
| DTO一括補正 | 特定処理時に使えるが「直後補完」には不向き |
他に「大量の DTO に共通補正を楽に埋め込みたい」や「Spring Validator で対応したい」などの方針があれば、それに合わせた方法も提案できます!
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
Spring + Lombok の @Data を使っていると、セッターは自動生成されるため、共通処理(例えば「空文字をスペースに変換」など)をセッターで実装したい場合は、個別にセッターをオーバーライドする必要があります。
✅ 目的:@Data 使用時に、NCHAR列用セッターで空文字をスペースに変換する共通処理を入れたい¶
📌 方法1:対象フィールドだけセッターを明示的に書く(Lombokとの併用OK)¶
import lombok.Data;
@Data
public class YourEntity {
private Long id;
private String yourColumn;
public void setYourColumn(String yourColumn) {
if (yourColumn != null && yourColumn.trim().isEmpty()) {
this.yourColumn = " ";
} else {
this.yourColumn = yourColumn;
}
}
}
-
@Dataは他のゲッター・セッターを自動生成しますが、明示的に定義したセッターは上書きされません。 - このようにして、該当するフィールドにだけ共通処理を埋め込めます。
📌 方法2:共通のユーティリティで処理し、各セッターで使う¶
public class TextUtils {
public static String normalizeEmpty(String value) {
return (value != null && value.trim().isEmpty()) ? " " : value;
}
}
public void setYourColumn(String yourColumn) {
this.yourColumn = TextUtils.normalizeEmpty(yourColumn);
}
📌 方法3(高度):全フィールドに共通処理したい → セッターの自動生成は避け、明示的に実装¶
-
@Dataを使わず、@Getter/@Setterを必要なものだけに使うか、完全に自分で記述します。 - あるいは、AOP や
@ModelAttribute・@ControllerAdviceを活用してバリュー変換の層を設ける方法もありますが、これは設計方針によります。
🔁 まとめ¶
| 方法 | 説明 | 備考 |
|---|---|---|
| セッターを手書きで上書き |
@Dataと併用可 |
最も実用的 |
| ユーティリティ関数化 | 複数フィールドでも再利用可 | セッター内で呼び出し |
| 全面的に自前実装 | 高度な共通処理に対応 | Lombokを部分的に制限 |
もし対象フィールドが多数ある、あるいはドメイン層で一元管理したい場合は、AOPやDTO層での前処理を提案できます。それも含めて方向性を絞りたい場合はお気軽にお知らせください。
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
import lombok.Data;
@Data
public class YourEntity {
private Long id;
private String yourColumn;
public void setYourColumn(String yourColumn) {
if (yourColumn != null && yourColumn.trim().isEmpty()) {
this.yourColumn = " ";
} else {
this.yourColumn = yourColumn;
}
}
}