MyBatis

MyBatisは、SQL中心の軽量な永続化フレームワークです。SQLの完全制御を提供し、XMLマッピングファイルとアノテーションベースの両方をサポートします。複雑なクエリと高パフォーマンスを重視し、企業のレガシーシステム統合に強みを持つJava向けORMライブラリです。

ORMJavaSQLマッピングSpring Boot

GitHub概要

mybatis/mybatis-3

MyBatis SQL mapper framework for Java

スター20,185
ウォッチ1,138
フォーク12,952
作成日:2013年2月14日
言語:Java
ライセンス:Apache License 2.0

トピックス

javamybatissql

スター履歴

mybatis/mybatis-3 Star History
データ取得日時: 2025/8/13 01:43

ライブラリ

MyBatis

概要

MyBatisは、SQL中心の軽量な永続化フレームワークです。SQLの完全制御を提供し、XMLマッピングファイルとアノテーションベースの両方をサポートします。複雑なクエリと高パフォーマンスを重視し、企業のレガシーシステム統合に強みを持つJava向けORMライブラリです。

詳細

MyBatisは従来のORMとは異なり、SQLを中心としたアプローチを採用しています。開発者が直接SQLを記述することで、データベースへの完全な制御を可能にし、複雑なクエリやストアドプロシージャとの統合を容易にします。動的SQL生成機能により、条件に応じて柔軟なクエリを構築できます。

主な特徴

  • SQL中心設計: 生SQLの記述による完全制御
  • XMLとアノテーション: 両方のマッピング方式をサポート
  • 動的SQL: 条件に応じた柔軟なクエリ生成
  • Spring Boot統合: シームレスな統合とトランザクション管理
  • 高パフォーマンス: 最適化されたクエリ実行とキャッシュ機能

メリット・デメリット

メリット

  • SQLの完全制御により複雑なクエリを効率的に実装
  • 既存のデータベースやストアドプロシージャとの統合が容易
  • 学習コストが低く、SQLに慣れた開発者に適している
  • 軽量で高速なクエリ実行とキャッシュ機能
  • Spring Bootとの優秀な統合によるエンタープライズ対応

デメリット

  • SQLを直接記述するため、データベース依存のコードになりやすい
  • 大量のXMLファイル管理が煩雑になる場合がある
  • 自動的なスキーマ管理機能が限定的
  • 動的型生成がないため、コンパイル時の型チェックが制限的

参考ページ

書き方の例

基本的なセットアップ

<!-- Maven依存関係 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: user
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.model
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true

基本操作(CRUD、XMLマッピング)

// User.java
public class User {
    private int id;
    private String name;
    private String email;
    // getters and setters
}
<!-- UserMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
  
  <select id="selectUser" resultType="User">
    SELECT id, name, email FROM users WHERE id = #{id}
  </select>
  
  <insert id="insertUser">
    INSERT INTO users (name, email) VALUES (#{name}, #{email})
  </insert>
  
  <update id="updateUser">
    UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}
  </update>
  
  <delete id="deleteUser">
    DELETE FROM users WHERE id = #{id}
  </delete>
</mapper>
// UserMapper.java
@Mapper
public interface UserMapper {
    User selectUser(int id);
    void insertUser(User user);
    void updateUser(User user);
    void deleteUser(int id);
}

アノテーションベース

@Mapper
public interface UserMapper {
    
    @Select("SELECT id, name, email FROM users WHERE id = #{id}")
    User selectUser(int id);
    
    @Insert("INSERT INTO users (name, email) VALUES (#{name}, #{email})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insertUser(User user);
    
    @Update("UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}")
    void updateUser(User user);
    
    @Delete("DELETE FROM users WHERE id = #{id}")
    void deleteUser(int id);
    
    @Select("SELECT * FROM users WHERE name LIKE CONCAT('%', #{name}, '%')")
    List<User> findUsersByName(String name);
}

動的SQL

<select id="findUsers" resultType="User">
  SELECT * FROM users
  <where>
    <if test="name != null">
      AND name LIKE CONCAT('%', #{name}, '%')
    </if>
    <if test="email != null">
      AND email = #{email}
    </if>
    <if test="status != null">
      AND status = #{status}
    </if>
  </where>
</select>

<update id="updateUserSelective">
  UPDATE users
  <set>
    <if test="name != null">name = #{name},</if>
    <if test="email != null">email = #{email},</if>
    <if test="status != null">status = #{status}</if>
  </set>
  WHERE id = #{id}
</update>

<select id="findUsersByIds" resultType="User">
  SELECT * FROM users
  WHERE id IN
  <foreach item="id" collection="ids" open="(" separator="," close=")">
    #{id}
  </foreach>
</select>

Spring Boot統合

@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    public User getUserById(int id) {
        return userMapper.selectUser(id);
    }
    
    public void createUser(User user) {
        userMapper.insertUser(user);
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void transferUserData(User fromUser, User toUser) {
        userMapper.updateUser(fromUser);
        userMapper.updateUser(toUser);
        // 例外が発生した場合、自動的にロールバック
    }
}

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable int id) {
        return userService.getUserById(id);
    }
    
    @PostMapping
    public void createUser(@RequestBody User user) {
        userService.createUser(user);
    }
}

高度な機能(カスタムタイプハンドラー、プラグイン等)

// カスタムタイプハンドラー
@Component
public class JsonTypeHandler implements TypeHandler<Map<String, Object>> {
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    @Override
    public void setParameter(PreparedStatement ps, int i, Map<String, Object> parameter, 
                           JdbcType jdbcType) throws SQLException {
        try {
            ps.setString(i, objectMapper.writeValueAsString(parameter));
        } catch (JsonProcessingException e) {
            throw new SQLException(e);
        }
    }
    
    @Override
    public Map<String, Object> getResult(ResultSet rs, String columnName) throws SQLException {
        try {
            String json = rs.getString(columnName);
            return json == null ? null : objectMapper.readValue(json, Map.class);
        } catch (JsonProcessingException e) {
            throw new SQLException(e);
        }
    }
}

// MyBatis設定
@Configuration
public class MyBatisConfig {
    
    @Bean
    public ConfigurationCustomizer mybatisConfigurer() {
        return configuration -> {
            // カスタムタイプハンドラー登録
            configuration.getTypeHandlerRegistry()
                .register(Map.class, JsonTypeHandler.class);
            
            // キャッシュ設定
            configuration.setCacheEnabled(true);
            configuration.setLazyLoadingEnabled(true);
            configuration.setAggressiveLazyLoading(false);
        };
    }
}

// 結果マップでの複雑なマッピング
<resultMap id="userWithProfileMap" type="User">
  <id property="id" column="user_id"/>
  <result property="name" column="user_name"/>
  <result property="profile" column="profile_json" 
          typeHandler="com.example.handler.JsonTypeHandler"/>
  <association property="department" javaType="Department">
    <id property="id" column="dept_id"/>
    <result property="name" column="dept_name"/>
  </association>
  <collection property="roles" ofType="Role">
    <id property="id" column="role_id"/>
    <result property="name" column="role_name"/>
  </collection>
</resultMap>

<select id="selectUserWithProfile" resultMap="userWithProfileMap">
  SELECT u.id as user_id, u.name as user_name, u.profile_json,
         d.id as dept_id, d.name as dept_name,
         r.id as role_id, r.name as role_name
  FROM users u
  LEFT JOIN departments d ON u.department_id = d.id
  LEFT JOIN user_roles ur ON u.id = ur.user_id
  LEFT JOIN roles r ON ur.role_id = r.id
  WHERE u.id = #{id}
</select>