今でもあなたは私の光丶

Mybatis入门

相关概念

对象/关系数据库映射(ORM)

ORM全称Object/Relation MApping:表示对象-关系映射的缩写

ORM完成面向对象的编程语言到关系数据库的映射,当ORM框架完成映射后,程序员可以利用面向对象程序设计语言的简单易用性,又可以利用关系数据库的技术优势。ORM把关系数据库包装成面向对象的模型。ORM框架是面向对象设计语言与关系型数据库发展不同步时的中间解决方案,采用ORM框架后,应用程序不在直接访问底层数据库,而是以面向对象的方式来操作持久化对象,而ORM框架则将这些面向对象的操作转换为底层的SQL操作。ORM框架实现的效果:把对持久化对象的保存、修改、删除等操作,转换为对数据库的操作

MyBatis简介

MyBatis是一款优秀的基于ORM的半自动持久层框架,它支持定制化SQL、存储过程以及高级映射。
MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
MyBatis可以使用简单的XML或注解来配置和映射原生类型、接口和java的POJO(Plain Old Java Objects ,普通老式Java对象)为数据库中的记录。

MyBatis历史

  1. 原是Apache的一个开源项目IBatis,2010年6月这个项目有Apache Software Foundation 迁移到了Google Code,随着开发团队转投Google Code旗下,iBatis3.x正式更名为MyBatis,代码于2013年11月迁移到GitHub
  2. iBatis一词来源于"internet"和"abatis"的组合,是一个基于Java的持久层框架。

MyBatis优势

MyBatis是一个半自动化的持久层框架,对开发人员而言,核心sql还是需要自己优化,sql和java编码分开,功能边界清晰,一个专注业务,一个专注数据。

快速入门

添加MyBatis坐标和项目配置

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--mybatis坐标-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <!--mysql驱动坐标-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
            <scope>runtime</scope>
        </dependency>
        <!--单元测试坐标-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.7</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-redis</artifactId>
            <version>1.0.0-beta2</version>
        </dependency>

        <!-- 分页助手 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>3.7.5</version>
        </dependency>
        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>0.9.1</version>
        </dependency>

        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>3.1.2</version>
        </dependency>

    </dependencies>

创建user数据表

CREATE TABLE `user` (
  `id` int(8) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名称',
  `password` varchar(50) NOT NULL DEFAULT '',
  `birthday` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表'

编写User实体类

package cn.wxrwcz.pojo;

public class User {
    private Integer id;
    private String name;
    private String password;
    private String birthday;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }
}

编写映射文件UserMapper.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="cn.wxrwcz.dao.IUserDao">
    <!--namespace:名称空间,与id组成sql的唯一标识
    reultType:表明返回值类型
    -->
    <!--抽取sql片段-->
    <sql id="selectUser">
        select * from User
    </sql>
    <!--    查询用户-->
    <select id="findAll" resultType="user">
        <include refid="selectUser"></include>
    </select>

    <!--    多条件组合查询-->
    <select id="findByCondition" resultType="user" parameterType="user">
        <include refid="selectUser"></include>
         <where>
             <if test="id != null ">
                and id = #{id}
             </if>
             <if test="name != null and name !=''">
                 and name = #{name}
             </if>
         </where>
    </select>

    <!--    多值查询:演示foreach-->
    <select id="findByIds" resultType="user" parameterType="list">
        <include refid="selectUser"/>
        <where>
            <foreach collection="array" open="id in (" close=")" separator="," item="id">
                id
            </foreach>
        </where>
    </select>
    <!--    查询单个用户-->
    <select id="findOne" resultType="user"  parameterType="int">
        <include refid="selectUser"></include>
        where id = #{id}
    </select>
    <!--添加用户-->
    <!--parameterType:参数类型-->
    <insert id="saveUser" parameterType="user">
        insert  into user values(#{id},#{name})
    </insert>
    <!--修改-->
    <update id="updateUser" parameterType="user">
        update user set name = #{name} where id = #{id}
    </update>
    <!--删除-->
    <delete id="deleteUser" parameterType="int">
        delete from user where id = #{id}
    </delete>
</mapper>

编写核心文件SqlMapConfig.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文件-->
    <properties resource="jdbc.properties"></properties>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--给实体类的全限定类名给别名-->
    <typeAliases>
        <!--给单独的实体起别名-->
        <!--  <typeAlias type="com.lagou.pojo.User" alias="user"></typeAlias>-->
        <!--批量起别名:该包下所有的类的本身的类名:别名还不区分大小写-->
        <package name="cn.wxrwcz.pojo"/>
    </typeAliases>

    <!--environments:运行环境-->
    <environments default="development">
        <environment id="development">
            <!--当前事务交由JDBC进行管理-->
            <transactionManager type="JDBC"></transactionManager>
            <!--当前使用mybatis提供的连接池-->
            <dataSource type="POOLED">
<!--                <property name="driver" value="com.mysql.jdbc.Driver"/>-->
<!--                <property name="url" value="jdbc:mysql://rm-8vbd0ga3vf5qbi322zo.mysql.zhangbei.rds.aliyuncs.com:3306/lagou?useUnicode=true&characterEncoding=utf8"/>-->
<!--                <property name="username" value="lagou"/>-->
<!--                <property name="password" value="Lagou123456"/>-->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--引入映射配置文件-->
    <mappers>
        <mapper resource="UserMapper.xml"></mapper>
    </mappers>


</configuration>

编写测试类

package cn.wxrwcz.test;


import cn.wxrwcz.dao.IUserDao;
import cn.wxrwcz.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {
    SqlSession sqlSession;
    @BeforeEach
    public void before() throws IOException {
        //1.Resoures工具类,配置文件的加载,把配置文件加载成字节输入流
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.解析了配置文件,并创建了sqlSessionFactory工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        //3.生产sqlSession,默认开启事务,但不会自动提交,增删改时需要手动提交
        //build.openSession(true)开启自动提交
        sqlSession = factory.openSession();
    }
    @Test
    public void test1()  {
        //4.sqlSession调用方法
        List<User> objects = sqlSession.selectList("userMapper.findAll");
        objects.stream().forEach(System.out::println);
    }
    @Test
    public void test2() {
        User user = new User();
        user.setName("王五");
        user.setId(3);
       sqlSession.insert("userMapper.saveUser",user);
       sqlSession.commit();
    }
    @Test
    public void test3() {
        User user =sqlSession.selectOne("userMapper.findOne",1);
        System.out.println(user);
        user.setName("张三三");
        sqlSession.update("userMapper.updateUser",user);
        sqlSession.commit();
    }
    @Test
    public void test4() {
        sqlSession.delete("userMapper.deleteUser",3);
        sqlSession.commit();
    }
    @Test
    public void test5() throws IOException {
//        IUserDao iUserDao = new UserDaoImpl();
//        iUserDao.findAll().stream().forEach(System.out::println);
        IUserDao mapper = sqlSession.getMapper(IUserDao.class);
        mapper.findAll().stream().forEach(System.out::println);
    }
    @Test
    public void test6() throws IOException {
//        IUserDao iUserDao = new UserDaoImpl();
//        iUserDao.findAll().stream().forEach(System.out::println);
        IUserDao mapper = sqlSession.getMapper(IUserDao.class);
        User user = new User();
        user.setName("李四");
        mapper.findByCondition(user).stream().forEach(System.out::println);
    }
    @Test
    public void test7() throws IOException {
//        IUserDao iUserDao = new UserDaoImpl();
//        iUserDao.findAll().stream().forEach(System.out::println);
        IUserDao mapper = sqlSession.getMapper(IUserDao.class);
        Integer[] array= {1,2};
        mapper.findByIds(array).stream().forEach(System.out::println);
    }
    @AfterEach
    public void after(){
        sqlSession.close();
    }
}

注意点

插入

  1. 插入语句用insert标签
  2. 在映射文件中使用parameterType属性指定要插入的数据类型
  3. Sql语句中使用#{实体属性名}方式引入实体中的属性值
  4. 插入操作使用的API是sqlSession.inser("命名空间.id",实体对象)

修改

  1. 修改语句要使用update标签
  2. 修改操作使用的API是sqlSession.update("命名空间.id",实体对象)

删除

  1. 删除语句使用的是delete标签
  2. Sql语句中使用#{任意字符串}方式引用传递的单个参数
  3. 删除操作使用的API是sqlSessiion.delete("命名空间.id",object)

增删改操作涉及数据库变化,所以要使用sqlSession对象显示的提交事务,即sqlSession.commit()

映射文件分析

图解

image-20200618174618778

配置头

<?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">

核心配置文件层级关系

配置头

<?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 属性
  • settings 设置
  • typeAliases 类型别名
  • typeHandlers 类型处理器
  • objectFactory 对象工厂
  • plugins 插件
  • environments 环境
  • environment 环境变量
    • transactionManager 事务管理器 type 事务管理类型
    • JDBC 这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的链接来管理事务操作
    • MANAGED 这个配置几乎没做什么,它从来不提交或回滚一个链接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文),默认情况下,它会关闭链接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为
    • dataSource 数据源 type 数据源类型
    • UNPOOLED 这个数据源的实现只是每次被请求时打开和关闭链接
    • POOLED 这个数据源的实现利用"池"的概念将JDBC链接对象组织起来
    • JNDI 这个数据源的实现是为了能在EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用
    • 图解
  • databaseIdProvider 数据库厂商标识
  • mappers 映射器
  • mapper标签
    • 作用:加载映射的
    • 方式
    • 使用相对与类路径的资源引用 <mapper resource="cn/wxrwcz/builder/UserMapper.xml"/>
    • 使用完全限定资源定位符(URL) <mapper url="file:///var/mappers/UserMapper.xml"/>
    • 使用映射器接口实现类的完全限定类名 <mapper class="org.mybatis.builder.AuthorMapper"/>
    • 将包内的映射器接口实现全部注册为映射器 <mapper name="org.mybatis.builder"/>

相关API介绍

SqlSessionFactory build(InputStream inputStream)

        //1.Resoures工具类,配置文件的加载,把配置文件加载成字节输入流
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.解析了配置文件,并创建了sqlSessionFactory工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象

创建sqlSession实例
  • openSession() 默认开启事务,但不会自动提交,增删改时需要手动提交
  • onpenSession(boolean autoCommit) 参数为是否自动提交,如果true,那么不需要手动提交事务

SqlSession 会话对象

  • 执行语句方法
  • <T> T selectOne(String statement, Object parameter)
  • <E> List<E> selectList(String statement, Object parameter)
  • int insert(String statement, Object parameter)
  • int update(String statement, Object parameter)
  • int delete(String statement, Object parameter)
  • 操作事务方法
  • void commit()
  • void rollback()

传统开发方式

  • 编写UserDao接口
  package cn.wxrwcz.dao;

  import cn.wxrwcz.pojo.User;

  import java.io.IOException;
  import java.util.List;

  public interface IUserDao {
      //查询所有用户
      List<User> findAll() throws IOException;
  }
  • 编写UserDaoImpl实现
  package cn.wxrwcz.dao;

  import cn.wxrwcz.pojo.User;
  import org.apache.ibatis.io.Resources;
  import org.apache.ibatis.session.SqlSession;
  import org.apache.ibatis.session.SqlSessionFactory;
  import org.apache.ibatis.session.SqlSessionFactoryBuilder;

  import java.io.IOException;
  import java.io.InputStream;
  import java.util.List;

  public class UserDaoImpl implements IUserDao{
      @Override
      public List<User> findAll() throws IOException {
          //1.Resoures工具类,配置文件的加载,把配置文件加载成字节输入流
          InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
          //2.解析了配置文件,并创建了sqlSessionFactory工厂
          SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
          //3.生产sqlSession,默认开启事务,但不会自动提交,增删改时需要手动提交
          //build.openSession(true)开启自动提交
          SqlSession sqlSession = build.openSession();
          List<User> objects = sqlSession.selectList("userMapper.findAll");
          return objects;
      }
  }
  • 测试
      @Test
      public void test5() throws IOException {
          IUserDao iUserDao = new UserDaoImpl();
          iUserDao.findAll().stream().forEach(System.out::println);
      }

代理开发方式

介绍

采用MyBatis的代理开发方式实现DAO层的开发,这种方式是企业的主流
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),有MyBatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

规范

  1. Mapper.xml文件中的namespace与mapper接口的全限定名相同
  2. Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
  3. Mapper接口方法的输入参数类型和Mapper.xml中定义的每个sql的parameterType的类型相同
  4. Mapper接口方法的输出类型和mapper.xml中定义的每个sql的resultType的类型相同
  5. 图解