MyBatis-Plusの実践的活用:条件ビルダー、カスタムSQL、Service層の設計パターン

MyBatis-Plusにおける条件ビルダー、カスタムSQL、Serviceインターフェースの設計について解説します。これらの機能を適切に活用することで、データアクセス層の実装効率と保守性を大きく向上させることができます。

条件ビルダーの活用

MyBatis-Plusは複雑なWHERE句を型安全に構築するためのAPIを提供しています。以下に代表的な使用例を示します。

特定条件でのカラム指定検索

ユーザ名に「o」を含み、残高が1000以上のレコードから特定カラムを取得する場合:

@Test
void fetchUsersWithFilter() {
    LambdaQueryWrapper<Account> criteria = new LambdaQueryWrapper<>()
            .select(Account::getId, Account::getUsername, 
                   Account::getProfile, Account::getBalance)
            .like(Account::getUsername, "o")
            .ge(Account::getBalance, 1000);
    
    List<Account> results = accountDao.selectList(criteria);
    results.forEach(System.out::println);
}

条件付き更新処理

特定ユーザの残高を更新する場合、UpdateWrapperを使用します:

@Test
void modifyBalanceByName() {
    UpdateWrapper<Account> updater = new UpdateWrapper<>()
            .set("balance", 2000)
            .eq("username", "jack");
    
    accountDao.update(null, updater);
}

SQLのSET句に式を含める場合:

@Test
void deductBalanceBatch() {
    List<Long> targetIds = Arrays.asList(1L, 3L, 5L);
    
    UpdateWrapper<Account> updater = new UpdateWrapper<>()
            .setSql("balance = balance - #{deduction}")
            .in("id", targetIds);
    
    accountDao.update(null, updater);
}

推奨事項:

  • Lambda版のWrapper(LambdaQueryWrapper・LambdaUpdateWrapper)を優先して使用し、文字列リテラルによるカラム名指定を回避
  • Query系は検索・削除・更新のWHERE句生成に、Update系は複雑なSET句が必要な場合に限定

カスタムSQLとの連携

WrapperでWHERE条件を構築し、残りのSQLは自作するハイブリッドアプローチを紹介します。

ユースケース:一括残高更新

指定ID群の残高を減算する処理を実装します。

Mapperインターフェース:

public interface AccountDao extends BaseMapper<Account> {
    
    void batchDeductBalance(
        @Param(Constants.WRAPPER) LambdaQueryWrapper<Account> criteria,
        @Param("deduction") int deduction
    );
}

XMLマッピング:

<update id="batchDeductBalance">
    UPDATE account 
    SET balance = balance - #{deduction}
    ${ew.customSqlSegment}
</update>

実行テスト:

@Test
void executeCustomUpdate() {
    List<Long> idList = Arrays.asList(1L, 3L, 5L);
    int deductionAmount = 200;
    
    LambdaQueryWrapper<Account> criteria = new LambdaQueryWrapper<>()
            .in(Account::getId, idList);
    
    accountDao.batchDeductBalance(criteria, deductionAmount);
}

ポイント:MapperパラメータのWrapperには@Param(Constants.WRAPPER)または@Param("ew")を必ず付与します。

Service層の標準実装

MyBatis-Plusはビジネスロジック層の標準化もサポートしています。

インターフェース定義

public interface AccountService extends IService<Account> {
    // 業務固有メソッドをここに追加
}

実装クラス

@Service
public class AccountServiceImpl 
        extends ServiceImpl<AccountDao, Account> 
        implements AccountService {
    // 基本的なCRUDは継承により自動実装
}

利用例

@SpringBootTest
class AccountServiceTest {
    
    @Autowired
    private AccountService accountService;
    
    @Test
    void createNewAccount() {
        Account entity = new Account();
        entity.setUsername("Yamada");
        entity.setPasswordHash("hashed_value");
        entity.setContact("08012345678");
        entity.setBalance(3000);
        entity.setProfile("{\"age\": 28, \"dept\": \"sales\"}");
        entity.setCreatedAt(LocalDateTime.now());
        
        accountService.save(entity);
    }
}

実装のベストプラクティス

機能 適用シナリオ 注意点
LambdaQueryWrapper 動的条件の検索 カラム変更時にコンパイルエラーで検出可能
UpdateWrapper 一括更新・条件付き更新 setSql使用時はSQLインジェクション対策を確認
カスタムSQL 複雑なJOIN・サブクエリ ${ew.customSqlSegment}の位置を正しく配置
IService継承 標準的な業務処理 特殊なトランザクション制御が必要な場合はオーバーライド

これらのパターンを組み合わせることで、単純なCRUDから複雑な業務ロジックまで、一貫した設計で対応できるデータアクセス層を構築できます。

タグ: MyBatis-Plus Spring Boot Java ORM データベース

9月26日 02:40 投稿