MyBatis全解

news2024/9/21 8:50:38

目录

一, MyBatis 概述

1.1-介绍 MyBatis 的历史和发展

1.2-MyBatis 的特点和优势

1.3-MyBatis 与 JDBC 的对比

1.4-MyBatis 与其他 ORM 框架的对比

二, 快速入门

2.1-环境搭建

2.2-第一个 MyBatis 应用程序

2.3-配置文件详解 (mybatis-config.xml)

2.4-Mapper 文件详解 (xxxMapper.xml)

三,核心概念

3.1-Session 和 SqlSessionFactory

3.2-映射器 (Mapper) 接口和 XML 映射文件

3.3-SQL 语句的执行流程

3.4-结果映射 (Result Mapping)

四,动态 SQL

4.1-if, choose, when, otherwise

4.2-trim, where, set

4.3-foreach

4.4-bind

五,参数处理

5.1-单个参数

5.2-多个参数

5.3-Map 参数

5.4-POJO 参数

六,类型处理器 (Type Handlers)

6.1-内置类型处理器

6.2-自定义类型处理器

七,高级特性

7.1-关联映射 (一对一、多对一)

7.2-集合映射 (一对多)

7.3-嵌套查询和嵌套结果

7.4-缓存机制 (一级缓存和二级缓存)

7.5-事务管理

八,插件机制

8.1-日志插件

8.2-分页插件

九,集成 Spring

9.1-Spring 中的 MyBatis 配置

9.2-使用 Spring 管理 MyBatis 的 SqlSessionFactory 和 Mapper

十, 最佳实践

10.1-代码组织结构

10.2-异常处理

10.3-测试策略

十一,实战案例

十二,社区和扩展

12.1-MyBatis 生态系统

12.2-第三方工具集成

12.3-社区资源和文档


一, MyBatis 概述

1.1-介绍 MyBatis 的历史和发展

  • 起源:MyBatis 的前身是 iBatis,最初由 Clinton Begin 在2001年创建。
  • 发展:2008年左右,iBatis 被捐赠给了 Apache 软件基金会,并更名为 MyBatis。
  • 版本演变:MyBatis 3.x 版本引入了大量的改进和新特性,成为目前最广泛使用的版本。
  • 社区与生态:MyBatis 拥有活跃的社区和丰富的插件生态系统,包括 MyBatis Generator、MyBatis Plus 等工具。

1.2-MyBatis 的特点和优势

  • 轻量级:MyBatis 相对于其他 ORM 框架来说更为轻量,易于上手且灵活性高。
  • SQL 语句编写:允许直接编写 SQL 语句,提供更高的定制能力和优化空间。
  • 结果映射:提供强大的结果映射机制,能够灵活地处理复杂的数据结构。
  • 动态 SQL:支持动态 SQL 语法,可以根据条件生成不同的 SQL 语句。
  • 插件机制:支持插件扩展,可以方便地添加日志记录、分页等功能。
  • 与 Spring 的良好集成:能够很好地与 Spring 框架集成,提供依赖注入和事务管理等功能。

1.3-MyBatis 与 JDBC 的对比

  • 封装性:MyBatis 对 JDBC 进行了封装,避免了直接与 JDBC API 交互的繁琐步骤。

  • 资源管理:MyBatis 自动管理 SQL 会话,减少了资源泄漏的风险。

  • 错误处理:MyBatis 提供更友好的异常处理机制,使得错误定位更加容易。

  • SQL 语句编写:MyBatis 支持直接编写 SQL 语句,而 JDBC 需要程序员自行构建 SQL 语句。

  • 结果映射:MyBatis 提供了自动化的结果映射机制,使得数据转换更为简单。

1.4-MyBatis 与其他 ORM 框架的对比

Hibernate:

  • 全自动 vs 半自动:Hibernate 是全自动的 ORM 框架,而 MyBatis 是半自动的。
  • 易用性:Hibernate 提供了更多的自动化特性,如懒加载、缓存等。
  • 性能:MyBatis 因为更接近 SQL 的控制,所以可能在某些场景下有更好的性能。

JPA (Java Persistence API):

  • 标准 vs 实现:JPA 是一种规范,而 Hibernate 是 JPA 的实现之一;MyBatis 则是一种具体的框架。
  • API 使用:JPA 使用面向对象的方式,而 MyBatis 更偏向于 SQL 语句的编写。

Spring Data JPA:

  • 集成度:Spring Data JPA 与 Spring 集成度更高,提供了更多的便捷方法。
  • 灵活性:MyBatis 在编写 SQL 方面提供了更大的灵活性。


二, 快速入门

2.1-环境搭建

1.安装 JDK

  • 确保已安装 JDK 1.8 或以上版本。
  • 设置环境变量 JAVA_HOME。

2.安装 Maven (可选)

  • 如果使用 Maven 管理项目依赖,则需安装 Maven。
  • 设置环境变量 M2_HOME 和 MAVEN_HOME。

3.创建 Maven 项目

  • 使用 IntelliJ IDEA 或 Eclipse 创建一个新的 Maven 项目。
  • 添加 MyBatis 和 MySQL 的依赖到 pom.xml 文件中。
   <dependencies>
       <!-- MyBatis -->
       <dependency>
           <groupId>org.mybatis</groupId>
           <artifactId>mybatis</artifactId>
           <version>3.5.11</version>
       </dependency>
       <!-- MySQL Connector/J -->
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>8.0.32</version>
       </dependency>
       <!-- Logging -->
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>1.7.36</version>
       </dependency>
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-simple</artifactId>
           <version>1.7.36</version>
       </dependency>
   </dependencies>

4.配置 MySQL 数据库

  • 创建数据库和表。
  • 配置数据库连接信息。

5.创建实体类

  • 根据数据库表结构创建对应的 Java 实体类。

6.创建 MyBatis 配置文件

  • 创建 mybatis-config.xml 文件。

7.编写 Mapper 文件

  • 创建 UserMapper.xml 文件。

2.2-第一个 MyBatis 应用程序

1.创建实体类

  • 创建 User.java 实体类。
        @Data
        @NoArgsConstructor
        @AllArgsConstructor    
         public class User {
         private int id;
         private String name;
         private String email;
        }

2.创建 Mapper 接口

  • 创建 UserMapper.java 接口。
     import org.apache.ibatis.annotations.Select;
     public interface UserMapper {
         @Select("SELECT * FROM users WHERE id = #{id}")
         User getUserById(int id);
     }

3.创建 Mapper 文件

  • 创建 UserMapper.xml 文件。
     <?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" resultType="com.example.entity.User">
             SELECT * FROM users WHERE id = #{id}
         </select>
     </mapper>

4.配置文件详解 (mybatis-config.xml)

  • 创建 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 id="development">
                 <transactionManager type="JDBC"/>
                 <dataSource type="POOLED">
                     <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                     <property name="url" value="jdbc:mysql://localhost:3306/mydatabase?useSSL=false&amp;serverTimezone=UTC"/>
                     <property name="username" value="root"/>
                     <property name="password" value="password"/>
                 </dataSource>
             </environment>
         </environments>
         <mappers>
             <mapper resource="com/example/mapper/UserMapper.xml"/>
         </mappers>
     </configuration>

5.编写测试类

  • 创建 UserTest.java 文件来测试 MyBatis 的基本功能。
     import org.apache.ibatis.io.Resources;
     import org.apache.ibatis.session.SqlSession;
     import org.apache.ibatis.session.SqlSessionFactory;
     import org.apache.ibatis.session.SqlSessionFactoryBuilder;

     import java.io.IOException;
     import java.io.InputStream;

     public class UserTest {
         public static void main(String[] args) throws IOException {
             InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
             try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
                 UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                 User user = userMapper.getUserById(1);
                 System.out.println(user);
             }
         }
     } 

2.3-配置文件详解 (mybatis-config.xml)

        mybatis-config.xml 是 MyBatis 的核心配置文件,用于配置 MyBatis 的运行环境以及其他各种属性。下面是 mybatis-config.xml 的详细说明:


1. 根元素 <configuration>

属性:

说明:根元素,包含了所有子元素。

2.<properties>

属性:

  • resource:指定外部 properties 文件的位置。
  • url:指定外部 properties 文件的 URL。
  • defaultPrefix:指定默认前缀,用于在 properties 文件中区分默认值。

说明:

  • 用于读取外部的 properties 文件,可以用来配置数据库连接信息等。

3. <settings>

属性:
说明:包含一系列的设置项,用于改变 MyBatis 的行为。

4. <typeAliases>

属性:
说明:用于配置别名,可以简化 Java 类型的引用。

5. <typeHandlers>

属性:
说明:用于配置自定义的类型处理器,可以处理特定类型的转换。

6. <objectFactory>

属性:
说明:用于配置 MyBatis 创建新实例的方式,默认使用 Java 默认构造器。

7. <plugins>

属性:
说明:用于配置 MyBatis 的插件,可以增强或修改 MyBatis 的行为。

8. <environments>

属性:
说明:用于配置不同的环境,如开发、测试、生产等。

<environment>:表示一个具体的环境配置。

        属性:
                id:环境标识符。
        子元素:
                <transactionManager>:配置事务管理器。
                        属性:
                                type:事务管理器类型,如 JDBC、MANAGED。
                <dataSource>:配置数据源。
                        属性:
                                type:数据源类型,如 UNPOOLED、POOLED、JNDI。
                        子元素:
                                <property>:配置数据源的属性,如 url、username、password。

9. <databaseIdProvider>

属性:
说明:用于配置数据库 ID 提供者,可以用于区分不同的数据库方言。

10. <mappers>

属性:
说明:用于配置 Mapper 接口或 Mapper XML 文件的位置。
        <mapper>:配置单个 Mapper 文件或接口。
                属性:
                        resource:指定 Mapper 文件的资源路径。
                        url:指定 Mapper 文件的 URL。
                        class:指定 Mapper 接口的全限定类名。
        <package>:批量注册指定包下的所有 Mapper 接口。
                属性:
                        name:指定包名。

<?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>
    <properties resource="db.properties"/>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <typeAliases>
        <typeAlias alias="User" type="com.example.entity.User"/>
    </typeAliases>
    <typeHandlers>
        <typeHandler handler="com.example.handler.MyTypeHandler"/>
    </typeHandlers>
    <objectFactory>
        <property name="defaultScope" value="prototype"/>
    </objectFactory>
    <plugins>
        <plugin interceptor="com.example.interceptor.MyInterceptor">
            <property name="someProperty" value="100"/>
        </plugin>
    </plugins>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/example/mapper/UserMapper.xml"/>
        <mapper class="com.example.mapper.UserMapper"/>
        <package name="com.example.mapper"/>
    </mappers>
</configuration>

2.4-Mapper 文件详解 (xxxMapper.xml)

xxxMapper.xml 是 MyBatis 的映射文件,用于定义 SQL 语句和结果映射规则。下面是 xxxMapper.xml 的详细说明:


1. 根元素 <mapper>

属性:namespace:指定 Mapper 接口的全限定类名或唯一标识符。
说明:根元素,包含了所有的映射语句。

2. <insert>

属性:

  • id:映射语句的唯一标识符。
  • parameterType:传入参数的类型。
  • useGeneratedKeys:是否使用主键自动生成。
  • keyProperty:指定自动生成的主键值放入哪个字段。
  • keyColumn:指定自动生成的主键值的列名。

说明:定义插入语句。

3. <update>

属性:

  • id:映射语句的唯一标识符。
  • parameterType:传入参数的类型。

说明:定义更新语句。

4. <delete>

属性:

  • id:映射语句的唯一标识符。
  • parameterType:传入参数的类型。

说明:定义删除语句。

5. <select>

属性:

  • id:映射语句的唯一标识符。
  • parameterType:传入参数的类型。
  • resultType:返回结果的类型。
  • resultSetType:结果集类型,如 FORWARD_ONLY、SCROLL_INSENSITIVE、SCROLL_SENSITIVE。

说明:定义查询语句。

6. <resultMap>

属性:

  • id:映射语句的唯一标识符。
  • type:返回结果的类型。

说明:定义结果映射规则,可以用于复杂的映射逻辑。

7. <sql>

属性:id:SQL 片段的唯一标识符。
说明:定义可重用的 SQL 片段。

8. <include>

属性:refid:引用 <sql> 元素的 ID。
说明:引用 <sql> 元素定义的 SQL 片段。

9. 动态 SQL

元素:
<if>, <choose>, <when>, <otherwise>,<where>,<set>,<foreach>,<bind>
说明:用于构建动态 SQL 语句。

<?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="selectUserById" parameterType="int" resultType="User">
        SELECT * FROM users WHERE id = #{id}
    </select>
    <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO users (name, email) VALUES (#{name}, #{email})
    </insert>
    <update id="updateUser" parameterType="User">
        UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}
    </update>
    <delete id="deleteUser" parameterType="int">
        DELETE FROM users WHERE id = #{id}
    </delete>
</mapper>

三,核心概念

3.1-Session 和 SqlSessionFactory

Session 和 SqlSessionFactory
SqlSessionFactory:

SqlSessionFactory 是 MyBatis 中最重要的对象之一,它是线程安全的,可以在应用程序的生命周期内存在一个实例。
它的主要作用是创建 SqlSession 对象。
通常情况下,SqlSessionFactory 会在应用程序启动时创建一次,并在整个应用的生命周期中复用。

SqlSession:

SqlSession 是 MyBatis 的主要 API,它代表了一次数据库会话。
每当需要执行 SQL 语句时,都需要从 SqlSessionFactory 中获取一个 SqlSession 实例。
SqlSession 是线程不安全的,因此每次使用后应该关闭它。
SqlSession 提供了执行 SQL 语句的方法,包括 insert、update、delete select。

3.2-映射器 (Mapper) 接口和 XML 映射文件

Mapper 接口:

Mapper 接口是 Java 接口,它定义了 SQL 语句的抽象方法。
这些方法的名称和参数类型被 MyBatis 用来定位相应的 SQL 语句。
当调用 Mapper 接口的方法时,MyBatis 会执行相应的 SQL 语句并返回结果。

XML 映射文件:

XML 映射文件包含了具体的 SQL 语句结果映射规则
映射文件中的每个 <mapper> 标签都有一个 namespace 属性,该属性的值通常是 Mapper 接口的全限定名。
映射文件中的 <select>、<insert>、<update> 和 <delete> 标签定义了 SQL 语句和参数类型。
<resultMap> 标签用于定义结果映射规则,它指定了如何将数据库结果集映射到 Java 对象。

3.3-SQL 语句的执行流程

1.获取 SqlSessionFactory:
通过 SqlSessionFactoryBuilder 构建一个 SqlSessionFactory 实例。
2.打开 SqlSession:
通过 SqlSessionFactory.openSession() 方法获取一个 SqlSession 实例。
3.获取 Mapper:
通过 SqlSession.getMapper() 方法获取一个 Mapper 接口的实现。
4.执行 SQL 语句:
调用 Mapper 接口中定义的方法来执行 SQL 语句。
MyBatis 会根据方法签名查找对应的 SQL 语句并执行。
5.处理结果:
如果是查询语句,MyBatis 会根据 <resultMap> 或 resultType 属性将结果集映射到 Java 对象。
如果是 DML 语句(如 insert、update、delete),则返回影响的行数。
6.提交或回滚:
如果需要,调用 SqlSession.commit() 提交事务。
如果出现错误,调用 SqlSession.rollback() 回滚事务。
7.关闭 SqlSession:
最后,调用 SqlSession.close() 关闭会话。

3.4-结果映射 (Result Mapping)

概述:
结果映射是 MyBatis 的核心特性之一,它允许开发者定义如何将 SQL 查询结果映射到 Java 对象。
通过 <resultMap> 标签可以定义复杂的映射规则,包括一对一一对多多对多的关系映射。
基本映射:

  • 在 <resultMap> 中,使用 <id> 和 <result> 标签来定义列到 Java 属性的映射。
  • <id> 通常用于主键字段,而 <result> 用于其他字段。
  • 可以指定 column 和 property 属性来关联数据库列名和 Java 属性名。

高级映射:

  • 对于复杂的一对多或一对多关系,可以使用 <association> 和 <collection> 标签。<association> 用于一对一关系,而 <collection> 用于一对多关系。
  • 这些标签支持嵌套查询和嵌套结果映射,从而可以处理复杂的对象图。

四,动态 SQL

4.1-if, choose, when, otherwise

if:

if:用于根据条件判断是否添加某个 SQL 子句。
属性:test: 表达式,如果为 true,则包含此元素内的 SQL 语句;否则忽略。
示例:

    <if test="name != null">
        AND name = #{name}
    </if>
    

 choose, when, otherwise:

choose, when, otherwise:
类似于 Java 中的 switch-case 语句,用于多个条件的选择。
choose 开始选择结构,when 定义一个条件,otherwise 定义默认情况。
示例:
 

    <choose>
        <when test="name != null">
            AND name = #{name}
        </when>
        <when test="age != null">
            AND age = #{age}
        </when>
        <otherwise>
            ORDER BY id DESC
        </otherwise>
    </choose>

4.2-trim, where, set

trim, where, set:

trim:用于去除多余的前缀或后缀,例如去除多余的 AND 或 OR。
属性:

  • prefix: 前缀字符串。
  • suffix: 后缀字符串。
  • prefixOverrides: 指定需要去除的前缀字符串。
  • suffixOverrides: 指定需要去除的后缀字符串。

示例:

    <trim prefix="WHERE" suffixOverrides="AND">
        <if test="name != null">
            name = #{name} AND
        </if>
        <if test="age != null">
            age = #{age} AND
        </if>
    </trim>

 where:

where:
用于自动处理 WHERE 子句的添加和多余的 AND 或 OR 的去除。
示例:

    <where>
        <if test="name != null">
            name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>

 set:

set:
用于自动处理 SET 子句的添加和多余的逗号的去除。
示例:

    <set>
        <if test="name != null">
            name = #{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
    </set>

4.3-foreach

foreach:

foreach:
用于遍历集合或数组,并生成多个 SQL 子句。
属性:

  • collection: 集合或数组的名称。
  • item: 遍历时使用的变量名称。
  • index: 集合索引的变量名称。
  • open: 开始符号。
  • close: 结束符号。
  • separator: 分隔符。

示例:

    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>

4.4-bind

bind:

bind:
用于绑定表达式的值到一个变量。
属性:

  • name: 绑定变量的名称。
  • value: 绑定的表达式。

示例:

    <bind name="searchName" value="'%' + name + '%'"/>
    <select id="findByName" resultType="User">
        SELECT * FROM users WHERE name LIKE #{searchName}
    </select>

五,参数处理

5.1-单个参数

直接使用:
当只有一个参数时,可以直接使用 #{} 占位符。
示例:

    <select id="findUserById" parameterType="int" resultType="com.example.entity.User">
        SELECT * FROM users WHERE id = #{id}
    </select>

使用 @Param 注解:
即使只有一个参数,也可以使用 @Param 注解来明确参数名称。
示例:

    <select id="findUserById" parameterType="int" resultType="com.example.entity.User">
        SELECT * FROM users WHERE id = #{id}
    </select> 

5.2-多个参数

使用 @Param 注解:
当有多个参数时,可以使用 @Param 注解来指定每个参数的名称。
示例:

JAVA:

 List<User> findUsers(@Param("name") String name, @Param("age") Integer age);

XML:

    <select id="findUsers" parameterType="map" resultType="com.example.entity.User">
        SELECT * FROM users
        <where>
            <if test="name != null">
                name = #{name}
            </if>
            <if test="age != null">
                AND age = #{age}
            </if>
        </where>
    </select>

使用 Map 参数:
也可以直接传递一个 Map 对象作为参数。
示例: 

JAVA:

    List<User> findUsers(Map<String, Object> params);

XML:

    <select id="findUsers" parameterType="map" resultType="com.example.entity.User">
        SELECT * FROM users
        <where>
            <if test="name != null">
                name = #{name}
            </if>
            <if test="age != null">
                AND age = #{age}
            </if>
        </where>
    </select>

5.3-Map 参数

直接传递 Map:
当需要传递多个参数时,可以使用 Map 对象。
示例:

JAVA:

    List<User> findUsersByParams(Map<String, Object> params);

XML:

    <select id="findUsersByParams" parameterType="map" resultType="com.example.entity.User">
        SELECT * FROM users
        <where>
            <if test="name != null">
                name = #{name}
            </if>
            <if test="age != null">
                AND age = #{age}
            </if>
        </where>
    </select>

5.4-POJO 参数

使用 POJO 对象:
当参数是一个 Java 对象时,可以使用该对象的属性作为 SQL 语句中的参数。
示例:

    public class UserSearchCriteria {
        private String name;
        private Integer age;
        // Getters and Setters
    }

    List<User> findUsers(UserSearchCriteria criteria);

XML:

    <select id="findUsers" parameterType="com.example.entity.UserSearchCriteria" resultType="com.example.entity.User">
        SELECT * FROM users
        <where>
            <if test="name != null">
                name = #{name}
            </if>
            <if test="age != null">
                AND age = #{age}
            </if>
        </where>
    </select>

六,类型处理器 (Type Handlers)

6.1-内置类型处理器

MyBatis 提供了一系列内置的类型处理器,用于处理常见的 Java 类型和 JDBC 类型之间的转换。这些内置类型处理器包括但不限于:
基本数据类型:

  • BooleanTypeHandler
  • ByteTypeHandler
  • ShortTypeHandler
  • IntegerTypeHandler
  • LongTypeHandler
  • FloatTypeHandler
  • DoubleTypeHandler
  • BigDecimalTypeHandler
  • StringTypeHandler
  • NStringTypeHandler
  • DateTypeHandler
  • TimestampTypeHandler
  • BlobTypeHandler
  • ClobTypeHandler

其他类型:

  • EnumTypeHandler
  • EnumOrdinalTypeHandler
  • LocalDateTimeTypeHandler
  • LocalDateTypeHandler
  • LocalTimeTypeHandler

这些内置类型处理器通常不需要额外配置,因为 MyBatis 会自动识别并使用它们。

6.2-自定义类型处理器

对于一些特殊的数据类型,如自定义的 Java 类或枚举类型,可能需要自定义类型处理器来实现转换逻辑。
创建自定义类型处理器:
创建一个类实现 org.apache.ibatis.type.TypeHandler 接口。
实现 setParameter 和 getResult 方法。
可以使用 @MappedJdbcTypes 和 @MappedTypes 注解来指定处理器支持的 JDBC 类型和 Java 类型。
注册自定义类型处理器:
在 MyBatis 的配置文件中注册类型处理器。
或者使用 typeHandlers 元素来注册自定义类型处理器。

七,高级特性

7.1-关联映射 (一对一、多对一)

在关联映射中,你需要处理两个实体之间的关系,例如一对多或多对一的关系。MyBatis 支持通过 <association> 和 <collection> 标签来处理这些关系。
一对一关联映射:
使用 <association> 标签来处理两个实体之间的一对一关系。
示例:

    <resultMap id="UserWithProfileResultMap" type="com.example.entity.User">
        <id property="id" column="user_id"/>
        <result property="username" column="username"/>
        <association property="profile" javaType="com.example.entity.Profile">
            <id property="id" column="profile_id"/>
            <result property="bio" column="bio"/>
        </association>
    </resultMap>

7.2-集合映射 (一对多)

多对一关联映射:
使用 <association> 标签来处理两个实体之间的多对一关系。
示例:

    <resultMap id="OrderWithCustomerResultMap" type="com.example.entity.Order">
        <id property="id" column="order_id"/>
        <result property="orderNumber" column="order_number"/>
        <association property="customer" javaType="com.example.entity.Customer">
            <id property="id" column="customer_id"/>
            <result property="name" column="name"/>
        </association>
    </resultMap>

当需要处理一对多的关系时,可以使用 <collection> 标签来处理。
一对多关联映射:
使用 <collection> 标签来处理两个实体之间的一对多关系。
示例:

    <resultMap id="CustomerWithOrdersResultMap" type="com.example.entity.Customer">
        <id property="id" column="customer_id"/>
        <result property="name" column="name"/>
        <collection property="orders" ofType="com.example.entity.Order">
            <id property="id" column="order_id"/>
            <result property="orderNumber" column="order_number"/>
        </collection>
    </resultMap>

7.3-嵌套查询和嵌套结果

在某些情况下,你可能希望先执行一次查询来获取主实体,然后再基于主实体的结果执行额外的查询来获取关联实体。这可以通过嵌套查询和嵌套结果来实现。
嵌套查询:
使用 <association> 或 <collection> 标签中的 select 属性来指定另一个查询 ID。
示例:

    <resultMap id="CustomerWithOrdersResultMap" type="com.example.entity.Customer">
        <id property="id" column="customer_id"/>
        <result property="name" column="name"/>
        <collection property="orders" ofType="com.example.entity.Order" select="com.example.mapper.OrderMapper.findOrdersByCustomerId"/>
    </resultMap>

嵌套结果:

使用 <association> 或 <collection> 标签中的 column 属性来指定关联查询所需的列。
示例:

    <resultMap id="CustomerWithOrdersResultMap" type="com.example.entity.Customer">
        <id property="id" column="customer_id"/>
        <result property="name" column="name"/>
        <collection property="orders" ofType="com.example.entity.Order">
            <id property="id" column="order_id"/>
            <result property="orderNumber" column="order_number"/>
        </collection>
    </resultMap>

    <select id="findCustomersWithOrders" resultMap="CustomerWithOrdersResultMap">
        SELECT c.*, o.order_id, o.order_number
        FROM customers c
        LEFT JOIN orders o ON c.customer_id = o.customer_id
    </select>

7.4-缓存机制 (一级缓存和二级缓存)

MyBatis 提供了一级缓存和二级缓存机制来提高应用程序的性能。
一级缓存:
默认开启,基于 PerSqlSession 的缓存,即同一个 SqlSession 中的两次相同的查询会从缓存中获取数据。
示例:

    try (SqlSession session = sqlSessionFactory.openSession()) {
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.findUserById(1);  // 第一次查询
        User user2 = userMapper.findUserById(1);  // 从缓存中获取
    }

二级缓存:

需要手动开启,并且默认关闭。
基于 PerNamespace 的缓存,即同一个命名空间下的所有 SQL 语句共享一个缓存。
示例:

    <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

    <select id="findUserById" parameterType="int" resultType="com.example.entity.User" useCache="true">
        SELECT * FROM users WHERE id = #{id}
    </select>

7.5-事务管理

MyBatis 支持多种事务管理方式,包括通过 JDBC 连接管理事务或集成 Spring 框架进行事务管理。
JDBC 事务管理:
在 SqlSession 中管理事务。
示例:

    try (SqlSession session = sqlSessionFactory.openSession()) {
        UserMapper userMapper = session.getMapper(UserMapper.class);
        userMapper.insertUser(new User("Alice", 30));
        session.commit();  // 提交事务
    } catch (Exception e) {
        session.rollback();  // 回滚事务
    }

Spring 事务管理:
通过 Spring 的 AOP 功能管理事务。
示例:

    @Transactional
    public void createUser(User user) {
        userMapper.insertUser(user);
    }

八,插件机制

8.1-日志插件

日志插件可以帮助你记录 SQL 语句和参数,这对于调试和性能分析非常有用。
实现:
创建一个实现了 org.apache.ibatis.plugin.Interceptor 接口的类。
实现 intercept 方法来拦截目标对象的方法调用。
使用 @Intercepts 注解来指定要拦截的目标对象和方法。
示例:

  import org.apache.ibatis.executor.Executor;
  import org.apache.ibatis.mapping.BoundSql;
  import org.apache.ibatis.mapping.MappedStatement;
  import org.apache.ibatis.plugin.*;
  import org.apache.ibatis.session.ResultHandler;
  import org.apache.ibatis.session.RowBounds;
  import java.util.Properties;
  @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
               @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
  public class LoggingInterceptor implements Interceptor {
      @Override
      public Object intercept(Invocation invocation) throws Throwable {
          MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
          BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
          System.out.println("Executing SQL: " + boundSql.getSql());
          return invocation.proceed();
      }
      @Override
      public Object plugin(Object target) {
          return Plugin.wrap(target, this);
      }
      @Override
      public void setProperties(Properties properties) {
      }
  }

注册:
在 MyBatis 的配置文件中注册插件。
示例:

    <?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>
      <plugins>
        <plugin interceptor="com.example.interceptor.LoggingInterceptor"/>
      </plugins>
      <!-- 其他配置 -->
    </configuration>

8.2-分页插件

分页插件可以自动修改 SQL 语句来支持分页查询。
实现:
创建一个实现了 org.apache.ibatis.plugin.Interceptor 接口的类。
实现 intercept 方法来修改 SQL 语句以支持分页。
使用 @Intercepts 注解来指定要拦截的目标对象和方法。
示例:

  import org.apache.ibatis.executor.Executor;
  import org.apache.ibatis.mapping.BoundSql;
  import org.apache.ibatis.mapping.MappedStatement;
  import org.apache.ibatis.plugin.*;
  import org.apache.ibatis.session.ResultHandler;
  import org.apache.ibatis.session.RowBounds;
  import java.sql.Connection;
  import java.util.Properties;
  @Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
  public class PaginationInterceptor implements Interceptor {
      @Override
      public Object intercept(Invocation invocation) throws Throwable {
          MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
          RowBounds rowBounds = (RowBounds) invocation.getArgs()[2];
          Connection connection = (Connection) invocation.getArgs()[3];
          BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
          String originalSql = boundSql.getSql();
          // Modify the SQL for pagination
          String paginatedSql = modifySqlForPagination(originalSql, rowBounds);
          // Update the BoundSql with the modified SQL
          BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), paginatedSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
          mappedStatement.setBoundSql(newBoundSql);
          return invocation.proceed();
      }
      private String modifySqlForPagination(String originalSql, RowBounds rowBounds) {
          // Implement pagination logic based on the database dialect
          // Example for MySQL:
          int offset = rowBounds.getOffset();
          int limit = rowBounds.getLimit();
          return originalSql + " LIMIT " + offset + ", " + limit;
      }
      @Override
      public Object plugin(Object target) {
          return Plugin.wrap(target, this);
      }
      @Override
      public void setProperties(Properties properties) {
      }
  }
  

注册:
在 MyBatis 的配置文件中注册插件。
示例:

    <?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>
      <plugins>
        <plugin interceptor="com.example.interceptor.PaginationInterceptor"/>
      </plugins>
      <!-- 其他配置 -->
    </configuration>

8.3-性能监控插件

性能监控插件可以用来记录 SQL 执行的时间和其他性能指标。
实现:
创建一个实现了 org.apache.ibatis.plugin.Interceptor 接口的类。
实现 intercept 方法来记录 SQL 执行的时间。
使用 @Intercepts 注解来指定要拦截的目标对象和方法。
示例:

  import org.apache.ibatis.executor.Executor;
  import org.apache.ibatis.mapping.MappedStatement;
  import org.apache.ibatis.plugin.*;
  import org.apache.ibatis.session.ResultHandler;
  import org.apache.ibatis.session.RowBounds;
  import java.util.Properties;
  @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
               @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
  public class PerformanceMonitorInterceptor implements Interceptor {
      @Override
      public Object intercept(Invocation invocation) throws Throwable {
          long start = System.currentTimeMillis();
          Object result = invocation.proceed();
          long end = System.currentTimeMillis();
          long executionTime = end - start;
          MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
          BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
          System.out.println("Execution time: " + executionTime + " ms for SQL: " + boundSql.getSql());
          return result;
      }
      @Override
      public Object plugin(Object target) {
          return Plugin.wrap(target, this);
      }
      @Override
      public void setProperties(Properties properties) {
      }
  }

注册:
在 MyBatis 的配置文件中注册插件。
示例:

    <?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>
      <plugins>
        <plugin interceptor="com.example.interceptor.PerformanceMonitorInterceptor"/>
      </plugins>
      <!-- 其他配置 -->
    </configuration>

解释
LoggingInterceptor:
实现了 Interceptor 接口。
使用 @Intercepts 注解指定了要拦截的 Executor 类的 update 和 query 方法。
在 intercept 方法中记录了 SQL 语句。


PaginationInterceptor:
实现了 Interceptor 接口。
使用 @Intercepts 注解指定了要拦截的 Executor 类的 query 方法。
在 intercept 方法中修改了 SQL 语句以支持分页。
修改了 BoundSql 对象以反映新的 SQL 语句。


PerformanceMonitorInterceptor:
实现了 Interceptor 接口。
使用 @Intercepts 注解指定了要拦截的 Executor 类的 update 和 query 方法。
在 intercept 方法中记录了 SQL 执行的时间。 

九,集成 Spring

9.1-Spring 中的 MyBatis 配置

首先确保你的项目包含了 Spring 和 MyBatis 相关的依赖。如果你使用 Maven,可以在 pom.xml 文件中添加如下依赖:

<!-- Spring 框架 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.version}</version>
</dependency>

<!-- MyBatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

<!-- 数据库驱动,例如MySQL -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${mysql.driver.version}</version>
</dependency>

9.2-使用 Spring 管理 MyBatis 的 SqlSessionFactory 和 Mapper

在 Spring 配置文件(如 applicationContext.xml 或使用 Java Config 类)中配置数据源和 SqlSessionFactory。

XML 配置示例:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb?useSSL=false&amp;serverTimezone=UTC"/>
    <property name="username" value="yourUsername"/>
    <property name="password" value="yourPassword"/>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!-- 配置 MyBatis 的配置文件位置 -->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!-- 配置 Mapper 文件的位置 -->
    <property name="mapperLocations" value="classpath*:mapper/*.xml"/>
</bean>

Java Config 示例:

@Configuration
public class DatabaseConfig {

    @Value("${spring.datasource.url}")
    private String dbUrl;
    
    @Value("${spring.datasource.username}")
    private String username;
    
    @Value("${spring.datasource.password}")
    private String password;
    
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl(dbUrl);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
        return factoryBean.getObject();
    }
}

配置 MapperScannerConfigurer

为了自动扫描和注册 Mapper 接口,你需要配置 MapperScannerConfigurer。这一步可以通过 XML 或 Java Config 完成。

XML 配置示例:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.yourcompany.yourproject.mapper"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

Java Config 示例:

@Configuration
public class MyBatisConfig {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.yourcompany.yourproject.mapper");
        configurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        return configurer;
    }
}

使用 Mapper 接口

在你的服务类中,直接注入 Mapper 接口,Spring 会自动创建代理对象并管理它的生命周期。

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public List<User> getAllUsers() {
        return userMapper.selectAll();
    }
}

十, 最佳实践

10.1-代码组织结构

1.模块化设计:

  • 将应用程序划分为多个模块或包,例如 dao, service, controller 等。
  • 每个模块负责特定的功能,如数据访问、业务逻辑处理、用户交互等。

2.DAO 层设计:

  • DAO (Data Access Object) 层用于封装对数据库的操作。
  • 每个实体类都应该有一个对应的 DAO 接口和实现类。
  • DAO 接口定义了与数据库交互的方法,实现类则使用 MyBatis 来执行具体的 SQL 操作。

3.Service 层设计:

  • Service 层用于处理业务逻辑。
  • 服务层调用 DAO 层提供的方法,并进行必要的业务逻辑处理。
  • 通常情况下,服务层不直接操作数据库。

4.Controller 层设计:

  • Controller 层负责接收 HTTP 请求并调用服务层进行处理。
  • 控制器层还负责将处理结果返回给客户端。

5.资源分离:

  • 将 MyBatis 的映射文件(XML 文件)与 Java 代码分离。
  • 映射文件应该放在专门的目录下,如 src/main/resources/mapper。
  • 映射文件中定义 SQL 查询语句和结果映射规则。

6.配置文件管理:

  • 使用单独的配置文件来管理 MyBatis 的配置信息,如数据库连接信息、插件配置等。
  • 配置文件应放置在 src/main/resources 目录下。

7.实体类设计:

  • 设计实体类时应遵循 POJO (Plain Old Java Object) 原则。
  • 实体类应仅包含属性和基本的 getter/setter 方法。
  • 避免在实体类中添加复杂的业务逻辑。

10.2-异常处理

统一异常处理:

  • 定义一套统一的异常处理机制,确保所有异常都能被正确捕获和处理。
  • 可以创建自定义异常类来区分不同类型的错误。

异常层次结构:

  • 根据异常类型定义不同的异常类,例如 DataAccessException、BusinessException 等。
  • 通过继承 RuntimeException 或 Exception 来创建自定义异常类。

异常抛出:

  • 在 DAO 层抛出 DataAccessException 类型的异常,表示数据访问过程中出现的问题。
  • 在 Service 层抛出 BusinessException 类型的异常,表示业务逻辑处理中的问题。

异常捕获:

  • 在 Controller 层捕获异常,并根据异常类型返回合适的响应。
  • 使用全局异常处理器来处理未被捕获的异常。

异常日志记录:

  • 在捕获异常时记录详细的错误信息,包括堆栈跟踪。
  • 使用日志框架(如 Logback、Log4j 等)记录异常信息

10.3-测试策略

单元测试:

  • 编写针对 DAO 层和服务层的单元测试。
  • 使用 JUnit 或 TestNG 框架编写测试用例。
  • 对于 DAO 层,可以使用内存数据库(如 H2)来进行测试。

集成测试:

  • 编写针对整个系统或子系统的集成测试。
  • 使用真实的数据库环境来测试数据访问层和业务逻辑层之间的交互。
  • 可以使用 Spring Boot 的 @SpringBootTest 注解来启动完整的应用上下文进行测试。

端到端测试:

  • 对整个应用流程进行端到端的测试,确保各个组件之间能够正常协作。
  • 使用 Selenium 或 RestAssured 等工具进行前端和后端的交互测试。

性能测试:

  • 使用 JMeter 或 Gatling 等工具进行性能测试,确保应用在高并发下的稳定性和性能。
  • 测试数据库查询性能,优化 SQL 语句和索引。

代码覆盖率:

  • 使用 JaCoCo 或 Cobertura 等工具来检查测试代码的覆盖率。
  • 努力提高代码覆盖率,确保关键路径和边界条件都得到了测试。

十一,实战案例

完整的应用示例
假设我们需要构建一个简单的博客系统,其中包含文章(Article)和标签(Tag)两个实体。我们将实现以下功能:

  • 添加文章。
  • 获取文章列表。
  • 更新文章。
  • 删除文章。
  • 添加标签。
  • 获取标签列表。
  • 更新标签。
  • 删除标签。

步骤 1: 创建项目结构
项目结构:
src/main/java: 存放 Java 源代码。
src/main/resources: 存放配置文件和映射文件。
模块划分:
com.example: 包含所有源代码。
com.example.model: 实体类。
com.example.mapper: 映射接口。
com.example.service: 服务层接口和实现。
com.example.controller: 控制器层。
com.example.config: 配置类。

步骤 2: 创建实体类
Article.java:

 package com.example.model;

   public class Article {
       private Integer id;
       private String title;
       private String content;
       private Integer authorId;

       // Getters and setters
   }

Tag.java:

   package com.example.model;

   public class Tag {
       private Integer id;
       private String name;

       // Getters and setters
   }
   

步骤 3: 创建映射接口
ArticleMapper.java:

   package com.example.mapper;

   import com.example.model.Article;
   import org.apache.ibatis.annotations.*;

   import java.util.List;

   public interface ArticleMapper {
       @Select("SELECT * FROM articles WHERE id = #{id}")
       Article findById(Integer id);

       @Insert("INSERT INTO articles(title, content, author_id) VALUES(#{title}, #{content}, #{authorId})")
       @Options(useGeneratedKeys = true, keyProperty = "id")
       void add(Article article);

       @Update("UPDATE articles SET title = #{title}, content = #{content} WHERE id = #{id}")
       void update(Article article);

       @Delete("DELETE FROM articles WHERE id = #{id}")
       void deleteById(Integer id);

       @Select("SELECT * FROM articles")
       List<Article> findAll();
   }
   

TagMapper.java:

   package com.example.mapper;

   import com.example.model.Tag;
   import org.apache.ibatis.annotations.*;

   import java.util.List;

   public interface TagMapper {
       @Select("SELECT * FROM tags WHERE id = #{id}")
       Tag findById(Integer id);

       @Insert("INSERT INTO tags(name) VALUES(#{name})")
       @Options(useGeneratedKeys = true, keyProperty = "id")
       void add(Tag tag);

       @Update("UPDATE tags SET name = #{name} WHERE id = #{id}")
       void update(Tag tag);

       @Delete("DELETE FROM tags WHERE id = #{id}")
       void deleteById(Integer id);

       @Select("SELECT * FROM tags")
       List<Tag> findAll();
   }
   

步骤 4: 创建服务层
ArticleService.java:

   package com.example.service;

   import com.example.mapper.ArticleMapper;
   import com.example.model.Article;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.stereotype.Service;

   import java.util.List;

   @Service
   public class ArticleService {
       private final ArticleMapper articleMapper;

       @Autowired
       public ArticleService(ArticleMapper articleMapper) {
           this.articleMapper = articleMapper;
       }

       public Article findById(Integer id) {
           return articleMapper.findById(id);
       }

       public void add(Article article) {
           articleMapper.add(article);
       }

       public void update(Article article) {
           articleMapper.update(article);
       }

       public void deleteById(Integer id) {
           articleMapper.deleteById(id);
       }

       public List<Article> findAll() {
           return articleMapper.findAll();
       }
   }
   

TagService.java:

   package com.example.service;

   import com.example.mapper.TagMapper;
   import com.example.model.Tag;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.stereotype.Service;

   import java.util.List;

   @Service
   public class TagService {
       private final TagMapper tagMapper;

       @Autowired
       public TagService(TagMapper tagMapper) {
           this.tagMapper = tagMapper;
       }

       public Tag findById(Integer id) {
           return tagMapper.findById(id);
       }

       public void add(Tag tag) {
           tagMapper.add(tag);
       }

       public void update(Tag tag) {
           tagMapper.update(tag);
       }

       public void deleteById(Integer id) {
           tagMapper.deleteById(id);
       }

       public List<Tag> findAll() {
           return tagMapper.findAll();
       }
   }
   

步骤 5: 创建控制器层
ArticleController.java:

   package com.example.controller;

   import com.example.model.Article;
   import com.example.service.ArticleService;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.web.bind.annotation.*;

   import java.util.List;

   @RestController
   @RequestMapping("/articles")
   public class ArticleController {
       private final ArticleService articleService;

       @Autowired
       public ArticleController(ArticleService articleService) {
           this.articleService = articleService;
       }

       @GetMapping("/{id}")
       public Article findById(@PathVariable Integer id) {
           return articleService.findById(id);
       }

       @PostMapping
       public void add(@RequestBody Article article) {
           articleService.add(article);
       }

       @PutMapping("/{id}")
       public void update(@PathVariable Integer id, @RequestBody Article article) {
           article.setId(id);
           articleService.update(article);
       }

       @DeleteMapping("/{id}")
       public void deleteById(@PathVariable Integer id) {
           articleService.deleteById(id);
       }

       @GetMapping
       public List<Article> findAll() {
           return articleService.findAll();
       }
   }
   

TagController.java:

   package com.example.controller;

   import com.example.model.Tag;
   import com.example.service.TagService;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.web.bind.annotation.*;

   import java.util.List;

   @RestController
   @RequestMapping("/tags")
   public class TagController {
       private final TagService tagService;

       @Autowired
       public TagController(TagService tagService) {
           this.tagService = tagService;
       }

       @GetMapping("/{id}")
       public Tag findById(@PathVariable Integer id) {
           return tagService.findById(id);
       }

       @PostMapping
       public void add(@RequestBody Tag tag) {
           tagService.add(tag);
       }

       @PutMapping("/{id}")
       public void update(@PathVariable Integer id, @RequestBody Tag tag) {
           tag.setId(id);
           tagService.update(tag);
       }

       @DeleteMapping("/{id}")
       public void deleteById(@PathVariable Integer id) {
           tagService.deleteById(id);
       }

       @GetMapping
       public List<Tag> findAll() {
           return tagService.findAll();
       }
   }
   

步骤 6: 配置 MyBatis
application.properties:

   spring.datasource.url=jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
   spring.datasource.username=root
   spring.datasource.password=root
   spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
   mybatis.type-aliases-package=com.example.model
   mybatis.mapper-locations=classpath:mapper/*.xml
   

MyBatisConfig.java:

   package com.example.config;

   import org.apache.ibatis.session.SqlSessionFactory;
   import org.mybatis.spring.SqlSessionFactoryBean;
   import org.mybatis.spring.annotation.MapperScan;
   import org.springframework.context.annotation.Bean;
   import org.springframework.context.annotation.Configuration;
   import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

   import javax.sql.DataSource;

   @Configuration
   @MapperScan("com.example.mapper")
   public class MyBatisConfig {
       @Bean
       public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
           SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
           factoryBean.setDataSource(dataSource);
           factoryBean.setTypeAliasesPackage("com.example.model");
           PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
           factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
           return factoryBean.getObject();
       }
   }
   

步骤 7: 创建映射文件
ArticleMapper.xml:

   <?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.ArticleMapper">
       <select id="findById

十二,社区和扩展

12.1-MyBatis 生态系统

MyBatis 的生态系统包括一系列官方支持的工具和扩展,以及社区贡献的各种插件和库。这些工具和扩展可以极大地提高开发效率和应用性能。
MyBatis Generator:

  • 一个强大的代码生成工具,可以根据数据库表自动生成映射文件和实体类。
  • 支持多种数据库和多种输出模板。

MyBatis Plus:

  • 一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
  • 提供了通用 Mapper、通用 Service,支持 Lambda 形式的条件构造器等特性。

MyBatis Cache:

  • 提供了一种缓存机制,可以显著提高查询性能。
  • 支持二级缓存,可以跨多个映射文件共享缓存数据。

MyBatis Type Handlers:

  • 提供了类型处理器,可以方便地处理不同类型的数据转换。
  • 支持自定义类型处理器,以满足特殊的数据类型需求。

MyBatis Pagination:

  • 提供了分页插件,可以方便地实现分页查询。
  • 支持多种数据库方言,自动处理分页 SQL。

MyBatis Dynamic SQL:

  • 提供了动态 SQL 的支持,可以在运行时构建 SQL 语句。
  • 支持多种条件组合,可以构建复杂的查询语句。

12.2-第三方工具集成

MyBatis 可以与许多第三方工具集成,以提高开发效率和应用质量。
Spring Boot:

  • 提供了自动配置的支持,可以轻松地将 MyBatis 集成到 Spring Boot 项目中。
  • 支持 MyBatis 的配置文件和映射文件的自动扫描。

Lombok:

  • 一个 Java 库,可以减少实体类中的样板代码。
  • 支持自动生成 getter 和 setter 方法,以及其他常用的方法。

Swagger:

  • 一个 API 文档生成工具,可以自动生成 RESTful API 的文档。
  • 支持从控制器类中提取 API 信息,生成详细的文档。

Flyway:

  • 一个数据库迁移工具,可以方便地管理数据库版本。
  • 支持 SQL 脚本和基于 Java 的迁移脚本。

JUnit/TestNG:

  • 测试框架,用于编写单元测试和集成测试。
  • 支持模拟对象和数据库操作,便于测试 MyBatis 的映射逻辑。

Mockito:

  • 一个流行的 Java 模拟框架,可以用来模拟对象的行为。
  • 适用于编写单元测试,特别是对于 DAO 层的测试。

Spring Data:

  • 提供了一种简单的方式来访问数据库。
  • 支持 CRUD 操作和分页等功能,可以与 MyBatis 结合使用。

HikariCP:

  • 一个高性能的 JDBC 连接池。
  • 支持多种数据库,可以显著提高数据库访问性能。

Druid:

  • 一个数据库连接池监控工具,提供了详细的监控信息。
  • 支持 SQL 执行时间统计、SQL 执行日志等功能。

PgPool-II:

  • PostgreSQL 的连接池和负载均衡工具。
  • 可以提高数据库的可用性和性能。

12.3-社区资源和文档

MyBatis 拥有丰富的社区资源和文档,可以帮助开发者快速上手和解决问题。
官方文档:
mybatis – MyBatis 3 | Introductionicon-default.png?t=N7T8https://mybatis.org/mybatis-3/提供了详细的使用指南和 API 文档。
GitHub 仓库:
GitHub - mybatis/mybatis-3: MyBatis SQL mapper framework for JavaMyBatis SQL mapper framework for Java. Contribute to mybatis/mybatis-3 development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/mybatis/mybatis-3包含了源代码、示例项目和贡献指南。
Stack Overflow:
Newest 'mybatis' Questions - Stack Overflowicon-default.png?t=N7T8https://stackoverflow.com/questions/tagged/mybatis提供了大量的问题解答和技术讨论。
Google Groups:
groups.google.comicon-default.png?t=N7T8https://groups.google.com/forum/#!forum/mybatis-user一个活跃的社区论坛,可以提问和交流经验。
中文社区:
Spring中文网 (mybatis.cn)icon-default.png?t=N7T8https://www.mybatis.cn/提供了中文版的文档和教程。
博客和教程:
MYBATIS Tutorial (tutorialspoint.com)icon-default.png?t=N7T8https://www.tutorialspoint.com/mybatis/index.htm
Page not found - Java Code Geeksicon-default.png?t=N7T8https://examples.javacodegeeks.com/spring-boot-mybatis-example/各种博客和网站上的教程和示例。
视频教程:

哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliicon-default.png?t=N7T8https://www.bilibili.com/Bilibili 上有很多关于 MyBatis 的视频教程,适合初学者入门学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2047616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件需求设计分析报告(Word原件)

第1章 序言 第2章 引言 2.1 项目概述 2.1.1 项目背景 2.1.2 项目目标 2.2 编写目的 2.3 文档约定 2.4 预期读者及阅读建议 第3章 技术要求 3.1 软件开发要求 3.1.1 接口要求 3.1.2 系统专有技术 3.1.3 查询功能 3.1.4 数据安全 3.1.5 可靠性要求 3.1.6 稳定性要求 3.1.7 安全性…

练习:python条件语句、循环语句和函数的综合运用

需求描述&#xff1a; 期望输出效果&#xff1a; 练习成果&#xff1a; #简单的银行业务流程 many 50000 def main_menu():print("----------主菜单----------"f"\n{name}您好&#xff0c;欢迎来到ATM&#xff0c;请选择操作&#xff1a;""\n查询余…

鼠标手势软件,效率办公必备!移动鼠标即可执行命令

鼠标手势软件是一种通过在屏幕上绘制特定手势来触发预设操作或命令的工具&#xff0c;它能够极大地提高用户的操作效率&#xff0c;特别是在进行重复性工作时尤为明显。这类软件通常支持多种手势操作&#xff0c;如拖拽、双击、滚动等&#xff0c;并允许用户自定义手势以适应个…

【Linux】系列入门摘抄笔记-8-权限管理chmod/chown

Linux操作系统中文件的基本权限由9个字符组成&#xff0c;分别为属主、属组和其他用户&#xff0c;用于规定是否对文件有读、写和执行权限。 文件/目录的权限与归属 目录列表中&#xff0c;有9列 第一列&#xff1a;文件类型与权限&#xff08;共10个字符&#xff0c;分为四组…

RAG完整构建流程-从入门到放弃

RAG完整构建流程 LLM模型缺陷&#xff1a; ​ 知识是有局限性的(缺少垂直领域/非公开知识/数据安全) ​ 知识实时性(训练周期长、成本高) ​ 幻觉问题(模型生成的问题) ​ 方法&#xff1a;Retrieval-Augmented Generation&#xff08;RAG&#xff09; ​ 检索&#xff1…

网络安全实训第三天(文件上传、SQL注入漏洞)

1 文件上传漏洞 准备一句话文件wjr.php.png&#xff0c;进入到更换头像的界面&#xff0c;使用BP拦截选择文件的请求 拦截到请求后将wjr.php.png修改为wjr.php&#xff0c;进行转发 由上图可以查看到上传目录为网站目录下的upload/avator,查看是否上传成功 使用时间戳在线工具…

有哪些好用的桌面管理

Fences&#xff08;适用于Windows&#xff09;&#xff1a; Fences 是一个非常流行的 Windows 桌面组织工具&#xff0c;它允许用户将图标、文件、文件夹等拖放到名为“Fences”的区域中&#xff0c;从而保持桌面的整洁有序。Fences 还支持自动隐藏空白的 Fences&#xff0c;以…

PC端实现语音识别功能

场景需求&#xff1a; 点击录音按钮&#xff1a; 如没有检测到录音设备&#xff0c;提示&#xff1a;“无法找到麦克风设备&#xff0c;请检查设备连接”。如录音设备连接正常&#xff1a;录音按钮变成录制状态&#xff0c;开始录制声音&#xff0c;同时输入框禁止键盘输入。 再…

修改esxi root用户密码

登录vcenter&#xff0c;鼠标右键点击主机&#xff0c;选择主机配置文件------提取主机配置文件 然后对提取的文件输入一个名称清晰容易分辨的名称&#xff0c;如xxx主机修改密码配置文件。 选择顶部的菜单&#xff0c;选择“策略和配置文件” 选择左侧的主机配置文件&#xf…

Oracle导入dmp文件

文章目录 写在前面一、详细知识点1、dmp文件2、导出dmp文件2.1、exp和expdp区别2.2、exp导出操作2.3、expdp导出操作2.4、PL/SQL使用客户端导出2.4.1 特殊说明2.4.2 操作指引 3、导入dmp文件3.1、imp和impdp区别3.2、imp导入操作3.3、impdp导入操作3.4、PL/SQL使用客户端导入 二…

语言无界,沟通无限:2024年翻译软件新趋势

无论是商务洽谈、学术研究、旅游探险&#xff0c;还是日常交流&#xff0c;高效、准确的翻译工具都成为了人们不可或缺的助手。百度翻译在线翻译是我最早接触的翻译工具&#xff0c;这次我们就一起找寻还有哪些好用的翻译工具吧。 1.福晰在线翻译 链接直达>>https://fa…

[4.10]-AutoSAR零基础学习-(SHE)Secure Hardware Extension规范<2>- Memory update protocol

既然已经看到这篇文章&#xff0c;说明开始研究SHE的安全更新协议了&#xff0c;协议具体去看原文吧&#xff0c;下面的工具是自己实现的桌面工具&#xff0c;可以在多平台使用&#xff0c;支持SHE&#xff0f;SHE / SHE_Extend&#xff0c;协议可自行定制。 >总目录<

亚马逊账户运营:揭秘那些你应该知道的坑。

在亚马逊平台上&#xff0c;选品过程与选择竞争对手的策略相似&#xff0c;都需要深思熟虑。许多卖家在选品时&#xff0c;会广泛收集并分析各种数据&#xff0c;但有时过于依赖数据而忽略了产品的实际竞争力和市场适应性。结果&#xff0c;他们可能选中了在理论上很理想&#…

leetcode 885. Spiral Matrix III

题目链接 You start at the cell (rStart, cStart) of an rows x cols grid facing east. The northwest corner is at the first row and column in the grid, and the southeast corner is at the last row and column. You will walk in a clockwise spiral shape to visi…

[算法题]在排序数组中查找元素的第一个和最后一个位置

题目链接: 在排序数组中查找元素的第一个和最后一个位置 二分查找求解, 但是普通版本的二分查找无法求解, 普通版本的二分查找每次根据中点的值与目标值进行区间划分, 但是该题数据为非递减顺序, 所以可能出现如下情况: nums [1, 2, 3, 3, 3, 4, 5, 6] target 3 此时普通版本…

将VAE用于时间序列:生成时间序列的合成数据

变分自编码器(VAEs)是一种生成式人工智能,因其能够创建逼真的图像而备受关注,它们不仅可以应用在图像上&#xff0c;也可以创建时间序列数据。标准VAE可以被改编以捕捉时间序列数据的周期性和顺序模式,然后用于生成合成数据。本文将使用一维卷积层、策略性的步幅选择、灵活的时…

如何开发一款网店后台多开软件?!

什么是网店后台多开软件&#xff1f; 网店后台多开软件指的是能够同时打开多个网店后台的一款软件&#xff0c;即一个软件上可以登录多个网店后台&#xff08;如&#xff1a;拼多多店铺后台、抖店店铺后台、快手小店店铺后台、孔网店铺后台、微店店铺后台、小红书店铺后台等等…

Leetcode 34. 二分查找 C++实现

Leetcode 34. 在排序数组中查找元素的第一个和最后一个位置 问题&#xff1a;给你一个按照非递减顺序排列的整数数组 nums &#xff0c;和一个目标值 target 。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target &#xff0c;返回 [-1, -1]。你…

Python之PyInstaller打包EXE程序(带音乐加图片等资源)

Python之PyInstaller打包EXE程序(带音乐加图片等资源) 要将你的 Python 程序打包成一个可执行文件&#xff08;.exe&#xff09;&#xff0c;并确保图片和音乐文件包含在其中&#xff0c;你可以使用 PyInstaller 工具。下面是一个简单的步骤来打包你的程序&#xff1a; 以我的…

Python实现水果忍者(开源)

一、整体介绍&#xff1a; 1.1 前言&#xff1a; 游戏代码基于Python制作经典游戏案例-水果忍者做出一些改动&#xff0c;优化并增加了一些功能。作为自己Python阶段学习的结束作品&#xff0c;文章最后有源码链接。 1.2 Python主要知识&#xff1a; &#xff08;1&#xf…