一文入门MyBatis

97 阅读11分钟

“携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

生命无罪,健康万岁,我是laity。

我曾七次鄙视自己的灵魂:

第一次,当它本可进取时,却故作谦卑;

第二次,当它在空虚时,用爱欲来填充;

第三次,在困难和容易之间,它选择了容易;

第四次,它犯了错,却借由别人也会犯错来宽慰自己;

第五次,它自由软弱,却把它认为是生命的坚韧;

第六次,当它鄙夷一张丑恶的嘴脸时,却不知那正是自己面具中的一副;

第七次,它侧身于生活的污泥中,虽不甘心,却又畏首畏尾。

九、Mybatis

9.1 什么是MyBatis

mybatis 是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。  mybatis通过xml或注解的方式将要执行的各种 statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。  最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc 进行了封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

MyBatis的前身是 ibatis

9.2 MyBatis快速入门

① 添加MyBatis的坐标 ② 创建user数据表 ③ 编写User实体类 ④ 编写映射文件UserMapper.xml ⑤ 编写核心文件SqlMapConfig.xml ⑥ 编写测试类

9.2.1 添加Mybatis的坐标

<dependencies>
    <!--单元测试坐标-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!--日志坐标-->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.12</version>
    </dependency>

    <!--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>
  </dependencies>

9.2.2 创建user数据表、创建User实例

package com.domain;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.domain.User
 * @Date: 2022年06月02日 22:37
 * @Description:
 */
public class User {

    private Integer id;
    private String username;
    private String password;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    
    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

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

9.2.3 配置文件

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

<!--
namespace:名称空间
resultType:对应你建立的包名
id:唯一标识不能重复
-->
<mapper namespace="userMapper">
    <select id="findAll" resultType="com.domain.User">
        select *
        from user
    </select>
</mapper>
<?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="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>  <!-- 事务管理器-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///java?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="wang9264"/>
            </dataSource>
        </environment>
    </environments>


    <!-- 加载映射文件 -->
    <mappers>
        <mapper resource="com/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

9.2.4 测试方法--根据实现类查询

package com.test;

import com.domain.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.Test;

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

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.test.MybatisTest
 * @Date: 2022年06月02日 22:58
 * @Description: mybatis 测试练习
 */
public class MybatisTest {
    @Test
    public void test1() {
        try {
            // 加载核心配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            // 获取对象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            // 构建工厂
            SqlSessionFactory factory = builder.build(is);
            // 构建会话
            SqlSession sqlSession = factory.openSession();
            // 执行 sql
            List<User> userList = sqlSession.selectList("userMapper.findAll");
            // 打印输出结果
            System.out.println(userList);
            // 释放资源
            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

9.3 MyBatis的映射文件概述

在这里插入图片描述

9.4 MyBatis的增删改查操作

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

<!--
namespace:名称空间
resultType:对应你建立的包名 - 封装的结果集
id:唯一标识不能重复
-->
<mapper namespace="userMapper">
    <!-- resultType 结果类型 -->
    <select id="findAll" resultType="com.domain.User">
        select *
        from user
    </select>

    <!-- parameterType 传入的参数类型 -->
    <insert id="insertUser" parameterType="com.domain.User">
        insert into user
        values (#{id}, #{username}, #{password})
    </insert>

    <delete id="deleteUser" parameterType="com.domain.User">
        delete
        from user
        where id = #{id}
    </delete>

    <update id="updateUser" parameterType="com.domain.User">
        update user
        set username = #{username},
            password = #{password}
        where id = #{id}
    </update>
</mapper>
package com.test;

import com.domain.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.Test;

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

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.test.MybatisTest
 * @Date: 2022年06月02日 22:58
 * @Description: mybatis 测试练习
 */
public class MybatisTest {
    @Test
    public void test1() {
        try {
            // 加载核心配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            // 获取对象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            // 构建工厂
            SqlSessionFactory factory = builder.build(is);
            // 构建会话
            SqlSession sqlSession = factory.openSession();
            // 执行 sql
            List<User> userList = sqlSession.selectList("userMapper.findAll");
            // 打印输出结果
            System.out.println(userList);
            // 释放资源
            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test2() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = factory.openSession();  // true
        User user = new User();
        user.setId(6);
        user.setUsername("家里蹲2");
        user.setPassword("123");
        int cut = sqlSession.insert("userMapper.insertUser", user);
        sqlSession.commit();
        System.out.println(cut);
        sqlSession.close();
    }

    @Test
    public void test3() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = factory.openSession();
        int cut = sqlSession.delete("userMapper.deleteUser", 7);
        sqlSession.commit();
        System.out.println(cut);
        sqlSession.close();
    }

    @Test
    public void test4() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = factory.openSession();
        int cut = sqlSession.delete("userMapper.updateUser", new User(6, "laity106", "123"));
        sqlSession.commit();
        System.out.println(cut);
        sqlSession.close();
    }
}

9.5 MyBatis核心配置文件概述

9.5.1 environments标签

其中,事务管理器(transactionManager)类型有两种: • JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。 • MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置 为 false 来阻止它默认的关闭行为。 其中,数据源(dataSource)类型有三种: • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。 • POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。 • JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置 一个 JNDI 上下文的引用。

9.5.2 mappers标签

该标签的作用是加载映射的,加载方式有如下几种: • 使用相对于类路径的资源引用,例如: • 使用完全限定资源定位符(URL),例如: • 使用映射器接口实现类的完全限定类名,例如: • 将包内的映射器接口实现全部注册为映射器,例如:

9.5.3 Properties标签

在这里插入图片描述

9.5.4 typeAliases标签

  • 取别名

在这里插入图片描述

在这里插入图片描述

9.6 MyBatis的Dao层实现

9.6.1 传统的方式

  • 就是JavaWeb阶段学的 -- 缺点:繁琐

9.6.2 代理开发

采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。 Mapper 接口开发需要遵循以下规范: 1、 Mapper.xml文件中的namespace与mapper接口的全限定名相同 2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同 3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同 4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

  • 编写UserMapper接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pCqTLArK-1656905616307)(image-20220603195428113.png)]

  • 测试代理方式
    /**
     * 通过dao层实现操作
     */
    @Test
    public void test5() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = factory.openSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> all = userDao.findAll();
        System.out.println(all);
        sqlSession.close();
    }

9.7 MyBatis映射文件深入 - 动态sql语句

Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。

9.7.1 动态SQL之 <if>

我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到.

    <select id="findByCondition" parameterType="user" resultType="user">
        select * from User
        <where>
            <if test="id!=0">
                and id=#{id}
            </if>
            <if test="username!=null">
                and username=#{username}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
        </where>
    </select>

9.7.2 动态SQL之<foreach>

  • 循环执行sql的拼接操作,例如:SELECT * FROM USER WHERE id IN (1,2,5)。
    <!--
        循环执行sql的拼接操作,例如:SELECT * FROM USER WHERE id IN (1,2,5)。
        collection 类型  集合 list 数组 array
        open= 以什么什么开始
        close= 以什么什么结束
        item 负责接收集合/数组 的每一个值
        separator 分隔符
    -->
    <select id="findByIds" parameterType="list" resultType="user">
        select * from User
        <where>
            <foreach collection="array" open="id in(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>
  • foreach标签的属性含义如下:
  • <foreach>标签用于遍历集合,它的属性:
    • • collection:代表要遍历的集合元素,注意编写时不要写#{} • open:代表语句的开始部分 • close:代表结束部分 • item:代表遍历集合的每个元素,生成的变量名 • sperator:代表分隔符

9.8 SQL片段抽取

Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的

    <!--抽取sql片段简化编写-->
    <sql id="selectUser"> select *
                          from User</sql>
    <select id="findById" parameterType="int" resultType="user">
        <include refid="selectUser"></include>
        where id=#{id}
    </select>
    <select id="findByIds" parameterType="list" resultType="user">
        <include refid="selectUser"></include>
        <where>
            <foreach collection="array" open="id in(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

用处不大

  • MyBatis映射文件配置:

    :查询 :插入 :修改 :删除 :where条件 :if判断 :循环 :sql片段抽取 9.9 MyBatis核心配置文件深入 9.9.1 typeHandlers标签 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器(截取部分)。 这种 就比如时间转换 有的就是时间戳 -- 转换成日期格式 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个JDBC类型。例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。 开发步骤: ① 定义转换类继承类BaseTypeHandler<T> ② 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult 为查询时 mysql的字符串类型转换成 java的Type类型的方法 ③ 在MyBatis核心配置文件中进行注册 ④ 测试转换是否正确 package com.headler; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; /** * @author: Laity * @Project: JavaLaity * @Package: com.headler.DateTypeHandler * @Date: 2022年06月03日 23:13 * @Description: */ public class DateTypeHandler extends BaseTypeHandler<Date> { /* * 将java类型转换成数据库需要类型 * */ @Override public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException { long time = date.getTime();// 获取的时间毫秒值 preparedStatement.setLong(i, time); // } /* * 后面的三个都是将数据库中的类型 转换成java类型 * String 字段 数据库中表的字段名称 * ResultSet 查询出的结果集 * */ @Override public Date getNullableResult(ResultSet resultSet, String s) throws SQLException { // 将获得的结果集中需要是数据 (long) 转换成date类型 返回 long aLong = resultSet.getLong(s); Date date = new Date(aLong); return date; } @Override public Date getNullableResult(ResultSet resultSet, int i) throws SQLException { // 将获得的结果集中需要是数据 (long) 转换成date类型 返回 long aLong = resultSet.getLong(i); Date date = new Date(aLong); return date; } @Override public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException { long aLong = callableStatement.getLong(i); Date date = new Date(aLong); return date; } } 注册 -- 告诉mybatis <!-- 注册类型处理器 --> <typeHandlers> <typeHandler handler="com.headler.DateTypeHandler"/> </typeHandlers> 9.9.2 plugins标签 分页 MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据 开发步骤: ① 导入通用PageHelper的坐标 ② 在mybatis核心配置文件中配置PageHelper插件 ③ 测试分页数据获取 导入坐标 <!-- 分页助手 --> <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> 在mybatis核心配置文件中配置PageHelper插件 <!-- 注意:分页助手的插件 配置在通用馆mapper之前 --> <plugin interceptor="com.github.pagehelper.PageHelper"> <!-- 指定方言 --> <property name="dialect" value="mysql"/> </plugin> 9.10 MyBatis的多表操作 9.10.1 一对一查询 对应的sql语句:select * from orders o,user u where o.uid=u.id; resultMap标签 手动指定字段与实体属性的映射关系 -- orm操作 id标签 column对应表中的字段名称,property是实体对象定义的属性名称 result标签 column对应表中的字段名称,property是实体对象定义的属性名称。 association标签 property是实体对象定义的属性名称(private User user),javaType当前实体中的属性的类型(User) 里面还需要配置 User的属性(id result) collection标签 property:集合名称,ofType:当前集合的数据类型 9.10.2 一对多查询 9.10.3 多对多查询 一对一配置:使用做配置 一对多配置:使用+做配置 多对多配置:使用+做配置 9.11 MyBatis的注解开发 这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。 @Insert:实现新增 @Update:实现更新 @Delete:实现删除 @Select:实现查询 @Result:实现结果集封装 @Results:可以与@Result 一起使用,封装多个结果集 @One:实现一对一结果集封装 @Many:实现一对多结果集封装 @Before -- 表示在任意使用@Test注解标注的public void方法执行之前执行。 public class MyBatisTest { private UserMapper mapper; @Before public void before() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(true); mapper = sqlSession.getMapper(UserMapper.class); } @Test public void testSave(){ User user = new User(); user.setUsername("tom"); user.setPassword("abc"); mapper.save(user); } @Test public void testUpdate(){ User user = new User(); user.setId(18); user.setUsername("lucy"); user.setPassword("123"); mapper.update(user); } @Test public void testDelete(){ mapper.delete(18); } @Test public void testFindById(){ User user = mapper.findById(2); System.out.println(user); } @Test public void testFindAll(){ List<User> all = mapper.findAll(); for (User user : all) { System.out.println(user); } } } 将上面的增删改成改为注解形式 public interface UserDao { @Select("select * from user") List<User> findAll(); } <!-- 注解的方式 --> <!-- 加载映射关系 TODO --> <mappers> <!-- 指定接口所在的包 进行扫描--> <package name="com.dao"/> </mappers> @Test public void test5() throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession = factory.openSession(); UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> all = userDao.findAll(); System.out.println(all); sqlSession.close(); } 9.12 MyBatis的注解实现复杂映射开发 实现复杂关系映射之前我们可以在映射文件中通过配置<resultMap>来实现,使用注解开发后,我们可以使用@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置 9.12.1 一对一 @Select("select * from orders") @Results({ @Result(column = "id",property = "id"), @Result(column = "ordertime",property = "ordertime"), @Result(column = "total",property = "total"), @Result( property = "user", //要封装的属性名称 column = "uid", //根据那个字段去查询user表的数据 javaType = User.class, //要封装的实体类型 //select属性 代表查询那个接口的方法获得数据 -- 引用别人的查询结果字段进行查询 one = @One(select = "com.itheima.mapper.UserMapper.findById") ) }) public List<Order> findAll(); 9.12.2 一对多 9.12.3 多对多 个人感觉复杂的映射关系 还是使用xml写比较好,简单的使用注解。 在后面的SpringBoot项目中多数都是注解,所以还是要好好学习的。