Skip to content

vemikrs/batis-fluid

Repository files navigation

BatisFluid (旧: SeasarBatis)

Maven Central Javadocs

注意:このライブラリは現在開発中です。不具合や未開発のAPIを含む点に注意してください。


BatisFluidは、Seasar2のJdbcManagerのような操作性を提供する、モダンでミニマルなMyBatisラッパーライブラリです。
fluent API、型安全性、externalized SQLサポートを提供します。

v0.0.2の変更点: SeasarBatisからBatisFluidにリブランドしました。
旧API(SB*クラス)は非推奨となり、v0.0.3以降で削除予定です。
移行ガイド: MIGRATION_GUIDE_v0.0.1_to_v0.0.2.md

インストール

Maven (v0.0.2 - 推奨)

<dependency>
    <groupId>jp.vemi</groupId>
    <artifactId>batis-fluid-core</artifactId>
    <version>0.0.2</version>
</dependency>

<!-- 必要な依存関係 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.15</version>
</dependency>

Gradle (v0.0.2 - 推奨)

dependencies {
    implementation 'jp.vemi:batis-fluid-core:0.0.2'
    
    // 必要な依存関係
    implementation 'org.mybatis:mybatis:3.5.15'
}

Maven (v0.0.1 - 非推奨)

<dependency>
    <groupId>jp.vemi</groupId>
    <artifactId>seasar-batis</artifactId>
    <version>0.0.1</version>
</dependency>

Gradle (v0.0.1 - 非推奨)

dependencies {
    implementation 'jp.vemi:seasar-batis:0.0.1'
}

スタンドアロンでの使用

v0.0.2 (推奨)

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
    .build(Resources.getResourceAsStream("mybatis-config.xml"));

// BatisFluidを使用
BatisFluid fluid = BatisFluid.of(sqlSessionFactory);
JdbcFlow flow = fluid.jdbcFlow();

v0.0.1 (非推奨)

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
    .build(Resources.getResourceAsStream("mybatis-config.xml"));
SBJdbcManager jdbcManager = new SBJdbcManager(sqlSessionFactory); // 非推奨

Spring Framework との統合

Spring Frameworkと統合する場合は、以下の追加依存関係が必要です。

Maven (v0.0.2 - 推奨)

<!-- BatisFluid Spring統合モジュール -->
<dependency>
    <groupId>jp.vemi</groupId>
    <artifactId>batis-fluid-spring</artifactId>
    <version>0.0.2</version>
</dependency>

<!-- Spring統合用の追加依存関係 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>3.0.3</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>6.1.3</version>
</dependency>

Gradle (v0.0.2 - 推奨)

// BatisFluid Spring統合モジュール
implementation 'jp.vemi:batis-fluid-spring:0.0.2'

// Spring統合用の追加依存関係
implementation 'org.mybatis:mybatis-spring:3.0.3'
implementation 'org.springframework:spring-jdbc:6.1.3'

Maven (v0.0.1 - 非推奨)

<!-- SeasarBatis Spring統合モジュール -->
<dependency>
    <groupId>jp.vemi</groupId>
    <artifactId>seasar-batis-spring</artifactId>
    <version>0.0.1</version>
</dependency>

Gradle (v0.0.1 - 非推奨)

// SeasarBatis Spring統合モジュール
implementation 'jp.vemi:seasar-batis-spring:0.0.1'

Spring Boot での設定

application.ymlに以下の設定を追加します:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/your_database
    username: your_username
    password: your_password

mybatis:
  configuration:
    map-underscore-to-camel-case: true

Javaコンフィグを追加:

@Configuration
public class SeasarBatisConfig {
    
    @Bean
    public SBJdbcManager sbJdbcManager(SqlSessionFactory sqlSessionFactory) {
        return new SBJdbcManager(sqlSessionFactory);
    }
}

SBJdbcManager の使用方法

基本的な使用方法

SBJdbcManagerはSeasar2のJdbcManagerに似た操作性を提供するユーティリティクラスです。

@Autowired
private SBJdbcManager jdbcManager;

// エンティティの取得(主キー指定)
User pk = new User();
pk.setId(1L);
User user = jdbcManager.findByPk(pk).getSingleResult();

// 一覧取得
List<User> users = jdbcManager.findAll(User.class);

// エンティティの保存
User newUser = new User();
newUser.setName("John");
jdbcManager.insert(newUser);

// エンティティの更新(主キーを持つエンティティを渡す)
user.setName("Jane");
jdbcManager.update(user);

// エンティティの削除(主キーを持つエンティティを渡す)
User toDelete = new User();
toDelete.setId(1L);
jdbcManager.delete(toDelete);

Seasar2ライクなクエリビルダー

Seasar2のJdbcManagerと同様のスタイルでクエリを構築できます:

// 単一の結果を取得
User user = jdbcManager
    .from(User.class)
    .where()
    .eq("name", "John")
    .getSingleResult();

// 検索結果のリストを取得
List<User> users = jdbcManager
    .from(User.class)
    .where()
    .ge("age", 20)
    .like("name", "J%")
    .orderBy("name ASC")
    .getResultList();

// ComplexWhereとの組み合わせ
List<User> complexUsers = jdbcManager
    .from(User.class)
    .where(jdbcManager
        .complexWhere()
        .add(jdbcManager.where().eq("status", "ACTIVE"))
        .or()
        .add(jdbcManager.where().eq("role", "ADMIN")))
    .orderBy("id DESC")
    .getResultList();

UPDATE操作

WHERE句を使用した更新操作も提供しています:

// 単純な条件での更新
int updatedRows = jdbcManager
    .update(User.class)
    .set("status", "INACTIVE")
    .set("updatedAt", new Date())
    .where(jdbcManager.where()
        .eq("department", "IT")
        .gt("age", 30))
    .execute();

// 複合条件での更新
int updatedRows = jdbcManager
    .update(User.class)
    .set("role", "MANAGER")
    .where(jdbcManager
        .complexWhere()
        .add(jdbcManager.where()
            .eq("status", "ACTIVE")
            .gt("experience", 5))
        .or()
        .add(jdbcManager.where()
            .eq("department", "SALES")
            .ge("performance", 90)))
    .execute();

Batch Operations

SBJdbcManagerは効率的なバッチ処理もサポートしています:

// 複数エンティティの一括登録
List<User> users = Arrays.asList(
    new User("Alice", 25),
    new User("Bob", 30),
    new User("Charlie", 35)
);
List<User> insertedUsers = jdbcManager.batchInsert(users);

// 複数エンティティの一括更新
users.forEach(user -> user.setStatus("ACTIVE"));
List<Integer> updateCounts = jdbcManager.batchUpdate(users);

// 複数エンティティの一括削除
List<Integer> deleteCounts = jdbcManager.batchDelete(users);

// 複数エンティティの一括登録または更新
List<User> mixedUsers = Arrays.asList(
    existingUser,  // 存在する場合は更新
    newUser1,      // 存在しない場合は登録
    newUser2       // 存在しない場合は登録
);
List<User> processedUsers = jdbcManager.batchInsertOrUpdate(mixedUsers);

// 独立したトランザクションでのバッチ処理
List<User> results = jdbcManager.batchInsert(users, true);

バッチ処理の利点:

  • 複数のエンティティを一度のトランザクションで処理するため、パフォーマンスが向上
  • トランザクションの境界が明確で、データ整合性が保たれる
  • エラー発生時は、バッチ全体がロールバックされる

使用上の注意:

  • 大量のデータを処理する場合は、メモリ使用量に注意してください
  • isIndependentTransactionフラグを使用して、既存のトランザクションとの分離レベルを制御できます
  • 空のリストやnullを渡すとSBIllegalStateExceptionがスローされます

トランザクション管理

トランザクションをラムダ式で簡単に扱えます:

jdbcManager.transaction(manager -> {
    User user = new User();
    user.setName("John");
    manager.insert(user);

    Address address = new Address();
    address.setUserId(user.getId());
    address.setCity("Tokyo");
    manager.insert(address);
});

SQL直接実行

SQL文を直接実行することもできます:

// SELECT文の実行
Map<String, Object> params = new HashMap<>();
params.put("status", "ACTIVE");
List<User> users = jdbcManager.selectBySql(
    "SELECT * FROM users WHERE status = /*status*/''", 
    params
);

// SQLファイルの実行
List<User> users = jdbcManager.selectBySqlFile(
    "sql/selectUsers.sql", 
    params
);

MyBatis Generator との統合

プラグインの設定

generatorConfig.xmlに以下のプラグイン設定を追加してください:

<plugin type="jp.vemi.seasarbatis.generator.SBEntityMetaPlugin">
    <property name="addSchemaName" value="true"/>
</plugin>

サンプル設定

完全な設定例はsrc/test/resources/sample-generatorConfig.xmlを参照してください。

国際化(i18n)対応

SeasarBatisは日本語と英語に対応した国際化機能を提供します。

ロケールの設定

import jp.vemi.seasarbatis.core.i18n.SBLocaleConfig;

// 日本語に設定
SBLocaleConfig.getInstance().setJapanese();

// 英語に設定
SBLocaleConfig.getInstance().setEnglish();

// システムのデフォルトロケールに設定
SBLocaleConfig.getInstance().setDefault();

メッセージの取得

import jp.vemi.seasarbatis.core.i18n.SBMessageManager;

SBMessageManager messageManager = SBMessageManager.getInstance();

// 基本的なメッセージの取得
String message = messageManager.getMessage("transaction.error.execution");

// パラメータ付きメッセージの取得
String paramMessage = messageManager.getMessage("transaction.error.savepoint.not.found", "SP001");

例外メッセージの国際化

SeasarBatisの例外クラスは自動的に現在のロケールに応じたメッセージを表示します:

// ロケールが日本語の場合:「トランザクション実行エラー」
// ロケールが英語の場合:「Transaction execution error」
throw new SBTransactionException("transaction.error.execution");

対応メッセージ

  • トランザクション関連エラー
  • エンティティ操作エラー
  • SQL実行エラー
  • 一般的なエラーメッセージ

詳細なメッセージ一覧はsrc/main/resources/jp/vemi/seasarbatis/messages.propertiesmessages_ja.propertiesを参照してください。

楽観的排他制御(Optimistic Locking)

BatisFluid は、バージョンカラムまたは最終更新日時カラムに基づく楽観的排他制御をサポートします。更新時に自動で条件を付与し、競合が検出された場合は例外をスローします。詳細は OPTIMISTIC_LOCKING.md を参照してください。

使い方(エンティティ注釈) - v0.0.2

@FluidTable(name = "users")  // v0.0.2の新アノテーション
public class User {
    @FluidColumn(name = "id", primaryKey = true)
    private Long id;

    @FluidColumn(name = "name")
    private String name;

    // バージョン方式
    @FluidColumn(name = "version", versionColumn = true)
    private Long version;

    // または、最終更新日時方式
    // @FluidColumn(name = "updated_at", lastModifiedColumn = true)
    // private LocalDateTime updatedAt;
}

使い方(エンティティ注釈) - v0.0.1 (非推奨)

@SBTableMeta(name = "users")  // v0.0.2で非推奨
public class User {
    @SBColumnMeta(name = "id", primaryKey = true)
    private Long id;

    @SBColumnMeta(name = "name")
    private String name;

    @SBColumnMeta(name = "version", versionColumn = true)
    private Long version;
}

設定ファイル

v0.0.2 (推奨)

src/main/resources/batisfluid-optimistic-lock.properties で制御方式を上書きできます。

batisfluid.optimistic-lock.enabled=true
batisfluid.optimistic-lock.default-type=NONE

# エンティティごとの上書き
batisfluid.optimistic-lock.entity.com.example.User.type=VERSION
batisfluid.optimistic-lock.entity.com.example.User.column=version

batisfluid.optimistic-lock.entity.com.example.Order.type=LAST_MODIFIED
batisfluid.optimistic-lock.entity.com.example.Order.column=updated_at

v0.0.1 (非推奨、ただしv0.0.2でも互換性維持)

src/main/resources/seasarbatis-optimistic-lock.properties も引き続きサポートされますが、 将来のバージョンでは削除されます。

seasarbatis.optimistic-lock.enabled=true
seasarbatis.optimistic-lock.default-type=NONE

# エンティティごとの上書き
seasarbatis.optimistic-lock.entity.com.example.User.type=VERSION
seasarbatis.optimistic-lock.entity.com.example.User.column=version

seasarbatis.optimistic-lock.entity.com.example.Order.type=LAST_MODIFIED
seasarbatis.optimistic-lock.entity.com.example.Order.column=updated_at

プログラムによる設定

import jp.vemi.seasarbatis.core.config.SBOptimisticLockConfig;
import jp.vemi.seasarbatis.core.config.SBOptimisticLockConfig.EntityLockConfig;
import jp.vemi.seasarbatis.core.config.SBOptimisticLockConfig.LockType;

SBOptimisticLockConfig config = new SBOptimisticLockConfig()
    .setEnabled(true)
    .setDefaultLockType(LockType.VERSION)
    .addEntityConfig(User.class, new EntityLockConfig(LockType.VERSION, null))
    .addEntityConfig(Order.class, new EntityLockConfig(LockType.LAST_MODIFIED, "updated_at"));

SBJdbcManager jdbcManager = new SBJdbcManager(sqlSessionFactory, config);

基本的な流れ

User user = jdbcManager.findByPk(pk).getSingleResult();
Long originalVersion = user.getVersion();

user.setName("Updated");
User updated = jdbcManager.update(user);

// VERSION なら自動で +1 される
assert updated.getVersion().equals(originalVersion + 1);

競合時の例外処理

try {
    User u = jdbcManager.findByPk(pk).getSingleResult();
    u.setName("Updated");
    jdbcManager.update(u);
} catch (SBOptimisticLockException e) {
    // 競合。最新データを取得して再実行など
}

より詳しい設計や制約、エッジケースは OPTIMISTIC_LOCKING.md を参照してください。

既知の制限事項と対応状況

  • /*IF*///*BEGIN*///*END*/ を含む S2JDBC スタイルの SQL コメントはネストや複数行に対応しました。コレクション型パラメータは IN 句で自動的に個別プレースホルダへ展開され、デフォルトリテラルがそのままフォールバック値として機能します。
  • LIKE 句ではデフォルトのワイルドカードを保持したまま #{param} へ置換されるため、/*keyword*/'%' のようなパターンも安全に利用できます。
  • SQL ファイル互換性テストに加え、H2 と MySQL/Oracle/PostgreSQL/SQL Server(いずれも Testcontainers)向けの統合テストを追加し、sql/complex-users-query.sql をサンプルとして実環境相当の動作確認を行っています。
  • 未実装の構文: /*ELSE*/ ブロック、FOR/FOREACH 互換記法、Oracle 固有方言の自動最適化などは今後のロードマップに残っています。これらは docs/SQL-PARSER-DESIGN.md に整理された設計方針と既知の制約を参照してください。

About

A custom SQL builder and 2WaySQL support library for MyBatis inspired by Seasar2.

Resources

License

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors

Languages