关于mybatis:假装是小白之重学MyBatis一

30次阅读

共计 16755 个字符,预计需要花费 42 分钟才能阅读完成。

在工作中发现对 MyBatis 还是有了解不到位的中央,所以就打算重新学习一下 MyBatis。

简介

MyBatis 是一款优良的长久层框架,它反对自定义 SQL、存储过程以及高级映射。MyBatis 罢黜了简直所有的 JDBC 代码以及设置参数和获取后果集的工作。MyBatis 能够通过简略的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,一般老式 Java 对象)为数据库中的记录。

首先是 MyBatis 给本人的定位是长久层框架,那什么是长久层,简略的说就是和数据库沟通的那一层,也就是 MVC 模式下的 dao(Data Access Object)层,dao 层的代码负责将业务代码变成对数据库表的操作,至于为什么叫长久层,我感觉是跟数据库的持久性特点有关系呢!

在 dao 层没有任何框架之前,咱们是间接应用原生的 jdbc 来操纵数据库的数据,拼接 SQL,设置参数,获取后果集重复性的工作经常让人非常腻烦,然而 JDBC 从设计思路上讲也足够优良了,做到了跨数据库,让使用者不用关怀是哪个数据库,而采取不同的操作,JDBC 就是一组接口,由各大数据库厂商提供对应的实现类,所以也不大可能做齐全的定制的化操作,所以 JDBC 的设计思维就是宽泛一点。

然而咱们心愿简略点,所以如果你看视频去学习的话,基本上学完 JDBC,就会讲如何封装一个工具类 JdbcUtils,来防止反复代码的编写,但这并不是一个 Java 程序员的痛点,对吗?Java 社区也关注到了这个问题,开始着手对 JDBC 进行扩大,进行降级。这也就是 MyBatis、Hibernate、Spring Data JPA 等 ORM 框架。

等等你方才又提到了一个名词,ORM 框架,那什么是 ORM 框架?ORM Object Relational Mapping 即对象关系映射,听起来好形象啊! 不要焦急,听我细细道来,Java 是一门面向对象的语言,咱们当初广泛应用的数据库是关系型数据库(表为次要模式),ORM 的思维就是是否将表映射为对象呢?一条数据记录就是一个对象。

所以 MyBatis 是一款优良的长久层、ORM 框架,在 JDBC 的根底上进行扩大、封装,罢黜了简直所有 JDBC 代码以及设置参数和获取后果集的工作,大大简化了长久层代码开发。

对简化了长久层代码的开发,简略点,简略点,咱们都喜爱简略的货色。

如何学习一门技术?

个别状况,学一门框架,最好还是去官网去学,以前我是图速度快,去 B 站找的视频。当初发现 MyBatis 官网写的教程挺不错的,更让我喜爱的是有中文版本:

好到我让我感觉我这篇博客,是不是还是有必要写。然而思虑再三,还是打算写,官网文档配合本人的了解,让本人的常识更成零碎。

筹备工作

要用 MyBatis,咱们首先要引入 MyBatis,MyBatis 是在原生 JDBC 的根底上做扩大,所以咱们要引入对应的数据库驱动 (数据库驱动就是数据库厂商实现的 JDBC),本篇咱们应用的是 MySQL,Druid 来治理数据库连贯。本篇咱们仍然应用 Maven 来搭建我的项目:
对应的依赖如下:

<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.5</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
            <scope>test</scope>
        </dependency>

slf4j 是日志框架,输入的信息会更粗疏,倡议引入。

如果你不会用 maven

倡议你去学 maven,参看我这篇博客: Maven 学习笔记, 十分通俗易懂的入门。

如果你不想学 Maven,想用 jar 包模式,也行,我的博客就是这么贴心,哈哈哈哈。

  1. 首先进入 MyBatis 官网。

3.

第一个 MyBatis 程序

首先咱们要建一个配置文件


对了还要建一个表,如果你不懂的建表,本篇文章可能就不适宜你,我建的表叫 Blog。
表语句我就不放了,最近重学 SSM,写了一些例子,都放在 GitHub 上了, 上面是链接:

  • https://github.com/CXK6013/SSM

大略解释一下配置文件

<?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="jdbc.properties"/>
    <!-- 指定默认环境, 个别状况下, 咱们有三套环境,dev 开发 ,uat 测试 ,prod 生产 -->
    <environments default="development">
        <environment id="development">
            <!-- 设置事务管理器的治理形式  -->
            <transactionManager type="JDBC"/>
            <!-- 设置数据源连贯的关联形式为数据池  -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.dev.driver}"/>
                <property name="url" value="${jdbc.dev.url}"/>
                <property name="username" value="${jdbc.dev.username}"/>
                <property name="password" value="${jdbc.dev.password}"/>
            </dataSource>
        </environment>
    </environments>  
    <mappers>
        <!-- 设置扫描的 xml,org/example/mybatis 是包的全类名,这个 BlogMapper.xml 会讲 -->
        <mapper resource="org/example/mybatis/BlogMapper.xml"/>
    </mappers>
</configuration>

Hello World

新建一个接口

public interface BlogMapper {Blog selectBlog(Long id);
}

新建一个 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">
<!-- 这里的 namespace 必须是 BlogMapper 的全类名 -->
<mapper namespace = "org.example.mybatis.BlogMapper">
      <!-- MyBatis 会主动将查问进去的记录封装为 resultType 设定类型的对象,这里领会一下 ORM 的思维 -->
    <select id = "selectBlog" resultType = "org.example.mvc.entity.Blog">
            select * from Blog where id = #{id}
    </select>
</mapper>

测试代码

 public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建一个 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 开启一个会话
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 加载指定的接口
        BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
        // 调用接口中的办法, 相当于执行对应 mapper 中的查问语句
        Blog blog = blogMapper.selectBlog(1L);
        // 打印查问语句
        System.out.println(blog);
    }

执行后果:

总结一下

第一个程序可能还比拟晕哈,先跟着做,而后咱们来解释一下 MyBatis 做了什么:

那 BlogMaper.java 到底是怎么和 BlogMapper.xml 关联起来的呢? 还记得下面的配置吗?


实质上还是动静代理,运行时创立接口的实现类,如果你不懂什么是动静代理,请参看:

  • 代理模式 -AOP 绪论

用 MyBatis 操纵数据库的根本流程:

  • 新建接口
  • 建 xml,这里要留神一下,XML 也不能瞎建,下面的束缚还是要留神一下:


不必记也没关系,能够在 MyBatis 官网拷贝一下:

  • 在配置文件的 mapper 标签,开启扫描标签,关联接口和 xml 文件(能够批量设定的, 前面会讲)
  • 而后 SqlSession 对象的获取指定接口的办法,即可调用对应的 SQL 语句。

认真领会下,这样绝对于原生的 JDBC 是不是更加清晰了呢。

传递参数

下面咱们在调用 BlogMapper 中的 selectBlog 仅仅只是在接口中写了参数,MYBatis 就能主动的将 #{id}替换为咱们传递的参数,是不是很弱小呢! 这种形式也反对多个参数,然而该 SQL 须要的参数有七八个怎么办?接口办法上写七八个参数? 其实也行,那传的是一个数组或者 List 呢?MyBatis 能遍历一下吗?MyBatis: 当然反对。
对于数据库来说,查总是最让咱们关怀的,select 标签的属性也是最多的,如上面代码所示:

<select id="selectPerson"
        parameterType="int"
        parameterMap="deprecated"
        resultType="hashmap"
        resultMap="personResultMap"
        flushCache="false"
        useCache="true"
        timeout="10"
        fetchSize="256"
        statementType="PREPARED"
        resultSetType="FORWARD_ONLY">
</select>

这些属性在 MyBatis 官网有具体的介绍:


咱们这里只拎进去罕用的来介绍,在传递参数这里,咱们介绍的就是 parameterType(参数类型)属性,这个属性是可选的,MyBatis 能够通过 Typehandler 来推断进去传递的参数,来将咱们 SQL 语句中的 #{id}(咱们下文会对立称之为占位符)替换掉。
留神单个参数,占位符中参数名和办法中的参数名无需保持一致。多个参数时,就须要启用 @Param 注解了。@Param 中的属性值要和占位符中的参数名保持一致,不然 MyBatis 拿到两个参数值,无奈推断出应该用哪个值替换占位符。

如果你想要用对象

间接传对象 (Map 也是一样),而后在占位符中写对应的属性名(Map 的时候是 key) 即可:

<!-- 留神这个时候写在占位符中的属性名要有 get 办法, 否则调用会失败 -->
    <select id="selectBlogByObj"   resultType="org.example.mvc.entity.Blog"  >
            select * from Blog where id = #{id}
    </select>

测试代码:

public static void main(String[] args) throws IOException {selectByObj();
    }
    private static void selectByObj() throws IOException {BlogMapper blogMapper = getMapper(BlogMapper.class);
        Blog blog = new Blog();
        blog.setId(1L);
        blog.setName("aa");
        blog = blogMapper.selectBlogByObj(blog);
        System.out.println(blog);
    }
    public static <T> T getMapper(Class<T> t) throws IOException {
        String resource = "mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建一个 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 开启一个会话
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return (T) sqlSession.getMapper(t);
    }

运行后果:

上面再介绍应用的时候,不会再贴 getMapper 和 main 办法,只写对应的办法。

汇合

遍历 List、数组、Map

汇合经常被用来构建 in 语句,那么在 xml 中怎么遍历呢? 通过 foreach 标签来遍历,像上面这样:

   <select id = "selectBlogByList" resultType="org.example.mvc.entity.Blog">
        select * from Blog where id in
        <foreach collection="list" item="item" open="(" separator="," close=")">
            #{item}
        </foreach>
    </select>

这个 foreach 标签就是把 java 中 foreach 映射进到 xml 了而已。item 是迭代元素 , list 是迭代的汇合。open、close 用于指定拼接造成的字符串,以及汇合项迭代的分隔符。也就是说假如我传入的 List 有 1,2 这两个元素,最终造成的 sql 语句就会是上面这样:

 select * from Blog where id in (1,2)

看 MyBatis 如许的智能。如果汇合类型是 Map 的时候,index 是键,item 是值。
下面我查到的可能是多个对象,那在 MyBatis 中应该怎么接管呢?只用将该标签 id 对应的办法改为 List<Blog> 就行。

  List<Blog> selectBlogByArray(Long[] idLongArray);

默认状况下,collection 即为汇合名的纯小写,比方传入的是 List 类型,那么 Collection 中就应该写 list。如果是数组就应该写 Array。如果用 @Param 指定了参数名,那么就写 @Param 中指定的参数名。

返回类型

Map

下面咱们曾经讲了返回一条数据和返回多条数据,这个用 Map 接管返回类型,看上去有点违反直觉,事实上他也是完全符合直觉的。咱们用 Map 接管一下试试看:

<select id = "selectAllReturnMap" resultType="map">
     select * from Blog
</select>

接口中申明的办法:

   Map<String,Object> selectAllReturnMap();
 private static void selectAllReturnMap() throws IOException {BlogMapper blogMapper = getMapper(BlogMapper.class);
        System.out.println(blogMapper.selectAllReturnMap());
    }

测试后果如下:

粗心就是返回了三个,你用一个接管,接管不了。
可能这个时候又同学就会问了,Map 不是能装多组值吗?为啥收不了三条记录,那咱们想一下 Map 的 key 是不是不能反复,三条记录的 key 都是属性名,某种意义上一个 Map 就是一个对象,key 就是属性名,value 就是属性值。所以咱们应该这么收:

List<Map<String,Object>> selectAllReturnMap();

测试后果:

{占位符} VS ${占位符}

下面咱们用的替换参数的占位符,都是 #号结尾的,MyBatis 会把#{参数名}替换为咱们调用接口对应办法时传递的参数值,个别咱们称之为# 号占位符,其实还有一种是以 $ 结尾的。咱们称之为 dollar(刀乐)占位符。默认状况下,应用 #{} 参数语法时,MyBatis 会创立 PreparedStatement 参数占位符,并通过占位符平安地设置参数(就像应用 ? 一样)。
上面咱们用一个例子来感受一下两者的不一样,咱们先配下日志,日志输入真正执行的 SQL,不便咱们剖析问题

咱们依照要求引入对应的依赖:

 <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.12.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>2.14.0</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

而后在 resources 建一个文件 log4j.properties,文件内容如下:

# 全局日志配置
log4j.rootLogger=DEBUG,ERROR, stdout
# MyBatis 日志配置 会输入执行包下的详细信息
log4j.logger.org.mybatis.example=DEBUG 
# 控制台输入
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

这个照着贴就行,介绍日志并不是本篇的内容,咱们是借助日志来钻研井号占位符和美元占位符的不同之处。
而后在配置文件中开启日志:

   <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

号占位符

xml 中的代码:

  <select id="selectByMark"  resultType = "org.example.mvc.entity.Blog">
             select * from Blog where id = #{id} and name =  #{name}
    </select>

BlogMapper. 中的代码:

List<Blog> selectByMark(@Param("id") String id,@Param("name") String name);

测试代码:

  private static void markVsDollar() throws IOException {BlogMapper blogMapper = getMapper(BlogMapper.class);
        blogMapper.selectByMark("1","aa; delete Blog;");
    // blogMapper.selectByDollar("1","aa; delete Blog");
    }

执行后果:

DEBUG [main] - Created connection 551479935.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@20deea7f]
DEBUG [main] - ==>  Preparing: select * from Blog where id = ? and name = ?
DEBUG [main] - ==> Parameters: 1(String), aa or 1 = 1(String)
DEBUG [main] - <==      Total: 0

所以最终的 SQL 语句就是: select * from Blog where id = '1' and name = 'aa or 1 = 1'
咱们的数据库并没有这样的数据,所以一条这样的数据都没查出来。

$ 号占位符

xml 中的代码:

  <select id="selectByDollar"  resultType = "org.example.mvc.entity.Blog">
             select * from Blog where id = ${id} and name = ${name}
    </select>
List<Blog> selectByDollar(@Param("id") String id, @Param("name") String name);

测试代码:

  private static void markVsDollar() throws IOException {BlogMapper blogMapper = getMapper(BlogMapper.class);
        blogMapper.selectByDollar("1","1 or  1 = 1;");
    }

执行后果:

DEBUG [main] - Created connection 1327006586.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f18837a]
DEBUG [main] - ==>  Preparing: select * from Blog where id = 1 and name = 1 or 1 = 1;
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 2

各位认真的比照一下两个理论执行的 SQL 语句,一个 Parameters 有值,有参数类型,一个没有,间接是原样替换。
第二种事实上被称之为 SQL 注入,查到了不应该查到的记录。然而 $ 号占位符也不是一无是处,比方在排序的时候,依据前端传递的字段来进行排序:

ORDER BY ${columnName}

可能有同学会说,这样是不是仿佛也有注入危险,不按约定的字段,轻易穿了一个过去,而后不就报错了,防止这个问题能够在 Java 中做判断,只有是约定的字段才会传给真正执行的 SQL。

总结一下

井号占位符会主动给咱们本义,依据类型来判断转不本义,如果是字符串类型,MyBatis 会主动为咱们将参数上加上引号。如果是数字类型,就不会加上。
美元占位符是原样替换,有 SQL 注入的危险,但有的时候在咱们并不想 MyBatis 为咱们拼上引号的时候,比如说依据前端传递的字段来进行排序,所以 $ 占位符要慎用。

update、delete、insert、调用存储过程

数据变更语句 insert,update 和 delete 的实现十分靠近:

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""keyColumn=""
  useGeneratedKeys=""timeout="20">

<update
  id="updateAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<delete
  id="deleteAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

parameterType 类型和 select 标签的 parameterType 应用形式一样,咱们次要讲 statementType、useGeneratedKeys。
statementType: 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 别离应用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
JDBC 根底不扎实的同学,可能会问 Statement、PreparedStatement、CallableStatement 是啥?
简略的说,Statement 接口提供了执行语句和获取后果的根本办法;PreparedStatement 接口增加了解决输出参数的办法;
CallableStatement 接口增加了调用存储过程核函数以及解决输入参数的办法。

useGeneratedKeys 应用示例

<!--
            useGeneratedKeys 开启接管主键
            keyProperty 将返回的主键放在对象的哪个属性上
 -->
<insert id="insertEntity"  useGeneratedKeys="true" keyProperty="id" >
     insert into Blog (name)
     values (#{name})
</insert>

java 代码:

   private static void insertEntity() throws IOException {
        String resource = "mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建一个 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 开启一个会话
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog = new Blog();
        blog.setName("aaa");
        blogMapper.insertEntity(blog);
        // 记得提交
        sqlSession.commit();
        // 敞开会话,不然新增不会胜利
        sqlSession.close();
        System.out.println(blog);
    }

所以下面的 getMapper 办法,咱们还要再革新一下,理论应用中也就是 SSM(Spring Spring MVC MyBatis)整合之后,引入数据库连接池之后,连贯应用结束之后会还给连接池,这里咱们就不革新了。
测试后果:

数据库后果:

数据库反对自增主键能够这么搞,不反对的就不能够这么搞了。

罕用标签

下面咱们在介绍参数类型是汇合时,曾经介绍了遍历标签,<foreach> , 然而有循环,怎么能够没有判断,switch 呢。那怎么把逻辑判断移入 xml 中呢?将判断变成一个一个标签吗? 那得记住多少标签啊?MyBatis 的前身 iBatis 就这么做的,MyBatis 做出的扭转就是通过 OGNL 表达式配合 if 标签来实现逻辑判断。

OGNL 表达式简介

OGNL(Object-Graph Navigation Language)是一种表达式语言(EL),简略来说就是一种简化了的 Java 属性的取值语言,在传统的 Struts 框架,以及 MyBatis 中都有大量应用,开源 Java 诊断利器 Arthas 也应用它做表达式过滤,简略但不失灵便的设计衍生进去了好多高阶玩法。

咱们这里简略的介绍一下罕用的,详情能够参看 OGNL 官网文档: http://commons.apache.org/pro…

OGNL 生来就是为了简化 Java 属性的取值,比方想依据名称 name 援用以后上下文环境中的对象,则间接键入即可,如果想要援用以后上下文环境中对象 text 的属性 title,则键入 text.title 即可。如果调用对象的办法,间接通过对象. 办法名 () 即可。
拜访数组,间接通过数组名 [索引] 拜访即可。

判断参数不为 null 和 汇合 size 大于 0

 <select id="selectBlogByList" resultType="org.example.mvc.entity.Blog">
        select * from Blog where id in
        <if test = "list != null and list.size() > 0">
            <foreach collection="list" item="item" open="(" separator="," close=")">
                #{item}
            </foreach>
        </if>
    </select>

参数值等于某个字符串

   <select id="selectByMark" resultType="org.example.mvc.entity.Blog">
             select * from Blog where id = #{id}
             <if test = '" 张三".equals(name) '>
                 and name =  #{name}
             </if>
    </select>

choose、when、otherwise

有时候,咱们不想应用所有的条件,而只是想从多个条件中抉择一个应用。针对这种状况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。为了丰盛咱们的实用场景,咱们又向 Blog 表里加了两个字段:title 和 status,以前只有 id,name。
示例:

   <select id="testChoose" resultType="org.example.mvc.entity.Blog">
         select * from Blog  where status = 'active'
        <choose>
            <when test="title != null">
                AND title like #{title}
            </when>
            <when test="name != null">
                AND name like #{name}
            </when>
            <otherwise>
                AND status != 'active'
            </otherwise>
        </choose>
    </select>

如果 title 不为空,就搜寻 title,如果 name 不为空,就含糊搜寻 name。如果这两个都是空,就去搜寻 status 不是 active 的。

where、trim、set

后面的示例咱们曾经解决了拼接 SQL 的问题,上面咱们来看上面一个 SQL:

  select * from Blog where id in
        <if test = "list != null and list.size() > 0">
            <foreach collection="list" item="item" open="(" separator="," close=")">
                #{item}
            </foreach>
        </if>

如果条件不成立,SQL 就会变成这样:

select * from Blog where id in

很显著这会报错,那为了防止这种状况,咱们是不是要在 where 前面拼接一个 1 = 1 呢? 而后 SQL 就变成了上面这样:

  select * from Blog where  1 = 1
        <if test = "list != null and list.size() > 0">
         id in
            <foreach collection="list" item="item" open="(" separator="," close=")">
                #{item}
            </foreach>
        </if>

然而这相当不优雅,MyBatis 提供了 <where> 标签,where 元素只会在子元素返回任何内容的状况下才插入“WHERE”子句。而且,若子句的结尾为“AND”或“OR”,where 元素也会将它们去除。所以咱们的 SQL 能够就变成了这样:

   <select id="selectBlogByList" resultType="org.example.mvc.entity.Blog">
        select * from Blog 
        <where>    
            <if test = "list != null and list.size() > 0">
                id in
                <foreach collection="list" item="item" open="(" separator="," close=")">
                    #{item}
                </foreach>
            </if>
    </where>
    </select>

如果 where 元素与你冀望的不太一样,你也能够通过自定义 trim 元素来定制 where 元素的性能。比方,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR">
  ...
</trim>

prefixOverrides 属性会疏忽通过管道符分隔的文本序列(留神此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
用于动静更新语句的相似解决方案叫做 set。set 元素能够用于动静蕴含须要更新的列,疏忽其它不更新的列。比方:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

这个例子中,set 元素会动静地在行首插入 SET 关键字,并会删掉额定的逗号(这些逗号是在应用条件语句给列赋值时引入的)。
来看看与 set 元素等价的自定义 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

留神,咱们笼罩了后缀值设置,并且自定义了前缀值。

bind 标签

bind 元素容许你在 OGNL 表达式以外创立一个变量,并将其绑定到以后的上下文:

<select id="selectByMark" resultType="org.example.mvc.entity.Blog">
        select * from Blog where id = #{id}
         <if test = '" 张三".equals(name) '>
             <bind name="pattern" value="'%' + name + '%'" />
             and name =  #{pattern}
         </if>
</select>

别名

还记得咱们在配置文件中配置的 mappers 吗?咱们其实也能够批量配置,批量关联:

<mappers>
        <!-- 批量关联 -->
        <package name = "org.example.mybatis"/>
 </mappers>

咱们的 resultType 写的是全类名,那不能不写那么多呢?能够,在配置文件中这样配置就能够了:

  <typeAliases>
        <package name="org.example.mvc.entity"/>
    </typeAliases>


留神这个程序,只能按 properties、settings、typeAliases 这个程序来。
而后咱们写 resultType 中的值写类名就能够了。

类型转换器

当初咱们来关注一下,MyBatis 实现 ORM 的一组外围类,类型转换器。咱们下面讲 MyBatis 将表的记录变成 Java 中的对象,那数据类型是怎么对的上的呢? 就是通过类型转换器:

能够重写已有的类型处理器或创立你本人的类型处理器来解决不反对的或非规范的类型, 具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口,或继承一个很便当的类 org.apache.ibatis.type.BaseTypeHandler,并且能够(可选地)将它映射到一个 JDBC 类型。
间接实现 TypeHandler 接口还是有点麻烦的,所以咱们这里介绍的是继承 BaseTypeHandler 来领会 MyBatis 中类型转换器的弱小性能。BaseTypeHandler 概览:

转换器是双向的,所以就有从数据库到 Java 的,从 Java 到数据库的:
setNonNullParameter Java 到数据库就是剩下的剩下的三个 get 办法,从名字上咱们就能够推断进去,是从数据库到 Java 的。
这次咱们定义的类型转换器就是将 Java 的 Boolean 转成数据库中的 int 类型。

public class MyTypeHandler extends BaseTypeHandler<Boolean> {
    // java-DB
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType) throws SQLException {if (parameter) {ps.setInt(i, 1);
        } else {ps.setInt(i, 0);
        }
    }

    // DB-java
    @Override
    public Boolean getNullableResult(ResultSet rs, String columnName) throws SQLException {return rs.getInt(columnName) == 1 ? true : false;
    }

    //  DB-java
    @Override
    public Boolean getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return rs.getInt(columnIndex) == 1 ? true : false;
    }

    //  java-Db
    @Override
    public Boolean getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return cs.getInt(columnIndex) == 1 ? true : false;
    }
}

而后在 Blog 实体和表中退出对应的字段,Blog 为 Boolean,Blog 表为 int,这里不再做展现了。
而后咱们在配置文件中注册应用这个类型转换器:

<typeHandlers>
  <package name="org.mybatis.example"/>
</typeHandlers>

测试代码:

private static void selectBlogByCollection() throws IOException {BlogMapper blogMapper = getMapper(BlogMapper.class);
    List<Long> idLongList = new ArrayList<>();
    idLongList.add(1L);
    System.out.println(blogMapper.selectBlogByList(idLongList));
}

数据库中的值:

测试后果:

转换胜利。

总结一下

本篇次要讲的是 MyBatis 的根本应用,如何配置、传参、返回类型、占位符、罕用标签、OGNL 表达式。开篇讲的基本上都是应用频率很高的,这算是把 MyBatis 又重学了一遍,以前学 MyBatis 看视频,当初发现如果官网文档写的比拟丰盛的话,看官网文档是更好的抉择。心愿对会大家有所帮忙

参考资料

  • MyBatis 官网文档
  • MyBatis 视频教程
  • OGNL 语法标准

正文完
 0