Mybatis入门

46 阅读11分钟

Mybatis是一款优秀的持久层(dao) 框架,对JDBC程序进行封装,用于简化JDBC的开发

第一部分 快速入门

之前用图形化界面操作数据库,是在图形化界面中编写SQL语句 并发送到数据库服务器来执行,数据库再将结果返回给图形化界面工具

而Mybatis就是在Java程序中编写SQL语句来操作数据库

其实很简单就三步:

  1. 准备工作(创建springboot工程、数据库表user、实体类User)
  2. 引入Mybatis的相关依赖,配置Mybatis
  3. 编写SQL语句(注解/XML)

②也被称为数据库连接的四要素,由于我们现在使用的是SpringBoot整合Mybatis所以这部分的信息我们就需要配置在springboot默认的配置文件application.properties当中

spring.application.name=springboot-mtbatis-quickstart
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
spring.datasource.username=root
spring.datasource.password=123456

把它放进去就好了~

③编写SQL语句这里我们用注解方式,先定义一个Mapper层(类似于Dao层)写一个UserMapper的接口,在接口上方加上@Mapper(表示这是一个mybatis的mapper接口,在运行时会自动生成实现类(动态代理对象),并将此对象交给IOC容器管理)

再在接口中定义一个方法用来查询所有用户的信息,返回值就设为一个List的集合就可以,集合的泛型就是User;再在这个方法的头上加上Select注解,来指定当前要执行的是一个查询操作,在该注解的value值中就可以来编写SQL语句(@Select @Insert@Delete@Update),到此入门的三步就完成了!

package com.itheima.Mapper;
import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;

@Mapper // 表示这是一个mybatis的mapper接口,在运行时会自动生成实现类(动态代理对象),并将此对象交给IOC容器管理
public interface UserMapper {
    // 查询所有用户信息
    @Select("select * from user")
    public List<User> list();
}

接下来就要做一个测试,在test文件下有个测试类

注意这一步!前面的userMapper是一个接口,这里为什么可以直接定义一个他的对象,接口不是不能实例化吗?

答:这就是在Mapper层中的UserMapper接口上方为什么要加@Mapper注解的关键!

它在运行时会自动生成实现类(一个动态代理对象),并将此对象交给IOC容器管理(就是前面学过的控制反转 类似 @Component这个操作 在IOC容器中就有了这个bean对象),所以在测试类中可以定义UserMapper对象,并且记住在其上方加上@Autowired注解表示依赖注入。(不熟的话可以去看之前的一篇笔记 请求响应系统优化实践:基于IOC与DI的分层解耦案例分析》

然后再testListUser方法中直接调用userMapper中的list方法即可,并用stream结合lambda表达式 简洁遍历输出结果,完成测试!

撒花~

第二部分 配置MySQL提示

配置好后,user可能会爆红,是因为Idea和数据库没有建立连接,不识别表信息,所以我们在Idea中去配置MySQL数据库连接

连好数据库后,在注解中写SQL语句就会出现提示了,写错了也会马上爆红


第三部分 JDBC介绍(以及跟Mybatis的对比)

• JDBC: (Java DataBase Connectivity), 就是使用Java语言操作关系型数据库的一套API 即接口。

是各个数据库厂商去实现这套接口,提供数据库驱动jar包

比如上面快速入门程序里xml中引入的依赖就有mysql的驱动:

<!--mysql驱动包-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

原始的JDBC的存在的弊端:

①代码量很大,注册驱动、获取连接 在项目开发中是比较容易变动的,特别是获取连接 在开发时会连接开发数据库,在测试时会连接测试数据库,在生产阶段会连接生产数据库,url、username、password都会变!

而这部分都是被写死在代码中(也称为硬代码),一旦变化,就要重新编译,打包之后才能运行。

②在封装解析结果时,要一个字段一个字段的解析,还要考虑字段类型该调用什么方法,字段一多就愈发的繁琐。

③在执行SQL语句前,先要获取连接,sql语句执行完毕之后,又要关闭连接;在项目中去频繁的获取连接会导致 资源浪费、性能降低


我们再来对比在Mybatis中是如何解决这些问题的!!

①首先关于硬编码,Mybatis是将数据库连接的四要素直接配置在application.properties中,可以直接在它里面操作就好~

②在解析结果时,Mybatis它会自动的将查询的结果封装到这个集合当中,查询返回的每一条记录都会封装为一个user对象,所有的user对象又会封装到一个List集合中,整个过程自动化进行

③关于频繁获取连接、释放连接 浪费资源问题,Mybatis中,在application.properties中每条配置语句前面都有spring.datasource前缀,springboot底层就会自动的采用数据库连接池技术来统一管理和分配这些连接

其实指的就是JDBC中的Connection对象那一行,有了连接池之后,每一次在执行sql语句的时候,我们只需要从连接池中获取一个连接去执行sql语句,完毕后,再将这个连接还给连接池,那这样就可以做到连接的复用,后面会再讲数据库连接池技术。

没有对比就没有伤害,Mybatis完胜!在我们使用springboot整合mybatis进行数据库操作时,我们主要关注两点:一个就是在application.properties中关于mybatis的相关配置;另一个方面就是mapper接口以及我们所定义的SQL语句~


第四部分 数据库连接池

数据库连接池是个容器,负责分配、管理数据库连接(Connection)

● 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个

● 释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏

优势:

①资源的重用

②提升系统响应速度 (创建连接是比较耗时的,但数据库连接池支持连接复用)

③避免数据库连接遗漏

在Java中默认使用的是Hikari追光者连接池,我们在下面可以看到HikariDataSource的源码实现了DataSource这个官方的标准接口

除了Hikari还有Druid(德鲁伊):

● Druid连接池是阿里巴巴开源的数据库连接池项目

● 功能强大,性能优秀,是java语言最好的数据库连接池之一

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>

并在application中加上:spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

第五部分 XML映射文件

在前面的Mybatis中配置SQL语句都是用的注解,现在我们要使用xml配置文件

三点规范:

● XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。注意在resources中建包时用 / 而不是 . :

● XML映射文件的namespace属性为Mapper接口全限定名一致。

xml文件中前面是一段约束,在Mybatis中文网-入门下方就有 直接粘贴进来就好,namespace这儿是要填接口的全类名!

● XML映射文件中sql语句的id与Mapper 接口中的方法名一致,并保持返回类型一致。

在原来我们是通过Mapper接口中写的方法上方写注解来执行SQL语句,现在用这个xml文件是,在调用这个接口代理对象(比如EmpMapper接口的empMapper对象)中的list方法时,根据方法名list去EmpMapper.xml文件中快速匹配到sql语句,并执行。

所以:使用注解来映射简单语句会使代码显得更加简洁, 但对于稍微复杂一点的语句, Java 注解不仅力不从心, 还会让你本就复杂的SQL语句更加混乱不堪。 因此, 如果你需要做一些很复杂的操作, 最好用XML 来映射语句

第六部分 Mybatis动态SQL

随着用户的输入或外部条件的变化而变化的SQL语句,我们称为动态SQL

比如下图我们的XML中配置的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.itheima.Mapper.EmpMapper">

    <select id="list" resultType="com.itheima.pojo.Emp">
      select * from emp where name like concat('%',#{name},'%') AND gender=#{gender} AND
      entrydate between #{createdTime} and #{updatedTime}


    </select>
</mapper>

Mapper接口里的方法参数也与之对应:

接着我们在测试类中调用list方法,正常传递每一个参数:

@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {
     @Autowired
    private EmpMapper empMapper;
     @Test
    public void testList(){
        List<Emp> empList = empMapper.list("张", (short)1, LocalDate.of(2010, 1, 1),
                LocalDate.of(2020, 1, 1 ) );
        System.out.println(empList);
    }
}

会得到正确的查询结果:

但是当只想输入部分参数的时候就会出现问题,比如把gender字段传null值:

哦豁就查不到了因为我们在xml写的SQL语句已经将其固定死了,所以就有了动态SQL这个说法其实很简单就是在Mybatis中提供了一个if标签做条件判断,当然还有很多标签。

我们先来看 标签

:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接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.itheima.Mapper.EmpMapper">

    <select id="list" resultType="com.itheima.pojo.Emp">
        select *
        from emp
        where
            <if test="name != null"  >
                name like concat('%', #{name}, '%')
            </if>

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

            <if test=" begin != null and end != null"  >
                and entrydate between #{begin} and #{end}
            </if>
      order by update_time desc


    </select>
</mapper>

这下我们在来尝试查询 只输入name含有“张”,其他都为null:

查出来了!!

看这里也能发现,我们输入几个参数他就有几个查询条件


我们再来测试 查询输入 name为张、性别为男、其余为null的:

也是成功查询到了!


再来试试name为null时、性别为男、其他也为null时:

这里会发现是报SQL语法的错,观察我们的SQL语句和<if标签>,如果name为null跳过第一个,gender有值,SQL语句就变成了 select * from emp where and gender = ...

所以这里where后面就直接是and了,语法就有问题!

所以就又有了一个标签来替代where这个关键字:将所有的查询条件包裹起来,有两个作用:①如果某个条件成立就会生成where关键字,若一个条件都不满足就不会生成 ②会自动帮你去除多余的and或者or

<?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.itheima.Mapper.EmpMapper">

    <select id="list" resultType="com.itheima.pojo.Emp">
        select *
        from emp
        <where>
            <if test="name != null"  >
                name like concat('%', #{name}, '%')
            </if>

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

            <if test=" begin != null and end != null"  >
                and entrydate between #{begin} and #{end}
            </if>
        </where>
        order by update_time desc


    </select>
</mapper>

再来测试一下:

成功啦~查到了所有男同胞们的信息


再来试试四个值都不传递(理想结果就应该是select * from emp order by update_time desc; 没有where!):

结果不出所料~它没有加上where关键字

跟where用法一模一样的还有一个set标签,它会自动去除多余的逗号,如果条件符合就会自动加上set关键字(一般都是配合Update使用)

<mapper namespace="com.itheima.Mapper.EmpMapper">
    <update id="update2">
        update emp
        <set>
            <if test="username!= null">username = #{username},</if>
            <if test="name!= null">username = #{name},</if>
            <if test="gender!= null">gender = #{gender},</if>
            <if test="image!= null">image = #{image},</if>
            <if test="job!= null">job = #{job},</if>
            <if test="entrydate!= null">entrydate = #{entrydate},</if>
            <if test="update_time!= null">update_time = #{update_time},</if>
        where id = #{id}
        </set>

    </update>

再同理在我们要实现批量删除操作时,我们还会用到一个标签是来遍历:

    <!--批量删除员工8,9,10-->
    <delete id="deleteByIds">
        <!--
          collection代表要遍历的集合
          item指遍历出来的元素
          separator代表遍历元素的分隔符
          open 和 close 分别代表最开始的符号和结束的符号
               -->
        delete from emp where id in
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </delete>
    

在测试类中写一个方法来测试一下看是否能成功删除:

    @Test
    public void testDeleteByIds(){
        List<Integer> ids = Arrays.asList(13,14,15);
        empMapper.deleteByIds(ids);
    }

可以发现成功的删除了13.14.15的信息


在动态SQL标签中,还有一个和的使用

比如在这个select语句中,前面标红的这两条代码都是重复的,就会存在项目后期修改字段,改动就会很大,所以就可以把重复的代码抽取到标签中,并且为这个select片段起一个id名

<sql id="commonSelect">
   select id,username,name,gender,image,job,entrydate,deptId,createTtime,updateTime from emp
</sql>

在原代码处用引用进来

  <sql id="commonSelect">
        select id,username,name,gender,image,job,entrydate,dept_id,create_time,update_time
  </sql>

    <select id="list" resultType="com.itheima.pojo.Emp">
        <include refid="commonSelect"/>
        from emp
        <where>
            <if test="name != null"  >
                name like concat('%', #{name}, '%')
            </if>

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

            <if test=" begin != null and end != null"  >
                and entrydate between #{begin} and #{end}
            </if>
        </where>
    </select>

这样就大大提升了代码的复用性。

Mybatis入门大概就差不多了,还要多实际练练才熟练!