Optional Note

MySQL

SQL

  1. 什么是SQL

结构化查询语言

  1. 通用语法
  1. 单行或多行, 以分号结尾.

  2. 用空格和缩进增强可读性

  3. 不区分大小写, 关键字建议用大写.

  4. 三种注释

    -- 单行注释
    # 单行注释
    /* */多行注释
    
  1. SQL分类
  1. DDL(Data definition Language) 数据定义语言, 定义数据库对象. 例如: 数据库, 表, 列等. 关键字: create, drop, alter等.

  2. DML(Data Manipulation Language) 桑菊操作语言 用来对数据库中的表的数据进行增删改, 关键字: insert, delete, update等.

  3. DQL(Data Query Language)数据查询语言 用来查询数据库中表的记录. 关键字: select, where等.

  4. DCL(Data Control Language)数据控制语言.用来定义数据库的访问级别以及创建用户. 关键字: grant, revoke等.

DDL: 操作数据库, 表

  1. 操作数据库: CRUD
  1. C(Create): 创建

    create database if not exists db character set gbk;

  2. R(Retrieve): 查询

    • 查询所有数据库名称: show databases;
    • 查询创建数据库语句: show create database mysql;
  3. U(Update): 修改

    • 修改数据库字符集: alter database db_name character set utf8;
  4. D(Delete): 删除

    • 删除数据库: drop database if exists db_name;
  5. 使用数据库

    • 查询当前数据库: select database();
    • 使用数据库: use db_name;
  1. 操作表
  1. C(Create): 创建

    create table 表名(

    ​ 列名1 数据类型1,

    ​ 列名2 数据类型2

    ​ ...

    ​ 列名n 数据类型n

    );

    数据库类型:

    1. int类型

    2. double(5, 2) 小数类型, 共五位, 小数点后保留两位.

    3. date 日期类型,只包含年月日 yyyy-MM-dd

    4. datetime 年月日时分秒 yyyy-MM-dd HH:mm:ss

    5. timestamp 时间戳,年月日时分秒 yyyy-MM-dd HH:mm:ss

      如果将来不给这个字段赋值或者赋值为null, 则默认使用当前系统时间自动赋值.

    6. varchar(20) 字符串类型,最大20个字符

//创建表
create table student(
	id int,
	name varchar(32),
	age int,
	score double(4,1),
	birthday date,
	insert_time timestamp
);
//复制表
create table 表名 like 被复制表名;
  1. R(Retrieve): 查询

    • 查询所有表名称: show tables;
    • 查询表结构: desc table_name;
  2. U(Update): 修改

    • 修改表名: alter table 表名 rename to 新表名;
    • 修改表的字符集: alter table 表名 character set utf8;
    • 添加一列: alter table 表名 add 列名 数据类型;
    • 修改列名称,类型: alter table 表名 change 列名 新列名 新数据类型;
    • 修改类型: alter table 表名 modify 列名 新数据类型;
    • 删除列: alter table 表名 drop 列名;
  3. D(Delete): 删除

    • drop table if exists 表名;

DML: 增删改表中的数据

  1. 添加数据

    insert into 表名(列名1, 列名2,...) values (值1, 值2, ...);
    
  2. 删除数据

    delete from 表名 [where 条件];
    TRUNCATE TABLE 表名; //删除表, 并创建一个一模一样的表
    
  3. 修改数据

    update 表名 set 列名1=值1, 列名2=值2,...[where 条件];
    

DQL: 查询表中的记录

  1. 语法

    select 
    	字段列表
    from
    	表名列表
    where
    	条件列表
    group by
    	分组字段
    having
    	分组之后的条件
    order by
    	排序字段
    limit
    	分页
    
  2. 基础查询

    • 多个字段的查询

      select 字段1, 字段2... from 表名;
      
    • 去除重复

      SELECT DISTINCT 字段名 FROM 表名;
      
    • 计算列

      -- 计算math 和English分数之和
      SELECT `name`, english, math+english FROM student;
      -- 如果有null参数的运算, 计算结果都为null,要用IFNULL设置默认值
      SELECT `name`, math, english, math+IFNULL(english,0) FROM student;
      
    • 起别名

      SELECT `name`, math, english, math+IFNULL(english,0) as 总分 FROM student;
      SELECT `name` 名字, math 数学, english 英语, math+IFNULL(english,0) 总分 FROM student;
      -- AS 可以省略
      
  3. 条件查询

    1. where子句后跟条件

    2. 运算符

      > < <= >= = <>
      BETWEEN...AND
      IN(集合)
      LIKE _单个任意字符 %多个任意字符
      IS NULL
      AND 或 &&
      OR 或 ||
      NOT 或 !
      
      -- 查询年龄大于20岁
      SELECT * FROM student WHERE age > 20;
      SELECT * FROM student WHERE age >= 20;
      -- 查询年龄等于20岁
      SELECT * FROM student WHERE age = 20;
      -- 查询年龄不等于20岁
      SELECT * FROM student WHERE age != 20;
      SELECT * FROM student WHERE age <> 20;
      -- 查询大于等于20, 小于等于30
      SELECT * FROM student WHERE age >= 20 && age <= 30;
      SELECT * FROM student WHERE age >= 20 AND age <= 30;
      SELECT * FROM student WHERE age BETWEEN 20 AND 30;
      -- 查询年龄22岁, 18岁, 25岁的信息
      SELECT * FROM student WHERE age = 22 OR age = 18 OR age = 25;
      SELECT * FROM student WHERE age IN(22, 18, 25);
      -- 查询英语成绩是null的记录, null不能使用=或者!=判断
      SELECT * FROM student WHERE english = NULL; -- 错误
      SELECT * FROM student WHERE english IS NULL;
      SELECT * FROM student WHERE english IS NOT NULL;
      
      -- 查询姓马的人
      SELECT * FROM student WHERE `name` LIKE '马%';
      -- 第二个字是化的人
      SELECT * FROM student WHERE `name` LIKE "_化%";
      -- 姓名是三个字的人
      SELECT * FROM student WHERE `name` LIKE "___";
      -- 查询姓名中包含马的人
      SELECT * FROM student WHERE `name` LIKE "%马%";
      

目录

  1. DQL: 查询语句
    1. 排序查询
    2. 聚合函数
    3. 分组查询
    4. 分页查询
  2. 约束
  3. 多表时间的关系
  4. 范式
  5. 数据库备份和还原

DQL: 查询语句

  1. 排序查询

    order by 子句;
    	order by 排序字段1 排序方式2, 排序字段2 排序方式2...
    排序方式: 
    	ASC: 升序
    	DESC: 降序
    -- 按照数学排序, 如果数学一样, 则按照英语排序
    SELECT * FROM student ORDER BY math, english;
    -- 如果有多个排序条件, 则当前面的条件值一样时, 才会判断第二个条件.
    
  2. 聚合函数: 将一列数据做整体, 进行纵向计算.

    1. count: 计算个数

      一般选择非空的列, 例如: 主键

    2. max: 计算最大值

    3. min: 计算最小值

    4. avg: 计算平均值

      聚合函数的计算, 排除null值了. 解决方法:

      1. 选择不包含非空的列
      2. IFNULL函数
      SELECT COUNT(`name`) FROM student;
      
      SELECT COUNT(IFNULL(english,0)) FROM student;
      
      SELECT MAX(math) FROM student;
      
      SELECT MIN(math) FROM student;
      
      SELECT SUM(math) FROM student;
      
      SELECT AVG(math) FROM student;
      
  3. 分组查询

    group by 分组字段;
    

    分组之后可以查询的字段: 分组字段, 聚合函数

    where 和 having区别:

    ​ where在分组之前限定, 如果不满足条件, 不参与分组

    ​ having在分组之后限定, 如果不满足having条件, 不被查询

    -- 按性别分组并计算平均分
    SELECT sex, AVG(math), COUNT(id) FROM student GROUP BY sex;
    
    -- 按性别分组并计算分数大于70分的平均分
    SELECT sex, AVG(math), COUNT(id) FROM student WHERE math > 70 GROUP BY sex;
    
    -- 按性别分组并计算分数大于70分的平均分, 并且分组之后分数大于2
    SELECT sex, AVG(math), COUNT(id) FROM student WHERE math > 70 GROUP BY sex HAVING COUNT(id) > 2;
    SELECT sex, AVG(math), COUNT(id) 人数 FROM student WHERE math > 70 GROUP BY sex HAVING 人数 > 2;
    
  4. 分页查询

    limit 开始索引, 查询的条数
    
    -- 每页显示三条记录
    SELECT * FROM student LIMIT 0,3; -- 第一页
    SELECT * FROM student LIMIT 3,3; -- 第二页
    -- 公式: 开始的索引 = (当前页码 - 1) * 每页数
    SELECT * FROM student LIMIT 6,3;  -- 第三页
    

    limit是一个MySQL的方言

约束

  • 概念: 对表中的数据进行限定, 保证数据的正确性, 有效性和完整性.

  • 分类:

    1. 主键约束: primary key
    2. 非空约束: not null
    3. 唯一约束: unique
    4. 外键约束: foreign key

    主键约束

    1. 非空且唯一
    2. 一个表只能有一个字段是主键
    3. 主键就是表中记录的唯一标识
    1. 创建表时添加主键

      CREATE TABLE stu(id INT PRIMARY KEY, name VARCHAR(20));
      
    2. 创建表之后添加主键

      ALTER TABLE stu MODIFY id INT PRIMARY KEY;
      
    3. 删除主键

      ALTER TABLE stu MODIFY id int; -- 错误,不生效
      ALTER TABLE stu DROP PRIMARY KEY;
      
    4. 自动增长

      • 如果某一列是数值类型, 使用auto_increment可以用来完成值的自动增长.

        -- 创建表时添加自动增长
        CREATE TABLE stu(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20));
        
        -- 删除自动增长
        ALTER TABLE stu MODIFY id INT;
        
        -- 添加自动增长
        ALTER table stu MODIFY id INT PRIMARY KEY AUTO_INCREMENT;
        

    非空约束: not null

    1. 创建表时添加约束

      CREATE TABLE stu(id INT, name VARCHAR(20) NOT NULL);
      
    2. 创建表完之后添加非空约束

      ALTER TABLE stu MODIFY `name` VARCHAR(20) NOT NULL;
      ALTER TABLE stu CHANGE `name` `name` VARCHAR(20) NOT NULL;
      
    3. 删除非空约束

      ALTER TABLE stu MODIFY `name` VARCHAR(20);
      ALTER TABLE stu CHANGE `name` `name` VARCHAR(20);
      

    唯一约束: unique

    同一列的值不能重复

    1. 创建表时添加唯一约束

      CREATE TABLE stu(id INT, phone VARCHAR(20) UNIQUE);
      -- 注意: MySQL中, 唯一约束限定的列的值可以有多个null
      
    2. 创建表后添加

      ALTER TABLE stu MODIFY phone VARCHAR(20) UNIQUE;
      
    3. 删除唯一约束

      ALTER TABLE stu MODIFY phone VARCHAR(20); -- 不能通过这种方式
      ALTER TABLE stu DROP INDEX phone;
      

    注意: MySQL中唯一约束限定的列的值可以有多个null

    外键约束

    CREATE TABLE emp (
       id INT PRIMARY KEY AUTO_INCREMENT,
       NAME VARCHAR(30),
       age INT,
       dep_name VARCHAR(30),
       dep_location VARCHAR(30)
    );
    
    -- 添加数据
    INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('张三', 20, '研发部', '广州');
    INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('李四', 21, '研发部', '广州');
    INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('王五', 20, '研发部', '广州');
    INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('老王', 20, '销售部', '深圳');
    INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('大王', 22, '销售部', '深圳');
    INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('小王', 18, '销售部', '深圳');
    
    -- 数据有冗余
    
    -- 解决方案:分成 2 张表
    -- 创建部门表(id,dep_name,dep_location)
    -- 一方,主表
    create table department(
       id int primary key auto_increment,
       dep_name varchar(20),
       dep_location varchar(20)
    );
    
    -- 创建员工表(id,name,age,dep_id)
    -- 多方,从表
    create table employee(
       id int primary key auto_increment,
       name varchar(20),
       age int,
       dep_id int,  -- 外键对应主表的主键
       CONSTRAINT emp_dept_fk FOREIGN KEY (dep_id) REFERENCES department(id)
    );
    
    -- 添加 2 个部门
    insert into department values(null, '研发部','广州'),(null, '销售部', '深圳');
    select * from department;
    
    -- 添加员工,dep_id 表示员工所在的部门
    INSERT INTO employee (NAME, age, dep_id) VALUES ('张三', 20, 1);
    INSERT INTO employee (NAME, age, dep_id) VALUES ('李四', 21, 1);
    INSERT INTO employee (NAME, age, dep_id) VALUES ('王五', 20, 1);
    INSERT INTO employee (NAME, age, dep_id) VALUES ('老王', 20, 2);
    INSERT INTO employee (NAME, age, dep_id) VALUES ('大王', 22, 2);
    INSERT INTO employee (NAME, age, dep_id) VALUES ('小王', 18, 2);
    select * from employee;
    
    -- 删除外键
    ALTER TABLE employee DROP FOREIGN KEY emp_dept_fk;
    
    -- 添加外键
    ALTER TABLE employee ADD CONSTRAINT emp_dept_fk FOREIGN KEY (dep_id) REFERENCES department(id);
    
    emp_dept_fk是外键名称
    
    -- 添加外键, 设置级联更新
    ALTER TABLE employee ADD CONSTRAINT emp_dept_fk FOREIGN KEY (dep_id) REFERENCES department(id) ON UPDATE CASCADE;
    
    -- 添加外键, 设置级联删除
    ALTER TABLE employee ADD CONSTRAINT emp_dept_fk FOREIGN KEY (dep_id) REFERENCES department(id) ON DELETE CASCADE;
    
    -- 同时设置
    ALTER TABLE employee ADD CONSTRAINT emp_dept_fk FOREIGN KEY (dep_id) REFERENCES department(id) ON UPDATE CASCADE ON DELETE CASCADE;
    

数据库的设计

  1. 多表之间的关系

    1. 分类

      1. 一对一:
        • 人和身份证
        • 分析: 一个人有一个身份证, 一个身份证对应一个人
      2. 一对多(多对一):
        • 部门和员工
        • 分析: 一个部门有多个员工, 一个员工只能对应一个部门
      3. 多对多:
        • 如: 学生和课程
        • 分析: 一个学生可以选择多个课程, 一个课程可以被多个学生选择
    2. 实现:

      1. 一对多:
        • 部门和员工
        • 实现方式: 在多的一方建立外键, 指向一的一方的主键.
      2. 多对多:
        • 学生和课程
        • 实现方式: 多对多关系实现需要借助第三张中间表,中间表至少包含两个字段, 这两个字段作为第三张表的外键, 分别指向其他两张表的主键.
      3. 一对一:
        • 学生和身份证
        • 实现方式: 可以在任意一方添加唯一(unique)外键指向另一方的主键.
    3. 案例:

      分类表

      cidame
      xx

      线路表

      ridnamepricecid

      用户表

      uidusernamepassword

      中间表

      riduid

      分类1 线路表N 一对多

      线路表N 用户M 多对多, 中间表

  2. 数据库设计的范式

    1. 第一范式: 每一列都是不可分割的原子数据项
    2. 第二范式: 在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)
    3. 第三方是: 在2NF的基础上, 任何非主属性不依赖于其他非主属性(在2NF基础上消除传递依赖)

数据库备份和还原

  1. 命令行
    • mysqldump -u用户名 -p密码 数据库名称 > 保存路径
    • 还原
      1. 登录数据库
      2. 创建数据库
      3. 使用数据库
      4. 执行文件. source 文件路径
  2. 图形化工具

目录

  1. 多表查询
  2. 事务
  3. DCL

多表查询

  • 查询语法

    select
    	列名列表
    from
    	表名列表
    where ...
    

    操作数据

    -- 部门表
    CREATE TABLE dept (
      id INT PRIMARY KEY PRIMARY KEY, -- 部门id
      dname VARCHAR(50), -- 部门名称
      loc VARCHAR(50) -- 部门所在地
    );
    
    -- 添加4个部门
    INSERT INTO dept(id,dname,loc) VALUES 
    (10,'教研部','北京'),
    (20,'学工部','上海'),
    (30,'销售部','广州'),
    (40,'财务部','深圳');
    
    
    
    -- 职务表,职务名称,职务描述
    CREATE TABLE job (
      id INT PRIMARY KEY,
      jname VARCHAR(20),
      description VARCHAR(50)
    );
    
    -- 添加4个职务
    INSERT INTO job (id, jname, description) VALUES
    (1, '董事长', '管理整个公司,接单'),
    (2, '经理', '管理部门员工'),
    (3, '销售员', '向客人推销产品'),
    (4, '文员', '使用办公软件');
    
    
    
    -- 员工表
    CREATE TABLE emp (
      id INT PRIMARY KEY, -- 员工id
      ename VARCHAR(50), -- 员工姓名
      job_id INT, -- 职务id
      mgr INT , -- 上级领导
      joindate DATE, -- 入职日期
      salary DECIMAL(7,2), -- 工资
      bonus DECIMAL(7,2), -- 奖金
      dept_id INT, -- 所在部门编号
      CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
      CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
    );
    
    -- 添加员工
    INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES 
    (1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
    (1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
    (1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
    (1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
    (1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
    (1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
    (1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
    (1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
    (1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
    (1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
    (1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
    (1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
    (1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
    (1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);
    
    
    
    -- 工资等级表
    CREATE TABLE salarygrade (
      grade INT PRIMARY KEY,   -- 级别
      losalary INT,  -- 最低工资
      hisalary INT -- 最高工资
    );
    
    -- 添加5个工资等级
    INSERT INTO salarygrade(grade,losalary,hisalary) VALUES 
    (1,7000,12000),
    (2,12010,14000),
    (3,14010,20000),
    (4,20010,30000),
    (5,30010,99990);
    
    -- 需求:
    
    -- 1.查询所有员工信息。查询员工编号,员工姓名,工资,职务名称,职务描述
    
    -- 2.查询员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置
       
    -- 3.查询员工姓名,工资,工资等级
    
    -- 4.查询员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
    
    -- 5.查询出部门编号、部门名称、部门位置、部门人数
     
    -- 6.查询所有员工的姓名及其直接上级的姓名,没有领导的员工也需要查询
    
  • 笛卡尔积

    • 有两个集合A,B, 取这来能个集合的所有组成情况
    • 要完成多表查询,需要消除无用的数据.
  • 多表查询分类:

    1. 内连接查询

      1. 隐式内连接(用where清除无用数据)

        -- 查询所有员工信息和对应部门信息
        SELECT * FROM emp, dept WHERE emp.dept_id = dept.id;
        
        -- 查询员工表姓名, 工资 以及部门名称
        SELECT emp.ename, emp.salary, dept.dname FROM emp, dept WHERE emp.dept_id = dept.id;
        SELECT t1.ename, t1.salary, t2.dname FROM emp t1, dept t2 WHERE t1.dept_id = t2.id;
        
      2. 显式内连接

        • 语法

          select 字段列表 from 表名1 inner join 表名2 on 条件
          
        • 例如:

          SELECT * FROM emp INNER JOIN dept on emp.dept_id = dept.id;
          SELECT * FROM emp JOIN dept on emp.dept_id = dept.id;
          
      3. 内连接查询

        1. 从哪些表中查询数据
        2. 条件是什么
        3. 查询哪些字段
    2. 外连接查询

      1. 左外连接

        • 语法

          select 字段列表 from 表1 left outer join 表2 on 条件
          
        • 查询的是左边表所有记录, 以及其交集部分.

      2. 右外连接

        • 语法

          select 字段列表 from 表1 right outer join 表2 on 条件
          
        • 查询的是右边表所有记录, 以及其交集部分.

        表名换个位置,左外就变成了右外.

    3. 子查询

      • 概念: 查询中嵌套查询, 称嵌套查询为子查询.

        -- 查询工资最高的员工信息
        -- 1. 查询最高的工资是多少
        SELECT MAX(salary) FROM emp;
        -- 2. 查询员工信息, 并且工资是9000
        SELECT * FROM emp WHERE emp.salary = 50000;
        -- 综合
        SELECT * FROM emp WHERE emp.salary = (SELECT MAX(salary) FROM emp);
        
      • 子查询的不同情况

        1. 子查询结果是单行单列

          • 子查询可以作为条件使用运算符判断. (> < =等等)

            -- 查询员工工资小于平均工资的人
            SELECT * from emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
            
        2. 子查询结果是多行单列

          • 子查询可以作为条件, 使用运算符IN

            -- 查询财务部和销售部所有员工信息
            SELECT id FROM dept WHERE dname = "财务部" OR dname = "市场部";
            SELECT * FROM emp WHERE dept_id = 40;
            
            SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE dname = "财务部" OR dname = "销售部");
            
        3. 子查询结果是多行多列, 子查询可以作为一个虚拟表

          • 子查询

            -- 查询员工入职日期是2011-11-11之后的员工信息和部门信息
            SELECT * FROM emp WHERE joindate > "2001-05-01";
            SELECT * FROM dept t1, (SELECT * FROM emp WHERE joindate > "2001-05-01") t2 WHERE t1.id = t2.dept_id;
            
          • 普通查询

            -- 普通内连接
            SELECT * FROM emp t1, dept t2 WHERE t1.dept_id = t2.id AND t1.joindate > "2001-05-01";
            
  • 多表查询练习

    -- 需求:
    -- 1.查询所有员工信息。查询员工编号,员工姓名,工资,职务名称,职务描述
    SELECT
      t1.id, t1.ename, t1.salary, t2.jname, t2.description
    FROM
      emp t1, job t2
    WHERE
      t1.job_id = t2.id;
    -- 2.查询员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置
    SELECT
      t1.id, t1.ename, t1.salary, t2.jname, t2.description, t3.dname, t3.loc
    FROM
      emp t1, job t2, dept t3
    WHERE
      t1.job_id = t2.id AND t1.dept_id = t3.id;
    -- 3.查询员工姓名,工资,工资等级
    SELECT 
      t1.ename,
      t1.salary, 
      t2.*
    FROM 
      emp t1, salarygrade t2
    WHERE
      t1.salary BETWEEN t2.losalary AND t2.hisalary;
    -- 4.查询员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
    SELECT
      t1.id, t1.ename, t1.salary, t2.jname, t2.description, t3.dname, t3.loc, t4.*
    FROM
      emp t1, job t2, dept t3, salarygrade t4
    WHERE
      t1.job_id = t2.id AND t1.dept_id = t3.id AND (t1.salary BETWEEN t4.losalary AND t4.hisalary);
    -- 5.查询出部门编号、部门名称、部门位置、部门人数
    SELECT
      t1.id, t1.dname, t1.loc, t2.total
    FROM dept t1,
        (SELECT dept_id, COUNT(id) total
          FROM emp
          GROUP BY dept_id) t2
    WHERE t1.id = t2.dept_id;
    -- 6.查询所有员工的姓名及其直接上级的姓名,没有领导的员工也需要查询
      SELECT 
      t1.ename, t1.mgr, t2.id, t2.ename
      FROM 
      emp t1, emp t2
      WHERE 
      t1.mgr = t2.id; -- 自关联
      
    -- 没有领导的员工也需要查询
    SELECT 
      t1.ename, t1.mgr, t2.id, t2.ename
      FROM 
      emp t1
    LEFT JOIN
      emp t2
    ON 
      t1.mgr = t2.id; -- 自关联
    

事务

  1. 事务的基本介绍

    1. 概念:

      • 如果一个包含多个步骤的业务操作被事务管理, 要么这些操作同时成功, 或者同时失败.
    2. 操作

      • 开启事务 start transaction
      • 回滚: rollback
      • 提交: commit
    3. 例子

    CREATE TABLE `account` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(10) COLLATE utf8mb4_general_ci DEFAULT NULL,
      `balance` double DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
    
    -- 开启事务
    START TRANSACTION;
    UPDATE account SET balance = balance - 500 WHERE name = "张三";
    UPDATE account SET balance = balance - 500 WHERE name = "李四";
    -- 如果出错
    ROLLBACK;
    -- 如果没错
    COMMIT;
    
    1. MySQL数据库中事务默认自动提交
    • 事务提交的两种方式
      • 自动提交
        • MySQL就是自动提交的
        • 一条DML(增删改)语句会自动提交一次事务
      • 手动提交:
        • Oracle数据库默认是手动提交事务
        • 需要先开启事务再提交
    • 修改事务的默认提交方式
      • 查看事务的默认提交方式: SELECT @@autocommit;
      • 修改事务的默认提交方式: set @@autocommit = 1; 1开启自动提交, 0关闭自动提交
  2. 事务的四大特征

    1. 原子性: 是不可分割的最小单位, 要么同时成功, 要么同时失败.
    2. 持久性: 当事务提交或回滚后, 数据库会持久化的保存数据.
    3. 隔离性: 多个事务之间, 相互独立.
    4. 一致性: 开始和结束之间的状态不会被其他事务看到.
  3. 事务的隔离级别(了解)

    • 概念: 多个事务是隔离的, 相互独立的. 但是如果多个事务操作同一批数据, 则会引发一些问题,设置不同的隔离级别就可以解决这些问题.

    • 存在的问题:

      1. 脏读: 一个事务读取到另一个事务没有提交的数据
      2. 不可重复读(虚读): 在同一个事务中, 两次读取的数据不一样.
      3. 幻读: 数据表中所有的记录, 另一个事物添加了一条数据, 则第一个事务查询不到自己的修改.
    • 隔离级别:

      1. read umcommited: 读未提交

        产生的问题: 脏读, 不可重复读, 幻读

      2. read commited: 读已提交

        产生的问题: 不可重复读, 幻读

      3. repeatable read: 可重复读

        产生的问题: 幻读

      4. serializable: 串行化

        可以解决所有问题

      注意: 隔离级别从小到大安全性越来越高, 但是性能越来越低

      数据库查询隔离级别: select @@transaction_isolation;

      设置数据库隔离级别: SET transaction_isolation = value;

       set global transaction isolation level read uncommitted;
       start transaction;
       -- 转账操作
       update account set balance = balance - 500 where id = 1;
       update account set balance = balance + 500 where id = 2;
      

DCL

  • SQL分类

    1. DDL: 操作数据库和表
    2. DML: 增删改表中数据
    3. DQL: 查询表中的数据
    4. DCL: 管理用户, 授权
  • DBA: 数据库管理员

  • DCL: 管理用户, 授权

    1. 管理用户

      -- 增加用户
      CREATE USER 'tomcat'@'localhost' IDENTIFIED BY 'root';
      -- 修改密码
      UPDATE USER SET PASSWORD = PASSWORD('新密码') WHERE USER = '用户名';
      UPDATE USER SET PASSWORD = PASSWORD('abc') WHERE USER = 'lisi';
      SET PASSWORD FOR '用户名'@'主机名' = PASSWORD('新密码');
      SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123');
      -- 删除用户
      DROP USER '用户名'@'主机名';
      -- 查询用户
      -- 1. 切换到mysql数据库
      USE myql;
      -- 2. 查询user表
      SELECT * FROM USER;
      * 通配符: % 表示可以在任意主机使用用户登录数据库
      			权限管理
      			1. 查询权限:
      				-- 查询权限
      				SHOW GRANTS FOR '用户名'@'主机名';
      				SHOW GRANTS FOR 'lisi'@'%';
      
      			2. 授予权限:
      				-- 授予权限
      				grant 权限列表 on 数据库名.表名 to '用户名'@'主机名';
      				-- 给张三用户授予所有权限,在任意数据库任意表上
      				
      				GRANT ALL ON *.* TO 'zhangsan'@'localhost';
      			3. 撤销权限:
      				-- 撤销权限:
      				revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名';
      				REVOKE UPDATE ON db3.`account` FROM 'lisi'@'%';
      
      • MySQL中忘记密码:

        1. cmd -- > net stop mysql 停止mysql服务
        * 需要管理员运行该cmd
        2. 使用无验证方式启动mysql服务: mysqld --skip-grant-tables
        3. 打开新的cmd窗口,直接输入mysql命令,敲回车。就可以登录成功
        4. use mysql;
        5. update user set password = password('你的新密码') where user = 'root';
        6. 关闭两个窗口
        7. 打开任务管理器,手动结束mysqld.exe 的进程
        8. 启动mysql服务
        9. 使用新密码登录。
        

JDBC

目录

  1. 数据库连接池
  2. Spring JDBCTemplate

数据库连接池

  1. 概念: 一个容器, 存放数据库连接的容器.

    当系统被初始化后, 容器被创建, 容器中会申请一些连接对象, 当用户来访问数据库的时候, 从容器中获取链接对象,用户访问完之后将链接对象归还给容器.

  2. 好处

    1. 节约资源
    2. 用户访问搞笑
  3. 实现

    1. 标准接口: DataSource javax.sql包下
      1. 方法
        • 获取链接 (略)
  4. C3P0: 数据库连接池技术

  5. Druid: 数据库连接池实现技术,由阿里巴巴提供的

Spring JDBC

Spring框架对JDBC的简单封装, 体统了一个JDBCTemplate对象简化JDBC的开发

  • 步骤: 略

MyBatis

  1. 创建maven工程并导入坐标
  2. 创建实体类和dao接口
  3. 创建mybatis的主配置文件SqlMapConfig.xml
  4. 创建映射配置文件UserDao.xml
.
├── main
│   ├── java
│   │   └── com
│   │       └── swifter
│   │           ├── dao
│   │           │   └── UserDao.java
│   │           └── domain
│   │               └── User.java
│   └── resources
│       ├── SqlMapConfig.xml
│       ├── com
│       │   └── swifter
│       │       └── dao
│       │           └── UserDao.xml
│       └── log4j.properties
└── test
    └── java
        └── com
            └── swifter
                └── test
                    └── MybatisTest.java
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` datetime DEFAULT NULL COMMENT '生日',
  `sex` char(1) DEFAULT NULL COMMENT '性别',
  `address` varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (41, '老王', '2018-02-27 17:47:08', '男', '北京');
INSERT INTO `user` VALUES (42, '小二王', '2018-03-02 15:09:37', '女', '北京金燕龙');
INSERT INTO `user` VALUES (43, '小二王', '2018-03-04 11:34:34', '女', '北京金燕龙');
INSERT INTO `user` VALUES (45, '传智播客', '2018-03-04 12:04:06', '男', '北京金燕龙');
INSERT INTO `user` VALUES (46, '老王', '2018-03-07 17:37:26', '男', '北京');
INSERT INTO `user` VALUES (48, '小马宝莉', '2018-03-08 11:44:00', '女', '北京修正');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;
  1. maven坐标

        <dependencies>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.3</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.6</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.12</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.10</version>
            </dependency>
        </dependencies>
    
  2. 创建实体类和dao接口

    package com.swifter.domain;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * @author 黑马程序员
     * @Company http://www.ithiema.com
     */
    public class User implements Serializable{
    
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    
        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 Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", birthday=" + birthday +
                    ", sex='" + sex + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    
    package com.swifter.dao;
    
    import com.swifter.domain.User;
    
    import java.util.List;
    
    /**
     * @author 黑马程序员
     * @Company http://www.ithiema.com
     *
     * 用户的持久层接口
     */
    public interface IUserDao {
    
        /**
         * 查询所有操作
         * @return
         */
        List<User> findAll();
    }
    
  3. 创建SqlMapConfig.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <!-- mybatis的主配置文件 -->
    <configuration>
        <!-- 配置环境 -->
        <environments default="mysql">
            <!-- 配置mysql的环境-->
            <environment id="mysql">
                <!-- 配置事务的类型-->
                <transactionManager type="JDBC"></transactionManager>
                <!-- 配置数据源(连接池) -->
                <dataSource type="POOLED">
                    <!-- 配置连接数据库的4个基本信息 -->
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/eesy?characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="jingxuetao"/>
                </dataSource>
            </environment>
        </environments>
    
        <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
        <mappers>
            <mapper resource="com/itheima/dao/IUserDao.xml"/>
        </mappers>
    </configuration>
    
  4. 创建映射配置文件

    <?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="com.swifter.dao.IUserDao">
    		<!--namespace dao接口的全限定类名-->
        <!--配置查询所有-->
        <select id="findAll" resultType="com.swifter.domain.User">
            select * from user
        </select>
    </mapper>
    
  5. 测试

    package com.swifter;
    
    import com.swifter.dao.IUserDao;
    import com.swifter.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 java.io.InputStream;
    import java.util.List;
    
    public class MyBatisTest {
        public static void main(String[] args) throws Exception {
            //1. 读取配置文件
            InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2. 创建SQLSessionFactory
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(in);
            //3. 使用工厂生产SQLSession对象
            SqlSession session = factory.openSession();
            //4. 使用SQLSession创建dao接口的代理对象
            IUserDao userDao = session.getMapper(IUserDao.class);
            //5.使用代理对象执行方法
            List<User> all = userDao.findAll();
            for (User user : all) {
                System.out.println(user);
            }
            //6.释放资源
            session.close();
            in.close();
        }
    }
    

注意实现

  1. 创建UserDao.xml 和 UserDao.java时, 名称是为了和之前的知识保持一致,mybatis把持久层操作接口名称和映射文件(Mapper) UserMapper和UserDao 应该只是名称的区别
  2. 在idea中创建目录时和包不一样, 包创建com.swifter.dao是三级目录, 目录创建是一级目录.
  3. mybatis映射配置文件必须和dao接口包结构相同
  4. 映射配置文件的mapper标签namespace属性取值,必须是dao接口的全限定类名.
  5. 映射配置文件的操作配置,id属性取值必须是dao接口的方法名.

自定义mybatis分析

  1. 创建代理对象

  2. 在代理对象中调用selectList方法

    1. 自定义mybatis需要的类:

    2. class Resources

    3. class SqlSessionFactoryBuilder

    4. interface SqlSessionFactory

    5. interface SqlSession

dao

package com.swifter.dao;

import com.swifter.domain.QueryVo;
import com.swifter.domain.User;

import java.util.List;

/**
 * @author 黑马程序员
 * @Company http://www.ithiema.com
 *
 * 用户的持久层接口
 */
public interface UserDao {

    /**
     * 查询所有操作
     * @return
     */
    List<User> findAll();

    /**
     * 保存用户
     * @param user
     */
    void saveUser(User user);

    /**
     * 更新用户
     * @param user
     */
    void updateUser(User user);

    /**
     * 删除用户
     * @param id
     */
    void deleteUser(Integer id);

    /**
     * 根据ID查用户
     * @param id
     */
    User findById(Integer id);

    /**
     * 模糊查询
     * @param name
     * @return
     */
    List<User> findByName(String name);

    /**
     * 总记录数
     * @return
     */
    int findTotal();

    /**
     * 根据QueryVo中的条件查询用户
     * @param vo
     * @return
     */
    List<User> findUserByVo(QueryVo vo);
}

domain

QueryVo.java

package com.swifter.domain;

public class QueryVo {

    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

User.java

package com.swifter.domain;

import java.io.Serializable;
import java.util.Date;

/**
 * @author 黑马程序员
 * @Company http://www.ithiema.com
 */
public class User implements Serializable{

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

    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 Date getBirthday() {
        return birthday;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

xml

com/swifter/dao

<?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="com.swifter.dao.UserDao">

    <!-- 当属性名和列名不一致时 -->
    <!-- 配置结果列名和实体类的属性名的对应关系 -->
    <resultMap id="userMap" type="com.swifter.domain.User">
        <!-- 主键对应 -->
        <id property="userId" column="id"></id>
        <!-- 非主键字段 -->
        <result property="userName" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
    </resultMap>

    <!-- 配置查询所有操作 -->
    <select id="findAll" resultMap="userMap">
--         select id as userId ...
        select * from user
    </select>
    <!-- ==============================================-->
    <!-- 配置查询所有操作 -->
    <select id="findAll" resultType="com.swifter.domain.User">
    select * from user
    </select>

    <!-- 插入用户-->
    <insert id="saveUser" parameterType="com.swifter.domain.User">
--         配置插入用户后的ID
        <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
            select last_insert_id();
        </selectKey>
        insert into user(username, address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday})
    </insert>

    <!-- 更新用户-->
    <update id="updateUser" parameterType="com.swifter.domain.User">
        update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id = #{id}
    </update>

    <delete id="deleteUser" parameterType="java.lang.Integer">
--         id就是个占位符, id, uid, userId都可以
        delete from user where id = #{id}
    </delete>

    <!-- 根据ID查用户 -->
    <select id="findById" parameterType="int" resultType="com.swifter.domain.User">
--         id就是个占位符, id, uid, userId都可以
        select * from user where id = #{id}
    </select>
<!--模糊查询-->
    <select id="findByName" parameterType="string" resultType="com.swifter.domain.User">
        select * from user where username like #{name}
    </select>

<!--    获取总记录条数-->
    <select id="findTotal" resultType="int">
        select count(id) from user
    </select>

<!--    根据QueryVo的条件查询用户-->
    <select id="findUserByVo" parameterType="com.swifter.domain.QueryVo" resultType="com.swifter.domain.User">
        select * from user where username like #{user.username}
    </select>
</mapper>

Test

import com.swifter.dao.UserDao;
import com.swifter.domain.QueryVo;
import com.swifter.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.After;
import org.junit.Before;

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

public class Test {

    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private UserDao userDao;

    @Before
    public void init() throws Exception {
        //1. 读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2. 创建SQLSessionFactory
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(in);
        //3. 使用工厂生产SQLSession对象
        session = factory.openSession();
        //4. 使用SQLSession创建dao接口的代理对象
        userDao = session.getMapper(UserDao.class);
    }

    @After
    public void destory() throws Exception {
        //提交事务
        session.commit();
        session.close();
        in.close();
    }

    @org.junit.Test
    public void testFindAll() throws Exception {
        //5.使用代理对象执行方法
        List<User> all = userDao.findAll();
        for (User user : all) {
            System.out.println(user);
        }
    }

    @org.junit.Test
    public void testInsertUser() throws Exception {
        User user = new User();
        user.setUsername("哈哈哈");
        user.setSex("男");
        user.setAddress("北京市朝阳区");
        user.setBirthday(new Date());
        System.out.println(user);
        userDao.saveUser(user);
        System.out.println(user);
    }

    @org.junit.Test
    public void updateUser() {
        User user = new User();
        user.setId(50);
        user.setUsername("哈哈哈");
        user.setSex("女");
        user.setAddress("北京市朝阳区");
        user.setBirthday(new Date());
        userDao.updateUser(user);
    }

    @org.junit.Test
    public void deleteUser() {
        userDao.deleteUser(50);
    }

    @org.junit.Test
    public void findById() {
        User user = userDao.findById(48);
        System.out.println(user);
    }

    @org.junit.Test
    public void findByName() {
        List<User> users = userDao.findByName("%王%");
        for (User user: users) {
            System.out.println(user);
        }
    }

    @org.junit.Test
    public void findTotal() {
        int total = userDao.findTotal();
        System.out.println(total);
    }

    @org.junit.Test
    public void findByVo() {
        QueryVo queryVo = new QueryVo();
        User user = new User();
        user.setUsername("%王%");
        queryVo.setUser(user);
        List<User> users = userDao.findUserByVo(queryVo);
        for (User u: users) {
            System.out.println(u);
        }
    }
}

MyBatis 标签使用及细节

    <!--resource 用户指定配置文件的位置,是按照类路径的写法来写, 并且必须存在于类路径下-->
    <!--<properties resource="jdbcConfig.properties">-->

    <!-- 按照URL方式来写地址 -->
    <properties url="file:///User/document/...">
    <!--<property name="driver" value="com.mysql.jdbc.Driver"/>-->
    <!--<property name="url" value="jdbc:mysql://localhost:3306/eesy?characterEncoding=UTF-8"/>-->
    <!--<property name="username" value="root"/>-->
    <!--<property name="password" value="jingxuetao"/>-->
    </properties>

jdbcConfig.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy?characterEncoding=UTF-8
jdbc.username=root
jdbc.password=jingxuetao

typeAliases使用

    <!--使用typeAliases配置别名, 他只能配货车domain中的别名-->
    <typeAliases>
        <typeAlias type="com.swifter.domain.User" alias="user"></typeAlias>
    </typeAliases>

package使用

    <mappers>
        <package name="com.swifter.dao"/>
    </mappers>
  1. mybatis中的连接池以及事务控制

    mybatis中连接池使用及分析

    mybatis事务控制的分析

  2. mybatis基于XML配置的动态SQL语句使用

    mappers配置文件中的几个标签:

  3. mybatis的多表查询

    1. 一对多
    2. 一对一
    3. 多对多
  4. 连接池:

    我们在实际开发中都会使用连接池, 可以减少我们获取链接所消耗的时间.

    连接池就是一个容器,就是一个集合, 必须是线程安全的,不能两个线程拿到同一个链接.

  5. mybatis连接池

    SqlMapConfig.xml, dataSource标签的type属性

    1. POOLED, 采用传统的javax.sql.DataSource规范中的连接池

    2. UNPOOLED, 虽然也实现了javax.sql.DataSource, 但是没有池的思想

    3. JNDI, 采用服务器提供的JNDI技术实现, 来获取DataSource对象,不同服务器所拿到的对象不一样

      如果不是web或者maven的war工程,不能使用.

<!--
    <select id="findUserByCondition" parameterType="com.swifter.domain.User" resultType="com.swifter.domain.User">
        select * from user where 1=1
        <if test="username != null">
            and username = #{username}
        </if>
        <if test="sex != null">
            and sex = #{sex}
        </if>
    </select>
    -->
    <select id="findUserByCondition" parameterType="com.swifter.domain.User" resultType="com.swifter.domain.User">
        select * from user
        <where> --避免加上1=1, 让SQL看起来更清晰简洁
            <if test="username != null">
                and username = #{username}
            </if>
            <if test="sex != null">
                and sex = #{sex}
            </if>
        </where>
    </select>

    <sql id="defaultUser">
        select * from user
    </sql>

    <select id="findUserInIds" parameterType="com.swifter.domain.QueryVo" resultType="com.swifter.domain.User">
--         select * from user
        <include refid="defaultUser"></include>
        <where>
            <if test="ids != null and ids.size()>0">
                <foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>
/**
     * 根据传入的参数条件查询
     * @return
     */
    List<User> findUserByCondition(User user);

    /**
     * 根据queryvo中的ID集合, 实现查询用户列表
     * @param vo
     * @return
     */
    List<User> findUserInIds(QueryVo vo);
public class QueryVo {
    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}
    @org.junit.Test
    public void findUserByCondition() {
        User user = new User();
        user.setUsername("老王");
        user.setSex("女");
        List<User> users = userDao.findUserByCondition(user);
        for(User u: users) {
            System.out.println(u);
        }
    }

    @org.junit.Test
    public void findUserInIds() {
        QueryVo queryVo = new QueryVo();
        List<Integer> integers = new ArrayList<Integer>();
        integers.add(41);
        integers.add(42);
        queryVo.setIds(integers);
        List<User> users = userDao.findUserInIds(queryVo);
        for(User u: users) {
            System.out.println(u);
        }
    }

MyBatis中的多表查询

表之间的关系有几种:

  1. 一对多
  2. 多对一
  3. 一对一
  4. 多对多

举例:

​ 用户和订单,一对多

​ 一个人只能有一个身份证号,一对一

​ 一个学生可以被多个老师教, 多对多

特例: 如果每个订单只属于一个用户, 所以MyBatis就把多对一看成一对一.

<!--封装account和user的resultMap-->
    <resultMap id="accountUserMap" type="com.swifter.domain.Account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--一对一的关系映射, 配置封装user的内容-->
        <association property="user" column="uid">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
        </association>
    </resultMap>

    <!-- ==============================================-->
    <!-- 配置查询所有操作 -->
    <select id="findAll" resultMap="accountUserMap">
        select u.*, a.id as aid, a.uid, a.money from account a, user u where u.id=a.UID
    </select>
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    //从表实体应该包含一个主表实体的对象引用
    private User user;

    public Integer getId() {
        return id;
    }

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

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                ", user=" + user +
                '}';
    }
}

mybatis中的多表查询:

​ 示例: 用户和账户

​ 一个用户可以有多个账户

​ 一个账户只能属于一个用户

​ 步骤:

  1. 先建立两张表, 一张用户表, 一张账户表.

  2. 建立实体类, 用户实体类, 账户实体类

  3. 建立两个配置文件

  4. 实现配置

    当我们查询用户时, 可以同时得到用户下所包含的账户信息.

    当我们查询账户时,可以同时得到账户的所有信息.

    <!--一对多  定义user resultMap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
<!--        配置user对象中account的映射-->
        <collection property="accounts" ofType="com.swifter.domain.Account">
            <id property="id" column="aid"></id>
            <result column="uid" property="uid"></result>
            <result column="money" property="money"></result>
        </collection>
    </resultMap>
    <!-- ==============================================-->
    <!-- 配置查询所有操作 -->
    <select id="findAll" resultMap="userAccountMap">
    select * from user u left join account a on u.id=a.uid
    </select>
public class User implements Serializable{

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

    //一对多关系映射, 主表实体应该包含从表实体的集合引用
    private List<Account> accounts;

    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 Date getBirthday() {
        return birthday;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

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

示例: 用户和角色

步骤:

  1. 两张表: 用户, 角色 还有中间表

  2. 建立两个实体类, 用户角色各自包含对方一个集合引用

  3. 两个配置文件: 用户配置文件, 角色配置文件

  4. 实现配置:

    当查询用户时, 可以同时得到用户所包含的角色信息

    当查询角色时, 可以同时得到角色下的用户信息.