美文网首页
MyBatis必知必会

MyBatis必知必会

作者: WhyDoWeLive | 来源:发表于2019-07-21 15:44 被阅读0次

流程

  • 创建持久化类(POJO类)
  • 编写持久化操作的Mapper文件,其中定义SQL语句
  • 创建配置文件:连接哪种数据库、配置数据源、mappers文件路径
  • 获取SqlSessionFactory和SqlSession,通过SqlSession操作数据库
  • 关闭SqlSession
public class MyBatisTest
{
    public static void main(String[] args)
    {
        //读取MyBatis的配置文件,其中包括连接哪种数据库,配置数据源,mappers文件路径等
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

        //获得Session实例
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = SqlSessionFactory.openSession();

        //创建User
        User user = new User("admin", "男", 26);

        //插入数据,insert的第一个参数为UserMapper文件中id为save的动作,这里是<insert ..>
        session.insert("org.test.mapper.UserMapper.save", user);
        session.commit();
        session.close();
    }
}

详解

UserMapper
<mapper namespace="org.test.mapper.UserMapper">
    <insert id="save" parameterType="org.test.domainUser" useGeneratedKeys="true">
        INSERT INTO TB_USER(name, sex, age) VALUES(#{name}, #{sex}, #{age})
    </insert>
</mapper>

id:用于在程序中引用该语句,也是session.insert()的第一个参数
parameterType:语句的接收参数,这里是我们定义的POJO类User

SqlSessionFactory

MyBatis的关键对象,它是单个数据库映射关系经过编译后的内存镜像

SqlSession
  • MyBatis的关键对象,是执行持久化操作的对象,类似于JDBC中的Connection。
  • 底层封装了JDBC连接,可用Session实例直接执行已映射的SQL语句
  • 线程非安全对象,每个线程都应用自己的SqlSession
MyBatis配置文件结构
  • properties:mapper文件中的"变量",可在Mapper文件的其它地方引用
  • settings:MyBatis中极为重要的调整,会改变MyBatis的运行时行为,例如useGeneratedKeys允许JDBC自动生成主键
  • typeAliases:为Java类型的全限定名设置别名,目的是更简短
  • typeHandlers:类型处理器。无论是在预处理语句(PreparedStatement)中设置一个参数,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换为Java类型
  • objectFactory:对象工厂,用于创建结果对象的新实例
  • Environments:配置数据源
  • Mapper映射器:指明Mapper文件在哪,进而找到其中的SQL语句
深入Mapper XML映射文件

这是MyBatis的魔力所在。跟具有相同功能的JDBC代码对比,将省掉约95%的代码。常用元素如下:

  • select/insert/update/delete:分别映射CRUD语句
  • sql:可被其它语句引用的可重用语句块
  • cache:给定命名空间的缓存配置
  • cache-ref:其它命名空间缓存配置的引用
  • resultMap:最复杂和最强大的元素,描述如何从数据库结果集中加载对象
select

CRUD的使用类似,这里用select举例说明

<select id="selectUser" parameterType="int" resultType="hashmap">
    SELECT * FROM TB_USER WHERE ID =  #{id}
</select>
  • id:命名空间中的唯一标识符,可以被用来引用这条语句
  • parameterType:语句接收的参数类型。可省略,MyBatis可以通过TypeHandler推出来
  • resultType:语句返回hash类型的对象,键是列名,值是结果行中的对应值,如{name=wxs, sex=男, grade=99}
  • #{id}:占位符,类似于sql语句中的"?"
    注意:若parameterType类型是自定义类型如User,则#{id/name}中的字段将会去查询User中的id/name

以上配置文件,在执行时会生成如下JDBC代码

String selectUser = "SELECT * FROM TB_USER WHERE ID=?";
Prepared ps = conn.preparedStatement(selectUser);
ps.setInt(1, id);
ResultMap

若使用resultType="map"的映射语句查询所有用户数据:

<select id="selectUser" resultType="map">
        SELECT * FROM TB_USER
</select>

则结果看起来是这样的,查询语句的每一行都被封装成一个Map:
{name=wxs, sex=男, grade=99}
{name=bdm, sex=女, grade=98}
...
更好的方式是将resultType赋值为对应的User类型,此时MyBatis会将查询到的数据的列和需要返回的对象(User)的属性逐一进行匹配赋值,但如果两者有不一致的现象,则就不会进行自动赋值了。这时可以使用resultMap来处理。

首先,我们构造一个上述的不一致现象——POJO的属性和数据库表中的属性名字不同

//POJO类
public class User
{
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    //省略get/set方法
}
//创建数据库
CREATE TABLE TB_USER2(
    user_id INT PRIMARY KEY AUTO_INCREMENT,
    user_name VARCHAR(18),
    user_sex VARCHAR(18),
    user_age INT
)

接着,我们在Mapper文件中利用resultMap完成这种“不一致”的映射

<resultMap id="userResultMap" type="org.test.domain.User">
        <id property="id" column="user_id" />
        <result property="name" column="user_name">
        <result property="sex" column="user_sex">
        <result property="age" column="user_age">
</resultMap>

可以看出,上述中间四条语句说明要将column映射到property,即数据库中的user_id/user_name/user_sex/user_age列映射到POJO对象的id/name/sex/age属性

在实际项目开发中,还有更加复杂的情况,例如执行的是一个多表查询语句,而返回的对象关联到另一个对象,此时简单的映射已经无法解决问题,必须使用<resultMap.../>元素来完成“定制的”映射

举一个关联查询的例子
首先,创建两个表,并通过外键关联。

CREATE TABLE TB_CLAZZ(
    id INT PRIMARY KEY AUTO_INCREMENT,
    code VARCHAR(18)
)
CREATE TABLE TB_STUDENT(
    user_id INT PRIMARY KEY AUTO_INCREMENT,
    user_name VARCHAR(18),
    user_sex VARCHAR(18),
    user_age INT,
    clazz_id INT,
    FOREIGN KEY (clazz_id) REFERENCES TB_CLAZZ(id)
)

接着,创建对应的POJO(省略get/set)

public class Clazz
{
    private Integer id;
    private String code;
}
public class Student
{
    private Integer id;
    private String name;
    private String sex;
    prvate Integer age;
    //关联的Clazz对象
    private Clazz clazz;

}

接着,编辑对应的Mapper文件

<!-- 映射学生对象的resultMap -->
<resultMap id="studentResultMap" type="org.test.domain.Student">
    <id property="id" column="id"/>
    <result property="name", column="name"/>
    <result property="sex", column="sex"/>
    <result property="age", column="age"/>
    <!-- 关联映射:根据clazz_id查询到Clazz对象并完成映射 -->
    <association property="clazz" column="clazz_id" 
      javaType="org.test.domain.Clazz"
      select="selectClazzWithId"/>
</resultMap>

<!-- 根据班级Id查询班级 -->
<select id="selectClazzWithId" resultType="org.test.domain.Clazz">
    SELECT * FROM TB_CLAZZ where id = #{id}
</select>

<!-- 查询所有学生信息,注意返回类型是resultMap -->
<select id="selectStudent" resultMap="studentResultMap">
    SELECT * FROM TB_STUDENT
<select>

<association>各元素说明
property:Student的属性名
column:数据表的列名
javaType:概属性对应的名称
select:依据clazz_id执行查询语句,将结果封装到property对应的属性中

最后,在程序中执行关联查询

List<Student> list = session.selectList("org.test.mapper.UserMapper.selectStudent")

查询结果类似:
Student [id = 1, name=wxs, sex=男, age=28, clazz=Clazz[id=1, code=j1601]]
Student [id = 2, name=bdm, sex=女, age=28, clazz=Clazz[id=1, code=j1601]]

类似的,如果在查询班级的同时也要查询出该班的所有学生,此时会用到<collection>标签,不详记录啦

MyBatis关联映射

一对一

一对一关系映射:比如一个人只能有一个身份证,一个身份证只能给一个人用。推荐使用唯一(unique)主外键关联:

CREATE TABLE tb_card(
id INT PRIMARY KEY AUTO_INCREMENT,
CODE VARCHAR(18)
);
CREATE TABLE tb_person(
id INT PRIMARY KEY AUTO_INCREMENT,
...,
cart_id INT UNIQUE,
FOREIGN KEY (card_id) REFERENCES tb_card(id)
);

mapper文件的编辑和POJO都类似上边的Student和Clazz,同样利用<association>完成关联查询,不写出来啦

  • mapper接口对象
    注意,之前测试时均使用SqlSession对象调用insert/update/delete/select测试,但官方建议通过mapper接口的代理对象访问mybatis,该对象关联了SqlSession对象,可通过该对象直接调用方法操作数据库。
    下面定义一个mapper接口对象,需要注意的是,mapper接口对象的类名和之前的XML文件中的mapper的namespace一致,而方法名和参数也必须和XML文件中的<select/update.../>元素的id属性和parameterType属性一致
<!-- mapper文件 -->
<mapper namespace="org.test.mapper.PersonMapper">
    <select id="selectPersonById" parameterType="int" .../>
</mapper>
//mapper接口对象
public interface PersonMapper{
    Person selectPersonById(Integer id);
}
//测试
//读取MyBatis的配置文件,其中包括连接哪种数据库,配置数据源,mappers文件路径等
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

    //获得Session实例
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession session = SqlSessionFactory.openSession();

    //获得mapper接口的代理对象
    PersonMapper personMapper = session.getMapper(PersonMapper.class);
    Person p = personMapper.selectPersonById(1);
    session.commit();
    session.close();
多对一

上面讲解resultMap时的clazz和student就是,就是多对一关系:一个学生只属于一个班级,一个班级可以有多个学生。多对一关系推荐使用主外键关联(与一对一的区别是没有unique)。

具体例子参考讲解resultMap时的clazz和student即可,通过<association>和<collection>完成映射。此外,了解一个概念,在执行关联查询时可配置立即加载或懒加载,即查询班级信息时,立即查询出相关学生信息或者在需要操作相关学生信息时,再去执行对应的关联查询。书中例子,一般情况下,一对多的关系时都使用懒加载

多对多

订单和商品就是多对多的关系:一个订单中可以购买多种商品,一种商品也可以属于多个不同的订单。多对多关系建议使用一个中间表来维护,中间表的订单id作为外键参照订单表的id,商品id作为外键参照商品表的id。例子树上有

小结
  • A和B是一/多对一关系时,则类A的数据结构中包含对象B,对应Mapper文件中使用<association>
  • A和B是一/多对多关系时,则类A的数据结构汇中包含List<B>,对应Mapper文件中使用<collection>

动态SQL

解决了根据不同条件拼接SQL语句的问题
MyBatis采用ONGL的表达式完成动态SQL
常用元素有if、choose(when、otherwise)、where、set、foreach、bind,接下来介绍如何在Mapper文件中使用。

if
<mapper namespace="...EmployeeMapper">
    <select id="selectEmployee" resultType=...>
        SELECT * FROM tb_employee WHERE state = 'ACTIVE'
        <if test="id != null">
            and id = #{id}
        </if>
</mapper>

说明:
1、<if>条件表明如果传入了id则还需通过id查询
2、#{id}获取参数有两种方式:从JavaBean中获取或者从HashMap中获取,即对应的mapper接口对象的selectEmployee方法的入参应为javaBean或HashMap,例如

public interface EmployeeMapper{
    List<Employee> selectEmployee(HashMap<String, Object> params)
}
choose(when、otherwise)

choose...when...otherwise处理逻辑类似switch...case...default

<select ...>
    SELECT * FROM tb_employee WHERE state = 'ACTIVE'
    <choose>
        <when test = "id != null">
            and id = #{id}
        </when>
        <when test="loginname != null and password != null">
            and loginname = #{loginname} and password = #{password}
        </when>
        <otherwise>
            and sex='男'
        </otherwise>
    </choose>
</select>

上述<select>表明:

  • 当id != null时,则根据id查询;
  • 否则,当loginame != null and password != null时,根据loginname和password查询;
  • 否则,根据sex='男'查询

任何一个条件满足后,就不再匹配后续的<when>或<otherwise>了

where

先看以下如果没有where,则下面的<select>会有什么问题

<select...>
    SELECT * FROM tb_employee WHERE
    <if test="state != null ">
        state = #{state}
    </if>
    <if test=" id != null ">
        and id= #{id}
    <if/>
</select>

如果执行上述select时,如果没有传入任何参数,则sql语句如下

SELECT * FROM tb_employee WHERE

如果执行上述select时,如果只传入id,则sql语句如下

SELECT * FROM tb_employee WHERE and id = #{id}

使用<where>后,它知道如何纠正上述错误,是否需要where字句,是否需要去掉and/or

SELECT * FROM tb_employee
<where>
    <if test=" state != null ">
        state = #{state}
    </if>
    <if test=" id != null ">
        and id= #{id}
    <if/>
</where>
set

用于更新语句:动态取舍需要更新的列

<update ...>
    UPDATE tb_employee 
        <set>
            <if test="loginname != null">loginname=#{loginname},</if>
            <if test="password != null">password=#{password}</if>
        </set>
</update>

说明

  • <set>会自动处理好逗号
  • 对应的Mapper接口对象的对应更新方法,入参为JavaBean(通常时之前查询出来的JavaBean),如
void updateEmployee(Employee employee)
foreach

对一个集合遍历,通常用于构建IN条件语句时

<select>
    SELECT * FROM tb_employee WHERE ID in
    <foreach item="item" index="index" collection="list" open="(" separator="," close=")">
    #{item}
    </foreach>
</select>

对应mapper接口对象的对应方法的入参应该为一个id的List

bind

从OGNL表达式中创建一个变量,将其绑定到上下文

<select id="selectEmployeeLikeName" ...>
    <bind name="pattern" value="'%' + _parameter.getName() + '%'"" />
    SELECT * FROM tb_employee WHERE loginname LIKE #{pattern}
</select>
...
//查询loginname包含"o"的所有员工信息
Employee employee = new Employee();
employee.setName("o");
List<Employee> list = employeeMapper.selectEmployeeLikeName(employee)

笔记总结于《Spring MVC+MyBatis企业应用实战》

相关文章

网友评论

      本文标题:MyBatis必知必会

      本文链接:https://www.haomeiwen.com/subject/gtamlctx.html