Фреймворк Java MyBatis

Java 常用类库Общие библиотеки классов Java


MyBatis — отличный фреймворк для работы с персистентностью, который упрощает взаимодействие между Java-приложениями и реляционными базами данных. MyBatis сопоставляет SQL-инструкции с объектами Java через XML или аннотации, избегая большого количества шаблонного кода в традиционном программировании JDBC.


Ключевые возможности MyBatis

1. Разделение SQL и кода

MyBatis позволяет разработчикам отделять SQL-инструкции от кода Java, храня их в XML-файлах или аннотациях, что делает код более понятным и простым в поддержке.

2. Автоматическое сопоставление

MyBatis может автоматически отображать результаты запросов к базе данных на объекты Java, значительно уменьшая объём работы по преобразованию данных.

3. Динамический SQL

MyBatis предоставляет мощные возможности динамического SQL, позволяя генерировать различные SQL-инструкции в зависимости от разных условий.

4. Механизм кэширования

MyBatis имеет встроенные кэши первого и второго уровня, которые могут эффективно повысить производительность приложения.

Базовая архитектура MyBatis

1. Основные компоненты

  • SqlSessionFactoryКласс-фабрика для создания SqlSession
  • SqlSessionОсновной интерфейс для выполнения SQL-команд
  • Интерфейс MapperОпределяет методы для работы с базой данных
  • Mapper XMLКонфигурационный файл, содержащий SQL-инструкции

2. Рабочий процесс

  1. Приложение создаёт SqlSessionFactory через SqlSessionFactoryBuilder.
  2. SqlSessionFactory создаёт SqlSession.
  3. SqlSession получает экземпляр интерфейса Mapper.
  4. Вызовите методы Mapper для выполнения операций с базой данных.
  5. Зафиксируйте транзакцию и закройте SqlSession.

Конфигурация MyBatis

1. Основной файл конфигурации (mybatis-config.xml)

Пример

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment>
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_db"/>
                <property name="username" value="root"/>
                <property name="password" value="password"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/example/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

2. Пример файла Mapper

Пример

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
    <select id="getUserById" parameterType="int" resultType="com.example.model.User">
        SELECT * FROM users WHERE id = #{id}
    </select>
   
    <insert id="insertUser" parameterType="com.example.model.User">
        INSERT INTO users(name, email) VALUES(#{name}, #{email})
    </insert>
</mapper>

基本用法

获取

Пример

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSession sqlSessionFactory = new SqlSessionBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();

2. 执行查询

Пример

// 方法 1: 直接使用 SqlSession
User user = session.selectOne("com.example.mapper.UserMapper.getUserById", 1);

// 方法 2: 通过 Mapper 接口
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.getUserById(1);

3. Выполнение вставки

Пример

User newUser = new User();
newUser.setName("Zhang San");
newUser.setEmail("[email protected]");

int rows = mapper.insertUser(newUser);
session.commit(); // Зафиксировать транзакцию

Динамический SQL в MyBatis

MyBatis предоставляет множество элементов динамического SQL, которые могут динамически генерировать SQL-инструкции в зависимости от различных условий:

1. Элемент if

Пример

<select parameterType="map" resultType="User">
    SELECT * FROM users
    WHERE 1=1
    <if test="name != null">
        AND name = #{name}
    </if>
    <if test="email != null">
        AND email = #{email}
    </if>
</select>

2. Элементы choose/when/otherwise

Пример

<select parameterType="map" resultType="User">
    SELECT * FROM users
    WHERE status = 'ACTIVE'
    <choose>
        <when test="name != null">
            AND name like #{name}
        </when>
        <when test="email != null">
            AND email = #{email}
        </when>
        <otherwise>
            AND 1=1
        </otherwise>
    </choose>
</select>

3. Элемент foreach

Пример

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

Ассоциативные запросы MyBatis

1. Ассоциация один-к-одному

Пример

<resultMap type="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <association property="address" javaType="Address">
        <id property="id" column="address_id"/>
        <result property="street" column="street"/>
        <result property="city" column="city"/>
    </association>
</resultMap>

<select resultMap="userWithAddress">
    SELECT
        u.id as user_id, u.name as user_name,
        a.id as address_id, a.street, a.city
    FROM users u
    LEFT JOIN addresses a ON u.address_id = a.id
    WHERE u.id = #{id}
</select>

2. Ассоциация один-ко-многим

Пример

<resultMap type="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <collection property="orders" ofType="Order">
        <id property="id" column="order_id"/>
        <result property="orderDate" column="order_date"/>
        <result property="amount" column="amount"/>
    </collection>
</resultMap>

<select resultMap="userWithOrders">
    SELECT
        u.id as user_id, u.name as user_name,
        o.id as order_id, o.order_date, o.amount
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id
    WHERE u.id = #{id}
</select>

Механизм кэширования MyBatis

1. Кэш первого уровня

Кэш первого уровня — это кэш уровня SqlSession, включённый по умолчанию. В рамках одной и той же SqlSession один и тот же запрос выполнит SQL только один раз.

Возможности:

  • Область видимости:SqlSessionУровень (включён по умолчанию, отключить нельзя)

  • Жизненный цикл: приSqlSessionсоздании SqlSession он создаётся; приSqlSessionзакрытии SqlSession он уничтожается.

  • Сценарии, вызывающие очистку:

    • ExecuteINSERT/UPDATE/DELETEоперации

    • ВызовsqlSession.clearCache()

    • Выполнить откат транзакции

    • Настроить другойStatement ID(даже если SQL тот же)

Примечания:

Пример

// Пример: один и тот же запрос выполняется только один раз в одном и том же SqlSession
User user1 = sqlSession.selectOne("getUserById", 1);  // Выполнить SQL
User user2 = sqlSession.selectOne("getUserById", 1);  // Получено из кэша

2. Кэш второго уровня

Кэш второго уровня — это кэш уровня Mapper, и несколько SqlSessions могут совместно использовать этот кэш. Его необходимо включить в файле конфигурации:

<settings>
    <setting name="cacheEnabled" value="true"/>  <!-- 默认true可省略 -->
</settings>

И настроить его в файле Mapper:

<cache
  eviction="LRU"                <!-- 淘汰策略(默认LRU) -->
  flushInterval="60000"         <!-- 刷新间隔(毫秒) -->
  size="512"                    <!-- 缓存对象数量 -->
  readOnly="true"/>             <!-- 只读模式(性能更优) -->

Сравнение стратегий кэширования:

СтратегияОписаниеПрименимый сценарий
LRUНаименее недавно использованныйОбычное использование
FIFOПервым пришёл — первым ушёлДоступ в фиксированном порядке
SOFTМягкая ссылкаСценарии, чувствительные к памяти
WEAKСлабая ссылкаКрайне чувствительно к памяти

Интеграция MyBatis со Spring

1. Конфигурация зависимостей (рекомендуется для современного Spring Boot)

Пример

<!-- pom.xml -->
<dependencies>
    <!-- Решение интеграции Spring Boot Starter (рекомендуется) -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.3</version>
    </dependency>

    <!-- Или используйте в традиционных проектах Spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>3.0.3</version>
    </dependency>
   
    <!-- Выберите драйвер базы данных в соответствии с фактической ситуацией -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

2. Подходы к конфигурации (три основных способа)

Подход 1: Автоконфигурация Spring Boot (самый простой)

Пример

# application.yml
mybatis
:
  mapper-locations
: classpath*:mapper/**/*.xml
  type-aliases-package
: com.example.model
  configuration
:
    map-underscore-to-camel-case
: true  # Автоматическое преобразование в camel case

Подход 2: Полная конфигурация Java Config (тонкая настройка)

Пример

@Configuration
public class MyBatisConfig {
   
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setTypeAliasesPackage("com.example.model");
       
        // Пользовательская конфигурация (пример)
        org.apache.ibatis.session.Configuration config = new Configuration();
        config.setMapUnderscoreToCamelCase(true);
        config.setDefaultFetchSize(100);
        factory.setConfiguration(config);
       
        // Конфигурация плагина (пример)
        factory.setPlugins(
            new MyBatisInterceptor(),
            new PaginationInterceptor()
        );
       
        return factory.getObject();
    }

    @Bean
    public MapperScannerConfigurer mapperScanner() {
        MapperScannerConfigurer scanner = new MapperScannerConfigurer();
        scanner.setBasePackage("com.example.mapper");
        scanner.setAnnotationClass(Repository.class); // Ограничить сканирование аннотаций
        return scanner;
    }
}

Подход 3: Традиционная конфигурация XML (совместимая с устаревшими проектами)

Пример

<!-- applicationContext.xml -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.mapper"/>
</bean>

3. Конфигурация управления транзакциями

Пример

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
   
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
   
    // Можно определить шаблон транзакции
    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager manager) {
        return new TransactionTemplate(manager);
    }
}

4. Рекомендации по использованию и лучшие практики

Определение интерфейса Mapper:

Пример

@Repository // Укажите аннотации слоя DAO
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectById(@Param("id") int id);
   
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("INSERT INTO users(name) VALUES(#{name})")
    int insert(User user);
}

Пример сервисного слоя:

Пример

@Service
@Transactional(readOnly = true) // Только для чтения по умолчанию
public class UserService {
   
    private final UserMapper userMapper;
   
    @Autowired // Рекомендуется внедрение через конструктор
    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
   
    @Transactional // Для операций записи открывайте транзакции отдельно
    public User createUser(String name) {
        User user = new User();
        user.setName(name);
        userMapper.insert(user);
        return userMapper.selectById(user.getId());
    }
}

5. Интеграция расширенных функций

Конфигурация динамического источника данных:

Пример

@Bean
@Primary
public DataSource dynamicDataSource() {
    DynamicDataSource ds = new DynamicDataSource();
    ds.setDefaultTargetDataSource(primaryDataSource());
    ds.setTargetDataSources(Map.of(
        "master", primaryDataSource(),
        "slave", secondaryDataSource()
    ));
    return ds;
}

Управление транзакциями с несколькими источниками данных:

Пример

@Bean
public PlatformTransactionManager transactionManager() {
    return new ChainedTransactionManager(
        new JpaTransactionManager(entityManagerFactory()),
        new DataSourceTransactionManager(dataSource())
    );
}

Лучшие практики для MyBatis

  1. Используйте интерфейсы Mapper вместо прямого использования SqlSession: это делает код более типобезопасным и упрощает его сопровождение.
  2. Используйте динамический SQL надлежащим образом: избегайте рисков SQL-инъекций и сохраняйте читаемость SQL.
  3. Пакетные операции: для больших пакетных операций с данными используйте пакетную обработку для повышения производительности.
  4. Используйте кэширование надлежащим образом: настраивайте подходящие стратегии кэширования в зависимости от бизнес-требований.
  5. Оптимизация SQL: пишите эффективные SQL-запросы в Mapper XML.

MyBatis — это гибкий и мощный ORM-фреймворк. При правильном использовании он может значительно повысить эффективность разработки и производительность приложения. Надеюсь, эта статья поможет вам понять и использовать фреймворк MyBatis.

Java 常用类库Общие библиотеки классов Java

Другие расширения