精华内容
下载资源
问答
  • 如图三: 题目: 解决方案:(参考博客链接) select DepartmentName from Department where DepartmentID in (select MinSalary.DepartmentID as DepartmentID from (select sal.DepartmentID,MIN(sal....

    如图三表:

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    题目:
    在这里插入图片描述
    解决方案:(参考博客链接

    select DepartmentName from Department where DepartmentID in
    (select MinSalary.DepartmentID as DepartmentID from
      (select sal.DepartmentID,MIN(sal.income) as minincome from  //✅用as为列命名以便后面使用
        	(	select DepartmentID,Salary.*
    			from Salary,Employee 
    			where Salary.EmployeeID = Employee.EmployeeID //✅等值链接
    		) as sal //查询带有DepartmentID的工资信息表并命名为sal
    	group by sal.DepartmentID
      )as Minsalary//查询每个部门的最低收入(根据DepartmentID分组)
    	 where Minsalary.minincome >3700 //查询最低收入大于3700的部门DepartmentID
    )//查询最低收入大于3700的部门详细信息
    

    同类型练习题及解决:
    Q:查询收入大于平均收入的所有雇员的个人信息
    A:在这里插入图片描述

    注意:

    1. where 后使用=号连接可能会报错:❌子查询返回的值不止一个。当子查询跟随在=、!=、、>= 之后,或子查询用作表达式时,这种情况是不允许的。 解决: 把等号改为in
    2. 要用as命名列名
    展开全文
  • 这中多表单独查询可以跨多个 mapper 文件,只要写上对应的 namespace 就可以了 结果嵌套查询多表连接查询):也有一个入口的 select 语句,与上面不一样的是,这个 select 语句将两张表的字段都选择出来了,我们...

    26c950b7cc63429bc139390ebdbdf09c.png

    注:代码已托管在GitHub上,地址是: https://github.com/Damaer/Mybatis-Learning ,项目是 mybatis-10-one2many ,需要自取,需要配置 maven 环境以及 mysql环境( sql 语句在 resource 下的 test.sql 中),觉得有用可以点个小星。

    docsify 文档地址在: https://damaer.github.io/Mybatis-Learning/#/

    很多时候,当查询条件涉及到具有关联关系的多个表的时候,需要使用到关联查询,关联查询一般有四种。

    • 一对一关联查询
    • 一对多关联查询
    • 多对一关联查询
    • 多对多关联查询

    下面我们需要实践的是一对多关联查询,所谓一对多就是一个对象里面的属性是一个对象的集合。比如每个国家都有几个领导。对应的国家表以及领导人的表。

    9fe8e812ff1af444aa6266f9dabbf0f0.png

    创建数据表

    #创建数据库
    CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
    #创建数据表
    CREATE TABLE `test`.`country` ( `cid` INT(10) NOT NULL AUTO_INCREMENT ,`cname` VARCHAR(20) NOT NULL ,PRIMARY KEY(`cid`)) ENGINE = MyISAM;
    CREATE TABLE `test`.`minister` ( `mid` INT(10) NOT NULL AUTO_INCREMENT ,`mname` VARCHAR(20) NOT NULL ,`countryId` INT(20) NOT NULL ,PRIMARY KEY(`mid`)) ENGINE = MyISAM;
    
    #初始化数据表
    INSERT INTO `country` (`cid`, `cname`) VALUES ('1', 'USA');
    INSERT INTO `country` (`cid`, `cname`) VALUES ('2', 'England');
    
    INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('1', 'aaa', '1');
    INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('2', 'bbb', '1');
    INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('3', 'ccc', '2');
    INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('4', 'ddd', '2');
    INSERT INTO `minister` (`mid`, `mname`, `countryId`) VALUES ('5', 'eee', '2');

    实体类

    country 对应的实体类,属性有: cid , cname ,以及 miniters ,实现 get() 以及 set() 方法, tostring() 方法:

    import java.util.Set;
    public class Country {
        private Integer cid;
        private String cname;
        private Set<Minister> ministers;
    
        public Integer getCid() {
            return cid;
        }
        public void setCid(Integer cid) {
            this.cid = cid;
        }
        public String getName() {
            return cname;
        }
        public void setName(String cname) {
            this.cname = cname;
        }
        public Set<Minister> getMinisters() {
            return ministers;
        }
        public void setMinisters(Set<Minister> ministers) {
            this.ministers = ministers;
        }
        @Override
        public String toString() {
            return "Country [cid=" + cid + ", cname=" + cname + ", ministers="
                    + ministers + "]";
        }
    }

    Minister 的实体类,也需要实现 set() 以及 get() 方法, toString() 方法:

    public class Minister {
        private Integer mid;
        private String mname;
        @Override
        public String toString() {
            return "Minister [mid=" + mid + ", mname=" + mname + "]";
        }
        public Integer getMid() {
            return mid;
        }
        public void setMid(Integer mid) {
            this.mid = mid;
        }
        public String getMname() {
            return mname;
        }
        public void setMname(String mname) {
            this.mname = mname;
        }
        
    }

    在 mybatis.xml 文件里面注册 mapper 文件:

    <!-- 注册映射文件 -->
        <mappers>
            <mapper resource="mapper/mapper.xml"/>
        </mappers>

    我们操作数据库的接口:

    public interface ICountryDao {
        Country selectCountryById(int cid);
        Country selectCountryById2(int cid);
    }

    mapper.xml 文件,对应的两种方式实现一对多查询:

    • 一种是嵌套查询(多表单独查询),也就是有一个入口的 select 语句,但是这个语句只选出 country 的信息,在 resultMap 里面自定义,包括一个 <collection></collection> ,在里面指定对应的类型, property 是属性的名字, ofType 是这个属所对应的类型, select 指定调用的另一个 select 语句, column 是传进去的参数,这样的方式可以实现延迟加载,也就是用不到的时候,不会调用里面的 SQL 。这中多表单独查询可以跨多个 mapper 文件,只要写上对应的 namespace 就可以了
    • 结果嵌套查询(多表连接查询):也有一个入口的 select 语句,与上面不一样的是,这个 select 语句将两张表的字段都选择出来了,我们需要指定一个 resultMap ,其他子标签一样,但是 <collection></collection> 里面不再调用另外的 sql 语句,只是指定了属性与字段对应就可以了。
    <?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="dao.ICountryDao">
    <!--     resultMap 能解决字段和属性不一样的问题 -->
        <!-- 以后用得比较多 ,是因为可以使用延迟加载-->
        <!-- 嵌套查询 -->
        <select id="selectMinisterByCountry" resultType="Minister">
        select mid,mname from minister where countryId=#{ooo}
        </select>
        <resultMap type="Country" id="countryMapper">
            <id column="cid" property="cid"/>
            <result column="cname" property="cname"/>
            <!-- country中有一个成员变量是ministers,它的泛型是Minister -->
            <collection property="ministers" 
                        ofType="Minister"
                        select="selectMinisterByCountry"
                        column="cid">
            </collection>
        </resultMap>
        <select id="selectCountryById" resultMap="countryMapper">
            select cid,cname 
            from country 
            where 
            cid=#{cid}
        </select>
    
        <!-- 嵌套结果-->
        <select id="selectCountryById2" resultMap="countryMapper2">
            select * from country c,minister m where c.cid = m.countryId and c.cid= #{cid}
        </select>
        <resultMap id="countryMapper2" type="Country">
            <id column="cid" property="cid"/>
            <result column="cname" property="cname"/>
            <collection property="ministers" ofType="Minister">
                <id property="mid" column="mid"/>
                <result property="mname" column="mname"/>
            </collection>
        </resultMap>
    </mapper>

    测试test:

    public class MyTest {
      private ICountryDao dao;
      private SqlSession sqlSession;
      @Before
      public void Before(){
        sqlSession=MyBatisUtils.getSqlSession();
        dao=sqlSession.getMapper(ICountryDao.class);
      }
      @Test
      public void TestselectCountryById(){
        Country country=dao.selectCountryById(1);
        System.out.println(country);
      }
    
      @Test
      public void TestselectCountryById2(){
        Country country=dao.selectCountryById2(1);
        System.out.println(country);
      }
    
    
      @After
      public void after(){
        if(sqlSession!=null){
          sqlSession.close();
        }
      }
    
    }

    使用到的工具类: MybatisUtils.java

    public class MyBatisUtils {
        static private SqlSessionFactory sqlSessionFactory;
        static public SqlSession getSqlSession() {
            InputStream is;
            try {
                is = Resources.getResourceAsStream("mybatis.xml");
                if (sqlSessionFactory == null) {
                    sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
                }
                return sqlSessionFactory.openSession();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
    }

    两个接口的结果一致:

    [service] 2018-07-12 14:01:15,835 - org.apache.ibatis.transaction.jdbc.JdbcTransaction -508  [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4abdb505]
    [service] 2018-07-12 14:01:15,837 - dao.ICountryDao.selectCountryById2 -510  [main] DEBUG dao.ICountryDao.selectCountryById2  - ==>  Preparing: select * from country c,minister m where c.cid = m.countryId and c.cid= ? 
    [service] 2018-07-12 14:01:15,869 - dao.ICountryDao.selectCountryById2 -542  [main] DEBUG dao.ICountryDao.selectCountryById2  - ==> Parameters: 1(Integer)
    [service] 2018-07-12 14:01:15,884 - dao.ICountryDao.selectCountryById2 -557  [main] DEBUG dao.ICountryDao.selectCountryById2  - <==      Total: 2
    Country [cid=1, cname=USA, ministers=[Minister [mid=2, mname=bbb], Minister [mid=1, mname=aaa
    来源:https://www.tuicool.com/articles/nYFBZbn
    展开全文
  • 简单查询 即最简单的关键字组合SELECT +FROM+ WHERE (+BETWEEN, +IN) 是SQL查询的地基 此简单查询可以应对部分提数需求,例如运营想查看某段时间订单 > 多表查询 即INNER JOIN、LEFT JOIN 等联结关键字 想象中的取数...

    6f9f76f2d76af5f43c555dab8421f6a5.png

    本文来自知乎问题:

    数据分析人员需要掌握sql到什么程度?

    8aebfb76de611247d9d2b8fdca5d2957.png

    作为专注数据分析结论/项目在业务落地以实现增长的分析师,建议在开始学习新技能前,先明确应用场景。有的放矢才能不枉费努力。

    翻译过来就是:先了解与SQL相关的数据分析工作有哪些?有了目标,才能知道需要准备什么知识来应对。

    按我目前与SQL相关的工作内容,为你提供以下参考:

    (食用说明:根据以下场景,选择需要重点学习的知识点)

    (星标根据使用频率标记,而非重要性)

    SQL应用场景及必备知识:

    数据查询 ★★★

    业务场景

    • 也就是常说的“提数”。

    • 实际工作场景中,如果向IT提提数需求,一般都需要沟通+排期,所以最有效率的建议就是自己会从数据库里提数

    • 数据分析师除了自身的分析工作外,有时(甚至是经常)还需要应付产品、运营等部门同事的提数需求

    必备知识

    > 简单查询

    • 即最简单的关键字组合SELECT +FROM+ WHERE (+BETWEEN, +IN) 是SQL查询的地基

    • 此简单查询可以应对部分提数需求,例如运营想查看某段时间订单

    > 多表查询

    • 即INNER JOIN、LEFT JOIN 等联结关键字

    • 想象中的取数可能是直接在某个表SELECT想要的字段?NO! 实际上为了查询效率,数据会散落到数据库的各个角落,例如想要了解一笔订单情况,信息存在这些表中:订单流水表、订单详情表、商品详情表、门店表、会员表等。

    • 该部分知识的关键在于【明确业务分析需求→选择合适的联结方式】

    数据更新 ★★☆

    业务场景

    • 即题主所说的“增删改”

    • 该场景之所以仅两星的原因,是实际工作中,数据库运维部门给到我们数据分析师的数据库账号多半是只读权限,也就无法去“增删改”;此外,还有数据管控的原因

    • 所以此场景可能更多在于自建数据库中,如在电脑上新建虚拟机搭建数据库服务器,导入数据后方便进行下一步分析

    必备知识

    > 数据库与表的创建、删除和更新

    • 该部分知识点关键在于【字段类型的设置】要符合后续分析需求,如订单商品数量就要设成数值类型、订单日期设成日期类型等。(因为见过都设成字符类型的表,所以就简单提一下)

    分析数据 ★★★

    业务场景

    • 该部分可谓是数据分析师的核心工作

    • 面对复杂的业务问题,重点在于将其拆解、转译成简单的SQL问题

    • 【案例】例如教育行业中某领导要求你“分析某课程的效果如何”→ 翻译:课程效果可通过学生成绩反映,即是要计算成绩最大值、最小值、学生成绩分布 → SQL语句

    必备知识

    > 汇总分析

    • 即GROUP BY关键字

    • 解决业务问题:如计算每个课程学生的平均成绩 → SELECT avg(成绩) FROM 成绩表 GROUP BY 课程

    > 复杂查询

    • 如嵌套子查询、标量子查询、关联子查询

    • 可应对更复杂的业务问题:如找出每个课程最高分的学生 → 需要按课程分组后找到最高成绩记录,可以应用关联子查询 → SELECT 学生名字 FROM 成绩表 a WHERE 成绩 = (SELECT max(成绩) FROM 成绩表 b WHERE a.课程=b.课程)

    > 窗口函数

    • 聚合/排序函数 ( ) OVER (PARTITION BY ..ORDER BY..)

    • 此函数可解决复杂业务问题,如常见的TOP N问题:找出每个课程成绩前三的学生 → 按课程分组对学生按成绩排名,再从中找出排名前三的学生 →SELECT 学生名字 FROM ( SELECT 学生名字, dense_rank()over(partition by 课程 order by 成绩 desc) as '成绩排名' FROM 成绩表) t WHERE t.成绩排名<4

    数据产品 ★☆☆

    业务场景

    对于部分岗位,如我在的集团用户数据中心,需要负责搭建如CDP这样的数据产品,虽然多数情况下是由开发负责数据库工作,但是对于里面核心的功能如运营指标体系、模型报表等,背后的计算逻辑、数据流,要求数据分析师了如指掌。

    此外,对于刚开始建立数据分析团队的部门,还存在【数据同步】的需求,即要从ERP、CRM等系统将需要分析的原数据同步到自己的数据库里便于分析,而此需求需要通过存储过程实现。

    必备知识

    > 存储过程

    即PROCEDURE,可以将某业务需求,或者数据产品中的报表对应的所有SQL语句放在一起,方便一键执行,如RFM模型里的语句可以写成存储过程,计算结果实时同步到前端

    >【SQL SERVER】计划

    面对【数据同步】需求,有了存储过程后,还需要进行定时任务,在非业务时间执行数据同步的存储过程。如是使用SQL SERVER版本,可以通过“计划”实现定时任务。

    项目部署 ★☆☆

    业务场景

    数据分析结论在业务场景测试有效后,就需要通过报表、模型等方式落地形成业务常态。而这个项目落地,可能交给开发处理,但更有效的方式是分析师可以参与到部署的过程中。而这个过程,其中一个重要的部分就是数据库的设计:如何设计表格以提高计算效率。

    必备知识

    > 数据库设计与【SQL三范式】

    SQL三范式的目的在于解决数据冗余、计算效率低等问题,另一方面对数据增加、修改更友好。

    这部分从业务场景出发,讨论业务问题的解决方案与SQL知识点的关系,帮助答主解决学习了SQL之后可以做什么的问题。

    【SQL业务实战】如何分析用户?

    ——用SQL做一份数据分析报告涉及什么哪些知识点?

    在工作中,每个数据分析师都离不开做数据分析报告,而一份可落地的报告更是要求灵活地应用工具及理论知识。接下来,我们从工具应用的角度,看看如何用SQL做一份完整的数据分析报告。

    1405426b153ff89f6b0d3cdfffd3de27.png

    数据导入

    • 新建数据库

    • 用优秀的数据库管理工具Navicat 连接数据库

    • 通过Navicat 将数据(如Excel、SQL脚本等格式)导入数据库

    数据清洗

    数据清洗的目的是为了将数据按照业务分析需求,剔除异常值、离群值,使分析结果更准确反映业务实际。

    常见的步骤如下:

    • 是否存在空值:可通过【WHERE `字段名` is null】实现

    • 是否存在重复数据:通过GROUP BY关键字实现,如【SELECT COUNT(*) FROM 表名 GROUP BY 字段名 HAVING COUNT(*) >1】

    • 是否存在业务定义以外的数据:如需要分析华南区域数据,而数据中出现华北数据

    数据格式化

    这一步是要根据后续分析需求,调整表格结构、数据格式等,如出于数据存放原因,拿到的数据表格可能是一维表,不满足分析需求,需要将其调整为二维表。

    常见的步骤如下:

    • 时间函数:如将【时间戳】格式化为日期、时间、月份、周几(常见于周分析)等,可通过【FROM_UNIXTIME】【DATE_FORMAT】等函数实现

    • 行列互换:如解决上述的一维表转为二维表的问题,可通过关键字【CASE WHEN】实现

    • 字段的拆分与合并:如将收货地址字段拆为省、市、镇等字段,可通过【CONCAT】【LEFT】【RIGHT】【SUBSTRING】等函数实现

    整体分析

    在开始真正的分析之前,需要进行探索性数据分析(Exploratory Data Analysis,EDA),也就是对现有数据进行整体分析,对现状有大体的了解。更重要的是,通过整体分析,找出业务运营存在的问题,进而提出业务目标,展开后续的深度分析。

    常见的步骤如下:

    • 漏斗分析:如海盗模型AARRR,阿里营销模型AIPL等,通过简单的【COUNT】函数,直接数就可实现

    建立视图

    面对复杂的业务分析,SQL语句也会变得复杂,往往需要不断嵌套。为了减少分析时语句的复杂性、避免重复执行相同语句,可以采用新建视图的方式,将重复性高的语句固定为视图,再在此基础上进行复杂查询。

    • 新建视图可通过【CREATE VIEW 视图名 AS SELECT..】实现

    用户分析

    从整体分析中,明确业务问题、目标后,便可开始进行用户分析。根据分析目的的不同,采用不同的分析方法,而常见的分析方法如下:

    【人货场】分析

    【复购】分析,核心问题在于如何计算“复购”:

    • 用【窗口函数+DENSE_RANK()】统计每个订单是该用户的第几次消费,命名为'N_CONSUME'

    • 第一次消费即为用户“首购订单”,大于等于第二次消费的订单则为“复购订单“

    • 针对复购订单进行统计,即可进行复购分析

    【RFM模型】分析,核心问题在于如何定义阈值及人群划分:

    • 通过【窗口函数】可计算出每个用户的RFM值:

      • R:每个用户最后消费日期,与分析日期相减的天数即为R

      • F:通过复购分析中得出的N_CONSUME,计算最大消费次数即为F

      • M:简单地SUM用户所有消费金额,即为M

    • 阈值:可通过计算所有用户的R,F,M平均值获得

    • 所有用户的RFM值与阈值比较,通过【CASE WHEN】转为 '高'、'低' 两个值

    • 根据RFM高低值通过【CASE WHEN】将所有用户划分到八个人群中

    总结建议

    根据前文进行的分析,即可总结得出的结论。此外,在业务分析中,更重要的是如何结合业务场景来给出可落地的业务建议。具体讨论可参考我另一回答:

    数据分析的结果该如何落地?

    43c877212f0aa1f1c10405ca43eb57f9.png

    展开全文
  • 多表嵌套查询 延迟(懒)加载【了解】 内置缓存【了解】 一级缓存 二级缓存 第一章 Mybatis动态SQL1 什么是动态SQL先来看一个需求 把页面输入的idusername封装到User实体中,并作为条件查询数据库 这个...

    今日内容

    • 动态sql

    • 多表关联查询

    • 多表嵌套查询

    • 延迟(懒)加载【了解】

    • 内置缓存【了解】

      • 一级缓存
      • 二级缓存

    第一章 Mybatis动态SQL

    1 什么是动态SQL

    先来看一个需求

    把页面输入的id和username封装到User实体中,并作为条件查询数据库

    这个时候我们执行的sql就有多种可能

    -- 如果id和用户名不为空
    select * from user where id= #{id} and username = #{username}

    -- 如果只有id
    select * from user where id= #{id}

    -- 如果只有用户名
    select * from user where username = #{username}

    -- 如果id和用户名都为空
    select * from user

    像上面这样, 根据传入的参数不同, 需要执行的SQL的结构就会不同,这就是动态SQL。

    2 环境搭建

    导入必要的jar包,创建实体,配件文件连接数据库

    3 if 和 where 标签

    需求:页面输入的id和username封装到User实体中,并作为条件查询数据库

    接口
    // if 和 where 标签
    public List<User> findByIf(User user);
    映射
    <!--
    if和where标签
    if:条件判断
    where : 动态where,如果没有条件where关键字不在拼接,如果有条件,去掉第一个 and 或是 or
    -->
    <select id="findByIf" parameterType="cn.com.mryhl.domain.User" resultType="cn.com.mryhl.domain.User">
    select * from user
    <where>
    <if test="id != null">
    and id = #{id}
    </if>
    <if test="username != null">
    and username = #{username}
    </if>
    </where>
    </select>
    测试
    public class UserMapperTest {
    private SqlSession sqlSession;
    private UserMapper userMapper;
    @Before
    public void before(){
    // 获取sqlSession
    sqlSession = MyBatisUtils.openSession();
    // 创建UserMapper代理对象
    userMapper = sqlSession.getMapper(UserMapper.class);
    }
    @After
    public void after(){
    // 释放资源
    MyBatisUtils.release(sqlSession);
    }

    /**
    * if和where测试
    */
    @Test
    public void test02() throws Exception {
    // 封装条件
    User user = new User();
    user.setId(41);
    user.setUsername("老王");

    // 执行查询
    List<User> userList = userMapper.findByIf(user);
    System.out.println(userList);

    }
    }
    小结
    * if标签用于单分支条件判断, 相当于java中的if关键字
    * where标签作用
    当where代码块中的条件都不成立的时候, where不在拼接
    当where代码块中的条件至少有一个成立的时候,它会帮你去掉第一个and|or

    4 if 和 set 标签

    需求:动态更新user表数据,如果该属性有值就更新,没有值不做处理

    接口
    // set标签
    public void updateIf(User user);
    映射
    <!--
    set标签
    帮你去掉最后一个逗号
    -->
    <update id="updateIf" parameterType="cn.com.mryhl.domain.User">
    update user
    <set>
    <if test="username != null">
    username = #{username},
    </if>
    <if test="birthday != null">
    birthday = #{birthday},
    </if>
    <if test="sex != null">
    sex = #{sex},
    </if>
    <if test="address != null">
    address = #{address},
    </if>

    </set>
    where id = #{id}
    </update>
    测试
    /**
    * set标签
    */
    @Test
    public void test03() throws Exception {
    // 封装修改内容
    User user = new User();
    user.setId(45);
    user.setUsername("东方盖花");
    // 执行更新
    userMapper.updateIf(user);


    }
    小结
    * set标签作用
    在代码块之前加入一个set关键字
    删除掉代码块中的最后一个逗号(,)

    5 foreach 标签

    需求:根据多个id查询,user对象的集合

    select * from user where id in (41,45,46);
    * <foreach>标签用于遍历集合,它的属性:

    • collection:代表要遍历的集合元素

    • open:代表语句的开始部分

    • close:代表结束部分

    • item:代表遍历集合的每个元素,生成的变量名

    • sperator:代表分隔符

    传递参数三种场景

    • 普通list集合
    • 普通array数组
    • 实体中list | array
    接口
    // 普通list集合
    public List<User> findList(List<Integer> ids);

    // 普通array数组
    public List<User> findArray(Integer[] ids);

    // 实体中的list属性
    public List<User> findQueryVo(QueryVo queryVo);
    映射
    <!--
    foreach标签:普通的list
    collection属性值只能为: list | collection

    -->
    <select id="findList" parameterType="list" resultType="cn.com.mryhl.domain.User">
    select * from user where id in
    <foreach collection="list" open="(" close=")" item="id" separator=",">
    #{id}
    </foreach>
    </select>


    <!--
    foreach标签:普通的数组array
    collection属性值只能为: array

    -->
    <select id="findArray" parameterType="int" resultType="cn.com.mryhl.domain.User">
    select * from user where id in
    <foreach collection="array" open="(" close=")" item="id" separator=",">
    #{id}
    </foreach>
    </select>


    <!--
    foreach标签:实体中的list
    collection属性值只能为: 实体属性名

    -->
    <select id="findQueryVo" parameterType="cn.com.mryhl.domain.QueryVo" resultType="cn.com.mryhl.domain.User">
    <include refid="selectUser"></include> where id in
    <foreach collection="ids" open="(" close=")" item="id" separator=",">
    #{id}
    </foreach>
    </select>



    <!--
    抽取sql片段
    -->
    <sql id="selectUser">
    select * from user
    </sql>
    测试
    /**
    * foreach标签
    */
    @Test
    public void test04() throws Exception {
    // 普通list
    /*ArrayList<Integer> ids = new ArrayList<>();
    ids.add(41);
    ids.add(45);
    ids.add(46);

    List<User> list = userMapper.findList(ids);
    System.out.println(list);*/

    // 普通array
    /*Integer[] ids = {41,45,46};
    List<User> userList = userMapper.findArray(ids);
    System.out.println(userList);*/


    // 实体list
    QueryVo queryVo = new QueryVo();

    List<Integer> ids = new ArrayList<>();
    ids.add(41);
    ids.add(45);
    ids.add(46);
    queryVo.setIds(ids);
    List<User> list = userMapper.findQueryVo(queryVo);
    System.out.println(list);
    }
    小结
    * foreach标签的collection属性根据传输的java类型不同而采用不同的值
    集合:list
    数组:array
    实体:实体的属性名

    6 SQL片段

    应用场景

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

    第二章 Mybatis多表查询

    1 表关系回顾

    在关系型数据库中,表关系分为以下三种:

    * 在数据库中表建立关系:通过主外键关联

    * 在java中实体建立关系:通过属性关联

    * 在mybatis框架中把(多对一)也可以理解(一对一)
    在订单的角度去看,一个订单只从属于一个用户

    今天讲解多表练习

    2 环境搭建

    ① 创建java模块,导入jar包
    ② 准备三个实体类
    // 用户
    public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    }


    // 订单实体类
    public class Order {

    private Integer id;

    private Date ordertime;

    private Double money;

    }

    // 角色
    public class Role {

    private Integer id;

    private String role_name;

    private String role_desc;
    }
    ③ 创建三个接口和三个映射文件
    ④ 创建mybatis核心文件
    ⑤ 导入MybatisUtils工具类
    ⑥ 抽取测试基类
    public class BaseMapperTest {
    protected SqlSession sqlSession;

    @Before
    public void before(){
    // 获取sqlSession
    sqlSession = MyBatisUtils.openSession();


    }
    @After
    public void after(){
    // 释放资源
    MyBatisUtils.release(sqlSession);
    }

    }

    3 一对一(多对一)

    一对一查询模型

    用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

    需求:查询一个订单,与此同时查询出该订单所属的用户

    sql语句

    SELECT * FROM orders o INNER JOIN `user` u ON o.`uid` = u.`id` WHERE o.id = 1
    ① 实体和表映射关系
    ② 创建Order实体
    public class Order {
    private Integer id;
    private Date ordertime;
    private Double money;

    // 一个订单从属于一个用户

    private User user;
    }
    ③ 编写OrderMapper接口
    public interface OrderMapper {

    // 根据订单id查询,返回订单信息和用户信息
    public Order findByIdWithUser(Integer id);
    }
    ④ 编写OrderMapper.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!--引入约束文件,DTD约束头-->
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--根标签 命名空间,与下面语句的id一起组成查询的标识-->
    <mapper namespace="cn.com.mryhl.mapper.OrderMapper">

    <!--一对一手动映射封装-->
    <resultMap id="orderBaseMap" type="cn.com.mryhl.domain.Order">
    <id column="id" property="id"></id>
    <result column="ordertime" property="ordertime"></result>
    <result column="money" property="money"></result>

    <!--
    association 一对一关联封装
    property="user" 订单中的实体属性名
    javaType="com.itheima.domain.User" 该属性对应的java类型
    -->
    <association property="user" javaType="cn.com.mryhl.domain.User">
    <id column="uid" property="id"></id>
    <result column="username" property="username"></result>
    <result column="birthday" property="birthday"></result>
    <result column="sex" property="sex"></result>
    <result column="address" property="address"></result>
    </association>
    </resultMap>
    <!--

    一对一查询
    -->
    <select id="findByIdWithUser" parameterType="int" resultMap="orderBaseMap">
    select * from orders o inner join user u on o.uid = u.id where o.id = #{id}
    </select>

    </mapper>
    ⑤ 测试
    import cn.com.mryhl.domain.Order;
    import cn.com.mryhl.mapper.OrderMapper;
    import org.junit.Test;

    public class OrderMapperTest extends BaseMapperTest {
    /**
    * 一对一测试
    */
    @Test
    public void test01() throws Exception {
    //获取代理对象
    OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
    // 查询
    Order order = orderMapper.findByIdWithUser(1);

    System.out.println(order);
    }
    }

    4 一对多

    一对多查询模型

    用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

    需求:查询一个用户,与此同时查询出该用户具有的订单

    sql语句

    SELECT *,o.id AS oid FROM `user` u INNER JOIN orders o ON u.`id` = o.`uid` WHERE u.id = 41
    ① 实体和表映射关系
    ② 编写User实体
    public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<Order> orderList;
    }
    ③ 编写UserMapper接口
    import cn.com.mryhl.domain.User;

    public interface UserMapper {
    /**
    * 一对多根据用户id,查询用户和订单信息
    */
    public User findByIdWithOrderList(Integer id);

    }
    ④ 编写UserMapper.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!--引入约束文件,DTD约束头-->
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--根标签 命名空间,与下面语句的id一起组成查询的标识-->
    <mapper namespace="cn.com.mryhl.mapper.UserMapper">
    <!--一对多手动映射-->
    <resultMap id="userWithOrderMap" type="cn.com.mryhl.domain.User">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="birthday" property="birthday"></result>
    <result column="sex" property="sex"></result>
    <result column="address" property="address"></result>
    <!--
    一对多封装使用collection标签
    property="orderList" 是user实体的订单属集合名称
    ofType="com.itheima.domain.Order" 封装数据的java类型,可以简单理解为list的泛型
    -->
    <collection property="orderList" ofType="cn.com.mryhl.domain.Order">
    <id column="oid" property="id"></id>
    <result column="ordertime" property="ordertime"></result>
    <result column="money" property="money"></result>
    </collection>

    </resultMap>

    <!--
    一对多根据用户id,查询用户和订单信息
    -->
    <select id="findByIdWithOrderList" parameterType="int" resultMap="userWithOrderMap">
    SELECT *,o.id oid FROM USER u INNER JOIN orders o ON u.`id` = o.`uid` WHERE u.`id` = #{id}
    </select>

    </mapper>
    ⑤ 测试
    import cn.com.mryhl.domain.User;
    import cn.com.mryhl.mapper.UserMapper;
    import org.junit.Test;

    public class UserMapperTest extends BaseMapperTest {
    /**
    * 一对多测试
    */
    @Test
    public void test01() throws Exception {
    // 创建代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    // 根据用户ID查询
    User byIdWithOrderList = userMapper.findByIdWithOrderList(41);
    System.out.println(byIdWithOrderList);

    // 运行结果
    // User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', orderList=[Order{id=1, ordertime=Mon May 20 02:58:02 CST 2019, money=999.5, user=null}, Order{id=3, ordertime=Sat Jun 01 21:00:02 CST 2019, money=1666.0, user=null}]}

    }
    }

    5 多对多

    多对多查询的模型

    用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用

    需求:查询用户同时查询出该用户的所有角色

    注意:多对多实现代码流程跟一对多是一样的,只是sql语句稍有不同

    SELECT * FROM `user` u
    INNER JOIN user_role ur ON u.id = ur.`uid` -- 用户关联中间表
    INNER JOIN role r ON ur.`rid` = r.`id` -- 中间表关联用户
    WHERE u.id = 41
    ① 实体和表映射关系
    ② 编写User和Role实体
    public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<Role> roleList;
    }
    ③ 编写UserMapper接口
    public interface UserMapper {

    // 多对多根据用户id,查询用户和角色信息
    public User findByIdWithRoleList(Integer id);
    }
    ④ 编写UserMapper.xml
    <!--
    多对多手动映射封装
    -->
    <resultMap id="userWithRoleMap" type="cn.com.mryhl.domain.User">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="birthday" property="birthday"></result>
    <result column="sex" property="sex"></result>
    <result column="address" property="address"></result>
    <!--
    多对多手动映射使用collection标签
    property="roleList" 需要封装实体list集合的属性名
    ofType="com.itheima.domain.Role" 该集合泛型的java类型
    -->
    <collection property="roleList" ofType="cn.com.mryhl.domain.Role">
    <id column="rid" property="id"></id>
    <result column="role_name" property="role_name"></result>
    <result column="role_desc" property="role_desc"></result>
    </collection>

    </resultMap>


    <!--
    多对多根据用户id,查询用户和角色信息
    -->
    <select id="findByIdWithRoleList" parameterType="int" resultMap="userWithRoleMap">
    SELECT * FROM `user` u
    INNER JOIN user_role ur ON u.id = ur.`uid`
    INNER JOIN role r ON ur.`rid` = r.`id`
    WHERE u.id = #{id}
    </select>
    ⑤ 测试
    /**
    * 多对多测试
    */
    @Test
    public void test02() throws Exception {
    // 创建代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 根据用户id查询
    User byIdWithRoleList = userMapper.findByIdWithRoleList(41);
    System.out.println(byIdWithRoleList);
    // User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=[Role{id=1, role_name='院长', role_desc='管理整个学院'}, Role{id=2, role_name='总裁', role_desc='管理整个公司'}], orderList=null}
    }

    6 知识小结

    一对一配置:使用<resultMap>+<association>做配置
    association:
    property:关联的实体属性名
    javaType:关联的实体类型

    一对多配置:使用<resultMap>+<collection>做配置
    collection:
    property:关联的集合属性名
    ofType:关联的集合泛型类型

    多对多配置:使用<resultMap>+<collection>做配置
    collection:
    property:关联的集合属性名
    ofType:关联的集合泛型类型

    多对多的配置跟一对多很相似,难度在于SQL语句的编写。

    第三章 MyBatis嵌套查询

    1 什么是嵌套查询

    嵌套查询就是将原来多表查询中的联合查询语句拆成单个表的查询,再使用mybatis的语法嵌套在一起。

    需求:查询一个订单,与此同时查询出该订单所属的用户

    * 关联查询:

    SELECT * FROM orders o INNER JOIN `user` u ON o.`uid` = u.`id` WHERE o.id = 1
    * 缺点
    sql语句比较复杂
    多表查询会产生笛卡尔积

    * 嵌套查询:
    1.先根据订单id查询订单信息
    select * from orders where id = 1;
    2.根据订单信息中的uid再去查询用户信息
    select * from user where id = 41;
    3.再由mybatis进行组合嵌套

    * 优点
    sql写起来简单
    嵌套查询不会出现笛卡尔积

    * 缺点
    需要编写二个单体映射封装,在进行嵌套组合,步骤较为繁琐

    2 环境搭建

    导入必要的jar包,创建实体,配件文件连接数据库

    3 一对一

    需求:查询一个订单,与此同时查询出该订单所属的用户

    sql语句

    -- 1.先根据订单id查询订单信息
    select * from orders where id = 1;
    -- 2.根据订单信息中的uid再去查询用户信息
    select * from user where id = 41;
    -- 3.再由mybatis进行组合嵌套
    ① OrderMapper接口
    public interface OrderMapper {

    // 一对一嵌套查询
    public Order findByIdWithUser(Integer id);

    }
    ② OrderMapper映射
    <?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.com.mryhl.mapper.OrderMapper">


    <!--一对一嵌套查询-->
    <select id="findByIdWithUser" parameterType="integer" resultType="com.itheima.domain.Order">
    select * from orders where id = #{id}
    </select>
    </mapper>
    ③ UserMapper接口
    public interface UserMapper {


    // 根据用户id查询用户信息
    public User findById(Integer id);
    }
    ④ UserMapper映射
    <?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.com.mryhl.mapper.UserMapper">


    <select id="findById" parameterType="integer" resultType="cn.com.mryhl.domain.User">
    select * from user where id = #{id}
    </select>

    </mapper>
    ⑤ mybatis嵌套组合
    <?xml version="1.0" encoding="UTF-8" ?>
    <!--引入约束文件,DTD约束头-->
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--根标签 命名空间,与下面语句的id一起组成查询的标识-->
    <mapper namespace="cn.com.mryhl.mapper.OrderMapper">

    <!-- 一对一查询手动封装-->
    <resultMap id="orderWithUserMap" type="cn.com.mryhl.domain.Order">
    <id column="id" property="id"></id>
    <result column="ordertime" property="ordertime"></result>
    <result column="money" property="money"></result>
    <!--
    一对一嵌套
    column="uid" 就是订单表的查询结果 uid作为条件
    select="cn.com.mryhl.mapper.UserMapper.findById" 去调用(UserMapper中findById方法)实现单表查询
    最后通过association组合嵌套,封装到 Order实体中user属性汇总
    -->
    <association property="user" column="uid" select="cn.com.mryhl.mapper.UserMapper.findById"></association>
    </resultMap>
    <!--一对一嵌套查询-->
    <select id="findByIdWithUser" parameterType="int" resultMap="orderWithUserMap">
    select * from orders where id = #{id}
    </select>



    </mapper>
    ⑥ 测试
    public class OrderMapperTest extends BaseMapperTest {

    /**

    }
    ⑦ 嵌套关系
    [2020-09-07 18:09:14,885] DEBUG r.OrderMapper.findByIdWithUser  - ==>  Preparing: select * from orders where id = ? 
    [2020-09-07 18:09:14,975] DEBUG r.OrderMapper.findByIdWithUser - ==> Parameters: 1(Integer)
    [2020-09-07 18:09:15,231] DEBUG r.OrderMapper.findByIdWithUser - <== Total: 1
    [2020-09-07 18:09:15,231] DEBUG yhl.mapper.UserMapper.findById - ==> Preparing: select * from user where id = ?
    [2020-09-07 18:09:15,232] DEBUG yhl.mapper.UserMapper.findById - ==> Parameters: 41(Integer)
    [2020-09-07 18:09:15,256] DEBUG yhl.mapper.UserMapper.findById - <== Total: 1
    Order{id=1, ordertime=Mon May 20 02:58:02 CST 2019, money=999.5, user=User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=null}}

    4 一对多

    需求:查询一个用户,与此同时查询出该用户具有的订单

    sql语句

    -- 1.先根据用户id查询用户信息
    select * from user where id = 41;

    -- 2.再根据用户id查询订单列表
    select * from orders where uid = 41;

    -- 3.最后由mybatis进行组合嵌套
    ① UserMapper接口
    // 一对多嵌套查询
    public User findByIdWithOrderList(Integer id);
    ② UserMapper映射
    <select id="findByIdWithOrderList" parameterType="integer" resultType="cn.com.mryhl.domain.User">
    select * from user where id = #{id}
    </select>
    ③ OrderMapper接口
    // 根据uid查询订单列表
    public List<Order> findByUid(Integer uid);
    ④ OrderMapper映射
    <!--一对多-->
    <select id="findByUid" parameterType="int" resultType="cn.com.mryhl.domain.Order">
    select * from orders where uid = #{uid}
    </select>
    ⑤ mybatis嵌套组合

    用户嵌套订单,UserMapper.xml

    <!-- 一对多 -->
    <!--手动映射封装-->
    <resultMap id="userWithOrderMap" type="cn.com.mryhl.domain.User">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="birthday" property="birthday"></result>
    <result column="sex" property="sex"></result>
    <result column="address" property="address"></result>
    <!--
    一对多嵌套
    column="id" 用户表查询结果作为条件
    select="cn.com.mryhl.mapper.OrderMapper.findByUid" 调用(OerderMapper中findByUid方法)查询 订单列表
    最后由mybatis组合嵌套封装到 user实体的orderList属性中

    -->
    <collection property="orderList" column="id" select="cn.com.mryhl.mapper.OrderMapper.findByUid"></collection>
    </resultMap>
    <select id="findByIdWithOrderList" parameterType="int" resultMap="userWithOrderMap">
    select * from user where id = #{id}
    </select>
    ⑥ 测试
    /**
    * 一对多测试
    */
    @Test
    public void test01() throws Exception {
    // 创建代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 根据用户ID查询
    User byIdWithOrderList = mapper.findByIdWithOrderList(41);
    System.out.println(byIdWithOrderList);

    // User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=[Order{id=1, ordertime=Mon May 20 02:58:02 CST 2019, money=999.5, user=null}, Order{id=3, ordertime=Sat Jun 01 21:00:02 CST 2019, money=1666.0, user=null}]}
    }
    ⑦ 嵌套关系
    [2020-09-07 18:18:48,356] DEBUG source.pooled.PooledDataSource  - Created connection 736778932.
    [2020-09-07 18:18:48,356] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2bea5ab4]
    [2020-09-07 18:18:48,365] DEBUG erMapper.findByIdWithOrderList - ==> Preparing: select * from user where id = ?
    [2020-09-07 18:18:48,456] DEBUG erMapper.findByIdWithOrderList - ==> Parameters: 41(Integer)
    [2020-09-07 18:18:48,644] DEBUG erMapper.findByIdWithOrderList - <== Total: 1
    [2020-09-07 18:18:48,645] DEBUG l.mapper.OrderMapper.findByUid - ==> Preparing: select * from orders where uid = ?
    [2020-09-07 18:18:48,646] DEBUG l.mapper.OrderMapper.findByUid - ==> Parameters: 41(Integer)
    [2020-09-07 18:18:48,652] DEBUG l.mapper.OrderMapper.findByUid - <== Total: 2
    User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=[Order{id=1, ordertime=Mon May 20 02:58:02 CST 2019, money=999.5, user=null}, Order{id=3, ordertime=Sat Jun 01 21:00:02 CST 2019, money=1666.0, user=null}]}

    5 多对多

    需求:查询用户同时查询出该用户的所有角色

    站在用户一方,此用户具有多个角色,实现步骤和一对多一样

    sql语句

    -- 1.先根据用户id查询用户信息
    select * from user where id = 41;
    -- 2.再根据用户id查询角色列表
    select * from role r inner join user_role ur on ur.rid = r.id where ur.uid= 41;
    -- 3.最后由mybatis进行组合嵌套
    ① UserMapper接口
    /**
    * 多对多
    */
    public User findByIdWithRoleList(Integer id);
    ② UserMapper映射
    <select id="findByIdWithRoleList" parameterType="integer" resultType="cn.com.mryhl.domain.User">
    select * from user where id = #{id}
    </select>
    ③ RoleMapper接口
    import cn.com.mryhl.domain.Role;

    import java.util.List;

    public interface RoleMapper {
    /**
    * 根据用户id查询角色列表
    */
    public List<Role> findByUid(Integer uid);
    }
    ④ RoleMapper映射
    <?xml version="1.0" encoding="UTF-8" ?>
    <!--引入约束文件,DTD约束头-->
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--根标签 命名空间,与下面语句的id一起组成查询的标识-->
    <mapper namespace="cn.com.mryhl.mapper.RoleMapper">
    <!--多对多查询-->
    <select id="findByUid" parameterType="int" resultType="cn.com.mryhl.domain.Role">
    select * from role r inner join user_role ur on ur.rid = r.id where ur.uid = #{uid}
    </select>
    </mapper>
    ⑤ mybatis嵌套组合

    用户嵌套角色,定位UserMapper.xml

    <!--多对多-->
    <resultMap id="userWithRoleMap" type="cn.com.mryhl.domain.User">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="birthday" property="birthday"></result>
    <result column="sex" property="sex"></result>
    <result column="address" property="address"></result>
    <!--多对多嵌套
    column="id" 用户表查询结果作为条件
    select="cn.com.mryhl.mapper.RoleMapper.findByUid" 去调用(RoleMapper中findByUid方法) 查询角色列表
    最后由mybatis组合嵌套,封装到User实体的roleList属性中
    -->
    <collection property="roleList" column="id" select="cn.com.mryhl.mapper.RoleMapper.findByUid"></collection>


    </resultMap>


    <select id="findByIdWithRoleList" parameterType="int" resultMap="userWithRoleMap">
    select * from user where id = #{id}
    </select>
    ⑥ 测试
    /**
    * 多对多测试
    */
    @Test
    public void test02() throws Exception {
    // 创建代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 根据用户id查询
    User byIdWithRoleList = userMapper.findByIdWithRoleList(41);
    System.out.println(byIdWithRoleList);

    // User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=[Role{id=1, role_name='院长', role_desc='管理整个学院'}, Role{id=2, role_name='总裁', role_desc='管理整个公司'}], orderList=null}
    }
    ⑦ 嵌套关系
    [2020-09-07 19:56:26,865] DEBUG source.pooled.PooledDataSource  - Created connection 736778932.
    [2020-09-07 19:56:26,865] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2bea5ab4]
    [2020-09-07 19:56:26,887] DEBUG serMapper.findByIdWithRoleList - ==> Preparing: select * from user where id = ?
    [2020-09-07 19:56:27,057] DEBUG serMapper.findByIdWithRoleList - ==> Parameters: 41(Integer)
    [2020-09-07 19:56:27,236] DEBUG serMapper.findByIdWithRoleList - <== Total: 1
    [2020-09-07 19:56:27,237] DEBUG hl.mapper.RoleMapper.findByUid - ==> Preparing: select * from role r inner join user_role ur on ur.rid = r.id where ur.uid = ?
    [2020-09-07 19:56:27,237] DEBUG hl.mapper.RoleMapper.findByUid - ==> Parameters: 41(Integer)
    [2020-09-07 19:56:27,242] DEBUG hl.mapper.RoleMapper.findByUid - <== Total: 2
    User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=[Role{id=1, role_name='院长', role_desc='管理整个学院'}, Role{id=2, role_name='总裁', role_desc='管理整个公司'}], orderList=null}

    6 知识小结

    一对一配置:使用<resultMap>+<association>做配置,通过column条件,执行select查询

    一对多配置:使用<resultMap>+<collection>做配置,通过column条件,执行select查询

    多对多配置:使用<resultMap>+<collection>做配置,通过column条件,执行select查询



    总结一句话:二张表数据过大就使用嵌套查询,否则还是关联查询足以....

    第四章 MyBatis加载策略

    1 什么是延迟加载?

    问题

    ​ 通过前面的学习,我们已经掌握了Mybatis中一对一,一对多,多对多关系的配置及实现,可以实现对象的关联查询。实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的订单信息。此时就是我们所说的延迟加载。

    举个栗子

    * 在一对多中,当我们有一个用户,它有个100个订单
    在查询用户的时候,要不要把关联的订单查出来?
    在查询订单的时候,要不要把关联的用户查出来?

    * 回答
    在查询用户时,用户下的订单应该是,什么时候用,什么时候查询。【延迟(懒)加载】
    在查询订单时,订单所属的用户信息应该是随着订单一起查询出来。

    延迟加载

    ​ 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

    * 在多表中:
    一对多,多对多:通常情况下采用延迟加载
    一对一:通常情况下采用立即加载

    * 注意:
    延迟加载是基于嵌套查询来实现的

    2 配置延迟加载

    2.1 全局延迟加载

    在Mybatis的核心配置文件中可以使用setting标签开启全局的加载策略

    <!--settings设置-->
    <settings>
    <!--开启全局延迟加载功能了-->
    <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

    2.2 局部延迟加载

    ​ 在Mapper映射文件中在association和collection标签中都有一个fetchType属性,通过修改它的值,可以修改局部的加载策略

    fetchType 属性
    eager 立即加载
    lazy 延迟加载
    <association fetchType="eager"></association>
    <collection fetchType="eager"></collection>

    注意:局部延迟加载优先级高于全局延迟加载,通常是为了覆盖全局延迟来设置的方式

    3 触发加载【了解】

    ​ 大家在配置了延迟加载策略后,发现即使没有调用关联对象的任何方法,但是在你调用当前对象的equals、clone、hashCode、toString方法时也会触发关联对象的查询。

    <!--settings设置-->
    <settings>
    <!--开启全局延迟加载功能了-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--关闭四个默认触发关联查询的功能,只有调用getOrderList()时才会触发关联查询-->
    <setting name="lazyLoadTriggerMethods" value=""></setting>
    </settings>

    第五章 MyBatis缓存

    1 什么是缓存

    缓存就是计算机内存中的一块区域

    为什么要使用缓存吗?

    为了提高查询效率,减少磁盘IO,降低数据库访问压力

    什么样的数据适合做缓存?

    经常访问但又不经常修改的数据

    缓存是用来提高查询效率的,所有的持久层框架基本上都有缓存机制
    Mybatis也提供了缓存策略,分为一级缓存,二级缓存

    2 一级缓存

    1.1 介绍

    MyBatis一级缓存是:SqlSession级别的缓存,默认开启

    1.2 验证

    import cn.com.mryhl.domain.User;
    import cn.com.mryhl.mapper.UserMapper;
    import cn.com.mryhl.util.MyBatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;

    public class UserMapperTest extends BaseMapperTest {
    /**
    * 一对多测试
    */
    @Test
    public void test01() throws Exception {
    // 创建代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 根据用户ID查询
    User byIdWithOrderList = mapper.findByIdWithOrderList(41);
    System.out.println(byIdWithOrderList);

    // User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=[Order{id=1, ordertime=Mon May 20 02:58:02 CST 2019, money=999.5, user=null}, Order{id=3, ordertime=Sat Jun 01 21:00:02 CST 2019, money=1666.0, user=null}]}
    }


    /**
    * 多对多测试
    */
    @Test
    public void test02() throws Exception {
    // 创建代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 根据用户id查询
    User byIdWithRoleList = userMapper.findByIdWithRoleList(41);
    System.out.println(byIdWithRoleList);

    // User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=[Role{id=1, role_name='院长', role_desc='管理整个学院'}, Role{id=2, role_name='总裁', role_desc='管理整个公司'}], orderList=null}
    }


    /**
    * 测试一级缓存
    */
    @Test
    public void test03() throws Exception {
    SqlSession sqlSession = MyBatisUtils.openSession();
    // 进行第一次查询
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user1 = mapper.findById(41);
    System.out.println(user1);

    // 如果加上清除缓存,将出现两次查询语句
    sqlSession.clearCache();

    // 第二次查询
    UserMapper mapper2 = sqlSession.getMapper(UserMapper.class);
    User user2 = mapper2.findById(41);
    System.out.println(user2);

    // 关闭sqlSession
    MyBatisUtils.release(sqlSession);

    /*
    [2020-09-07 20:09:42,277] DEBUG source.pooled.PooledDataSource - Created connection 736778932.
    [2020-09-07 20:09:42,277] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2bea5ab4]
    [2020-09-07 20:09:42,285] DEBUG yhl.mapper.UserMapper.findById - ==> Preparing: select * from user where id = ?
    [2020-09-07 20:09:42,344] DEBUG yhl.mapper.UserMapper.findById - ==> Parameters: 41(Integer)
    [2020-09-07 20:09:42,368] DEBUG yhl.mapper.UserMapper.findById - <== Total: 1
    User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=null}
    User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=null}
    只执行了一次查询语句
    */
    /*
    加入清理缓存后的结果
    [2020-09-07 20:11:27,470] DEBUG source.pooled.PooledDataSource - Created connection 736778932.
    [2020-09-07 20:11:27,471] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2bea5ab4]
    [2020-09-07 20:11:27,480] DEBUG yhl.mapper.UserMapper.findById - ==> Preparing: select * from user where id = ?
    [2020-09-07 20:11:27,544] DEBUG yhl.mapper.UserMapper.findById - ==> Parameters: 41(Integer)
    [2020-09-07 20:11:27,570] DEBUG yhl.mapper.UserMapper.findById - <== Total: 1
    User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=null}
    [2020-09-07 20:11:27,571] DEBUG yhl.mapper.UserMapper.findById - ==> Preparing: select * from user where id = ?
    [2020-09-07 20:11:27,572] DEBUG yhl.mapper.UserMapper.findById - ==> Parameters: 41(Integer)
    [2020-09-07 20:11:27,573] DEBUG yhl.mapper.UserMapper.findById - <== Total: 1
    User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=null}
    [2020-09-07 20:11:27,574] DEBUG ansaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2bea5ab4]
    [2020-09-07 20:11:27,574] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2bea5ab4]
    [2020-09-07 20:11:27,574] DEBUG source.pooled.PooledDataSource - Returned connection 736778932 to pool.

    */
    }
    }

    1.3 分析

    ​ 一级缓存是SqlSession范围的缓存,不同的sqlsession之间的缓存区域是互相不影响的,执行SqlSession的C(增加)U(更新)D(删除)操作,或者调用clearCache()、commit()、close()方法,都会清空缓存

    3 二级缓存

    2.1 介绍

    ​ MyBatis的二级缓存虽然是默认开启的,但需要在映射文件中配置<cache/>标签才能使用,而且要求实体类的必须实现序列化接口

    2.2 验证

    /**
    * 测试二级缓存
    */
    @Test
    public void test04() throws Exception {
    // 模拟用户一
    SqlSession sqlSession1 = MyBatisUtils.openSession();
    // 进行第一次查询
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    User user1 = mapper1.findById(41);
    System.out.println(user1);

    // 关闭sqlSession
    MyBatisUtils.release(sqlSession1);


    // 模拟用户二
    SqlSession sqlSession2 = MyBatisUtils.openSession();
    // 进行第一次查询
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    User user2 = mapper2.findById(41);
    System.out.println(user2);

    // 关闭sqlSession
    MyBatisUtils.release(sqlSession2);
    // user需要序列化
    /*
    [2020-09-07 20:23:24,775] DEBUG source.pooled.PooledDataSource - Created connection 1848415041.
    [2020-09-07 20:23:24,775] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6e2c9341]
    [2020-09-07 20:23:24,783] DEBUG yhl.mapper.UserMapper.findById - ==> Preparing: select * from user where id = ?
    [2020-09-07 20:23:24,846] DEBUG yhl.mapper.UserMapper.findById - ==> Parameters: 41(Integer)
    [2020-09-07 20:23:24,876] DEBUG yhl.mapper.UserMapper.findById - <== Total: 1
    User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=null}
    [2020-09-07 20:23:24,889] DEBUG ansaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6e2c9341]
    [2020-09-07 20:23:24,889] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6e2c9341]
    [2020-09-07 20:23:24,889] DEBUG source.pooled.PooledDataSource - Returned connection 1848415041 to pool.
    [2020-09-07 20:23:24,967] DEBUG cn.com.mryhl.mapper.UserMapper - Cache Hit Ratio [cn.com.mryhl.mapper.UserMapper]: 0.5
    User{id=41, username='老王', birthday=Mon May 27 17:47:08 CST 2019, sex='男', address='北京', roleList=null, orderList=null}

    */

    }

    1.3 分析

    ​ 二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

    二级缓存相比一级缓存的范围更大(按namespace来划分)

    4 知识小结

    1. mybatis的缓存,都不需要我们手动存储和获取数据。mybatis自动维护的。

    2. 使用mybatis,如果是中小型项目,使用自带缓存的机制是可以满足需求的。如果是大型(分布式)项目,mybatis的缓存灵活性不足,需要使用第三方的缓存技术(redis)解决问题。

    总结

    一 Mybatis动态SQL
    <if>
    条件判断
    <where>
    去掉第一个条件的 前 and | or
    <set>
    去掉最后一个条件的 逗号
    <foreach>
    普通类型 List,collection属性:list
    普通类型 Array,collection属性:array
    复杂类型实体对象,collection属性:实体的属性名
    <sql>
    将公共代码抽取,实现复用性
    二 Mybatis多表查询
    表关系回顾


    一对一(多对一)
    使用<resultMap>+<association>做配置
    property
    关联实体属性名
    javaType
    关联实体属性类型
    一对多
    使用<resultMap>+<collection>做配置
    property
    关联集合属性名
    ofType
    关联集合泛型类型
    多对多
    使用<resultMap>+<collection>做配置
    property
    关联集合属性名
    ofType
    关联集合泛型类型
    三 MyBatis嵌套查询
    一对一配置:使用<resultMap>+<association>做配置,通过column条件,执行select查询
    一对多配置:使用<resultMap>+<collection>做配置,通过column条件,执行select查询
    多对多配置:使用<resultMap>+<collection>做配置,通过column条件,执行select查询
    思路
    1)先查询用户表
    2)在查询订单列表
    3)再由mybatis组合嵌套
    四 MyBatis加载策略
    全局延迟加载
    <settings>
    <!--开启全局延迟加载功能-->
    <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
    局部延迟加载
    <association> 和 <collection> 标签
    fetchType="lazy | eager"
    局部的加载策略优先级高于全局的加载策略。
    五 MyBatis缓存
    一级缓存
    是SqlSession级别的缓存
    二级缓存
    是mapper映射级别的缓存,需要手动配置且实体类需要实现serializable接口
    展开全文
  • select [<表名1>.]<列名>,[表名2.]<列名2> from <表名1>,<表名2>...select Student.*, SC.* from Student, ...查询选修01号课程且成绩在80分以上的所有学生的学号姓名 select Stude...
  • Mybatis多表联合查询,嵌套查询,动态SQL Mybatis多表联合查询 一对一 一对一查询:通过一方关联查询出另外一方的关系数据 创建数据库相关数据表 创建需要关联查询的实体类,里面包含相关的属性 //丈夫查妻子 ...
  • 有学生老师两个实体集,假设学生与辅导员老师的关系是一位老师教个学生,一个学生只有一位辅导员老师。建表、建maven项目、导入依赖、写配置文件等等 1、对一 一个学生只有一位辅导员老师,则学生的实体类...
  • 以下是课本上关于多表查询 操作的一些试题 老规矩,先写总结: 多表连接中注意事项小结: 多表连接时候,当属性列在查询的多个表里面是唯一的就可以省略表名前缀,否则必须加上表名前缀; 一张表进行自身连接时,...
  • 一:student_info 学号 姓名 性别 出生年月 家庭住址 备注 0001 张三 男 1981-8-9 北京 NULL ...在GRADE中查找80-90份的学生学号分数 select 学号,分数 from grade where 分数 between 80 and 90 在GRADE
  • 需要使用column属性,用于指定第二步查询的输入参数,第二步查询只有一个输入参数时,使用第一步查询结果的column名称即可; 2.mapper接口 package lz.cn.mapper; import lz.cn.vo.Employee; public interface ...
  • 一:student_info 学号 姓名 性别 出生年月 家庭住址 备注 0001 张三 男 1981-8-9 北京 NULL ...在GRADE中查找80-90份的学生学号分数 select 学号,分数 from grade where 分数 between 80 and 90 在GRADE
  • sql语句会用到许多查询语句,如果牵扯到... 嵌套查询:   select * from bi_BillItem where BillID in (select BillID from bi_Bill where IsArchived='0' and IsCheckOuting='2') group by menuId,MenuPri...
  • 有联合查询和嵌套查询: 联合查询是几个联合查询,只查询一次,通过在 resultMap里面配置 association 节点配置一 对一的类就可以完成; 嵌套查询是先查一个,根据这个里面的结果的外键id,去再另外一个里面...
  • 关联查询、嵌套查询和公共表达式 Correlated Queries, Nested Queries, and Common Table Expressions在本章中,您将学习如何使用嵌套和关联子查询从关系数据库中提取更复杂的数据。您还将了解公共表达式,以及...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 841
精华内容 336
关键字:

多表查询和嵌套查询