MyBatis
ShiJh Lv3

mybatis是一个优秀的持久层框架,通过配置的方式即可对数据库进行持久化操作。主要讲述原始Mybatis的用法,偏入门向

🐦MyBatis

mybatis是一个优秀的持久层框架,通过配置的方式即可对数据库进行持久化操作。

推荐IDEA插件FreeMyBatisPlugin

配置详解

Maven坐标

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

XML核心配置

运行环境配置

运行环境需要指定数据库连接池和事务管理类型。完成以下配置即可完成最简单的持久化操作。

  • 数据源环境

    <environments default="id">
        <environment id="">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="" value="${}"/>
            </dataSource>
        </environment>
    </environments>

    数据源环境可以配置多个,其中指定一个ID为默认运行环境,配置DataSource的方法与Spring配置相似,同样可以使用properties文件。

  • 加载映射文件

    <mappers>
    	<mapper resource="xml-url"/>
    </mappers>

详细配置

  • environment

    transactionManager:事务管理器类型,分为 JDBC、MANAGED

    dataSource:连接池类型,分为UNPOOLED、POOLED、JNDI,也可以指定一个实现UnpooledDataSourceFactory的类作为type,如果要使用第三方连接池就需要这么做

    JDBCPOOLED是最常用的,其他功能不做解释。

  • mapper

    加载映射器可以使用多种方式,如以下三种:

    resource:相对类路径,相当于classpath:,用于加载映射器配置文件

    class:加载类,通过全限定类名加载,用于加载实现映射器接口的类(注解配置)

    packagename:加载包名下所有的类,同样是用于注解配置

  • properties

    加载配置文件,使用resource="path"加载类下路径的文件,不需要加classpath

  • typeAliases

    定义类型别名,用于简化全限定类名,创建别名和真名的映射关系。Mybatis内部将Java基础类型与包装器类型映射起来了,所以使用intdouble等名称时,对应的是包装器类名。

  • typeHanlder

    配置自定义类型转处理器,需要包裹在typeHanlders中,通过属性handler="类全限定名"配置一个处理器。

XML映射关系配置

命名空间

每个映射文件对应一个命名空间,需要在标签内指定。命名空间的作用同Java包,使用映射关系时,需要以命名空间为开头的路径。

<mapper namespace="spaceName"></mapper>

sql语句配置

id是语句的标识符,sql语句可以直接写在标签中。

  • SELECT

    <!--resultType是返回结果的类型,指定一个类Mybatis能够自动封装-->
    <select id="" resultType="">sql</select>
  • UPDATE

    <update id="" parameterType="">sql</update>

  • DELETE

    <delete id="" >sql</delete>

  • INSERT

    <!--后面三个属性表示返回插入的主键id并使用指定的映射关系!-->
    <insert id="" useGeneratedKeys=true keyProperty="" keyColumn="">sql</insert>
使用占位符

mybatis中占位符用OGNL#{name}表示,这个占位符是安全的,与Jdbc的?一样。在符号内部可以填写多个属性,如限定这个占位符的类型等,更多属性的设置请到官方文档中查询。

#{id, javaType=int, jdbcType=NUMERIC}
OGNL表达式

ognl只能对应数据库存在的值类型,但是它能够解析数组和POJO

  1. 数组

    #{listName[index].propertyName}

  2. POJO(JavaBean)

    #{propertyName}

    直接使用POJO内的属性名,如果是数组则需参照1

⚠️当使用注解编写Mapper时,要注意多个参数时,应使用@Param注解指定参数对应OGNL表达式中的属性名,且对于数组类型的参数将默认使用**“list”**

ℹ️EL表达式${}会将参数直接作为字符串插入,属于不安全操作

用实体对象作为占位符参数

Mybatis具备将复杂对象的属性配置到占位符中的功能,其中对象的属性名需要和占位符内name的参数名保持一致,mybatis即可自动的配置到占位符中。

sql标签使用parameterType属性可以指定该条语句所需要的参数对象类型,在缺省的情况下Mybatis能够自动推断。

ResultMap

测试用例子

结果映射,通过resultType指定的类型,能够被mybatis自动创建并设置其中的属性值,当列名和属性名匹配上时,不需要额外配置,而复杂情况下可以通过显式配置resultMap的方式完成映射,大多数情况下ResultMap在配置一对一、一对多、多对多关联查询时使用。

  1. 配置一个所需的resultMap
<resultMap  id="" type="class">
    <!--把class的属性名同列名关联起来-->
    <id property="" column=""/>
	<result property="name" column="name"/>
    <!--一对一关系映射-->
    <assosiation property="" javaType="class"></assosiation>
    <!--一对多/多对多关系映射-->
    <collection property="" ofType="class"></collection>
</resultMap>

主键一般使用<id>标签来配置。

  1. 将sql语句的resultType更换为resultMap
<select resultMap="mapId">sql</select>

更加复杂的高级结果映射请看官方文档包含多表关系查询需要的复杂嵌套映射

SelectKey

该标签用在增删改语句中,能够完成一次子查询并封装到参数中。

<selectKey keyProperty="" keyColumn="" resultType="" order="">sql</selectKey>
  • keyPropertykeyColumn 配置映射关系,结果将保存到keyProperty对应参数中(可以是对象中的属性)
  • resultType 返回结果对应的JavaType
  • order 指定为BEFORE/AFTER,意为在主查询之前还是之后进行这个子查询

SelectKey需要注意order属性,像Mysql一类支持自动增长类型的数据库中,order需要设置为after才会取到正确的值

⚠️如果是需要获取自增长的主键ID,推荐使用insert标签中的useGeneratorKeys模式,如果是注解方法,请使用@Options配置相同的内容。mssql中,用SelectKey且使用SCOPE_IDENTITY()来获取主键的话是无法获取的(null)但使用@@IDENTITY能够正确获取

缓存优化

当一条语句被调用时,mybatis能够将其返回结果缓存起来,若有多次查询则可以使用缓存直接返回。

Select语句默认是打开缓存的,缓存开启方式如下:

在sql语句标签中添加属性useCache="true"即可开启,当使用属性flushCache="true"时,调用sql会刷新缓存区,默认为false

注解配置

常用注解 说明
@Insert, @Update, @Delete, @Select 增删改查语句,填写sql语句,参数通用#{}
@Result 映射封装结果,相当于xml中的id/result,使用column和property配置映射关系,使用@One/@Many作为参数配置复杂映射,参数id=true表明为主键
@Results 封装多个结果,相当于xml中的resultMap,可指定属性id,组合多个@Result标签
@One 实现一对一查询封装,调用其他接口,完成子查询操作,返回单一的对象
@Many 实现一对多、多对多查询封装,调用其他接口,完成子查询操作,返回集合对象
@ResultMap @Select提供<resultMap>/@Results的id,复用resultMap,参数为一个int数组
@Options 配置对应方法的sql属性,如useGeneratorKey、useCahce等,无法指定空值,所以一旦使用就受所有默认值的支配。
@SelectKey 提供在insert、update、delete时进行二次查询,并返回某些属性。参数keyPropertykeyColumn指定映射关系,statement指定sql语句,before代替order属性,使用bool决定,还有其他参数同上文
  • 一对一查询

    原理是进行两次查询,第二次查询的条件取决于第一次查询得到的外键

    @Results({
        @Result(id=true,column="",property=""),
        // 进行一次子查询封装
       	@Resutl(
            property="",javaType=class,
            column="下一次查询使用哪一列的值"
        	one=@One(select="接口方法全限定名")
        )
    })
    @Select("sql")
  • 一对多、多对多查询

    @Result(
        @Result(
    		/*省略相同部分*/
            many=@Many(select="")
        )
    )
    @Select("sql")

动态SQL

在配置映射关系时,Mybatis提供了一系列标签,使得sql语句能够动态的生成。根据参数不同,在同一个标签下能够生成不同语句。

动态标签

动态标签是用于select等标签内部,用来动态生成sql语句的标签。

  • <where>

    用于包裹其他动态标签,防止不使用条件判断时造成sql语法问题,当where标签内没有生成任何sql语句时,不会在原有sql上加入where字段,反之则加上where字段。(实际上是添加一个’ where 1=1 ')

  • <if>

    <if test="bool表达式">
        sql
    </if>
  • <foreach>

    <foreach collection="集合类型" open="开始字段" close="结束字段" item="name" separator="分隔符">
    	sql,使用#{name}引用item
    </foreach>
  • <trim> 与where用法相似

    <trim perfix="" suffix="" prefixOverrides="" suffixOverrides="">
    	sql
    </trim>
    属性 描述
    perfix 给sql语句拼接的前缀
    suffix 给sql语句拼接的后缀
    prefixOverrides 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
    suffixOverrides 去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定

    更多标签详见官方文档

片段抽取

<sql>片段标签,使用这个标签可以抽取重复片段,并使用标签id作为变量代替。

创建sql片段

<sql id="">sql</sql>

引用sql片段

<include refid="sqlId"/>

注解使用动态SQL

Mybatis对注解方式的SQL也有动态功能的支持,一共有两种方法。

  1. 方法一

    在sql字符串内使用<script>把sql语句包裹起来,在<script>内部就能够使用上面讲到的标签了,用法相同,但需要注意内部的引号”“需要使用转义字符!

  2. 方法二

    使用@XXXProvider标签,XXX包括Select、Insert等,如:

    @SelectProvider(type=A.class, method="mySelect")

    type指定一个类,method指定一个方法的名称,该方法的参数应与接口方法参数相同方便判空,并返回一个String对象作为为Sql字符串。所以我们需要在方法中实现Sql的动态生成!

    在这个方法中,Sql如何动态生成完全取决于我们自己,我们可以使用自己定义的逻辑来完成这个工作。但是Mybatis也为我们提供了一种选择——使用SQL对象构建sql语句。

SQL对象

SQL对象是一个构建Sql的工具类,可以很方便的构造一个Sql语句,使用其提供的SELECT()WHERER()JOIN()等方法来拼接字符串,且无需担心语法问题。

简单用例(来自官方文档)

// 匿名内部类风格
public String deletePersonSql() {
  return new SQL() {{
    DELETE_FROM("PERSON");
    WHERE("ID = #{id}");
  }}.toString();
}

// Builder / Fluent 风格
public String insertPersonSql() {
  String sql = new SQL()
    .INSERT_INTO("PERSON")
    .VALUES("ID, FIRST_NAME", "#{id}, #{firstName}")
    .VALUES("LAST_NAME", "#{lastName}")
    .toString();
  return sql;
}

// 动态条件(注意参数需要使用 final 修饰,以便匿名内部类对它们进行访问)
public String selectPersonLike(final String id, final String firstName, final String lastName) {
  return new SQL() {{
    SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME");
    FROM("PERSON P");
    if (id != null) {
      WHERE("P.ID like #{id}");
    }
    if (firstName != null) {
      WHERE("P.FIRST_NAME like #{firstName}");
    }
    if (lastName != null) {
      WHERE("P.LAST_NAME like #{lastName}");
    }
    ORDER_BY("P.LAST_NAME");
  }}.toString();
}

使用Provider时,Mapper接口上的@Param将无法对应到在SQL构造器方法中的参数,一定要在构造器方法的参数上使用@Param

Plugins

使用第三方插件或自己开发的插件对Mybatis的功能进行拓展

配置Plugin

<plugins>
	<plugin interceptor="类全限定名">
        <property name="" value=""/>
    </plugin>
</plugins>

常用插件

  • pageHelper

    非常好用的分页插件,简单的参数设定,简单的数据获取。

    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
    </dependency>

自定义插件

实现Interceptor接口,主要实现三个方法

  1. 核心方法,通过invocation参数能够获得映射好的sql语句,该方法的返回值是invocation对象proceed方法的返回值,与Proxy代理模式一样。

    public Object intercept(Invocation invocation) throws Throwable {}
  2. 主要用来将对象封装成代理对象,确保interceptI()能够被调用

    public Object plugin(Object target) {
    	// 一般都是通过该方法封装
        return Plugin.wrap(target, this);
    }
  3. 提供插件自定义属性,这个方法的属性能够在配置阶段进行注入

    public void setProperties(Properties properties) {}

配置拦截注解

在自定义插件类完成后,需要使用注解@Intercepts来表明该插件需要拦截的方法签名。

Mybatis只允许拦截以下类中的方法:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
  • @Singnature:表示一个构造一个方法签名
    • type:被拦截类名
    • method:被拦截类中的方法名
    • args:被拦截方法的参数

JAVA-API

使用mybatis提供的java开发包,用java对数据库进行持久化操作。

快速入门

快速入门演示了mybatis-api的一个基本使用流程

//1. 加载核心配置
InputStream configStream = Resources.getResourcesAsStream("sqlMapConfig.xml");
//2. 获得SqlSession工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(configStream);
//3. SqlSession对象
SqlSession session = factory.openSession();
//4. 执行sql语句
List<Object> list = session.selectList("mapping-parttern");
// 提交事务
session.commit();
//5. 释放SqlSession
session.close();

常用对象详解

  • SqlSession

    mybatis主要的java接口,一切数据库操作都是由这个对象完成的。其地位类似于JdbcTemplate,在使用spring时也能通过IOC进行注入。

    主要方法有selectXxx()update()delete()以及事务管理的commit、rollback

  • SqlSessionFactoryBuilder

    用于创建SqlSessionFactory的对象,其中构建方法为build(),支持使用InputStream加载配置文件来创建工厂对象。

  • SqlSessionFactory

    创建SqlSession对象的工厂类,用openSession()方法创建,可以使用其重载来设置SqlSession的一些属性,如是否自动提交事务以及事物的隔离级别等。

    引用官方文档的一段话

    SqlSessionFactory 有六个方法创建 SqlSession 实例。通常来说,当你选择其中一个方法时,你需要考虑以下几点:

    • 事务处理:你希望在 session 作用域中使用事务作用域,还是使用自动提交(auto-commit)?(对很多数据库和/或 JDBC 驱动来说,等同于关闭事务支持)
    • 数据库连接:你希望 MyBatis 帮你从已配置的数据源获取连接,还是使用自己提供的连接?
    • 语句执行:你希望 MyBatis 复用 PreparedStatement 和/或批量更新语句(包括插入语句和删除语句)吗?

DAO层接口代理

Mybatis能够将编写的Mapper.xml通过Java的接口进行动态代理,使得开发人员调用dao接口就能完成mapper中定义的数据库操作。使用接口代理的有以下的条件约束:

  1. mapper中namesapce属性的值需要为所需接口的全限定名。
  2. mapper中每个定义的sql语句id属性需要与接口中的方法名一一对应。
  3. sql语句的参数类型、结果类型即为接口方法的参数和返回值。
  4. 接口的设计不能使用重载。

ℹ️使用IDEA插件能够根据数据库关系自动生成实体和接口

API

通过SqlSessiongetMapper(dao.class)获得接口的代理对象,直接使用接口即可。

typeHandler

类型处理器,在Mybatis生成PreparedStatement或获取结果时会调用类型处理器来转化sql所需的参数。Mybatis默认使用了许多转换器,在使用时能够自动推断类型使用响应转换器,也能事先通过parameterTyperesultType指明参数类型。

自定义typeHandler

类型转换器需要实现TypeHandler接口或继承BaseTypeHandler<T>类,泛型为需要自定义转化的Java类。

  • parameter转化逻辑

    当mybatis设置数据到数据库时调用,使用这个方法来自定义参数转化逻辑

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, T obj, 
                                    JdbcType jdbcType) {
        //直接使用preparedStatement的set方法设置参数'obj',参数的index为'i'
    }
  • result转化逻辑

    从数据库取出数据时调用的转化方法

    //从三类不同的结果集取出给定的数据并转化成Java数据,第二个参数即为结果集中所需参数的位置
    @Override
    public T getNullableResult(ResultSet resultSet, String s) {}
    
    @Override
    public T getNullableResult(ResultSet resultSet, int i) {}
    
    @Override
    public T getNullableResult(CallableStatement callableStatement, int i) {}
 评论