Optional Note
MySQL
SQL
- 什么是SQL
结构化查询语言
- 通用语法
单行或多行, 以分号结尾.
用空格和缩进增强可读性
不区分大小写, 关键字建议用大写.
三种注释
-- 单行注释 # 单行注释 /* */多行注释
- SQL分类
DDL(Data definition Language) 数据定义语言, 定义数据库对象. 例如: 数据库, 表, 列等. 关键字: create, drop, alter等.
DML(Data Manipulation Language) 桑菊操作语言 用来对数据库中的表的数据进行增删改, 关键字: insert, delete, update等.
DQL(Data Query Language)数据查询语言 用来查询数据库中表的记录. 关键字: select, where等.
DCL(Data Control Language)数据控制语言.用来定义数据库的访问级别以及创建用户. 关键字: grant, revoke等.
DDL: 操作数据库, 表
- 操作数据库: CRUD
C(Create): 创建
create database if not exists db character set gbk;
R(Retrieve): 查询
- 查询所有数据库名称: show databases;
- 查询创建数据库语句: show create database mysql;
U(Update): 修改
- 修改数据库字符集: alter database db_name character set utf8;
D(Delete): 删除
- 删除数据库: drop database if exists db_name;
使用数据库
- 查询当前数据库: select database();
- 使用数据库: use db_name;
- 操作表
C(Create): 创建
create table 表名(
列名1 数据类型1,
列名2 数据类型2
...
列名n 数据类型n
);
数据库类型:
int类型
double(5, 2) 小数类型, 共五位, 小数点后保留两位.
date 日期类型,只包含年月日 yyyy-MM-dd
datetime 年月日时分秒 yyyy-MM-dd HH:mm:ss
timestamp 时间戳,年月日时分秒 yyyy-MM-dd HH:mm:ss
如果将来不给这个字段赋值或者赋值为null, 则默认使用当前系统时间自动赋值.
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 被复制表名;
R(Retrieve): 查询
- 查询所有表名称: show tables;
- 查询表结构: desc table_name;
U(Update): 修改
- 修改表名: alter table 表名 rename to 新表名;
- 修改表的字符集: alter table 表名 character set utf8;
- 添加一列: alter table 表名 add 列名 数据类型;
- 修改列名称,类型: alter table 表名 change 列名 新列名 新数据类型;
- 修改类型: alter table 表名 modify 列名 新数据类型;
- 删除列: alter table 表名 drop 列名;
D(Delete): 删除
- drop table if exists 表名;
DML: 增删改表中的数据
-
添加数据
insert into 表名(列名1, 列名2,...) values (值1, 值2, ...);
-
删除数据
delete from 表名 [where 条件]; TRUNCATE TABLE 表名; //删除表, 并创建一个一模一样的表
-
修改数据
update 表名 set 列名1=值1, 列名2=值2,...[where 条件];
DQL: 查询表中的记录
-
语法
select 字段列表 from 表名列表 where 条件列表 group by 分组字段 having 分组之后的条件 order by 排序字段 limit 分页
-
基础查询
-
多个字段的查询
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 可以省略
-
-
条件查询
-
where子句后跟条件
-
运算符
> < <= >= = <> 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 "%马%";
-
目录
- DQL: 查询语句
- 排序查询
- 聚合函数
- 分组查询
- 分页查询
- 约束
- 多表时间的关系
- 范式
- 数据库备份和还原
DQL: 查询语句
-
排序查询
order by 子句; order by 排序字段1 排序方式2, 排序字段2 排序方式2... 排序方式: ASC: 升序 DESC: 降序 -- 按照数学排序, 如果数学一样, 则按照英语排序 SELECT * FROM student ORDER BY math, english; -- 如果有多个排序条件, 则当前面的条件值一样时, 才会判断第二个条件.
-
聚合函数: 将一列数据做整体, 进行纵向计算.
-
count: 计算个数
一般选择非空的列, 例如: 主键
-
max: 计算最大值
-
min: 计算最小值
-
avg: 计算平均值
聚合函数的计算, 排除null值了. 解决方法:
- 选择不包含非空的列
- 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;
-
-
分组查询
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;
-
分页查询
limit 开始索引, 查询的条数
-- 每页显示三条记录 SELECT * FROM student LIMIT 0,3; -- 第一页 SELECT * FROM student LIMIT 3,3; -- 第二页 -- 公式: 开始的索引 = (当前页码 - 1) * 每页数 SELECT * FROM student LIMIT 6,3; -- 第三页
limit是一个MySQL的
方言
约束
-
概念: 对表中的数据进行限定, 保证数据的正确性, 有效性和完整性.
-
分类:
- 主键约束: primary key
- 非空约束: not null
- 唯一约束: unique
- 外键约束: foreign key
主键约束
- 非空且唯一
- 一个表只能有一个字段是主键
- 主键就是表中记录的唯一标识
-
创建表时添加主键
CREATE TABLE stu(id INT PRIMARY KEY, name VARCHAR(20));
-
创建表之后添加主键
ALTER TABLE stu MODIFY id INT PRIMARY KEY;
-
删除主键
ALTER TABLE stu MODIFY id int; -- 错误,不生效 ALTER TABLE stu DROP PRIMARY KEY;
-
自动增长
-
如果某一列是数值类型, 使用
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
-
创建表时添加约束
CREATE TABLE stu(id INT, name VARCHAR(20) NOT NULL);
-
创建表完之后添加非空约束
ALTER TABLE stu MODIFY `name` VARCHAR(20) NOT NULL; ALTER TABLE stu CHANGE `name` `name` VARCHAR(20) NOT NULL;
-
删除非空约束
ALTER TABLE stu MODIFY `name` VARCHAR(20); ALTER TABLE stu CHANGE `name` `name` VARCHAR(20);
唯一约束: unique
同一列的值不能重复
-
创建表时添加唯一约束
CREATE TABLE stu(id INT, phone VARCHAR(20) UNIQUE); -- 注意: MySQL中, 唯一约束限定的列的值可以有多个null
-
创建表后添加
ALTER TABLE stu MODIFY phone VARCHAR(20) UNIQUE;
-
删除唯一约束
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;
数据库的设计
-
多表之间的关系
-
分类
- 一对一:
- 人和身份证
- 分析: 一个人有一个身份证, 一个身份证对应一个人
- 一对多(多对一):
- 部门和员工
- 分析: 一个部门有多个员工, 一个员工只能对应一个部门
- 多对多:
- 如: 学生和课程
- 分析: 一个学生可以选择多个课程, 一个课程可以被多个学生选择
- 一对一:
-
实现:
- 一对多:
- 部门和员工
- 实现方式: 在多的一方建立外键, 指向一的一方的主键.
- 多对多:
- 学生和课程
- 实现方式: 多对多关系实现需要借助第三张中间表,中间表至少包含两个字段, 这两个字段作为第三张表的外键, 分别指向其他两张表的主键.
- 一对一:
- 学生和身份证
- 实现方式: 可以在任意一方添加唯一(unique)外键指向另一方的主键.
- 一对多:
-
案例:
分类表
cid ame x x 线路表
rid name price cid 用户表
uid username password 中间表
rid uid 分类1 线路表N 一对多
线路表N 用户M 多对多, 中间表
-
-
数据库设计的范式
- 第一范式: 每一列都是不可分割的原子数据项
- 第二范式: 在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)
- 第三方是: 在2NF的基础上, 任何非主属性不依赖于其他非主属性(在2NF基础上消除传递依赖)
数据库备份和还原
- 命令行
- mysqldump -u用户名 -p密码 数据库名称 > 保存路径
- 还原
- 登录数据库
- 创建数据库
- 使用数据库
- 执行文件. source 文件路径
- 图形化工具
目录
- 多表查询
- 事务
- 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, 取这来能个集合的所有组成情况
- 要完成多表查询,需要消除无用的数据.
-
多表查询分类:
-
内连接查询
-
隐式内连接(用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;
-
显式内连接
-
语法
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;
-
-
内连接查询
- 从哪些表中查询数据
- 条件是什么
- 查询哪些字段
-
-
外连接查询
-
左外连接
-
语法
select 字段列表 from 表1 left outer join 表2 on 条件
-
查询的是左边表所有记录, 以及其交集部分.
-
-
右外连接
-
语法
select 字段列表 from 表1 right outer join 表2 on 条件
-
查询的是右边表所有记录, 以及其交集部分.
表名换个位置,左外就变成了右外.
-
-
-
子查询
-
概念: 查询中嵌套查询, 称嵌套查询为子查询.
-- 查询工资最高的员工信息 -- 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);
-
子查询的不同情况
-
子查询结果是单行单列
-
子查询可以作为条件使用运算符判断. (> < =等等)
-- 查询员工工资小于平均工资的人 SELECT * from emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
-
-
子查询结果是多行单列
-
子查询可以作为条件, 使用运算符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 = "销售部");
-
-
子查询结果是多行多列, 子查询可以作为一个虚拟表
-
子查询
-- 查询员工入职日期是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; -- 自关联
事务
-
事务的基本介绍
-
概念:
- 如果一个包含多个步骤的业务操作被事务管理, 要么这些操作同时成功, 或者同时失败.
-
操作
- 开启事务
start transaction
- 回滚:
rollback
- 提交:
commit
- 开启事务
-
例子
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;
- MySQL数据库中事务默认自动提交
- 事务提交的两种方式
- 自动提交
- MySQL就是自动提交的
- 一条DML(增删改)语句会自动提交一次事务
- 手动提交:
- Oracle数据库默认是手动提交事务
- 需要先开启事务再提交
- 自动提交
- 修改事务的默认提交方式
- 查看事务的默认提交方式:
SELECT @@autocommit;
- 修改事务的默认提交方式:
set @@autocommit = 1; 1开启自动提交, 0关闭自动提交
- 查看事务的默认提交方式:
-
-
事务的四大特征
- 原子性: 是不可分割的最小单位, 要么同时成功, 要么同时失败.
- 持久性: 当事务提交或回滚后, 数据库会持久化的保存数据.
- 隔离性: 多个事务之间, 相互独立.
- 一致性: 开始和结束之间的状态不会被其他事务看到.
-
事务的隔离级别(了解)
-
概念: 多个事务是隔离的, 相互独立的. 但是如果多个事务操作同一批数据, 则会引发一些问题,设置不同的隔离级别就可以解决这些问题.
-
存在的问题:
- 脏读: 一个事务读取到另一个事务没有提交的数据
- 不可重复读(虚读): 在同一个事务中, 两次读取的数据不一样.
- 幻读: 数据表中所有的记录, 另一个事物添加了一条数据, 则第一个事务查询不到自己的修改.
-
隔离级别:
-
read umcommited: 读未提交
产生的问题: 脏读, 不可重复读, 幻读
-
read commited: 读已提交
产生的问题: 不可重复读, 幻读
-
repeatable read: 可重复读
产生的问题: 幻读
-
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分类
- DDL: 操作数据库和表
- DML: 增删改表中数据
- DQL: 查询表中的数据
- DCL: 管理用户, 授权
-
DBA: 数据库管理员
-
DCL: 管理用户, 授权
-
管理用户
-- 增加用户 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
目录
- 数据库连接池
- Spring JDBCTemplate
数据库连接池
-
概念: 一个容器, 存放数据库连接的容器.
当系统被初始化后, 容器被创建, 容器中会申请一些连接对象, 当用户来访问数据库的时候, 从容器中获取链接对象,用户访问完之后将链接对象归还给容器.
-
好处
- 节约资源
- 用户访问搞笑
-
实现
- 标准接口: DataSource javax.sql包下
- 方法
- 获取链接 (略)
- 方法
- 标准接口: DataSource javax.sql包下
-
C3P0: 数据库连接池技术
-
Druid: 数据库连接池实现技术,由阿里巴巴提供的
Spring JDBC
Spring框架对JDBC的简单封装, 体统了一个JDBCTemplate对象简化JDBC的开发
- 步骤: 略
MyBatis
- 创建maven工程并导入坐标
- 创建实体类和dao接口
- 创建mybatis的主配置文件
SqlMapConfig.xml
- 创建映射配置文件
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;
-
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>
-
创建实体类和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(); }
-
创建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>
-
创建映射配置文件
<?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>
-
测试
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(); } }
注意实现
- 创建UserDao.xml 和 UserDao.java时, 名称是为了和之前的知识保持一致,mybatis把持久层操作接口名称和映射文件(Mapper) UserMapper和UserDao 应该只是名称的区别
- 在idea中创建目录时和包不一样, 包创建com.swifter.dao是三级目录, 目录创建是一级目录.
- mybatis映射配置文件必须和dao接口包结构相同
- 映射配置文件的mapper标签namespace属性取值,必须是dao接口的全限定类名.
- 映射配置文件的操作配置,id属性取值必须是dao接口的方法名.
自定义mybatis分析
-
创建代理对象
-
在代理对象中调用selectList方法
-
自定义mybatis需要的类:
-
class Resources
-
class SqlSessionFactoryBuilder
-
interface SqlSessionFactory
-
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>
-
mybatis中的连接池以及事务控制
mybatis中连接池使用及分析
mybatis事务控制的分析
-
mybatis基于XML配置的动态SQL语句使用
mappers配置文件中的几个标签:
-
mybatis的多表查询
- 一对多
- 一对一
- 多对多
-
连接池:
我们在实际开发中都会使用连接池, 可以减少我们获取链接所消耗的时间.
连接池就是一个容器,就是一个集合, 必须是线程安全的,不能两个线程拿到同一个链接.
-
mybatis连接池
SqlMapConfig.xml, dataSource标签的type属性
-
POOLED, 采用传统的javax.sql.DataSource规范中的连接池
-
UNPOOLED, 虽然也实现了javax.sql.DataSource, 但是没有池的思想
-
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中的多表查询
表之间的关系有几种:
- 一对多
- 多对一
- 一对一
- 多对多
举例:
用户和订单,一对多
一个人只能有一个身份证号,一对一
一个学生可以被多个老师教, 多对多
特例: 如果每个订单只属于一个用户, 所以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中的多表查询:
示例: 用户和账户
一个用户可以有多个账户
一个账户只能属于一个用户
步骤:
-
先建立两张表, 一张用户表, 一张账户表.
-
建立实体类, 用户实体类, 账户实体类
-
建立两个配置文件
-
实现配置
当我们查询用户时, 可以同时得到用户下所包含的账户信息.
当我们查询账户时,可以同时得到账户的所有信息.
<!--一对多 定义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 +
'}';
}
}
示例: 用户和角色
步骤:
-
两张表: 用户, 角色 还有中间表
-
建立两个实体类, 用户角色各自包含对方一个集合引用
-
两个配置文件: 用户配置文件, 角色配置文件
-
实现配置:
当查询用户时, 可以同时得到用户所包含的角色信息
当查询角色时, 可以同时得到角色下的用户信息.