Mybatis基础快速入门

16 篇文章 1 订阅
12 篇文章 0 订阅

前言

    真的是一刻都不敢停下学习的脚步,只想快点基础过完开始使用,前期学习把这些都记录下来,方便后期的使用,本节我们就来学习MyBatis使用。

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


这里直接看看百度百科(MyBatis百度百科

MyBatis中文文档地址(点击查看

参考文章(参考文章1


学习内容

MyBatis入门

首先在我们本地MySQL中导入我们测试数据,sql脚本分享地址:

链接:https://pan.baidu.com/s/1yaHBgoLvUkbeK2vPSKZwgw
提取码:of6m

MyBatis工作流程

在这里插入图片描述

MyBatis配置使用
  1. 使用IDEA新建Maven项目,在pom.xml引入依赖;
 <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.ibatis</groupId>
            <artifactId>ibatis-core</artifactId>
            <version>3.0</version>
        </dependency>
    </dependencies>

请添加图片描述

  1. 在如下文件夹下文件MyBatis的配置文件数据库信息;
  • 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属性引入外部配置文件 -->
    <properties resource="database.properties">
        <!-- property里面的属性全局均可使用 -->
        <property name="jdbc.username" value="root"/>
        <property name="jdbc.password" value="123456"/>
        <!-- 启用默认值特性,这样${}拼接符才可以设置默认值 -->
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
    </properties>

    <!-- 环境配置:事务管理器和数据源配置 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <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="com/brave/dao/mapper/UpmsUserMapper.xml" />-->
<!--    </mappers>-->
</configuration>

  • database.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/babytun?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
  1. 编写测试类,测试配置是否正确;
    请添加图片描述
package com.five.mybatis;

import org.junit.Test;
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.Reader;
import java.sql.Connection;

/**
 * Description:
 *
 * @Author: kk(专业bug开发)
 * DateTime: 2022-01-10 21:23
 */
public class MybatisTester {

    @Test
    public void testSqlSessionFactory() throws IOException {
        //1.读取resources下面的mybatis-config.xml文件
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        //2.使用SqlSessionFactoryBuilder创建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession sqlSession = null;
        try {
            //3.通过sqlSessionFactory创建SqlSession
            sqlSession = sqlSessionFactory.openSession();
            Connection connection = sqlSession.getConnection();
            System.out.println(connection);    //打印一下创建的连接
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(sqlSession != null){
                //如果使用的是数据库连接池,连接被回收
                //如果没有使用数据库连接池,连接直接关闭
                sqlSession.close();
            }
        }
    }
}

  1. 测试结果;
    请添加图片描述
MyBatisUtils工具类
  1. 上面经过配置我们已经成功测试了MyBatis的使用,但是我们能明显发现一个问题,这样写我们每次操作数据库都需要读取配置文件等重复操作,显然不合适;
    请添加图片描述
  2. 新建下面的工具类,存放我们的工具类;
    请添加图片描述
package com.five.mybatis.utils;

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.Reader;

/**
 * Description: 用于创建一个全局唯一的SqlSessionFactory对象
 *
 * @Author: kk(专业bug开发)
 * DateTime: 2022-01-11 11:17
 */
public class MyBatisUtils {
    //利用static修饰,属于类不属于对象,且全局唯一
    private static SqlSessionFactory sqlSessionFactory = null;

    //利用静态块在初始化类的时候实例化一个SqlSessionFactory对象
    static {
        Reader reader = null;
        try {
            //1.读取resources下面的mybatis-config.xml文件
            reader = Resources.getResourceAsReader("mybatis-config.xml");
            //2.使用SqlSessionFactoryBuilder创建SqlSessionFactory
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * 获得SqlSession
     */
    public static SqlSession openSession(){
        return sqlSessionFactory.openSession();
    }

    /**
     * 释放sqlSession对象
     */
    public static void closeSession(SqlSession sqlSession) {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

  1. 测试类;
    请添加图片描述
    请添加图片描述
MyBatis查询参数传递
  • 传递单个参数
<select id="selectById" parameterType="Integer" resultType="com.five.mybatis.entity.Goods">
        select * from t_goods where goods_id = #{value}
</select>
    @Test
    public void testSelectById() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
//            这时候传递一个参数
            Goods goods = (Goods) sqlSession.selectOne("goods.selectById", 1606);
            System.out.println(goods.getTitle());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }
  • 传递多个参数(Map法)
<select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.five.mybatis.entity.Goods">
        select * from t_goods
        where
            current_price between #{min} and #{max}
        order by current_price
        limit 0,#{limit}
</select>
    @Test
public void selectByPriceRange() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
//            这时候传递多个参数
            Map param = new HashMap();
            param.put("min",100);
            param.put("max",500);
            param.put("limit",10);
            List<Goods> list = sqlSession.selectList("goods.selectByPriceRange",param);
            for(Goods g: list){
                System.out.println(g.getTitle());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }
MyBatis多表关联查询
  • 使用LinkedHashMap
<!--    多表关联查询  结果使用Map-->
    <select id="selectGoodsMap" resultType="java.util.LinkedHashMap">
        select g.*,c.category_name from t_goods g, t_category c
        where g.category_id = c.category_id
    </select>
    @Test
    public void selectGoodsMap() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
            List<Map> list = sqlSession.selectList("goods.selectGoodsMap");
            for(Map map: list){
                System.out.println(map);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }
  • 使用resultMap映射结果

对实体类进行扩展
请添加图片描述

<!--    结果映射-->
    <resultMap id="rmGoods" type="com.five.mybatis.dto.GoodsDTO">
        <!--        设置主键字段与属性映射-->
        <id property="goods.goodsId" column="goods_id"/>
        <!--        设置非主键字段与属性映射-->
        <result property="goods.title" column="title"/>
        <result property="goods.originalCost" column="original_cost"/>
        <result property="goods.currentPrice" column="current_price"/>
        <result property="goods.discount" column="discount"/>
        <result property="goods.isFreeDelivery" column="is_free_delivery"/>
        <result property="goods.categoryId" column="category_id"/>
        <result property="categoryName" column="category_name"/>
        <result property="test" column="test"/>
    </resultMap>
    <select id="selectGoodsDTO" resultMap="rmGoods">
        select g.*,c.category_name,'1' as test from t_goods g, t_category c
        where g.category_id = c.category_id
    </select>
    @Test
    public void testselectGoodsDTO() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
            List<GoodsDTO> list = sqlSession.selectList("goods.selectGoodsDTO");
            for(GoodsDTO goodsDTO: list){
                System.out.println(goodsDTO.getGoods().getTitle());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }
MyBatis写入操作
  • 插入
    <insert id="insert" parameterType="com.five.mybatis.entity.Goods">
        insert into t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery,category_id)
        values (#{title}, #{subTitle}, #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})
--         selectKey返回最近一次插入的id
        <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
            select last_insert_id()
        </selectKey>
    </insert>
    @Test
    public void testInsert() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
            Goods goods = new Goods();
            goods.setTitle("测试商品标题");
            goods.setSubTitle("测试商品子标题");
            goods.setOriginalCost(200.0);
            goods.setCurrentPrice(100.0);
            goods.setDiscount(0.5);
            goods.setIsFreeDelivery(1);
            goods.setCategoryId(43);
            //返回值表示插入的数据总数
            int num = sqlSession.insert("goods.insert", goods);
            //提交事务数据
            sqlSession.commit();
            System.out.println(num);
        } catch (Exception e) {
            e.printStackTrace();
            if(sqlSession != null){
                //遇到错误,数据库事务回滚
                sqlSession.rollback();
            }
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }
  • 更新和删除
<!--更新数据-->
    <update id="update" parameterType="com.five.mybatis.entity.Goods">
        update t_goods
        set
           title = #{title},
           sub_title = #{subTitle},
           original_cost = #{originalCost},
           current_price = #{currentPrice},
           discount = #{discount},
           is_free_delivery = #{isFreeDelivery},
           category_id = #{categoryId}
        where
              goods_id = #{goodsId}
    </update>
<!--删除数据-->
    <delete id="delete" parameterType="Integer">
        delete from t_goods where goods_id = #{value}
    </delete>
    @Test
    public void testUpdate() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
            Goods goods = sqlSession.selectOne("goods.selectById", 740);
            goods.setTitle("测试更新的标题");
            goods.setSubTitle("测试更新子标题");
            System.out.println(goods);
            int num = sqlSession.update("goods.update", goods);
            //提交数据库事务
            sqlSession.commit();
            System.out.println("更新数据条数:"+ num);
        } catch (Exception e) {
            e.printStackTrace();
            if(sqlSession != null){
                //遇到错误,数据库事务回滚
                sqlSession.rollback();
            }
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }

    @Test
    public void testDelete() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
            int num = sqlSession.update("goods.delete", 739);
            //提交数据库事务
            sqlSession.commit();
            System.out.println("删除数据条数:"+ num);
        } catch (Exception e) {
            e.printStackTrace();
            if(sqlSession != null){
                //遇到错误,数据库事务回滚
                sqlSession.rollback();
            }
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }
MyBatis防止SQL注入

文档地址:官方文档地址

使用#{}而不使用${}!!!即可避免

请添加图片描述

Mybatis进阶

MyBatis日志管理

在这里插入图片描述

  1. pom.xml文件引入依赖;
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
  1. 直接引入以后就会打印日志;
    请添加图片描述

  2. 自定义日志;
    请添加图片描述

  3. logback.xml文件内容;

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/logs" />
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/Test.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!-- 日志输出级别 -->
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
动态SQL
<!--    根据Map里面的信息动态组织SQL语句-->
    <select id="dynamicSQL" parameterType="java.util.Map" resultType="com.five.mybatis.entity.Goods">
        select * from t_goods
        <where>
            <if test="categoryId!=null">
                and category_id = #{categoryId}
            </if>
            <if test="currentPrice!=null">
                and current_price &lt; #{currentPrice}
            </if>
        </where>
    </select>
    @Test
    public void testDynamicSQL() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
            Map param = new HashMap();
            //两个参数和一个参数决定查询语句(动态SQL)
            param.put("categoryId", 66);
            param.put("currentPrice", 300);
            List<Goods> list = sqlSession.selectList("goods.dynamicSQL", param);
            for (Goods g : list){
                System.out.println(g.getTitle() + '-' + g.getCategoryId() + '-' + g.getCurrentPrice());
            }
        } catch (Exception e) {
            e.printStackTrace();
            if(sqlSession != null){
                //遇到错误,数据库事务回滚
                sqlSession.rollback();
            }
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }
MyBatis二级缓存机制
  • 一级缓存默认开启,缓存范围为SqlSession会话
  • 二级缓存手动开启,范围为Mapper Namespace

二级缓存规则

  1. 二级开启后默认所有查询操作均使用缓存
  2. 写操作commit提交时对namespace缓存强制清空
  3. 配置useCache-false可以不用缓存
  4. 配置flushCache=true代表强制清空缓存
  • 测试一级缓存机制
    @Test
    public void tesLv1Cache(){
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
            //这时候传递一个参数
            Goods goods = sqlSession.selectOne("goods.selectById", 1603);
            Goods goods1 = sqlSession.selectOne("goods.selectById", 1603);
            //查看两次查询同一条数据的哈希地址
            System.out.println(goods.hashCode() + "===" + goods1.hashCode());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }

这里我们连续两次通过id查找同一数据,通过打印两次数据的哈希地址比较;通过哈希地址我们可以很清楚的看到,其实MyBatis并没有再执行一次查询,而是将第二次的查询直接指向第一次;
请添加图片描述


测试一级缓存的范围:
请添加图片描述
请添加图片描述


  • 测试写操作commit提交时对namespace缓存强制清空
    请添加图片描述
    ---------------------------------------------分隔--------------------------------------------
  • 测试二级缓存机制
<!--    配置二级缓存机制-->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

eviction属性是清除策略,默认的清除策略是 LRU。

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

测试使用:

请添加图片描述
请添加图片描述
tip:如果查询的操作数据量很大,我们不使用缓存!!
请添加图片描述

MyBatis处理一对多关系

这里我们的数据库中有一张商品表和一张商品详情表,很明显这两张表之间的关系是一对多的关系,当我们利用MyBatis进行查询操作的时候如果进行呢,下面我们一起来看一下。

  1. 首先将我们的商品详情表映射为实体类GoodsDetail;
    请添加图片描述
  2. goods.xml
<!--    resultMap可以用于说明一对多或者是多对一的映射逻辑-->
<!--    id是resultMap属性引用的标志-->
<!--    type指向1对应的实体-->
    <resultMap id="rmGoods1" type="com.five.mybatis.entity.Goods">
<!--        映射goods对象的主键到goods_id字段-->
        <id column="goods_id" property="goodsId"/>
<!--        collection的含义是当查询语句得到结果后,对所有的Goods对象遍历得到goods_id字段值-->
<!--        带入到goodsDetail命名空间的findByGoodsId的SQL语句中查询-->
<!--        将得到的商品详情信息赋值给goodsDetails List对象-->
        <collection property="goodsDetail" select="goodsDetail.selectByGoodsId" column="goods_id"/>

    </resultMap>
    <select id="selectOneToMany" resultMap="rmGoods1">
        select * from t_goods limit 0,1
    </select>

goods_detail.xml

    <select id="selectByGoodsId" parameterType="Integer" resultType="com.five.mybatis.entity.GoodsDetail">
        select *
        from t_goods_detail
        where goods_id = #{value}
    </select>
  1. 编写测试类;
    @Test
    public void testOneToMany() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
            List<Goods> list = sqlSession.selectList("goods.selectOneToMany");
            for (Goods g : list){
                System.out.println(g.getTitle() + '-' + g.getGoodsDetail());
            }
        } catch (Exception e) {
            e.printStackTrace();
            if(sqlSession != null){
                //遇到错误,数据库事务回滚
                sqlSession.rollback();
            }
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }
}

请添加图片描述

总结

本节我们一起学习了MyBatis的基础和进阶使用,相信通过这次的使用会让你对于MyBatis有一个更深的认识,文中的原理不是很多,但是所有涉及到原理的地方都在代码中添加了注释,加油~

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值