欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Mybatis从认识到了解

程序员文章站 2022-06-28 12:46:03
[TOC] 首发日期:2018 10 31 MyBatis的介绍 介绍: MyBatis是Apache旗下的开源项目,是一个持久层框架 与Hibernate的全自动ORM框架可以仅仅依靠映射关系进行SQL查询不同,MyBatis是一个基于SQL语句的半自动ORM框架,它需要我们手动去定义SQL语句。 ......

目录

首发日期:2018-10-31


mybatis的介绍

介绍:

  • mybatis是apache旗下的开源项目,是一个持久层框架
  • 与hibernate的全自动orm框架可以仅仅依靠映射关系进行sql查询不同,mybatis是一个基于sql语句的半自动orm框架,它需要我们手动去定义sql语句。


为什么选择mybatis:

与jdbc的对比中,mybatis是基于orm的,天然要比jdbc强,基于orm使得mybatis具有查询的数据可以自动封装到对象中的等好处,而jdbc需要手动编码的位置就较多了(就算是使用dbutils,你也需要使用定义不同的方法来给不同的持久类赋值。)。

在互联网应用程序开发中,对于存储过程和复杂的sql,hibernate并没有很好地去处理,所以它比较难提升性能(并不是说不能,而是耗费心思较多),无法满足高并发和高响应的需求(如果要强行优化,又破坏了hibernate的全自动orm设计)。

与之对应的,mybatis可以说是基于sql的,它需要我们去自定义sql,那么我们可以根据需要去设计和优化sql,这种可针对sql的优化的持久层框架,恰巧符合高并发和高响应的互联网应用程序开发开发需求。


与hibernate的对比:

  • orm区别:
    • hibernate是完全面向pojo的,它基本不需要编写sql,可以直接通过映射关系来操作数据。
    • 而mybatis需要我们自定义sql语句。
  • 面向的需求:
    • 对于一些不需要太高性能的,可以使用hibernate去开发。【erp,crm,oa之类的】
    • 对于有性能要求的,通常需要使用mybatis。


mybatis的优点:

  • 支持存储过程
  • 由于是自定义sql,方便进行sql优化
  • 可以简化开发,对比jdbc开发,省去了很多代码;
  • 基于mapper发送sql的方式使得dao层仅需要定义接口,而不需要dao实现类了。



入门示例


1.首先要下载依赖包https://github.com/mybatis/mybatis-3/releases

  • 文件\文件夹详情:
    • lib:包含mybatis依赖包,mybatis的功能需要这些依赖包。
    • mybatis-3.4.5.jar:mybatis的核心包。
    • mybatis-3.4.5.pdf:mybatis的官方文档。


2.创建普通javase工程,导入依赖包(我这里使用 ):

  • 导入mybatis核心包:mybatis-3.4.5.jar
  • 导入数据库驱动包:mysql-connector-java-5.1.7-bin.jar
  • 导入mybatis依赖包:
    • Mybatis从认识到了解


3.创建持久类和创建数据表:持久类是用于封装从数据库中返回的结果,可以说持久类的对象就是数据库记录在java中的实体(这里约定数据表的列名与持久类的属性名一致)。

  • 持久类
   package work.pojo;
   
   public class user {
    private integer id;
    private string name;
    private integer age;
    //省略setter,getter,tostring
   }
  • 数据表:
create table user(
id int primary key auto_increment,
name varchar(20),
age int
);


4.在持久类user.java同级目录下创建映射文件user.xml:这个映射文件的意义是用来定义sql语句,使得我们可以在代码中调用sql语句【不同类的标签定义不同类的sql,id用于标识sql语句,parametertype是传入sql语句的参数,resulttype定义了返回结果的类型(这里由于表字段和类属性名一致,查询的记录的各个值会自动赋给对象)】

<?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="user">
    <!--id用于标识sql,parametertype是传给sql的参数的类型,resulttype是返回结果的类型  -->
    <select id="getuserbyid" parametertype="int" resulttype="work.pojo.user" >
    <!-- 使用#{}来传入参数 -->
        select * from user where id = #{id}
    </select>
</mapper>


5.配置mybatis配置文件,这里命名为mybatis-config.xml。配置文件的意义是配置mybatis的运行设置,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>
    <environments default="development">
        <!-- environment用来定义数据库连接环境,environments用来定义多个environment -->
        <environment id="development">
            <!-- transactionmanager配置事务管理,使用jdbc事务管理 -->
            <transactionmanager type="jdbc" />
            <!-- datasource配置数据库连接池 -->
            <datasource type="pooled">
                <property name="driver" value="com.mysql.jdbc.driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </datasource>
        </environment>
    </environments>
    <mappers>
        <!-- 导入映射文件,这样mybatis才能管理到sql语句 -->
        <mapper resource="work/pojo/user.xml"/>
    </mappers>
</configuration>


6.编写测试方法:

配置文件配完后,要想通过mybatis操作数据库,那么就需要使用sqlsessionfactorybuilder、sqlsessionfactory和sqlsession几个对象。

  • sqlsessionfactorybuilder:用来读取配置文件,得到一个sqlsessionfactory
  • sqlsessionfactory:sqlsessionfactory相当于连接池工厂,可以获得一个sqlsession对象
  • sqlsession:相当于jdbc的connection对象,可以调用方法来操作数据表
@test
    public void test1() throws ioexception {
        // 创建sqlsessionfactorybuilder对象
        sqlsessionfactorybuilder sfb = new sqlsessionfactorybuilder();
        // 通过mybatis包的resources类来将配置文件转成输入流
        inputstream inputstream = resources.getresourceasstream("mybatis-config.xml");
        // 加载配置文件输入流,创建sqlsessionfactory对象
        sqlsessionfactory   sqlsessionfactory = sfb.build(inputstream);
        // sqlsessionfactory创建sqlsession对象
        sqlsession sqlsession = sqlsessionfactory.opensession();
        // 执行查询,第一个参数是映射文件中定义的sql的id名,第二个参数是传给sql的参数。
        user user = sqlsession.selectone("getuserbyid", 1);
        // 输出查询结果
        system.out.println(user);
        // 释放资源
        sqlsession.close();
    }


这里给一下文件结构、数据表数据和测试结果。

文件结构:

Mybatis从认识到了解

数据表数据:

Mybatis从认识到了解

测试结果:user [id=1, name=沙僧, age=18]



上面演示了mybatis的使用,相信你对mybatis已经有了一个初步的了解,下面将对各个部分的知识进行讲解。

1.核心组件【关于sqlsessionfactorybuilder这些对象,以及了解mybatis的运行流程】
2.mybatis配置文件的配置【关于数据库连接之类的配置】
3.映射文件的配置【关于sql语句的定义方式】


mybatis核心组件


四大核心组件

  • sqlsessionfactorybuilder:负责根据配置文件来生成sqlsessionfactory
  • sqlsessionfactory:一个用来生成sqlsession的工厂(这个工厂由sqlsessionfactorybuilder生成)
  • sqlsession:sqlsession是会话的意思,即与数据库的会话,相当于jdbc中的connection。它是一个既可以既可以发送sql去执行并返回结果,也可以获取mapper的接口。
  • sql mapper:它是mybatis新设计的组件,它由一个java接口和一个配置文件(xml式或注解式的)组成,它可以获取配置文件中的sql语句和映射关系,它也可以发生sql去执行并返回结果。

Mybatis从认识到了解


sqlsessionfactorybuilder

  • sqlsessionfactorybuilder是一个基于建造者设计模式的接口,它可以根据配置去生成sqlsessionfactory
  • sqlsessionfactorybuilder生成sqlsessionfactory依靠build方法,build可以传入多种参数(下面是其中三种方式),常见情况是传入一个配置文件的字节输入流对象。
    • build(inputstream inputstream)【传入一个配置文件的字节输入流对象】
    • build(reader reader)【传入一个配置文件的字符输入流对象】
    • build(reader reader, string environment)【传入一个配置文件的字符输入流对象,并根据指定的environment生成工厂,默认是使用environments中的默认值】
    • build(configuration config)【传入一个configuration 对象,这里不讲它怎么配置

以传入一个配置文件的字节输入流对象为例,mybatis提供了一个resource类,它可以很方便地将配置文件转成输入流:

  • // 创建sqlsessionfactorybuilder对象
    sqlsessionfactorybuilder sfb = new sqlsessionfactorybuilder();
    // 通过resources类来将配置文件转成输入流,resources是mybatis提供的
    inputstream inputstream=resources.getresourceasstream("mybatis-config.xml");
    // 加载配置文件输入流,创建sqlsessionfactory对象
    sqlsessionfactory sqlsessionfactory = sfb.build(inputstream);


sqlsessionfactory

  • sqlsessionfactory是sqlsessionfactorybuilder读取了配置文件之后生成的对象,它解析了配置文件的信息(mybatis的数据库连接信息、缓存信息、事务管理等等)。
  • sqlsessionfactory可以调用opensession方法来获取一个sqlsession,opensession方法是可以传入参数的,如果你传入一个布尔值那么将影响事务管理,为true时将自动提交,为false将默认不提交,默认为false,这时候需要你手动提交:sqlsession.commit();
  • opensession除了可以传入布尔值,还可以传入其他类型的对象,这里不讲。
    // sqlsessionfactory创建sqlsession对象
    sqlsession sqlsession = sqlsessionfactory.opensession();
    //开启自动提交,默认增、删、改是不自动提交的
    //sqlsession sqlsession = sqlsessionfactory.opensession(true);


sqlsession

  • sqlsession的作用类似于jdbc中的connection对象。

  • 作用:
    • 管理事务:sqlsession可以调用commit和rollback方法【要注意,mybatis使用jdbc作为事务管理器时,事务是默认不自动提交的】
    • 提交sql:sqlsession内置了selectone、insert、update、delete等超过20种操作数据库的方法。
      • 调用的方法里面通常的第一个参数都是字符串,这个字符串是映射文件中的sql语句的id,本质上是调用映射文件中的sql语句。
    • 释放资源:sqlsession可以调用close方法来关闭连接资源。
  • 使用示例:

    sqlsession sqlsession = sqlsessionfactory.opensession();
    user user = new user();
    user.setname("铁头娃");
    user.setage(4);
    //发送sql
    sqlsession.insert("insertuser", user);
    //提交事务
    sqlsession.commit();
    // 释放资源
    sqlsession.close();


sql mapper

  • sql mapper是mybatis新设计的一个组件,也是一种新的执行sql的模式,在ibatis时代,主要使用sqlsession来进行查询,而有了sql mapper之后,只要遵循一种开发规范,那么就可以利用一个接口和一份映射文件
  • 如果这个接口遵循了mapper的开发规范,并且有对应的映射文件,那么sqlsession调用getmapper方法可以获取一个mapper对象,mapper对象可以直接调用接口方法来执行映射文件中对应的sql语句。

    • sqlsession.getmapper(某个接口.class);
  • mapper的规范
    • 映射文件的namespace是接口的全限定名
    • 接口的方法名通常与映射文件的sql id同名【使得mapper的方法与sql对应起来,这样调用方法就是调用对应的sql】【还有各种各样的要求,由于这涉及两种开发方式,所以这将在后面讲】
    • 映射文件就是配置sql语句的文件
  • 功能:

    • mapper也可以发送sql,调用mapper接口中的方法就相当于sqlsession调用映射文件中的sql。【这样的调用是完全面向对象的,现在普遍用这种。】

      • sqlsession sqlsession = sqlsessionfactory.opensession();
        //发送sql
        usermapper mapper = sqlsession.getmapper(usermapper.class);
        user user = mapper.getuserbyid(1);
        //user user = sqlsession.selectone("getuserbyid", 1); //原方式
        system.out.println(user);
        // 释放资源
        sqlsession.close();


核心组件的生命周期

  • sqlsessionfactorybuilder的主要作用是创建sqlsessionfactory,一旦创建了 sqlsessionfactory 后,这个类就不需要存在了。通常只让它处在局部环境(本地方法变量)中。
  • sqlsessionfactory用于创建session接口对象,所以sqlsessionfactory应该是长期有效的,但不应该重复创建,所以通常使用单例模式或静态单例模式来获取sqlsessionfactory对象。
  • sqlsession相当于一个连接对象,每次获取它,都是用来处理业务的,所以每个业务(线程)都应该有一个sqlsession对象,而它通常应该在业务结束后归还给sqlsessionfactory,sqlsession的范围为一个业务的请求范围。
  • mapper是绑定了sql的接口,在使用mapper来操作的时候,很明显的--mapper的生命周期与sqlsession有关。mapper的生命周期最大可以与sqlsession等同,但如果把mapper放到try-catch这样的语句或方法中,那么它的生命周期就会变小了。


总结

稍微了解了一下核心组件之后,再回头看一下之前的代码:

@test
    public void test1() throws ioexception {
        // 创建sqlsessionfactorybuilder对象
        sqlsessionfactorybuilder sfb = new sqlsessionfactorybuilder();
        // 通过mybatis包的resources类来将配置文件转成输入流
        inputstream inputstream = resources.getresourceasstream("mybatis-config.xml");
        // 加载配置文件输入流,创建sqlsessionfactory对象
        sqlsessionfactory   sqlsessionfactory = sfb.build(inputstream);
        // sqlsessionfactory创建sqlsession对象
        sqlsession sqlsession = sqlsessionfactory.opensession();
        // 执行查询,第一个参数是映射文件中定义的sql的id名,第二个参数是传给sql的参数。
        user user = sqlsession.selectone("getuserbyid", 1);
        // 输出查询结果
        system.out.println(user);
        // 释放资源
        sqlsession.close();
    }



映射文件的配置


我们现在先来了解一些映射文件的配置,也就是映射文件里面所谓的sql语句之类的怎么写。这里了解了映射文件怎么配置之后,你可以先使用sqlsession来执行sql体验一下其中的意义的。

    sqlsession sqlsession = sqlsessionfactory.opensession();
    user user = new user();
    user.setname("铁头娃");
    user.setage(4);
    //发送sql
    sqlsession.insert("insertuser", user);
    //提交事务
    sqlsession.commit();
    // 释放资源
    sqlsession.close();


dtd约束:

映射文件需要dtd约束,在依赖包没有包含dtd的配置方法,只能从官方文档中获取,这里给一下。

<!doctype mapper
public "-//mybatis.org//dtd mapper 3.0//en"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">


可配置标签:

基础结构,下面是一个大致的映射文件结构,在mapper标签下面配置其他标签:

<?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="user">
    <select id="getuserbyid" parametertype="int" resulttype="work.pojo.user" >
        select * from user where id = #{id}
    </select>
</mapper>


mapper标签:

  • mapper标签中的属性namespace是命名空间的意思,它可以将定义的sql语句区分到不同的命名空间上,就好像把类区分到不同的包中。命名空间可以解决sql id相同的问题,sql id唯一时,可以调用sql id来调用对应的sql语句;不唯一时,应该使用命名空间.sql id
  • mapper标签是映射文件的*标签,子标签有:
    • select:用于定义查询语句,可以自定义入参类型和返回的结果集类型。
    • insert:用于定义插入语句,可以自定义入参类型,返回结果是插入的记录数。
    • update:用于定义更新语句,可以自定义入参类型,返回结果是更新的记录数。
    • delete:用于定义删除语句,可以自定义入参类型,返回结果是删除的记录数。
    • sql:用来封装一部分经常出现的sql,这样可以在别的地方进行引用,避免了老是重复。
    • resultmap:用来手动将数据库返回的结果与java中的数据建立上关系,使用了resultmap的地方,会根据resultmap的规则来封装数据。
    • cache:给命名空间配置缓存。【配置缓存可以影响crud语句是否保留缓存值】
    • cache-ref:引用其他命名空间中的缓存配置。


select标签:

  • select标签用来定义查询类的sql语句。

  • 属性:
    • id:与命名空间组合,用来唯一标识sql语句。mybatis可以根据id来调用对应的sql语句。【要注意唯一性,唯一的时候,可以直接使用sql id;如果不是唯一,那么要使用命名空间.sql id 来调用对应的sql语句。】
    • parametertype:用来定义传给sql语句的参数的数据类型。可以是各种数据类型,也可以是pojo类对象(要求是类的全限定名),也可以给出别名(别名是指数据类型的别名或类的全限定名的别名,由于mybatis提前定义了一堆别名,所以我们直接使用int的时候,相当于使用了java.lang.integer)。
    • resulttype:用来定义sql语句返回结果的参数类型。可以是各种数据类型,也可以是pojo类(要求是类的全限定名),也可以给出别名(数据类型的别名或类的全限定名的别名)。【给出pojo类的时候,如果允许自动匹配,将自动根据映射关系自动将返回结果封装到对象中】。【resulttype不能与resultmap一起用,他们的功能重复了
    • resultmap:resultmap标签用来定义返回结果与类的映射关系,在select中使用resultmap属性时,可以将返回结果根据映射关系来赋值。【后面将具体介绍】
  • 示例:

    • <select id="getuserbyid" parametertype="int" resulttype="user" >
          select * from user where id = #{id1}
      </select>
  • 使用问题:
    • 传参问题:
      • 可以使用#{参数名}来向sql语句传递参数。
        • 对于基本数据类型,由于仅有一个参数(不像集合或类包含多个变量),参数名是可以随意取的。
          • <select id="getuserbyid" parametertype="int" resulttype="work.pojo.user" >
                  select * from user where id = #{id1}
            </select>
        • 对于对象类型,由于对象中有属性,所以要使用#{属性名}来把对象中的数据传递给sql。如果对象的属性还是一个对象那么可以使用#{内含对象名.属性名}

          • <!-- 由于传参是一样的用法,这里用insert作为例子,user中有属性name和age -->
            <insert id="insertuser" parametertype="work.pojo.user" >
                  insert into user(name,age)
                  values 
                  (#{name},#{age});
            </insert>
      • 对于集合类的入参,这需要使用到动态sql中的foreach标签,这个留到后面再讲。


  • 对于模糊查询,在传参的时候可能会有问题,你可能会思索%这个东西在哪里存:
    • 方式一:在函数调用方法并传参给sql的时候,手动拼接;然后在sql正常的使用#{参数名}来获取参数。
      • 代码中进行拼接:sqlsession.selectlist("user.getuserbyname","%唐%");,sql中使用#{参数名}select * from user where name like #{name};
    • 方式二:${参数名}可以用来字符串拼接,它可以在将字符串包裹的${参数名}转成对应的数据,但对于非pojo类型的参数名只能为value,对于pojo类型的就只能使用pojo类型的属性了。
      • select * from user where name like '%${value}%';
    • 方式三:调用sql内置函数concat来拼接。【这个要注意数据库有没有这个函数】
      • select * from user where name like concat('%',#{name},'%')


  • 返回结果问题:
    • 可以使用resulttype和resultmap来定义返回结果,由于resultmap属性借助resultmap标签,resultmap留到resultmap标签再讲
    • resulttype的值可以是各种数据类型,不过要符合数据的封装要求,比如返回多列数据时resulttype不应该设置成int,resulttype的值是什么要你自己去判断,只要你符合封装规则,那么就能封装成功。比如select * from user where id = #{id}的返回值应该是一个user类。
    • 返回结果是多条记录的时候,返回结果类型也可以是pojo类,mybatis会自动将返回结果转成pojo类的数组。


  • 映射规则问题:
    • 如果传入一个pojo类,默认是自动映射的,【如果属性名不一致,可以使用字段别名或者resultmap来进行强制映射】
      • 假设数据表中的字符名为username,但对象中的属性名为name,那么怎么对应上呢?

        <!-- select id,username ,age from user where id = #{id}  --><!--由于字符名与属性名不完全相同,自动映射失败-->
        <!-- 使用下面的语句,利用别名 -->
        select id,username as name ,age from user where id = #{id} 
      • 使用resultmap,显式对应:property是类对象的属性名,column是字段名

        <resultmap type="work.pojo.user" id="usermap">
                  <id property="id" column="id" />
                  <!-- 手动把字段名与属性对应上 -->
                  <result property="name" column="username" />
                  <result property="age"  column="age"/>
        </resultmap>
    • 多表时必须使用resultmap。因为没有自动跨表映射(除非你定义了返回结果是一个包含了多个表所有字段的类)。


resultmap

  • resultmap用来显式定义数据封装规则,resultmap把数据表的数据与对象的属性一一对应起来,如果sql语句的返回结果类型使用了resultmap,这样mybatis就会根据resultmap中的规则来封装返回的数据。

  • 属性
    • id:resultmap的id,有了id,就可以在其他标签的resultmap属性中引用了。
    • type:要建立映射关系的pojo类的全限定名。
  • id标签:用来定义主键的映射关系
    • property:对象的属性名,代表映射到对象的哪个属性中。
    • column:代表数据表中的列名。
  • result标签:用来定义普通字段的映射关系。

  • <!-- 定义resultmap -->  
    <resultmap type="work.pojo.user" id="usermap">
              <!--id标签把数据表中的id字段与user类中的id属性对应起来 -->
              <id property="id" column="id" />
              <result property="name" column="name" />
              <result property="age"  column="age"/>
    </resultmap>
    <!-- 引用resultmap -->
    <select id="getuserbyid" parametertype="int"  resultmap="usermap">
          select * from user where id = #{id1}
    </select>
  • resultmap还与关联查询有关系,关联查询依靠resultmap来定义映射关系。这留到后面讲。


insert标签:

  • insert用来定义插入语句

  • 属性:

    • parametertype:定义传入的参数类型,使用可以参考select中的parametertype。
    • usegeneratedkeys:获取插入记录的主键(数据库自动生成的)
    • keyproperty:用来标识主键返回结果赋给哪个变量,复合主键时,使用逗号分隔变量名。【keycolumn】
  • 使用说明:增删改三种语句默认是不提交,要commit

  • <insert id="insertuser" parametertype="work.pojo.user" >
          insert into user(name,age)
          values 
          (#{name},#{age});
    </insert>
  • 主键返回问题,有时候我们希望插入了一条记录后,获取到它的id来处理其他业务,那么这时候我们怎么获取它的主键呢?下面列举两种方法:

jdbc式:在insert标签上配置usegeneratedkeys和keyproperty属性,usegeneratedkeys用来表明这个数据库是不是自增长id的,所以这个获取主键的方式不适用与oracle。keyproperty用来指明返回的主键值存储到哪个属性中。

<insert id="save" parametertype="work.domain.user" usegeneratedkeys="true" keyproperty="userid" >
    insert into user(username,password,comment)
    values(#{username},#{password},#{comment})
</insert>

数据库式:利用数据库的函数来获取主键,keyproperty用来指明返回的主键值存储到哪个属性中。order用来指定执行的顺序,究竟是插入前获取--before,还是插入后获取--after。resulttype用来指明返回主键的数据类型。

    <insert id="save" parametertype="work.domain.user" >
       <selectkey resulttype="java.lang.long" order="after" keyproperty="userid">
          select last_insert_id()
      </selectkey>
        insert into user(username,password) values(#{username},#{password})
    </insert>


update标签:

  • 属性与insert差不多

  • mybatis由于是根据sql去修改,所以它不会发生覆盖修改问题(hibernate基于对象来查询,对于没有设置值的属性也会修改到数据表中,所以hibernate中通常都是先查询后修改)

  • <update id="updateuser" parametertype="com.pojo.user">
          update user set username = #{username} where id = #{id}
    </update>


delete标签:

  • 属性与insert差不多

  • <delete id="deleteuser" parametertype="int">
          delete from user where `id` = #{id1}
    </delete>


sql标签:

  • sql标签可以用来定义一部分sql语句,定义的部分sql语句可以在别的地方引用。比如我们经常会用到列名,我们需要反复填写列名,如果我们将这一部分列名定义到sql标签中,我们就可以通过引用来使用了。
  <sql id="usercols">
        id,name,age
  </sql>
  <!-- 开始引用 -->
  <select id="getuserbyid" parametertype="int"  resulttype="work.pojo.user">
        <!-- select id,name,age from user where id = #{id1} -->
        select <include refid="usercols"></include> from user where id = #{id1} 
  </select>


补充:

  • 由于cache和cache-ref涉及缓存问题,这将留到后面缓存部分再讲。



sql发送


上面学过了映射文件里面定义sql,那么下面来了解一下怎么使用这些sql语句。发送sql的方式有两种,一种是使用sqlsession来发送,一种是使用mapper来发送。


使用sqlsession发送:



1.创建映射文件,这里假设为user.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="user">
    <select id="getuserbyid" parametertype="int" resulttype="user" >
        select id,name,age from user where id = #{id} 
    </select>
</mapper>


2.在配置文件中引入映射文件:【由于没有怎么讲mybatis配置文件,你可以先按照基础示例里面来修改】

<mappers>
        <!-- 导入映射文件 -->
        <mapper resource="work/pojo/user.xml"/>
</mappers>


3.获取sqlsession,并使用sqlsession调用对应的sql:

    @test
    public void test3() throws ioexception {
        sqlsessionfactorybuilder sfb=new sqlsessionfactorybuilder();
        inputstream inputstream = resources.getresourceasstream("mybatis-config.xml");
        sqlsessionfactory sessionfactory = sfb.build(inputstream);
        sqlsession sqlsession = sessionfactory.opensession();
        //根据sql id来发送sql,第二个是传给sql的参数。
        user user = sqlsession.selectone("user.getuserbyid",3);
        system.out.println(user);
        sqlsession.close();
    }


常用方法

  • selectone("sql id")方法表示调用的是查询语句,并且返回的是一个对象【如果返回结果是多个,会报错。】
  • selectlist("sql id")方法表示调用的是查询语句,并且返回的是一个列表。
  • insert("sql id")方法表示调用的是插入语句,对应映射文件中使用insert标签定义的sql语句。
  • delete("sql id")方法表示调用的是删除语句,对应映射文件中使用delete标签定义的sql语句。
  • update("sql id")方法表示调用的是修改语句,对应映射文件中使用update标签定义的sql语句。


使用mapper接口发送:动态代理开发


1.首先创建一个接口,

  • 接口名通常都是xxxmapper,如果是针对user类,可以是usermapper
  • 接口名要求与映射文件名一致
  • 接口的方法名要求与映射文件的sql id一致,
  • 接口的方法的形参必须要与对应sql的parametertype类型一致,
  • 接口的返回类型必须与对应的sql的resulttype类型一致。
package work.pojo;

public interface usermapper {
    public user getuserbyid(int id);
}

2.创建映射文件(这里假设为usermapper.xml),映射文件要求namespace必需是接口的全路径名:

<!-- namespace要写接口的全限定名 -->
<mapper namespace="work.pojo.usermapper">
    <!--注意返回类型和入参类型要与接口的一致 -->
    <select id="getuserbyid" parametertype="int" resulttype="work.pojo.user" >
        select id,name,age from user where id = #{id}
    </select>
</mapper>

3.在配置文件中引入mapper:

<mappers>
        <!-- 导入映射文件 -->
        <!--既可以导入接口,也可以导入映射文件 -->
        <!--<mapper resource="work/pojo/usermapper.xml"/> -->
        <mapper class="work.pojo.usermapper"/>
</mappers>

4.在代码中利用sqlsession获取接口的mapper,并使用mapper发送sql(调用对应的接口方法就是发送对应sql):【这里提一下,虽然获取的是接口,但由于遵循了开发规范,mybatis会帮忙创建一个这个接口的实现类,所以实质返回的是一个实现类对象】

    @test
    public void test4() throws ioexception {
        sqlsessionfactorybuilder sfb=new sqlsessionfactorybuilder();
        inputstream inputstream = resources.getresourceasstream("mybatis-config.xml");
        sqlsessionfactory sessionfactory = sfb.build(inputstream);
        sqlsession sqlsession = sessionfactory.opensession();
        //根据接口.class来获取mapper
        usermapper mapper = sqlsession.getmapper(usermapper.class);
     //调用接口的方法就是调用方法对应的sql。
        user user = mapper.getuserbyid(3);
        system.out.println(user);
        sqlsession.close();
    }

mapper说明:

  • usermapper mapper = sqlsession.getmapper(usermapper.class);传入一个usermapper.class就可以获取一个能够调用方法的mapper,我们仅仅定义了usermapper接口,为什么能获取一个能够获取一个能够调用方法的mapper呢?因为mybatis底层自动帮我们去创建这个接口的实现类了,由于我们遵循了设计规范,mybatis能够很容易地了解该如何去创建实现类。创建完实现类后,返回一个实现类的对象。


两种方式对比与dao层开发问题

  • mybatis通常用于数据访问层dao的开发,对于使用sqlsession发送sql的方式来说,它跟以往的开发一样,也需要定义dao接口和实现方法;但对于使用mapper发送sql的方式来说,它跟以往的开发不同,它只需要定义接口了,而不再需要接口实现类了。
  • sqlsession方式:Mybatis从认识到了解

  • mapper方式:由于usermapper接口已经包含原来的userdao的功能了,并且不需要自定义实现类,所以这里可以省去dao层。Mybatis从认识到了解



两种方式比较:

  • 建议使用mapper方式发送
  • sqlsession发送是直接发送;mapper方式是先通过sqlsession获取mapper接口再发送。
  • sqlsession方式是面向sql的,mapper方式是面向对象的。sqlsession发送sql,需要一个sql id 去匹配sql。mapper发送sql只需要调用调用对应的方法,例如mapper.getrole(1l)
  • 使用mapper方式发生sql要调用对应的接口方法,接口方法能帮我们检查入参类型,参数类型不对时编译器会检错,而sqlsession.selectone()只有在运行时才报错。



mybatis配置文件


  • 上面谈完了一些“定义sql和发送sql”方面的内容,这节主要是讲解mybatis配置文件的内容,配置文件是用来配置mybatis的运行环境的,例如数据库驱动、url、用户名、密码、采用的事务管理方式等。
  • 配置文件的命名是可以随意的,通常可以命名为mybatis-config.xml


配置文件的约束

  • 配置文件的dtd约束配置信息并没有写在jar包中,所以我们不能从依赖包中查到,只能依靠官方文档来获取,下载的包中的mybatis-3.4.5.pdf中就有,在 getting started中可以复制dtd约束。

    • <!doctype configuration public "-//mybatis.org//dtd config 3.0//en" "http://mybatis.org/dtd/mybatis-3-config.dtd">

可配置标签:

标签的出现的顺序必须按照顺序。

<configuration><!-- configuration是配置文件的*标签  -->
    <!-- properties用于配置一些参数,这些参数可以被引用到下面的其他配置中,相当于properties文件 -->
    <properties></properties>
    <!-- setting用于配置一些mybatis底层相关的属性,比如缓存 -->
    <settings></settings>
    <!--typealiases 用于配置类型命名 -->
    <typealiases></typealiases>
    <!-- typehandlers用于配置类型处理器  -->
    <typehandlers></typehandlers>
    <!-- objectfactory用于配置对象工厂  -->
    <objectfactory type=""></objectfactory>
    <!--plugins用于配置插件 -->
    <plugins></plugins>
    <!-- environments配置不同的数据库连接环境 -->
    <environments default="">
        <environment id=""> <!--environment用于配置数据库连接环境  -->
            <!-- transactionmanager用于配置事务管理器 -->
            <transactionmanager type=""></transactionmanager> 
            <!--datasource用于配置数据源  -->
            <datasource type=""></datasource>
        </environment>
    </environments>
    <!-- databaseidprovider是数据库产商标识 -->
    <databaseidprovider type=""></databaseidprovider>
    <!-- mappers用于配置映射器 -->
    <mappers></mappers>
</configuration>


上面的标签中,plugins涉及插件,是偏向底层的内容,如果不了解mybatis运行原理,那么可能会比较晦涩,这会留到另外一篇博文再讲述(如果有空的话)。【databaseidprovider和objectfactory不讲述】
下面主要讲properties、settings、typealiases、typerhandler、environments、mappers。



常用标签:

environments标签

  • environments用来配置多个数据库连接环境,每一个环境都使用environment标签来定义。(不同的环境可以定义不同的数据库连接信息)

    • 唯一属性--default:代表默认使用哪个环境,值为environment的id,这里的值影响sqlsessionfactorybuilder调用build(配置文件输入流)时使用哪个环境初始化sqlsessionfactory。例如:<environments default="development">省略environment配置</environments>代表默认使用id="development"的环境。
  • environment标签:是environments的子标签,用来定义数据库连接环境,子标签有transactionmanager(用来定义事务管理器,事务管理器负责定义这个数据库环境中的事务怎么处理)、datasource(用来定义数据源,主要是配置数据库连接信息)。
    • 唯一属性——id:用来唯一标识每个环境。
    • 子标签transactionmanager:用来定义事务管理器
      • 唯一属性:type:
        • jdbc:以jdbc的方式对数据库的提交和回滚进行操作。mybatis中jdbc事务管理器的事务默认是不自动提交的,所以增删改操作需要sqlsession.comit()来进行事务提交。
        • managed:把事务交给容器来管理,如jboss,weblogic这样的容器。
      • transactionmanager有子标签property,可以配置事务管理器的一些规则【这里不讲,有兴趣自查。】
      • 示例:<transactionmanager type="jdbc" ></transactionmanager>
    • 子标签datasource:用来定义数据源
      • 唯一属性:type:
        • pooled:数据库连接池的方式,配置这个值代表使用mybatis自带的数据库连接池。
        • unpooled:非数据库连接池的方式
        • jndi:使用外部数据库连接源,tomcat数据源之类的外部数据库连接源
        • 第三方连接池【mybatis可以整合第三方连接池,但这需要创建新的类,所以留到后面再讲。】
      • 子标签property可以配置一些数据库连接的参数,除了基本的数据库driver、url、username、password,还可以根据type的不同值来配置一些不同参数,不同type里面的数据库驱动的定义方式可能也是不同的。
  <environments default="development">
        <!-- environment用来定义数据库环境,environments用来定义多个environment -->
        <environment id="development">
            <!-- 配置事务管理,使用jdbc事务管理 -->
            <transactionmanager type="jdbc" />
            <!-- 配置数据库连接池 -->
            <datasource type="pooled">
                <property name="driver" value="com.mysql.jdbc.driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis2" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </datasource>
        </environment>
  </environments>


properties标签

  • properties标签的作用类似properties文件,properties标签可以导入properties配置文件,也可以直接使用子标签property来配置参数。properties标签中配置的参数可以在后面的其它属性配置中引用。

  • 使用方式:

    • 用properties标签导入配置文件:

      • <!-- 示例 -->
        <properties resource="jdbc.properties"></properties>
        导入后,根据${键名}来引用配置文件中配置的参数,例如在配置数据源的时候:<property name="driver" value="${jdbc.driverclass}" />
    • 用properties标签定义property变量,定义后也可以使用${键名}来引用。

      • 设置参数:

        • <properties>
                  <property name="db.driver" value="com.mysql.jdbc.driver"/>
                  <property name="db.url" value="jdbc:mysql://localhost:3306/mybatis2"/>
                  <property name="db.user" value="root"/>
                  <property name="db.password" value="123456"/>
              </properties>
  • 引用参数:

    •    <environment id="test">
                  <!-- 配置事务管理,使用jdbc事务管理 -->
                  <transactionmanager type="jdbc" />
                  <!-- 配置数据库连接池 -->
                  <datasource type="pooled">
                      <property name="driver" value="${db.driver}" />
                      <property name="url" value="${db.url}" />
                      <property name="username" value="${db.user}" />
                      <property name="password" value="${db.password}" />
                  </datasource>
              </environment> 
  • 为了分清楚配置的属性的作用,通常都会使用点分法来定义变量,如可以使用db.xxx代表这个配置与数据库相关。【当然,不使用点分法也可以】


settings标签

  • settings用于配置一些涉及mybatis底层运行的参数(比如日志打印、自动映射、缓存管理),大部分情况下使用默认值。
  • 配置项以及配置项的值,可以参考这篇博文https://blog.csdn.net/u014231523/article/details/53056032,好像写的算靠谱了。


typealiases标签

  • typealiases通常用来别名,定义别名后,可以把很长的名字定义成一个较短的名字,例如类的全限定名(qualified name)很长时,可以用一个别名来代表这个类路径。

  • 别名可以使用在映射文件中,通常可以把类的全限定名定义成别名,这样就可以简写了。

  • 别名分为系统定义别名和自定义别名

    • 系统定义别名主要是一些数据类型别名,所以要注意定义sql的入参类型和结果类型,sql语句的结果类型resulttype定义成int的时候,返回的结果应该用integer类型的变量存储

      别名 映射的类型
      _byte byte
      _long long
      _short short
      _int int
      _integer int
      _double double
      _float float
      _boolean boolean
      string string
      byte byte
      long long
      short short
      int integer
      integer integer
      double double
      float float
      boolean boolean
      date date
      decimal bigdecimal
      bigdecimal bigdecimal
    • 自定义别名:

      • 可以使用typealias标签来定义别名:

        • <typealiases>
                  <!-- alias的值是别名,type的值是要定义别名的名字 -->
                  <typealias type="work.pojo.user" alias="user"/>
          </typealiases>
      • 别名包扫描:

        • 由于别名很多时候都用在映射文件中(解决类全限定名过长问题),所以mybatis支持对包进行别名扫描,扫描的包下的类都被定义成别名,别名就是类名
      • <typealiases>
            <!-- 整个包下的类都被定义别名,别名为类名,不区分大小写-->
              <package name="work.pojo"/>
            <!--这样pojo下的类都被定义成了别名,例如work.pojo.user可以简写成user -->
        </typealiases>
  • 别名是忽略大小写的,所以别名user,你既可以使用user来引用,也可以使用user来引用。




typehandler标签:

  • typehandler是用来处理类型转换问题的,从数据库中获取的数据要想装入到对象中,势必要经过类型转换,好比mybatis必须知道如何将varchar转成string。对于常见的类型转换,mybatis已经帮我们定义了不少类型转换器,下面给出常见的。

    • 数据库数据类型 java类型 类型转换器
      char, varchar string stringtypehandler
      boolean boolean booleantypehandler
      smallint short shorttypehandler
      integer integer integertypehandler
      float float floattypehandler
      double double doubletypehandler
  • 由于mybatis的转换器已经能够解决大部分问题了,所以这里不讲怎么自定义转换器,有兴趣的可以自查。



mappers标签

  • mappers是用来引入映射器(映射文件)的,有了映射器,mybatis才能够管理到在映射文件中定义的sql。

  • 引入方式:

    • 导入配置文件,通过配置文件的路径名导入:

      • <mappers>
              <!-- 第一种方式,根据映射文件路径-->
              <mapper resource="work/pojo/user.xml"/>
        </mappers>
    • 使用mapper方式开发时,由于映射文件与mapper对应,也可以通过mapper的全限定名导入:

      • 【要求映射文件与mapper处于同一目录,而且映射文件的文件名与要mapper名一致】

        <mappers>
              <!-- 对下面的配置,要求映射配置文件的文件名为usermapper.xml;要求usermapper.xml与usermapper处于同一目录下-->
              <mapper class="work.mapper.usermapper"/> 
        </mappers>
    • 使用mapper方式开发时,由于映射文件与mapper对应,也可以通过扫描mapper所在的包来导入:

      • 【要求映射文件与mapper处于同一目录,而且映射文件的文件名与要mapper名一致】

      • <mappers>
              <!-- 第三种方式,包扫描器:
                     1、映射文件与接口同一目录下
                     2、映射文件名必需与接口文件名称一致
                -->
              <package name="work.mapper"/>
        </mappers>


补充:

  • plugin插件是一个比较大的内容,而且偏向底层,这会留到另外一篇博文再讲述。。
  • 除了上面的,还有databaseidprovide数据库厂商标识,objectfactory对象工厂,但不常用。有兴趣的自查。



动态sql

  • 当允许使用不定数量的条件来查询的时候(这种情况就是多条件查询的情况),入参无法确定是否已经正确填写,那么你可能遇到查询条件为空的情况。所以我们需要对查询条件进行判断,动态sql就可以解决这种问题。


标签:

if标签

  • 单条件分支判断
  • 属性:
    • test:对条件进行判断,test里面可以是一个表达式。判断字符串时,允许字符串调用函数。
  • 下面的sql语句,如果调用的时候不传参,将得到select * from user where age = null;

    <select id="getuserbyage" parametertype="int" resulttype="work.pojo.user">
          select * from user where age = #{age};
    </select>
  • 利用if标签解决问题:

    <select id="getuserbyage" parametertype="int" resulttype="work.pojo.user">
          select * from user 
          <if test="age!=null ">
          where  age = #{age}
          </if>
    </select>


where标签

  • where主要用于处理多个判断条件的拼接。在where a条件 and b条件可能需要判断一下a和b的合法性,如果仅仅使用if标签那么可能会导致where、and字符串重复,而where会自动处理多余的and和where,还会自动加上where。

  • <select id="getuserbyageorname" parametertype="work.pojo.user" resulttype="work.pojo.user">
          select * from user 
          <where>
              <if test="age!=null ">
                  and age = #{age}
              </if>
              <if test="name!=null">
                  and name like '%${name}%'
              </if>
          </where>
    </select>


set标签:

  • where标签可以解决查询条件中的where问题,但如果对一个对象进行update时,如何判断传入的set值是否为空呢?可以使用if来进行判断,然后再使用set标签解决条件字符串重复问题,set标签可以去除多余的逗号。

  • <update id="updateuser" parametertype="work.pojo.user">
          update user set 
              name=#{name},
              age=#{age}
          where id=#{id}
    </update>
  • <update id="updateuser" parametertype="work.pojo.user">
          update user
          <set>
              <if test="name!=null">name=#{name},</if>
              <if test="age!=null">age=#{age}</if>
          </set>
              where id=#{id}
    </update>


foreach标签

  • 当传入的参数是一个集合的时候,遇到的问题是怎么把集合中的数据遍历出来,foreach标签可以遍历集合。
  • 标签的属性:
    • collection:要遍历的集合【传入类型为list,那么集合名为list;传入的是数组,数组名为array】
    • item:用来迭代的变量
    • open:循环开始之前输出的内容
    • close:循环完成后输出的内容
    • seperator:分隔符
  <select id="getuserbyagecollection" parametertype="list" resulttype="work.pojo.user">
        select * from user where 
        <foreach item="age" collection="list" open="age  in("  separator=","  close=")">
            #{age}
        </foreach>
    </select>


补充:

  • 除了上面的标签,还有例如trim,choose,when标签,由于上面的足够日常使用了,所以就不讲那么多了。其他的有兴趣可以自查了解一下。



关联查询


  • 在开发中,关联查询是非常常见的,关联查询就是在查询某个表时,通过字段关联关系,查出我们所需要的另外一个表中的数值。例如:通常来说,班级信息表和学生表是独立的,但班级信息表和学生表是有关系的,我们不能从班级信息表中获取到学生信息,如果我们查询班级信息表时希望同时获取到班级下的学生信息,那么我们就需要使用到关联查询。
  • 关联查询通常有一对一查询(关系类似于一个部门只有一个经理),一对多查询(关系类似于一个部分可以有多个员工),多对多查询(关系类似于一个学生可以选修多门课,一门课也可以被多名学生选修)。


一对一关联查询:

以一个部门对应一个经理为例。


方式一:

创建一个pojo类,用连接查询语句查询出两个表的数据,通过resulttype封装到一个pojo类中。


步骤:
1.首先创建一个pojo类,要求包含了部门信息和经理信息(如果你不想重复定义属性,也可以选择继承单个类的信息继承两个类,这里考虑到有选择地查询字段所以不继承):

package work.pojo;

public class department_manager {
    //部门的信息
    private integer did;
    private string department_name;
    //经理的信息
    private integer mid;
    private string name;
    private double salary;
    public integer getdid() {
        return did;
    }
    public void setdid(integer did) {
        this.did = did;
    }
    public string getdepartment_name() {
        return department_name;
    }
    public void setdepartment_name(string department_name) {
        this.department_name = department_name;
    }
    public integer getmid() {
        return mid;
    }
    public void setmid(integer mid) {
        this.mid = mid;
    }
    public string getname() {
        return name;
    }
    public void setname(string name) {
        this.name = name;
    }
    public double getsalary() {
        return salary;
    }
    public void setsalary(double salary) {
        this.salary = salary;
    }
    @override
    public string tostring() {
        return "department_manager [did=" + did + ", 部门名=" + department_name + ", mid=" + mid + ", 经理名="+ name + ", 工资=" + salary + "]";
    }
}



数据表的创建(要注意,由于存在关系,那么两个表之间肯定有一个字段用于建立关联,在pojo类中可以没有这个字段,但表中一定要有,因为sql语句依靠这个来查询):

use mybatis2;
create table department(
did int primary key auto_increment,
department_name varchar(20)
);
create table manager(
mid int primary key auto_increment,
name varchar(20),
salary double(7,2),
did int,
foreign key(did) references department(did) --这里用外键关联
);
insert into department values(null,'开发部'),(null,'市场部');
insert into manager values(null,'张三',20000.00,1),(null,'李四',20000.00,2);


2.编写sql语句:【这里使用mapper方式】

定义接口:

package work.pojo;

public interface usermapper {
    public department_manager[] getdepartmentmanager();
}

创建映射文件:

<?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="work.pojo.usermapp