• day50_mybatis


    今日内容

    0 复习昨日
    一、分页插件
    二、ORM映射【重点】
    三、多表联查 【重点】
    四、动态SQL 【重点】
    五、$和#

    零、复习昨日

    mybatis orm框架,作用于持久层,高效开发,只关注sql,其他不用关心
    思考MyBatis到底帮你省了哪些事情?
    jdbc第四步sql自己编写之外,其他mybatis都做了…
    接口文件和映射文件如何关联?
    namespace
    接口文件中方法又是如何和映射文件中的语句关联?
    接口的方法名与映射文件标签的id一致
    语句执行时入参都可以有哪些?有什么注意事项?
    基本类型,String,Map,List,POJO(javabean/对象)
    语句执行后返回的有哪些类型?(出参)
    基本类型,字符串,对象

    BUG:

    1 idea中resuorces和test文件不识别

    手动设置标记

    image-20230607092633522

    2 编码格式

    控制台错误提示:

    MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效。

    解决方案,在pom文件中加入配置

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    properties>
    
    • 1
    • 2
    • 3

    补充: mapper文件位置

    mapper映射文件放置位置有两个

    • resources(推荐)
    • java
      • 如果使用这种,还需再pom文件加入build设置,让idea加载java下的xml文件

    myabtis-config.xml文件加载映射文件时也要两种方案

    • 使用
      但是这种写法,会随着项目模块的增多,这个地方也会随之配置增多

    • 使用

      这种写法可以一次加载一个包下的所有映射文件,但是包结构要与接口文件包结构一致

    总结,以后就按照以下写法配置:

    • 映射文件全部放在resources
    • resources下放映射文件包结构要与java放接口文件包结构一致
    • 文件名要一致

    一、分页插件

    现在我们要学习使用一个常用的mybatis的插件 --> 分页插件-PageHelper

    最早: findAll() ---> 查全部
    后来要分页: findAll(pageNo,pageSize) ---> 改动SQL 加上 limit x,y
    还行count(*)来计数
    
    • 1
    • 2
    • 3

    使用分页插件之后,只编写正常的查询SQL即可,关于分页的操作插件会自动完成.

    引入依赖

            <dependency>
                <groupId>com.github.pagehelpergroupId>
                <artifactId>pagehelperartifactId>
                <version>5.3.0version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    全局配置文件使用插件

        
        <plugins>
            <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
        plugins>
    
    • 1
    • 2
    • 3
    • 4

    在查询时使用分页功能

    public interface UserMapper {
        List<User> findAll();
    }
    
    
    <select id="findAll" resultType="User">
        select * from tb_user
    </select>
    
    @Test
    public void findAll() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 【在执行查询前设置】
        // 参数1: 当前页
        // 参数2: 每页大小
        PageHelper.startPage(2,2);
    
        List<User> all = mapper.findAll( );
        for (User user : all) {
            System.out.println(user );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    image-20221215152540842

    mybatis插件是对运行时某一点进行拦截

    pagehelper插件是拦截运行时发出的SQL,自动在SQL后面拼接关键词


    后续还可以获得更新消息的分页数据,比如共多少条数据?共多少页?当前页?下一页?目前是不是第一页?

        @Test
        public void findAll() {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            // 【在执行查询前设置】
            // 参数1: 当前页
            // 参数2: 每页大小
            PageHelper.startPage(2,2);
    
            // 执行查询全部
            List<User> userList = mapper.findAll( );
    
            // 后续可以获得更详细的信息
            PageInfo<User> pageInfo = new PageInfo<>(userList);
            System.out.println(pageInfo );
            // 获得总条数
            System.out.println(pageInfo.getTotal() );
            // 获得总页数
            System.out.println(pageInfo.getPages() );
            // 获得总数据(当前页中的总数据)
            System.out.println(pageInfo.getList() );
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    ps: 可以看源码,中国人开发,注释非常好理解

    二、ORM映射


    2.1 MyBatis自动ORM失效

    MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系,二者不同时,无法自动ORM。

    自动ORM失效
    image-20230601164912980

    2.2 方案一:列的别名

    在SQL中使用 as 为查询字段添加列别名,以匹配属性名。

    <mapper namespace="com.qf.mapper.UserMapper">
        <select id="findUserById" resultType="User">
    		select id as idd,username,password,phone,create_time,sex,money from tb_user where id = #{id}
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.3 方案二:结果映射(ResultMap - 查询结果的封装规则)

    通过< resultMap id=“” type=“” >映射,匹配列名与属性名。

    <mapper namespace="com.qf.mapper.UserMapper">
    
        
        <resultMap id="findUserByIdResultMap" type="user">
          	
            <id property="idd" column="id" />
        resultMap>
      
         
        <select id="findUserById" resultMap="findUserByIdResultMap">
            select id,username,password,phone,create_time,sex,money from tb_user where id = #{id}
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    三、 多表联查 【重点】

    表关系: 一对一,一对多,多对多

    多表联查的SQL

    3.1 OneToOne

    需求: 实现一对一查询,查询订单以及对应的用户信息

    数据: tb_user表, tb_order表

    关系:

    用户 —> 订单 (1 VS N) 一个用户有多个订单
    订单 —> 用户 (1 VS 1) 一个订单只会属于一个人

    tb_user表

    CREATE TABLE `tb_user` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',
      `username` varchar(10) DEFAULT NULL COMMENT '用户名',
      `password` varchar(10) DEFAULT NULL COMMENT '密码',
      `phone` varchar(11) DEFAULT NULL COMMENT '手机号',
      `create_time` date DEFAULT NULL COMMENT '注册时间',
      `money` double(10,2) DEFAULT NULL COMMENT '账户余额'
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    tb_order表

    CREATE TABLE `tb_order` (
      `oid` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',
      `order_time` datetime DEFAULT NULL COMMENT '订单时间',
      `order_desc` varchar(255) DEFAULT NULL COMMENT '订单详情',
      `uid` int(11) DEFAULT NULL COMMENT '关联用户id',
      PRIMARY KEY (`oid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    
    INSERT INTO `tb_order` VALUES (1, '2022-11-17 15:06:29', '笔记本电脑', 1);
    INSERT INTO `tb_order` VALUES (2, '2022-12-16 11:00:41', 'Cherry键盘', 1);
    INSERT INTO `tb_order` VALUES (3, '2022-12-16 11:01:23', 'Logi鼠标', 2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    实体类

    public class Order {
    
        private int oid;
        private Date orderTime;
        private String orderDesc;
        private int uid;
     	// set get...   
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    但是上面的实体类,只有订单信息,我们要查询的是订单和用户! 上面的类就无法展现全部数据,所以需要扩展类

    public class OrderVO extends Order {
        private User user;
        // set get
    }
    
    • 1
    • 2
    • 3
    • 4

    OrderMapper.java接口文件

    public interface OrderMapper {
        OrderVO findOrderWithUserById(int oid);
    }
    
    • 1
    • 2
    • 3

    OrderMapper.xml映射文件

        <resultMap id="orderWithUserResultMap" type="OrderVO">
            
            <id column="oid" property="oid"/>
            <result column="order_time" property="orderTime"/>
            <result column="order_desc" property="orderDesc"/>
            <result column="uid" property="uid"/>
            
            
            
            <association property="user" javaType="com.qf.model.User">
                
                <id column="id" property="id"/>
                <result column="username" property="username"/>
                <result column="password" property="password"/>
                <result column="phone" property="phone"/>
                <result column="create_time" property="createTime"/>
                <result column="money" property="money"/>
            association>
        resultMap>
    
        
        <select id="findOrderWithUserById" resultMap="orderWithUserResultMap">
            SELECT
                o.*,
                u.*
            FROM
                tb_order o,
                tb_user u
            WHERE
                o.uid = u.id
            AND o.oid = 1
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    测试

        @Test
        public void findOrderWithUserById() {
            OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
            OrderVO orderVO = mapper.findOrderWithUserById(1);
    
            // 获得订单信息
            int oid = orderVO.getOid( );
            System.out.println("oid = " + oid);
            Date orderTime = orderVO.getOrderTime( );
            System.out.println("orderTime = " + orderTime);
            String orderDesc = orderVO.getOrderDesc( );
            System.out.println("orderDesc = " + orderDesc);
        
            // 获得订单一家关联的用户信息
            User user = orderVO.getUser( );
            System.out.println(user );
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.2 OneToMore

    需求: 一对多,查询用户关联查询出所有的订单

    SELECT
    	* 
    FROM
    	tb_user u
    LEFT JOIN tb_order o ON u.id = o.uid 
    WHERE
    	u.id = 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    目的查询用户,以及关联多个订单,User类不够展现全部数据,那么就创建扩展类UserVO,UserVO类继承User就可以存储用户信息,还需要再UserVO类中添加Order类来存储信息,但是!!不是一个Order类,因为是一对多,一个用户关联多个订单,所有要设置List

    User扩展实体类

    public class UserVO extends User{
    
        private List<Order> orderList;
    
        @Override
        public String toString() {
            String s = super.toString( );
            return s +" \r\n UserVO{" +
                    "orderList=" + orderList +
                    '}';
        }
    
        public List<Order> getOrderList() {
            return orderList;
        }
    
        public void setOrderList(List<Order> orderList) {
            this.orderList = orderList;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    UserMapper.java接口

    public interface UserMapper {
        UserVO findUserWithOrdersById(int id);
    }
    
    • 1
    • 2
    • 3

    UserMapper.xml映射文件

        
        <resultMap id="userWithOrdersResultMap" type="UserVO">
            
            <id column="id" property="id"/>
            <result column="username" property="username"/>
            <result column="password" property="password"/>
            <result column="phone" property="phone"/>
            <result column="create_time" property="createTime"/>
            <result column="money" property="money"/>
            
            
            
            <collection property="orderList" ofType="com.qf.model.Order">
                <id column="oid" property="oid"/>
                <result column="order_time" property="orderTime"/>
                <result column="order_desc" property="orderDesc"/>
                <result column="uid" property="uid"/>
            collection>
        resultMap>
    
        
        <select id="findUserWithOrdersById" resultMap="userWithOrdersResultMap">
            SELECT
            *
            FROM
            tb_user u
            LEFT JOIN tb_order o ON u.id = o.uid
            WHERE
            u.id = #{id}
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    3.3 关联查询总结

    正常封装使用resultMap

    一对一封装使用association

    一对多封装使用collection

    四、动态SQL【重点】

    动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。


    自己话理解: 帮助我们拼接SQL

    常见的动态SQL语法

    • SQL片段(官方不是在动态SQL章节)
    • where , if
    • set
    • trim
    • foreach

    4.1 SQL片段

    这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。

    自己的话: 减少代码重复,主要用于抽取字段,表名等

        
        <sql id="userField">
            id,
            username,
            password,
            phone,
            create_time,
            money,
            sex
        sql>
    
        <select id="findAll" resultType="User">
            select
                
                <include refid="userField"/>
            from
                tb_user
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4.2 if

    if就是用来判断,主要用于判断要不要拼接对应的条件语句

    -- 需求:查询用户,条件是money=1000,如果密码不为空,也根据密码查
    select * from tb_user where money = 1000
    select * from tb_user where money = 1000 and password= '123456'
    
    • 1
    • 2
    • 3

    UserMapper.java接口方法

    public interface UserMapper {
        /**
         * 演示if动态sql
         */
        List<User> findByMap(HashMap<String,Object> map);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    UserMapper.xml

        <select id="findByMap" resultMap="userResultMap">
            select
                <include refid="userField"/>
            from
                tb_user
            where
                money = #{money}
            <if test="password != null and password != ''">
                and password = #{password}
            if>
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    测试

    /**
         * if动态sql
         */
    @Test
    public void showIf() {
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String, Object> map = new HashMap<>( );
        map.put("money",1000);
        List<User> list = userMapper.findByMap(map);
        for (User user : list) {
            System.out.println(user );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.3 where

    如果说只有if,可能会出现这么一种情况

    SELECT * FROM tb_user WHERE
    
    • 1

    多出一个where关键词!!


    所以我们需要一个智能的,有条件时帮我们拼接where关键词,没有条件查询时,不拼接where

        
        <select id="findUserBySex2" resultType="User">
            select
                <include refid="userField"/>
            from
                tb_user
            <where>
                <if test="sex != null">
                    sex = #{sex}
                if>
            where>
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    所以一般会where和if一起用

    4.4 set

    用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。

    UserMapper.java接口方法

    public interface UserMapper {
        int updateUser(User user);
    }
    
    • 1
    • 2
    • 3

    UserMapper.xml

        
        <update id="updateUser">
            update tb_user
            
            <set>
                
                
                <if test="username != null">username = #{username},if>
                <if test="password != null">password = #{password},if>
                <if test="phone != null">phone = #{phone},if>
                <if test="createTime != null">create_time = #{createTime},if>
                <if test="money != null">money = #{money},if>
                <if test="sex != null">sex = #{sex},if>
            set>
            where id = #{id}
        update>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    测试

        @Test
        public void testUpdate(){
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = new User( );
            user.setId(1);
            // 只更新这个2字段,其他字段不动
            user.setUsername("QF");
            user.setPassword("qf666");
    
            int i = mapper.updateUser(user);
            System.out.println(i > 0?"OK":"ERR" );
            // 增删改要提交
            sqlSession.commit();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4.5 foreach

    场景: 批量删除

    delete from tb_user where id in (1,2,3,...);
    
    • 1
    String sql = "delete from tb_user where id in (";
    int iMax = idsArr.length - 1;// 最大下标
    for (int i = 0; i < idsArr.length; i++) {
        int id = idsArr[i];
        sql += id;
        if (i != iMax) {
            sql += ",";
        } else {
            sql += ")";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    UserMapper.java

    public interface UserMapper {
        // 为了演示动态sql foreach
        int deleteBatch(List<Integer> ids);
    }
    
    • 1
    • 2
    • 3
    • 4

    UserMapper.xml

        
        <delete id="deleteBatch">
            delete from tb_user
            where id in
            
            <foreach collection="list" item="id" open="(" separator="," close=")">
                #{id}  
            foreach>
        delete>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    测试

       /**
         * 测试foreach
         */
        @Test
        public void testForeach(){
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            ArrayList<Integer> list = new ArrayList<>( );
            list.add(31);
            list.add(32);
            list.add(33);
            int i = mapper.deleteBatch(list);
            System.out.println("i = " + i);
            System.out.println(i > 0?"OK":"ERR" );
            // 增删改要提交
            sqlSession.commit();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    任务

    使用项目中的表,重复1遍
    合同加房屋实现多表联查
    做笔记,写注释,画图标记
    
    • 1
    • 2
    • 3
    使用项目中的表,重复1遍
    合同加房屋实现多表联查
    做笔记,写注释,画图标记
    
    • 1
    • 2
    • 3
  • 相关阅读:
    Zookeeper入门
    开源云管平台有哪些?有哪些优势?
    CDN工作原理
    XShell远程连接Ubuntu
    2.HTML中常用浏览器
    EN 13241工业、商业、车库门和大门—CE认证
    用户身份验证的令牌—Token教程
    【数模系列】03_层次分析法
    数据库重点总结
    JavaWeb面经
  • 原文地址:https://blog.csdn.net/weixin_39641494/article/details/131145367