MyBatis==>简介

1、概述html

  MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎全部的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJO(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。 java

2、MyBatis的优缺点sql

优势:数据库

  • MyBatis是最简单的持久化框架,小巧而且简单易学;
  • MyBatis至关灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中完全分离,下降耦合度,便于统一管理和优化,并可重用;
  • 提供XML标签,支持编写动态SQL语句;
  • 提供映射标签,支持对象与数据库的ORM字段关系映射;
  • 与JDBC相比,减小了50%以上的代码量。(对JDBC进行了封装);

缺点:api

  • 编写SQL语句时工做量很大,尤为是字段多、关联表多时,更是如此;
  • SQL语句依赖于数据库,致使数据库移植性差,不能更换数据库;

3、MyBatis功能架构缓存

Mybatis的功能架构分为三层:安全

  • API接口层:提供给外部使用的接口API,开发人员经过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
  • 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操做。
  • 基础支撑层:负责最基础的功能支撑,包括链接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来做为最基础的组件。为上层的数据处理层提供最基础的支撑。

MyBatis层次架构图:mybatis

4、MyBatis工做原理架构

先来看看下面这个图app

工做原理解析:

  • 加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select | update | delete | insert>标签项;
  • SqlSessionFactoryBuilder经过Configuration对象生成SqlSessionFactory,用来开启SqlSession;
  • SqlSession对象完成和数据库的交互;
    • 用户程序调用mybatis接口层api(即Mapper接口中的方法);
    • SqlSession经过调用api的Statement ID找到对应的MappedStatement对象;
    • 经过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象;
    • JDBC执行 SQL;
    • 借助 MappedStatement 中的结果映射关系,将返回结果转化成 HashMap、JavaBean 等存储结构并返回;

5、详细流程

  • MyBatis 应用程序经过 SqlSessionFactoryBuilder 从 mybatis-config.xml 配置文件中构建出 SqlSessionFactory(SqlSessionFactory是线程安全的);
  • SqlSessionFactory的实例直接开启一个SqlSession,再经过 SqlSession 实例得到 Mapper 对象并运行 Mapper 映射的 SQL 语句,完成对数据库的 CRUD 和事务提交;
  • 关闭SqlSession;

下面这段代码可以帮你们进一步理解这个流程

public class MyBatisTest { public static void main(String[] args) { // 指定全局配置文件
    String resource = "mybatis-config.xml"; InputStream inputStream = null; SqlSessionFactory sqlSessionFactory = null; SqlSession sqlSession = null; try { // 读取配置文件
      inputStream = Resources.getResourceAsStream(resource); // 构建sqlSessionFactory
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取sqlSession
      sqlSession = sqlSessionFactory.openSession(); /** * 执行SQL语句 * 第一个参数:接口全路径 + 方法名,对应mapper映射文件中的namespace和标签的id * 第二个参数:指定传入sql的参数:这里是用户id */ String str = sqlSession.selectOne("com.jack.course.mybatis.dao.UserDao.findNameById",1); System.out.println(str); }catch (IOException ie) { ie.printStackTrace(); }finally { if (null != sqlSession) { sqlSession.close(); } } } }
MyBatisTest

6、mybatis-config.xml 全局配置文件

先来看一个标准的配置,以下:

<?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="db.properties"></properties>
    <!-- 环境,能够配置多个,default:指定采用哪一个环境 -->
    <environments default="test">
        <!-- id:惟一标识 -->
        <environment id="test">
            <!-- 事务管理器,JDBC类型的事务管理器 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 数据源,池类型的数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
        <environment id="dev">
            <!-- 事务管理器,JDBC类型的事务管理器 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 数据源,池类型的数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${usname}"/>
                <property name="password" value="${passwd}"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 映射的mapper文件,SQL语句写在这类文件中 -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>
mybatis-config.xml

properties标签

  • 主要做用就是引入外部的properties是文件,通常是数据库的链接配置;

environments标签

  • 数据库的链接配置在该标签内,能够配置多套;
  • 使用 default 来指明使用哪套环境,对应下面具体环境的 id;

mapper标签

  • 指明 mapper 映射文件,具体的 SQL 语句在该文件中编写;

7、Mapper.java 接口文件

public interface UserMapper { String findNameById(User user); String findNameByIdAndPasswd(User user); User findUserById(@Param("id") Integer id); int insertUser(@Param("id") Integer id, @Param("name") String name, @Param("passwd") String passwd); int insertAuto(@Param("name") String name, @Param("passwd") String passwd); int updateNameById(@Param("id") Integer id, @Param("name") String name); int deleteById(@Param("id") Integer id); List<User> findAllUsers(); List<User> findUsersByPasswd(@Param("password") String passwd,@Param("user_id") Integer id); }
UserMapper

该接口主要定义查询数据库的方法,对应下面 mapper.xml 文件中的 SQL语句的 id。参数能够传自定义类型和 Java自带的类型,若是是自定义类型会自动匹配自定义类中的属性,若是是 Java 自带的类型,须要在参数前面加上 @Param 注解。

8、mapper.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:命名空间,随便写,通常保证命名空间惟一 -->
<mapper namespace="com.jack.course.mybatis.dao.UserMapper">
    <resultMap id="userMap" type="com.jack.course.mybatis.pojo.User">
        <result property="user_id" column="id"></result>
        <result property="name" column="name"></result>
        <result property="password" column="passwd"></result>
    </resultMap>

    <select id="findNameById" resultType="java.lang.String"> select name from user where id = #{id} </select>

    <select id="findNameByIdAndPasswd" resultType="java.lang.String"> select name from user where id = #{id} and passwd = #{passwd} </select>

    <select id="findUserById" resultType="com.jack.course.mybatis.pojo.User"> select * from user where id = #{id} </select>

    <insert id="insertUser"> insert into user(id,name,passwd) values (#{id},#{name},#{passwd}) </insert>

    <insert id="insertAuto" useGeneratedKeys="true" keyProperty="id"> insert into user(name,passwd) values (#{name},#{passwd}) </insert>

    <update id="updateNameById"> update user set name = #{name} where id = #{id} </update>

    <delete id="deleteById"> delete from user where id = #{id} </delete>

    <select id="findAllUsers" resultMap="userMap"> select * from user </select>

    <select id="findUsersByPasswd" resultMap="userMap"> select * from user where passwd = #{password} and id > #{user_id} </select>

</mapper>
UserMapper.xml

注意:

  • mapper 标签中的 namespace 对应的是上面 UserMapper 接口的全路径;
  • select 标签中的 id 对应的是 UserMapper 接口中的方法,resultType 对应的是该方法的返回值类型,若是是自定义类型,则要写全路径;
  • 当咱们自定义类中的属性和数据库表字段不同时,咱们能够用 resultMap 来映射其对应关系;
    • id 为咱们为这个 resultMap 自定义的名称,type 为咱们自定义类的全路径;
    • resultMap 中的 result 标签,property 为自定义类中的属性名,column 为数据库表中的字段名;
    • 使用时就能够将 resultType 替换 为 resultMap,其值就对应上面咱们为 resultMap 自定义的 id;
    • 当咱们使用 resultMap 之后,SQL语句的参数就不能用数据库的字段名,而是要用其对应的自定义类的属性名;
    • 同时,UserMapper 接口中 @Param 注解后面带的参数也要跟着变化;

最后来看下测试程序:

public class App { private UserMapper userMapper; private InputStream inputStream = null; private SqlSession sqlSession = null; @Before public void setUp() throws IOException { // 指定全局配置文件
    String resource = "mybatis-config.xml"; // 读取配置文件
    inputStream = Resources.getResourceAsStream(resource); // 构建sqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取sqlSession
    sqlSession = sqlSessionFactory.openSession(true); // 1. 映射文件的命名空间(namespace)必须是mapper接口的全路径 // 2. 映射文件的statement的id必须和mapper接口的方法名保持一致 // 3. Statement的resultType必须和mapper接口方法的返回类型一致 // 4. statement的parameterType必须和mapper接口方法的参数类型一致
    userMapper = sqlSession.getMapper(UserMapper.class); } @Test public void findNameById() { User user = new User(); user.setId(1); String name = userMapper.findNameById(user); System.out.println("name = " + name); } @Test public void findNameByIdAndPasswd() { User user = new User(); user.setId(8); user.setPasswd("123459"); String name = userMapper.findNameByIdAndPasswd(user); System.out.println("name = " + name); } @Test public void findUserById() { User user = userMapper.findUserById(6); System.out.println(user); } @Test public void insertUser() { int i = userMapper.insertUser(3,"大叔","111222"); System.out.println("i = " + i); } @Test public void insertAuto() { int i = userMapper.insertAuto("大叔","111222"); System.out.println("i = " + i); } @Test public void updateNameById() { int i = userMapper.updateNameById(3,"牛牛"); System.out.println("i = " + i); } @Test public void deleteById() { int i = userMapper.deleteById(3); System.out.println("i = " + i); } @Test public void findAllUsers() { List<User> users = userMapper.findAllUsers(); for (User user : users) { System.out.println(user); } } @Test public void findUsersByPasswd() { List<User> users = userMapper.findUsersByPasswd("111222",13); for (User user : users) { System.out.println(user); } System.out.println("users = " + users); } @After public void tearDown(){ try { if (null != sqlSession) { sqlSession.close(); } if (null != inputStream) { inputStream.close(); } }catch (IOException ie) { ie.printStackTrace(); } } }
App 测试程序

9、动态SQL

动态SQL是MyBatis框架中特性之一,在一些组合查询页面须要根据用户输入的条件生成不一样的查询SQL语句,在JDBC中须要在代码中拼接sql,容易出错,MyBatis能够解决这种问题。动态SQL经常使用的包括下面几类:

  • if
  • choose (when, otherwise)
  • foreach
  • trim (if, where)

动态 SQL 仍是在 mapper.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:命名空间,随便写,通常保证命名空间惟一 -->
<mapper namespace="com.jack.course.mybatis.dao.UserMapper">
    <resultMap id="userMap" type="com.jack.course.mybatis.pojo.User">
        <result property="user_id" column="id"></result>
        <result property="name" column="name"></result>
        <result property="password" column="passwd"></result>
    </resultMap>

    <!-- 动态SQL -->
    <!-- 当 passwd传入null时,则不执行 "and passwd = #{password}", 只执行"select * from user where name = #{name}" -->
    <select id="findAllUsersByNameAndPassword" resultMap="userMap"> select * from user where name = #{name} <if test="password != null"> and passwd = #{password} </if>

    </select>

    <!-- choose标签是按顺序判断其内部when标签中的test条件出否成立, 若是有一个成立,则 choose 结束。当 choose 中全部 when 的条件都不满则时, 则执行 otherwise 中的sql -->
    <select id="findAllUsersByIdAndNameAndPassword" resultMap="userMap"> select * from user where id > #{id} <choose>
            <when test="name != null"> and name = #{name} </when>
            <when test="password != null"> and passwd = #{password} </when>
            <otherwise> order by id desc </otherwise>
        </choose>
    </select>

    <!-- 当where下面的两个条件都不成立时,where关键字也会去掉 若是标签返回的内容是以AND 或OR 开头的,则它会剔除掉-->
    <select id="findAllUsersByIdAndName" resultMap="userMap"> select * from user <where>
            <if test="id != null"> id > #{id} </if>
            <if test="name != null"> and name = #{name} </if>
        </where>
    </select>

    <!-- 当id取值是一个list时使用 -->
    <select id="findAllUsersByIds" resultMap="userMap"> select * from user where id in <foreach collection="list" item="item" index="index" open="(" separator="," close=")"> #{item} </foreach>
    </select>

    <!-- 使用set标签,最后一个条件“password != null”不知足的话也不会报错 -->
    <update id="updateNameAndPasswordById"> update user <set>
                <if test="name != null"> name = #{name}, </if>
                <if test="password != null"> passwd = #{password}, </if>
            </set> where id = #{id} </update>


    <!-- prefix:给sql语句拼接的前缀 -->
    <!-- prefixOverrides:去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定 -->
    <!-- suffix:给sql语句拼接的后缀 -->
    <!-- suffixOverrides:去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定 -->
    <select id="findUsersByIdAndNameAndPasswd" resultMap="userMap"> select * from user <trim prefix="where" prefixOverrides="or | and">
            <if test="user_id != null"> id > #{user_id} </if>
            <if test="name != null"> and name = #{name} </if>
            <if test="password != null"> and passwd = #{password} </if>
        </trim>
    </select>

    <insert id="insertUserTrim"> insert into user <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="name != null"> name, </if>
            <if test="password != null"> passwd </if>
        </trim>
        <trim prefix="values(" suffix=")" suffixOverrides=",">
            <if test="name != null"> #{name}, </if>
            <if test="password != null"> #{password} </if>
        </trim>
    </insert>

</mapper>
UserMapper.xml
public class App2 { private UserMapper userMapper; private InputStream inputStream = null; private SqlSession sqlSession = null; @Before public void setUp() throws IOException { // 指定全局配置文件
    String resource = "mybatis-config.xml"; // 读取配置文件
    inputStream = Resources.getResourceAsStream(resource); // 构建sqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取sqlSession,参数true表明自动提交
    sqlSession = sqlSessionFactory.openSession(true); // 1. 映射文件的命名空间(namespace)必须是mapper接口的全路径 // 2. 映射文件的statement的id必须和mapper接口的方法名保持一致 // 3. Statement的resultType必须和mapper接口方法的返回类型一致 // 4. statement的parameterType必须和mapper接口方法的参数类型一致
    userMapper = sqlSession.getMapper(UserMapper.class); } /** * name传入为null * UserMapper文件中使用if条件判断 */ @Test public void findAllUsersByNameAndPassword() { String passwd = null; // String passwd = "123456";
    List<User> users = userMapper.findAllUsersByNameAndPassword("大叔",passwd); for (User user : users) { System.out.println(user); } } @Test public void findAllUsersByIdAndNameAndPassword() { // String name = "jack"; // String passwd = null;
 String name = null; String passwd = "123455"; List<User> users = userMapper.findAllUsersByIdAndNameAndPassword(3, name,passwd); for (User user : users) { System.out.println(user); } } @Test public void findAllUsersByIdAndName() { // Integer id = null; // String name = null;
 Integer id = null; String name = "jack"; List<User> users = userMapper.findAllUsersByIdAndName(id,name); for (User user : users) { System.out.println(user); } } @Test public void findAllUsersByIds() { ArrayList<Integer> list = new ArrayList<>(); list.add(10); list.add(11); list.add(12); List<User> users = userMapper.findAllUsersByIds(list); for (User user : users) { System.out.println(user); } } @Test public void updateNameAndPasswordById() { Integer id = 1; String name = null; String passwd = "111222"; int i = userMapper.updateNameAndPasswordById(id, name, passwd); System.out.println("i = " + i); } @Test public void findUsersByIdAndNameAndPasswd() { Integer id = 1; String name = "jack"; String passwd = null; List<User> users = userMapper.findUsersByIdAndNameAndPasswd(id, name, passwd); for (User user : users) { System.out.println(user); } } @Test public void insertUserTrim() { String name = "牛牛"; String passwd = null; int i = userMapper.insertUserTrim(name, passwd); System.out.println("i = " + i); } @After public void tearDown(){ try { if (null != sqlSession) { sqlSession.close(); } if (null != inputStream) { inputStream.close(); } }catch (IOException ie) { ie.printStackTrace(); } } }
App 测试程序

但在实际项目中,不建议使用动态SQL,缘由是:判断一个字段是否为空,是否传值,最好在代码中进行判断,这样能使代码更具有可读性。

10、MyBatis使用步骤总结

一、build.gradle:文件中导入依赖包,包括 MyBatis 和 MySql;

二、mybatis-config.xml:配置数据源,mapper映射文件等信息,通常放在src/main/resource目录下;

三、POJO类:创建与数据库字段对应的POJO类;

四、编写 Mapper 接口,使用接口中的方法操做数据库;

五、mapper.xml:映射文件,编写SQL语句,mapper标签中的 namespace 对应实体类的全路径

六、执行类:读取配置文件、构建sqlSessionFactory、获取sqlSession,经过sqlSession操做数据库。

另外,使用纯注解来使用 MyBatis 比较简单,使用方法在如下这篇文章中已经介绍过,欢迎你们查看

SpringBoot==>简介

相关文章
相关标签/搜索