MyBatis

发布于:2025-09-04 ⋅ 阅读:(13) ⋅ 点赞:(0)

JDBC编程和ORM模型


  JDBC回顾

        A-加载驱动

                 导入JDBC连接数据库的jar包,利用CLASS.forName加载驱动;

        B-获取连接

                利用DriverManager获取Connection,然后创建Statement;

        C-执行SQL语句

                利用Statement执行Sql语句并结合ResultSet处理结果i集映射java对象;

        D-关闭资源

                先开后关的顺序关闭ResultSet,Statement,Connection等资源;

   JDBC的弊端

        a-硬编码

                SQL语句在java代码中,不能很好的分离数据库语句和java语句明早成代码不易维护;

        b-sql参数固定

                Sql语句的参数固定,使用sql语句不灵活,无法满足多变的场景;

        c-代码重复度高

                大量的重复代码。以resultSet为例,每次哦都需要重复解析结果集。

        d-底层技术

jdbc属于底层的技术,不支持分布式,对于复杂场景应对不好;

        1.hibernate是全自动化ORM;Mybatis是半自动化ORM。

        2.Hibernate是一个重量级的框架,内部生成SQL语句,反射操作太多,导致性能下降;

Mybatis是一个轻量级的框架,需要自己写sql语句,有较少的反射操作

        3.Hibernate简化dao层,不用考虑SQL语句的编写和结果映射,重点放在业务逻辑上;Mybatis需要手写SQL语句以及结果映射。

        4.Hibernate不方便做SQL优化,遇到较复杂的SQL语句需要绕过框架实现复杂,对多字段的结构进行部分映射困难。

Mybatis不仅可以做SQL优化还可以SQL与Java分离,还可以自行编写映射关系,复杂的SQL语句Mybatis效率更高

Mybatis优势:

可以进行更细致的sql优化,容易掌握;

Hibernate优势:

dao层开发比mybatis简单,mybatis需要维护SQL和结果映射,hibernate数据库移植性好;

mybatis解决了jdbc的问题:

1.数据库连接创建,释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决

可解决此问题。

解决:在SqlMapConfig.xml配置数据连接池,使用连接池管理数据库链接。

2.sql语句写在代码中,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要

改变java代码。

解决:将sql语句配置在xxxMapper.xml文件中与java代码分离。

3.向sql语句传参数麻烦,因为where条件不一定,可能多可能少,占位符需要和参数11对应。

解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType

定义输入参数的类型。

4.对比 结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。

为什么选择Mybatis?

简单易学,灵活度高,功能完整,提供了连接管理,缓存支持,线程支持,事务管理等功能,还提供了其他ORM模型发支持。

什么是MyBatis?

MyBatis是一款ORM模型,支持定制化sql,存储过程以及高级映射。可以使用简单的xml或注解来配置和映射原生信息,将接口和java的pojo(plain old java Objects 普通的java对象)映射成数据库中的记录

MyBatis生命周期
1)读取 MyBatis 配置文件:mybatis - config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
2)加载映射文件:映射文件为 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句。需要在 MyBatis 配置文件 mybatis - config.xml 中加载,mybatis - config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等。
7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型,输入参数映射过程类似于 JDBC 对 preparedStatement 设置参数的过程。
8)输出结果映射:输出结果类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型,输出结果映射过程类似于 JDBC 对结果集的解析过程。

什么是ORM?

对象关系映射,用于实现面向对象编程语言的不同类型系统的数据之间的转换;

Mybatis的优势: 简单易学,灵活度高,功能完整。

Mybatis的生命周期:SqlSession FactoryBuilder ,SqlSessionFactory,SqlSession,SQL Mapper;


MyBatis配置文件

数据库连接部分
<dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource>

<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
    <environment id="development">
       <!-- 使用jdbc事务管理 -->
       <transactionManager type="JDBC" />
       <!-- 数据库连接池 -->
       <dataSource type="POOLED">
          <property name="driver" value="${jdbc.driver}" />
          <property name="url"
             value="${jdbc.url}" />
          <property name="username" value="${jdbc.username}" />
          <property name="password" value="${jdbc.password}" />
       </dataSource>
    </environment>
</environments>

配置数据库环境信息,注册数据原,配置数据库事务,默认使用default

注意:<transactionManager>配置数据库事务;

<dataSource>数据源

  • unpooled-非连接池数据源
  • pooled-连接池的数据源
  • jndi-JNDI数据源
  • 自定义数据源-其他类型数据源
    <properties resource="jdbc.properties">
    </properties>
    <typeAliases>
<!--       <typeAlias alias="aaa" type="pojo.Student"/>-->
       <package name="pojo"/>
    </typeAliases>

Mybatis配置文件的功能

构建SqlSessionFactory的依据,对整个MyBatis体系深远;

properties元素的功能

配置属性的元素,可以在配置文件的上下文中使用该属性;

enviroments元素的功能

配置环境信息,注册数据源,配置数据库事务;

mappers元素的功能

用来在mybatis初始化的时候引入映射器


MyBatis动态代理和映射器

什么是mapper的动态代理?

在接口中有方法的返回值定义,参数的定义,方法名,在sqlMapper.xml中也对这接口赋予了赋值,这时候dao的实现就显得多余,这是mybatis可以帮助我们自动产生实现类,并可以调取方法得到结果,这就是Mybatis的mapper动态代理。

动态代理规范

Mapper接口开发方法只需要程序员编写Mapper接口(相当于dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象模型,代理对象的方法体同上边Dao接口实现类的方法。

Mapper接口开发需要遵循一下规范

  1. Mapper.xml文件中的namespace与mapper接口的类路径相同。
  2. Mapper接口方法名和Mapper.xml中定义的每个statement中的id相同
  3. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
  4. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。

使用sqlsession的方法getmapper()让Mybatis自动生成对应接口的实现对象。

selectOne和selectList

动态代理对象,调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。

namespace

mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。

select元素

  • id 唯一标识,接口中的方法名;
  • parameterType参数类型;
  • resultType结果的类型;
  • resultMap复杂的结果集映射关系

语法规则:<select 属性 = "值">查询类sql语句</select>

例如:

    <resultMap id="querry_book" type="book">
        <id property="bookId" column="book_id"/>
        <result property="bookName" column="book_name"/>
        <result property="bookPrice" column="book_price"/>
        <result property="bookAuthor" column="book_author"/>
        <result property="bookNum" column="book_num"/>
    </resultMap>
    <select id="queryBookByName" parameterType="string" resultMap="querry_book">
        select *
        from book
        <where>
            <if test="bookName != null">
                and book_name like concat('%',#{bookName},'%')
            </if>
            <if test="bookAuthor != null">
                and book_author like concat('%',#{bookAuthor},'%')
            </if>
        </where>
    </select>


    public List<Book> queryBookByName(Book  book);

insert元素

  • id 唯一标识,接口中的方法名;
  • parameter Type 参数的类型;
  • keyProperty 表示以那个列作为属性的主键,不能和keyColumn同时使用
  • keyColumn 指明那一列是主键,不能和keyProperty同时使用
  • useGeneratedKeys 使用JDBC的getGeneratedKeys方式来取有数据库内部生成的主键

语法规则:<insert 属性=“值”> 新增类SQL语句</insert>(当主键在数据库中为自增字段时候,新增成功后回填字段)

    public Integer addBook(Book book);
   
 <insert id="addBook" parameterType="book">
        insert into book(book_name, book_price, book_author, book_num)
        values (#{bookName}, #{bookPrice}, #{bookAuthor}, #{bookNum})
    </insert>

update元素和delete元素

语法规则:<update 属性=“值”> 查询类的sql语句</update>

                <delete 属性=“值”>查询类sql语句</delete>

    <update id="updateBook" parameterType="book">
        update book
        set book_num = #{bookNum}
        where book_id = #{bookId}
    </update>


    public Integer updateBook(Book book);

为什么要用resultMap元素

  1. 定义映射规则
  2. 级联操作-多表之间存在主外键关系时,主表和从表之间的关联关系
  3. 类型转换-数据库字段的类型和pojo类属性的类型转换

使用pojo封装结果集

  • property-映射到列结果的字段或者属性,通常指pojo的属性;
  • column-对应的数据库字段;
  • javaType-Java类型,可以时基本数据类型,也可以是类完全限定名;
  • jdbcType-jdbc的类型,基本支持所有常用的数据库类型;
<!--    一对多-->
    <resultMap id="CardResultMap" type="pojo.Card">
        <id property="cardId" column="card_id"/>
        <result property="cardName" column="card_name"/>
        <result property="cardMoney" column="card_money"/>
        <collection property="bookList" ofType="pojo.Book">
            <id property="bookId" column="book_id"/>
            <result property="bookName" column="book_name"/>
            <result property="bookPrice" column="book_price"/>
        </collection>
    </resultMap>

一、动态 SQL 概述

定义:根据不同条件动态拼接 SQL 语句,实现对数据库更精准的操作。
实现方式:通过映射器配置文件或注解实现。

常用动态 SQL 元素

  • if:单条件分支判断
  • choose (when, otherwise):多条件分支判断(类似 switch)
  • trim (where, set):处理 SQL 拼接问题的辅助元素
  • foreach:循环语句,常用于 in 语句等列举条件
  • bind:自定义上下文变量,传递参数

二、if 元素

功能:进行单条件分支判断

语法

<if test="条件"></if>

注意事项

  • 拼接 SQL 语句时需注意 AND 和逗号的使用
  • 条件判断基于传入的参数值

三、choose、when、otherwise 元素

适用场景

  1. 当新闻编号不为空,仅用新闻编号作为查询条件
  2. 当新闻编号为空而标题不为空,用标题进行模糊查询
  3. 当新闻编号和标题都为空,要求作者不为空

功能:多条件分支判断,类似 Java 的 switch 语句

语法

<choose>
  <when test="条件"></when>
  <otherwise></otherwise>
</choose>

注意事项

  • 拼接 SQL 语句时注意 AND 和逗号的使用
  • 若没有条件匹配,将执行 otherwise 中的内容

四、trim、where、set 元素

where 元素

功能:智能处理 WHERE 子句的拼接

语法

<where>
  <if test="title != null">and title = #{title}</if>
</where>

说明

  • 仅当至少有一个子元素条件成立时,才插入 "WHERE" 子句
  • 自动去除语句开头的 "AND" 或 "OR"

set 元素

功能:智能处理 UPDATE 语句中的 SET 子句

<set>
  <if test="条件"></if>
</set>

说明

  • 在包含的语句前输出 "SET"
  • 自动忽略语句结尾的逗号
  • 若包含内容为空会出错

trim 元素

功能:更灵活地处理 SQL 拼接,可替代 where 和 set

语法:

属性说明

  • prefix:需要拼接的前缀(如 where、set)
  • suffixOverrides:忽略指定的后缀文本(用管道分隔)
  • prefixOverrides:忽略指定的前缀文本(用管道分隔)
  • suffix:需要拼接的后缀

示例:

<trim prefix="where" suffixOverrides="and" prefixOverrides="or"></trim>

五、foreach 元素

功能:循环处理集合或数组,常用于 in 语句

语法

<foreach item="" index="" collection="" open="" separator="" close="">
  #{item}
</foreach>

属性说明

  • item:循环中的当前元素
  • index:当前循环元素的位置下标
  • collection:传入的参数(数组或集合)
  • open:集合元素包装的起始符号
  • close:集合元素包装的结束符号
  • separator:元素间的分隔符

适用场景

  • 处理 IN 条件中的多个值
  • 批量插入、更新操作

六、bind 元素

功能:自定义上下文变量,方便参数处理

语法

<bind name="" value="_parameter"></bind>

属性说明

  • name:自定义变量名
  • value:变量值,_parameter 表示传递进来的参数

示例

<bind name="name1" value="'%' + _parameter + '%'"></bind>

适用场景

  • 处理模糊查询的通配符拼接
  • 统一参数格式转换

小结:

  • if:单条件分支判断
  • choose (when、otherwise):多条件分支判断
  • trim (where、set):处理 SQL 拼接问题的辅助元素
  • foreach:循环处理集合或数组,适用于 in 语句等
  • bind:自定义上下文变量,方便参数处理

Mybatis映射器注解

一、映射器注解概述

1.1 映射器配置文件的缺陷

  • 繁琐:配置文件书写复杂,需掌握大量 XML 语法规则
  • 不直观:配置文件与接口仅通过名称关联,对应关系不清晰

1.2 常用注解分类

注解类型 功能说明
基本注解 实现简单的增删改查(CRUD)操作
结果映射注解 定义数据库字段与 JavaBean 属性的映射关系,支持级联映射
动态 SQL 注解 实现注解方式的动态 SQL 拼接

二、基本注解(CRUD 操作)

2.1 @Insert(新增操作)

功能:替代 XML 配置中的<insert>元素,实现数据新增
语法

@Insert("SQL语句")
public 返回值 方法名(参数);
@Insert("insert into news (title, author) values (#{title}, #{author})")
public void addNews(News news);
主键处
  • @Options(useGeneratedKeys = true, keyProperty = "newsId")
    @Insert("insert into news (title, author) values (#{title}, #{author})")
    public void addNews(News news);
    
  • @SelectKey(
        statement = "select max(newsid) + 1 as newsId from news",
        keyProperty = "newsId",
        resultType = int.class,
        before = true
    )
    @Insert("insert into news (newsId, title, author) values (#{newsId}, #{title}, #{author})")
    public void addNews(News news);
    

2.2 @Delete(删除操作)

功能:替代 XML 配置中的<delete>元素,实现数据删除
语法

@Delete("SQL语句")
public 返回值 方法名(参数);
@Delete("delete from news where newsid = #{newsId}")
public void delNews(News news);

2.3 @Update(更新操作)

功能:替代 XML 配置中的<update>元素,实现数据更新
语法

@Update("SQL语句")
public 返回值 方法名(参数);
@Update("update news set title = #{title}, author = #{author} where newsid = #{newsId}")
public void updateNews(News news);

2.4 @Select(查询操作)

功能:替代 XML 配置中的<select>元素,实现数据查询
语法

@Select("SQL语句")
public 返回值 方法名(参数);
@Select("select * from news where title = #{title}")
public List<News> getNewsByTitle(News news);

2.5 多参数传递方式

  1. Map 方式:通过 Map 集合传递多个参数,SQL 中通过键名取值
  2. JavaBean 方式:通过实体类对象传递参数,SQL 中通过属性名取值
  3. @Param 方式:通过注解指定参数名,适用于参数较少的场景
    @Select("select * from news where title = #{title} and author = #{author}")
    public List<News> getNews(@Param("title") String title, @Param("author") String author);
    

三、结果映射注解

3.1 @Results(定义结果映射)

功能:建立数据库字段与 JavaBean 属性的映射关系,支持主键标识
语法

@Results({
    @Result(id = 是否为主键, column = "数据库字段名", property = "JavaBean属性名"),
    // 多个字段映射...
})
public 返回值 方法名(参数);
@Select("select newsid as newsId, title, author from news where newsid = #{newsId}")
@Results({
    @Result(id = true, column = "newsId", property = "newsId"),
    @Result(column = "title", property = "title"),
    @Result(column = "author", property = "author")
})
public News getNewsById(Integer newsId);

3.2 @ResultMap(复用结果映射)

功能:复用已定义的结果映射(支持 XML 中的<resultMap>或注解中的@Results
使用场景

  1. 复用 XML 中的结果映射:
    // XML中定义:<resultMap id="newsResultMap">...</resultMap>
    @Select("select * from news where newsid = #{newsId}")
    @ResultMap("newsResultMap") // 引用XML中的resultMap
    public News getNewsById(Integer newsId);
    
  2. 复用注解中的结果映射:
    // 先定义带id的@Results
    @Results(id = "newsResult", value = {
        @Result(id = true, column = "newsId", property = "newsId"),
        @Result(column = "title", property = "title")
    })
    @Select("select * from news where newsid = #{newsId}")
    public News getNewsById(Integer newsId);
    
    // 复用已定义的@Results
    @Select("select * from news where author = #{author}")
    @ResultMap("newsResult") // 引用注解中的结果映射id
    public List<News> getNewsByAuthor(String author);
    

注意:一个 SQL 语句不能同时使用@Results@ResultMap

3.3 级联映射注解

3.3.1 @One(一对一映射)

功能:实现一对一关联查询,如 “用户 - 身份证” 关联
语法

@Result(
    column = "关联字段名", 
    property = "关联属性名", 
    one = @One(
        select = "关联查询的Mapper方法全路径", 
        fetchType = 加载方式
    )
)
@Select("select * from user where userid = #{userId}")
@Results({
    @Result(id = true, column = "userid", property = "userId"),
    @Result(column = "username", property = "username"),
    // 一对一关联查询身份证信息
    @Result(
        column = "card_id", // 用户表中关联身份证表的字段
        property = "idCard", // User类中关联的IdCard属性
        one = @One(
            select = "com.example.dao.IdCardMapper.getCardById",
            fetchType = FetchType.EAGER // 即时加载(EAGER)/延迟加载(LAZY)
        )
    )
})
public User getUserWithIdCard(Integer userId);
3.3.2 @Many(一对多映射)

功能:实现一对多关联查询,如 “分类 - 文章” 关联
语法

@Result(
    column = "关联字段名", 
    property = "关联属性名", 
    many = @Many(
        select = "关联查询的Mapper方法全路径", 
        fetchType = 加载方式
    )
)
@Select("select * from category where categoryid = #{categoryId}")
@Results({
    @Result(id = true, column = "categoryid", property = "categoryId"),
    @Result(column = "categoryname", property = "categoryName"),
    // 一对多关联查询文章列表
    @Result(
        column = "categoryid", // 分类表中关联文章表的字段
        property = "newsList", // Category类中关联的List<News>属性
        many = @Many(
            select = "com.example.dao.NewsMapper.getNewsByCategoryId",
            fetchType = FetchType.LAZY // 延迟加载
        )
    )
})
public Category getCategoryWithNews(Integer categoryId);

四、动态 SQL 注解

4.1 脚本 SQL(<script>标签)

功能:直接在注解中嵌入 XML 风格的动态 SQL,适用于简单场景
语法:在 SQL 语句外层包裹<script>标签,内部使用ifchoose等动态元素
示例

@Select("<script>" +
        "select * from news " +
        "<where>" +
        "   <if test='title != null'>and title like #{title}</if>" +
        "   <if test='author != null'>and author = #{author}</if>" +
        "</where>" +
        "</script>")
public List<News> getNewsByCondition(News news);

4.2 Provider 注解(动态构建 SQL)

功能:通过外部类动态生成 SQL 语句,适用于复杂动态 SQL 场景
常用注解

注解 对应操作 说明
@SelectProvider 查询 动态生成 SELECT 语句
@InsertProvider 新增 动态生成 INSERT 语句
@UpdateProvider 更新 动态生成 UPDATE 语句
@DeleteProvider 删除 动态生成 DELETE 语句
使用步骤
  1. 创建 SQL 提供类:定义生成 SQL 的方法(返回 String 类型)

    public class NewsSqlProvider {
        // 动态生成查询SQL
        public String getNewsByCondition(News news) {
            StringBuilder sql = new StringBuilder("select * from news where 1=1");
            if (news.getTitle() != null) {
                sql.append(" and title like concat('%', #{title}, '%')");
            }
            if (news.getAuthor() != null) {
                sql.append(" and author = #{author}");
            }
            return sql.toString();
        }
    
        // 动态生成更新SQL
        public String updateNews(News news) {
            StringBuilder sql = new StringBuilder("update news set ");
            if (news.getTitle() != null) {
                sql.append("title = #{title}, ");
            }
            if (news.getAuthor() != null) {
                sql.append("author = #{author}, ");
            }
            // 去除末尾多余的逗号
            sql.deleteCharAt(sql.lastIndexOf(","));
            sql.append(" where newsid = #{newsId}");
            return sql.toString();
        }
    }
    
  2. 在 Mapper 接口中关联 Provider

    public interface NewsMapper {
        // 关联查询SQL提供类
        @SelectProvider(type = NewsSqlProvider.class, method = "getNewsByCondition")
        List<News> getNewsByCondition(News news);
    
        // 关联更新SQL提供类
        @UpdateProvider(type = NewsSqlProvider.class, method = "updateNews")
        void updateNews(News news);
    }
    

4.3 SQL 语句构造器

功能:通过 MyBatis 提供的SQL类构建 SQL 语句,避免手动拼接字符串(减少语法错误)
核心方法

方法 说明
SELECT(String... columns) 指定查询字段
FROM(String table) 指定查询表
WHERE(String condition) 添加 WHERE 条件
AND(String condition) / OR(String condition) 追加 AND/OR 条件
INSERT_INTO(String table) 指定插入表
VALUES(String columns, String values) 指定插入的字段和值
UPDATE(String table) 指定更新表
SET(String... sets) 指定更新的字段和值
DELETE_FROM(String table) 指定删除表

public class NewsSqlProvider {
    public String getNewsByCondition(News news) {
        return new SQL() {{
            SELECT("*");
            FROM("news");
            if (news.getTitle() != null) {
                WHERE("title like concat('%', #{title}, '%')");
            }
            if (news.getAuthor() != null) {
                AND("author = #{author}"); // 追加AND条件
            }
        }}.toString();
    }

    public String updateNews(News news) {
        return new SQL() {{
            UPDATE("news");
            if (news.getTitle() != null) {
                SET("title = #{title}");
            }
            if (news.getAuthor() != null) {
                SET("author = #{author}");
            }
            WHERE("newsid = #{newsId}");
        }}.toString();
    }
}

五、总结

  1. 基本注解

    • @Insert/@Delete/@Update/@Select:分别实现增删改查操作,支持主键回填和多参数传递
  2. 结果映射注解

    • @Results:定义数据库字段与 JavaBean 属性的映射关系,支持主键标识
    • @ResultMap:复用已定义的结果映射(XML 或注解方式)
    • @One/@Many:实现一对一、一对多级联映射,支持即时 / 延迟加载
  3. 动态 SQL 注解

    • 脚本 SQL:通过<script>标签嵌入 XML 风格动态 SQL
    • Provider 注解:@SelectProvider/@InsertProvider等,通过外部类动态生成 SQL
    • SQL 语句构造器:使用SQL类构建 SQL,避免手动拼接错误
  4. 核心优势:注解方式简化配置,减少 XML 文件依赖,使 SQL 与接口更紧密关联,提高代码可读性和维护性