javaEE:企业级java开发 Web
前端(页面:展示数据!)
后台(连接点:连接数据库JDBC,Mybatis,连接前端(servlet、Spring))控制视图跳转,和给前端传数据
数据库(存数据)
1、岗位需求
2、大数据时代,得数据者得天下
3、被迫需求:存数据
4、数据库是所有软件体系中最核心得存在
数据库(DB)
概念:数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在[计算机内的、有组织的、可共享的、统一管理的大量数据的集合
作用:存储数据、管理数据
关系型数据库(SQL):
非关系型数据库(NOSQL)(Not Only):
Redis、MongDB
对象存储,通过对象自身的属性来决定。
DBMS(数据库管理系统)
MySQL是一个 关系型数据库管理系统
由瑞典[MySQL AB](https://baike.baidu.com/item/MySQL AB/2620844) 公司开发,属于 Oracle 旗下产品
MySQL 是最流行的关系型数据库管理系统之一,
MySQL是开放源码软件,体积小、速度快,因此可以大大降低总体拥有成本。
中小型网站、大型网站,集群!
教程:https://www.cnblogs.com/xa-xiaochen/p/14684423.html
Sqyog软件安装:https://pan.baidu.com/s/1hK-YaUH2TjYJlVcUsHyLXA#list/path=%2F
创建数据库:
每一个sqlyog的执行操作,本质就是对应了一条sql语句,可以在历史记录查看
非常好用!
建表:
create database databaseName ; //创建一个数据库
DROP DATABASE databaseName;//删除数据库
mysql -uroot -p;//连接数据库
show databases;//查看所有数据库
use databaseName//使用/切换数据库
show tables;//查看数据库中的所有表
describe tableName;//查看数据库中该表的描述信息
SQL注释: --(单行注释) /*多行注释*/
CREATE TABLE `teacher`( `id` INT(10) NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL, `age` INT(3) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci; //创建表
ALTER table tableName RENAME AS newTableName//修改表名
ALTER table tableName ADD age int(11) //增加字段
ALTER table tableName MODIFY age varcher(11)//修改字段类型(约束)
ALTER table tableName CHANGE age ageNew//修改字段名(重命名)
ALTER table tableName DROP age //删除表的字段
DROP table IF EXISTS tableName //删除表
字段最好用 `` (Tab键上面)包起来
数值
数据类型 | 描述 | 大小 |
---|---|---|
tinyint | 十分小的数据 | 1Byte |
smallint | 较小的数据 | 2Byte |
mediumint | 中等大小的数据 | 3Byte |
int | 标准的数据 | 4Byte |
bigint | 较大的整数 | 8Byte |
float | 浮点数 | 4Byte |
double | 浮点数 | 8Byte |
decimal | 字符串形式的浮点数(金融计算) | 数字型,128bit |
字符串
数据类型 | 描述 | 大小 |
---|---|---|
char | 固定大小字符串 | 0-255 |
varchar | 可变字符串 | 0-65535 |
tinytext | 微型文本 | 2^8 -1 |
text | 文本串(大文本) | 2^16 -1 |
时间日期
类型 | 格式 | 描述 |
---|---|---|
data | YYYY-MM-DD | 日期格式 |
time | HH:mm:ss | 时间格式 |
datatime | YYYY-MM-DD HH:mm:ss | 常用时间格式 |
timestamp | 1970.1.1到现在的毫秒数 | 时间戳 |
year | - | 年份表示 |
Unsigned:
zerofill:
自增:
非空: NOT NULL
默认:
/* 每一个表,都必须存在以下五个字段! id 主键 version 乐观锁 is_delete 伪删除 gmt_create 创建时间 gmt_update 修改时间 */
建表语句:
student2 CREATE TABLE `student2` ( `id` int(4) NOT NULL AUTO_INCREMENT COMMENT '学号', `name` varchar(30) NOT NULL DEFAULT '匿名' COMMENT '姓名', `pwd` varchar(20) NOT NULL DEFAULT '123456' COMMENT '密码', `sex` varchar(2) NOT NULL DEFAULT '女' COMMENT '性别', `birthday` datetime DEFAULT NULL COMMENT '出生日期', `address` varchar(100) DEFAULT 'NULL' COMMENT '家庭住址', `email` varchar(50) DEFAULT 'NULL' COMMENT '邮箱', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
格式
create table tableName( `字段名` 列类型 [属性] [索引] [注释] `字段名` 列类型 [属性] [索引] [注释] ...... `字段名` 列类型 [属性] [索引] [注释] )[表类型][字符集设置][注释]
常用命令:
show create database databaseName//查看创建数据库的语句 show create table tableName //查看创建数据表的语句 desc tableName//显示表的结构
/*数据库引擎: INNODB(默认使用) MYISAM(早年间) */
INNODB | MYISAM | |
---|---|---|
事务支持 | 支持 | 不支持 |
数据行锁定 | 支持(行锁) | 不支持(表锁) |
外键 | 支持 | 不支持 |
全文索引 | 不支持 | 支持 |
表空间大小 | 较大 | 较小 |
常规使用操作:
在物理空间存在的位置
所有的数据库文件都在data目录下,一个文件夹对应一个数据库
本质还是文件存储!
MySQL 引擎在物理文件上的区别
设计数据库表的字符集编码
CHARSET=utf8
不设置的话,会是mysql默认的字符集编码(不支持中文!)
MySQL默认编码是Latin1,不支持中文
在 my.ini 中配置默认的编码
character-set-server=utf8
方式一:创建表时增加约束
CREATE TABLE `grade`( `gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id', `gradename` VARCHAR(50) NOT NULL COMMENT '年级名称', PRIMARY KEY(`gradeid`) )ENGINE=INNODB DEFAULT CHARSET=utf8`grade` CREATE TABLE `student` ( `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号', `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名', `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码', `sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性别', `birthday` DATETIME DEFAULT NULL COMMENT '出生日期',`student` `gradeid` INT(10) NOT NULL COMMENT '学生的年级', `address` VARCHAR(100) DEFAULT 'NULL' COMMENT '家庭住址', `email` VARCHAR(50) DEFAULT 'NULL' COMMENT '邮箱', PRIMARY KEY (`id`), KEY `FK_gradrid` (`gradeid`) , CONSTRAINT `FK_gradrid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`) )ENGINE=INNODB DEFAULT CHARSET=utf8
删除有外键关系的表的时候,必须要先删除引用别人的表,再删除被引用的表
方式二
-- 建表的时候没有外键关系 建表完成后添加外键关系 /* ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`); */
格式:
ALTER TABLE tableName ADD CONSTRAINT 约束名 FOREIGN KEY (字段名)
REFERENCES targeTableName(字段名)
以上操作都是物理外键,数据库级别的外键,我们不建议使用(避免数据过多造成困扰)
最佳实现:
DML语言:数据操作语言
insert into tableName(字段名,2,3...)values(值1,2,3...); //根据字段名匹配值(一一对应)
insert into tableName values(值1,2,3...) //根据字段名顺序匹配 值 insert into tableName values(值1,2,3...),(值1,2,3...)... //插入多行
update tableName SET colnum_name='xxx' WHERE 条件;//修改(带条件) update tableName SET colnum_name1='xxx', colnum_name2='xxx' ... WHERE 条件;//修改多个(带条件) //谨慎使用! update tableName SET name='xxx' ;//修改名字(不带条件)修改全部名字 /*条件: where 字句 运算符 运算符: 基本运算符 = <= ..... 特殊运算符 BETWEEN ... AND ... 代表范围 ( BETWEEN 2 AND 5) 【闭合区间】 //通过多个条件定位数据 update tableName SET colnum_name='xxx' WHERE 条件1 and 条件2 and 条件3...;
delete from tableName //避免这样写,会全部删除 delete from tableName where [条件] //删除一条记录(一行)
truncate tableName //清空表
DELETE的问题:删除完毕后,重启数据库,现象:
所有的查询语句都用它
select * from tableName //查询表的全部信息
select clonum_name1,clonum_name2 from tableName //查询指定字段
select clonum_name1 AS xxx,clonum_name2 AS xxx from tableName AS newTableName //给查询结果取别名,也可以给表取别名
//函数: Concat(a,b)//拼接字符串 => ab select concat('需要追加的字符串',clonum_name) AS newClonum_name from tableName;
select distinct 字段名 from tableName//发现重复数据 去重
运算符 | 语法 | 描述 |
---|---|---|
IS NULL | a is null | 如果操作符为null,结果为真 |
IS NOT NULL | a is not null | 如果操作符为not null,结果为真 |
BETWEEN | a between b and c | 若a在b和c之间,结果为真 |
LIKE | a like b | a匹配b |
IN | a in (a1,a2,a3…) | a在这些里面(其中一个) |
select clonumName1,clonumName2 form tableName where clonumName2 like '刘%' //查询姓刘的人 select clonumName1,clonumName2 form tableName where clonumName2 like '刘_' //查询姓刘的人,名字只有一个字的 select clonumName1,clonumName2 form tableName where clonumName2 like '刘__' //(两个下划线) //查询姓刘的人,名字只有两个字的 select clonumName1,clonumName2 form tableName where clonumName2 like '%刘%' //(两个下划线) //查询名字中带有’ 刘 ‘的人
in 具体的一个或多个值
select clonumName1,clonumName2 form tableName where clonumName1 in (1001,1002,1003) ; //范围 //查询1001,1002,1003 号学员 select clonumName1,clonumName2 form tableName where clonumAddressName1 in ('安徽') ; //范围 //查询地址在安徽的学员
分析需求,分析查询的字段来自哪些表(连接查询)?
确定使用那种连接查询?
select s.studentNo,studentName,SubjectNo,studentResult FROM student AS s INNER JOIN result AS r WHERE s.studentNo = r.studentNo //查询学生学号、名字、学科、成绩,因为学生表里面没有成绩和学科,所以要关联成绩表,它们之间的交叉点是 学生学号 这叫内联查询
select s.studentNo,studentName,SubjectNo,studentResult FROM student s LEFT JOIN result r ON s.studentNo=r.studentNo //左外连接
select s.studentNo,studentName,SubjectNo,studentResult FROM student s RIGHT JOIN result r ON s.studentNo=r.studentNo //右外连接
操作 | 描述 |
---|---|
inner join | 如果表中至少有一个匹配,就返回行 |
left join | 会从左表中返回行,即使右表中没有匹配 |
right join | 会从右表中返回行,即使左表中没有匹配 |
//查询缺考的同学 select s.studentNo,studentName,SubjectNo,studentResult FROM student s LEFT JOIN result r ON s.studentNo=r.studentNo WHERE studentResult IS NULL
join(连接的表) on (判断的条件): 连接查询
where 等值查询
E:查询了参加考试的学生信息:学号、学生姓名、科目名、分数
在三张不同的表里面
select s.studentNo,studentName,SubjectName studentResult from student s right join result r on r.studentNo=s.studentNo //先查学生表 和 考试表 right join 以考试表为基准 inner join subject sub on r.studentNo=sub.studentNo;
自连接
自己的表和自己的表连接 核心:一张表拆为两张表
父类:
categoryid | categoryName |
---|---|
2 | 信息技术 |
3 | 软件开发 |
5 | 美术设计 |
子类:
pid | categoryid | categoryName |
---|---|---|
3 | 4 | 数据库 |
2 | 8 | 办公信息 |
3 | 6 | web开发 |
5 | 7 | ps技术 |
操作:查询父类对应的子类关系
父类 | 子类 |
---|---|
信息技术 | 办公信息 |
软件开发 | 数据库 |
软件开发 | web开发 |
美术设计 | ps技术 |
-- 查询父子信息(把一张表看成两张一模一样的表) SELECT a.categoryname AS '父栏目',b.categoryname AS '子栏目' FROM category AS a,category AS b WHERE a.`categoryid`=b.`pid`;
结果:
-- 分页和排序 升序:ASC 降序 DESC -- 排序 order by clonumName ASC/DESC -- 分页: limit 0-pageSize;
-- 查询数据库1的所有考试结果(学号、科目编号、成绩),降序排序 -- 方式一 使用连接查询 select studentNo,r.subjectNo,studentResult from result r inner join subject sub on r.subjectNo=sub.subjectNo where subjectName='数据库结构1' order by studentResult DESC; -- 方式二 子查询 select studentNo,subjectNo,studentResult from result where subjectNo=( select subjectNo from subject where subjectName='数据库结构1' )order by studentResult DESC;
-- 分数不小于80分的学生学号和姓名(连接查询) select s.studentNo,studentName from student s inner join result r on s.studentNo=r.studentNo where studentResult>=80; -- 高等数学 分数不小于80分的学生学号和姓名(子查询) select s.studentNo,studentName from student s inner join result r on r.subjectNo=s.subjectNo where studentResult>=80 and subjectNo=( select subjectNo from subject where subjectName='高等数学' ); -- 高等数学 分数不小于80分的学生学号和姓名(连接查询) select s.studentNo,studentName from student s inner join result r on s.subjectNo=r.subjectNo inner join subject sub on r.subjectNo=sub.subjectNo where subjectName='高等数学' and studentResult>=80; -- 继续改造(由里及外执行) select studentNo,studentName from student where studentNo in( select studentNo from result where studentResult>=80 and subjectNo =( select subjectNo from subject where subjectName='高等数学' ) ); -- c语言 前5名同学的成绩信息(学号、姓名、分数) select studentNo,studentName,studentResult from student where studentNo in( select studentNo from result where studentResult in( select studentResult from result order by 0-5 DESC and subjectName=( select subjectName from subject where subjectName='c语言' ) ) );
一、数学函数
ABS(x) 返回x的绝对值 BIN(x) 返回x的二进制(OCT返回八进制,HEX返回十六进制) CEILING(x) 返回大于x的最小整数值 EXP(x) 返回值e(自然对数的底)的x次方 FLOOR(x) 返回小于x的最大整数值 GREATEST(x1,x2,...,xn)返回集合中最大的值 LEAST(x1,x2,...,xn) 返回集合中最小的值 LN(x) 返回x的自然对数 LOG(x,y) 返回x的以y为底的对数 MOD(x,y) 返回x/y的模(余数) PI()返回pi的值(圆周率) RAND()返回0到1内的随机值,可以通过提供一个参数(种子)使RAND()随机数 生成器生成一个指定的值。 ROUND(x,y)返回参数x的四舍五入的有y位小数的值 SIGN(x) 返回代表数字x的符号的值 SQRT(x) 返回一个数的平方根 TRUNCATE(x,y) 返回数字x截短为y位小数的结果
二、聚合函数(常用于GROUP BY从句的SELECT查询中)
AVG(col)返回指定列的平均值 COUNT(col)返回指定列中非NULL值的个数 MIN(col)返回指定列的最小值 MAX(col)返回指定列的最大值 SUM(col)返回指定列的所有值之和 GROUP_CONCAT(col) 返回由属于一组的列值连接组合而成的结果 三、字符串函数 ASCII(char)返回字符的ASCII码值 BIT_LENGTH(str)返回字符串的比特长度 CONCAT(s1,s2...,sn)将s1,s2...,sn连接成字符串 CONCAT_WS(sep,s1,s2...,sn)将s1,s2...,sn连接成字符串,并用sep字符间隔 INSERT(str,x,y,instr) 将字符串str从第x位置开始,y个字符长的子串替换为字 符串instr,返回结果 FIND_IN_SET(str,list)分析逗号分隔的list列表,如果发现str,返回str在list中的位置 LCASE(str)或LOWER(str) 返回将字符串str中所有字符改变为小写后的结果 LEFT(str,x)返回字符串str中最左边的x个字符 LENGTH(s)返回字符串str中的字符数 LTRIM(str) 从字符串str中切掉开头的空格 POSITION(substr,str) 返回子串substr在字符串str中第一次出现的位置 QUOTE(str) 用反斜杠转义str中的单引号 REPEAT(str,srchstr,rplcstr)返回字符串str重复x次的结果 REVERSE(str) 返回颠倒字符串str的结果 RIGHT(str,x) 返回字符串str中最右边的x个字符 RTRIM(str) 返回字符串str尾部的空格 STRCMP(s1,s2)比较字符串s1和s2 TRIM(str)去除字符串首部和尾部的所有空格 UCASE(str)或UPPER(str) 返回将字符串str中所有字符转变为大写后的结果
四、日期和时间函数
CURDATE()或CURRENT_DATE() 返回当前的日期 CURTIME()或CURRENT_TIME() 返回当前的时间 DATE_ADD(date,INTERVAL int keyword)返回日期date加上间隔时间int的结果(int必须按照关键字进行格式化),如:SELECTDATE_ADD(CURRENT_DATE,INTERVAL 6 MONTH); DATE_FORMAT(date,fmt) 依照指定的fmt格式格式化日期date值 DATE_SUB(date,INTERVAL int keyword)返回日期date加上间隔时间int的结果(int必须按照关键字进行格式化),如:SELECTDATE_SUB(CURRENT_DATE,INTERVAL 6 MONTH); DAYOFWEEK(date) 返回date所代表的一星期中的第几天(1~7) DAYOFMONTH(date) 返回date是一个月的第几天(1~31) DAYOFYEAR(date) 返回date是一年的第几天(1~366) DAYNAME(date) 返回date的星期名,如:SELECT DAYNAME(CURRENT_DATE); FROM_UNIXTIME(ts,fmt) 根据指定的fmt格式,格式化UNIX时间戳ts HOUR(time) 返回time的小时值(0~23) MINUTE(time) 返回time的分钟值(0~59) MONTH(date) 返回date的月份值(1~12) MONTHNAME(date) 返回date的月份名,如:SELECT MONTHNAME(CURRENT_DATE); NOW() 返回当前的日期和时间 QUARTER(date) 返回date在一年中的季度(1~4),如SELECT QUARTER(CURRENT_DATE); WEEK(date) 返回日期date为一年中第几周(0~53) YEAR(date) 返回日期date的年份(1000~9999) 一些示例: 获取当前系统时间: SELECT FROM_UNIXTIME(UNIX_TIMESTAMP()); SELECT EXTRACT(YEAR_MONTH FROM CURRENT_DATE); SELECT EXTRACT(DAY_SECOND FROM CURRENT_DATE); SELECT EXTRACT(HOUR_MINUTE FROM CURRENT_DATE); 返回两个日期值之间的差值(月数): SELECT PERIOD_DIFF(200302,199802);
在Mysql中计算年龄:
SELECT DATE_FORMAT(FROM_DAYS(TO_DAYS(NOW())-TO_DAYS(birthday)),'%Y')+0 AS age FROM employee;
这样,如果Brithday是未来的年月日的话,计算结果为0。
下面的SQL语句计算员工的绝对年龄,即当Birthday是未来的日期时,将得到负值。
SELECT DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(birthday, '%Y') -(DATE_FORMAT(NOW(), '00-%m-%d') <DATE_FORMAT(birthday, '00-%m-%d')) AS age from employee
五、加密函数
AES_ENCRYPT(str,key) 返回用密钥key对字符串str利用高级加密标准算法加密后的结果,调用AES_ENCRYPT的结果是一个二进制字符串,以BLOB类型存储
AES_DECRYPT(str,key) 返回用密钥key对字符串str利用高级加密标准算法解密后的结果
DECODE(str,key) 使用key作为密钥解密加密字符串str
ENCRYPT(str,salt) 使用UNIXcrypt()函数,用关键词salt(一个可以惟一确定口令的字符串,就像钥匙一样)加密字符串str
ENCODE(str,key) 使用key作为密钥加密字符串str,调用ENCODE()的结果是一个二进制字符串,它以BLOB类型存储
MD5() 计算字符串str的MD5校验和
PASSWORD(str) 返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不同的算法。
SHA() 计算字符串str的安全散列算法(SHA)校验和
示例:
SELECT ENCRYPT('root','salt'); SELECT ENCODE('xufeng','key'); SELECT DECODE(ENCODE('xufeng','key'),'key');#加解密放在一起 SELECT AES_ENCRYPT('root','key'); SELECT AES_DECRYPT(AES_ENCRYPT('root','key'),'key'); SELECT MD5('123456'); SELECT SHA('123456');
六、控制流函数
MySQL有4个函数是用来进行条件操作的,这些函数可以实现SQL的条件逻辑,允许开发者将一些应用程序业务逻辑转换到数据库后台。
MySQL控制流函数:
CASE WHEN[test1] THEN [result1]…ELSE [default] END如果testN是真,则
返回resultN,否则返回defaultCASE [test] WHEN[val1] THEN[result]…ELSE
[default]END 如果test和valN相等,则返回resultN,否则返回default
IF(test,t,f) 如果test是真,返回t;否则返回fIFNULL(arg1,arg2) 如果arg1不
是空,返回arg1,否则返回arg2NULLIF(arg1,arg2) 如果arg1=arg2返回
NULL;否则返回arg1这些函数的第一个是IFNULL(),它有两个参数,并且对
第一个参数进行判断。如果第一个参数不是NULL,函数就会向调用者返回第
一个参数;如果是NULL,将返回第二个参数。
如:SELECT IFNULL(1,2), IFNULL(NULL,10),IFNULL(4*NULL,‘false’);
NULLIF()函数将会检验提供的两个参数是否相等,如果相等,则返回NULL,如果不相等,就返回第一个参数。
如:SELECT NULLIF(1,1),NULLIF(‘A’,‘B’),NULLIF(2+3,4+1);
和许多脚本语言提供的IF()函数一样,MySQL的IF()函数也可以建立一个简单的条件测试,这个函数有三个参数,第一个是要被判断的表达式,如果表达式为真,IF()将会返回第二个参数,如果为假,IF()将会返回第三个参数。
如:SELECTIF(1<10,2,3),IF(56>100,‘true’,‘false’);
IF()函数在只有两种可能结果时才适合使用。然而,在现实世界中,我们可能发现在条件测试中会需要多个分支。在这种情况下,MySQL提供了CASE函数,它和PHP及Perl语言的switch-case条件例程一样。
CASE函数的格式有些复杂,通常如下所示:
CASE [expression to be evaluated] WHEN [val 1] THEN [result 1] WHEN [val 2] THEN [result 2] WHEN [val 3] THEN [result 3] ...... WHEN [val n] THEN [result n] ELSE [default result] END
这里,第一个参数是要被判断的值或表达式,接下来的是一系列的WHEN-THEN块,每一块的第一个参数指定要比较的值,如果为真,就返回结果。所有的WHEN-THEN块将以ELSE块结束,当END结束了所有外部的CASE块时,如果前面的每一个块都不匹配就会返回ELSE块指定的默认结果。如果没有指定ELSE块,而且所有的WHEN-THEN比较都不是真,MySQL将会返回NULL。
CASE函数还有另外一种句法,有时使用起来非常方便,如下:
CASE WHEN [conditional test 1] THEN [result 1] WHEN [conditional test 2] THEN [result 2] ELSE [default result] END
这种条件下,返回的结果取决于相应的条件测试是否为真。
示例:
mysql>SELECT CASE 'green' WHEN 'red' THEN 'stop' WHEN 'green' THEN 'go' END; SELECT CASE 9 WHEN 1 THEN 'a' WHEN 2 THEN 'b' ELSE 'N/A' END; SELECT CASE WHEN (2+2)=4 THEN 'OK' WHEN(2+2)<>4 THEN 'not OK' END ASSTATUS; SELECT Name,IF((IsActive = 1),'已激活','未激活') AS RESULT FROMUserLoginInfo; SELECT fname,lname,(math+sci+lit) AS total, CASE WHEN (math+sci+lit) < 50 THEN 'D' WHEN (math+sci+lit) BETWEEN 50 AND 150 THEN 'C' WHEN (math+sci+lit) BETWEEN 151 AND 250 THEN 'B' ELSE 'A' END AS grade FROM marks; SELECT IF(ENCRYPT('sue','ts')=upass,'allow','deny') AS LoginResultFROM users WHERE uname = 'sue';#一个登陆验证
七、格式化函数
DATE_FORMAT(date,fmt) 依照字符串fmt格式化日期date值
FORMAT(x,y) 把x格式化为以逗号隔开的数字序列,y是结果的小数位数
INET_ATON(ip) 返回IP地址的数字表示
INET_NTOA(num) 返回数字所代表的IP地址
TIME_FORMAT(time,fmt) 依照字符串fmt格式化时间time值
其中最简单的是FORMAT()函数,它可以把大的数值格式化为以逗号间隔的易读的序列。
示例:
SELECT FORMAT(34234.34323432,3); SELECT DATE_FORMAT(NOW(),'%W,%D %M %Y %r'); SELECT DATE_FORMAT(NOW(),'%Y-%m-%d'); SELECT DATE_FORMAT(19990330,'%Y-%m-%d'); SELECT DATE_FORMAT(NOW(),'%h:%i %p'); SELECT INET_ATON('10.122.89.47'); SELECT INET_NTOA(175790383);
八、类型转化函数
为了进行数据类型转化,MySQL提供了CAST()函数,它可以把一个值转化为指定的数据类型。类型有:BINARY,CHAR,DATE,TIME,DATETIME,SIGNED,UNSIGNED
示例:
SELECT CAST(NOW() AS SIGNED INTEGER),CURDATE()+0; SELECT 'f'=BINARY 'F','f'=CAST('F' AS BINARY);
九、系统信息函数
DATABASE() 返回当前数据库名 BENCHMARK(count,expr) 将表达式expr重复运行count次 CONNECTION_ID() 返回当前客户的连接ID FOUND_ROWS() 返回最后一个SELECT查询进行检索的总行数 USER()或SYSTEM_USER() 返回当前登陆用户名 VERSION() 返回MySQL服务器的版本 示例: SELECT DATABASE(),VERSION(),USER(); SELECTBENCHMARK(9999999,LOG(RAND()*PI()));#该例中,MySQL计算LOG(RAND()*PI())表达式9999999次。
分组过滤
-- 查询不同课程的名字、平均分、最高分、最低分 select subjectName,AVG(studentResult) as 平均分,MAX(studentResult),MIN(studentResult) from result inner join subject sub on r.subjectNo=sub.subjectNo group by r.subjectNo -->通过什么字段来分组 having 平均分>80
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
主要增强算法复杂度和不可逆性
MD5不可逆 ,具体的值的MD5值是一样的
MD5破解网站的原理,背后有一个字典,MD5加密后的值,加密前的值
-- ---------测试MD5加密--------- CREATE TABLE testmd5( `id` INT(4) NOT NULL, `name` VARCHAR(20) NOT NULL, `pwd` VARCHAR(50) NOT NULL, PRIMARY KEY (`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8; -- 明文密码 INSERT INTO testmd5 VALUES(1,'zhangsan',123456), (2,'lisi',123456),(3,'wangwu',123456); -- 加密 UPDATE testmd5 SET pwd=MD5(pwd) WHERE id=1; -- 加密全部的密码 UPDATE testmd5 SET pwd=MD5(pwd); -- 插入的时候加密 INSERT INTO testmd5 VALUES(4,'xiaoming',MD5(123456)); -- 如何校验:将用户传递进来的密码进行md5加密,然后对比加密后的值 SELECT * FROM testmd5 WHERE `name`='xiaoming' AND pwd=MD5(123456);
-- 顺序!!! select 去重(distinct) 要查询的字段 from 表 (表和字段可以取别名) xxx join 要关联的表 on 等值判断 where (具体的值/子查询语句) group by (通过哪个字段来分组) having (过滤分组后的信息 和where作用一样,只是位置不同) order by (通过哪个字段排序) ASC/DESC limit startIndex-pageSize
什么是事务?
要么都成功,要么都失败
转账例子:A->B && B<-A
将一组sql放在一个批次中去执行
事务原则:ACID原则 :原子性、一致性、隔离性、持久性
**事务的原子性(Atomicity):**是指一个事务要么全部执行,要么不执行,也就是说一个事务不可能只执行了一半就停止了。比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱。不可能划了卡,而钱却没出来。
这两步必须同时完成,要么就不完成。
**事务的一致性(Consistency):**是指事务的运行并不改变数据库中数据的一致性。例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变。
独立性(Isolation):事务的独立性也称作隔离性,是指两个以上的事务不会出现交错执行的状态。因为这样可能会导致数据不一致,更加具体的来讲,就是事务之间的操作是独立的。
隔离所导致的一些问题:
脏读:指一个事务读取了另外一个事务未提交的数据
不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同(这个不一定是错误,只是某些场合不对)
虚读(幻读)是指在一个事务内读取到了别的事务插入的数据,导致前后读取数量总量不一致。
**持久性(Durability):**事务的持久性是指事务提交执行成功以后,该事务对数据库所作的更改便是持久的保存在数据库之中,不会无缘无故的回滚。
执行事务
-- --------------------事务------------------------------ -- mysql 是默认开启事务自动提交的 SET autocommit = 0 /* 关闭 */ SET autocommit = 1 /*开启*/ -- 手动处理事务 SET autocommit = 0 -- 关闭自动提交 -- 事务开启 START TRANSACTION -- 标记一个事务的开始,从这条语句过后的sql都在同一个事务内 -- 提交:持久化 (成功!) COMMIT -- 回滚:回到原来的样子 (提交失败!) ROLLBACK -- 事务结束 SET autocommit = 1 /*开启自动提交*/ -- 了解 SAVEPOINT -- 设置一个事务的保存点 ROLLBACK TO SAVEPOINT -- 回滚到保存点 RELEASE SAVEPOINT -- 撤销指定的保存点
模拟场景
-- 转账------------------- CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci; USE shop; CREATE TABLE account( id INT(3) NOT NULL AUTO_INCREMENT, `name` VARCHAR(30) NOT NULL, money DECIMAL(30) NOT NULL, PRIMARY KEY (id) )ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO account(`name`,money)VALUES ('A',2000.00),('B',10000.00); -- 模拟转账------------------------------------------ SET autocommit = 0 -- 关闭自动提交 START TRANSACTION -- 开启事务 UPDATE account SET money=money-500 WHERE `name`='A'; -- A减500 UPDATE account SET money=money+500 WHERE `name`='B'; -- B加500 COMMIT; -- 提交事务 就是持久化了 ROLLBACK; -- 回滚 SET autocommit = 1 -- 恢复默认自动提交 -- -----------------------------------------------------
在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息。
MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质,索引是数据结构
在一个表中,主键索引只能有一个,而唯一索引可以有多个
基础语法:
-- 索引的使用----------------- -- 1.在创建表的时候给字段增加索引 -- 2.创建完毕后,增加索引 -- 3.create index 索引名 on 表(字段) -- 显示所有的索引信息 SHOW INDEX FROM student; -- 增加一个全文索引 (索引名(列名)) ALTER TABLE school.student ADD FULLTEXT INDEX `name`(`name`); -- EXPLAIN 分析sql执行的状况 EXPLAIN SELECT * FROM student; -- 非全文索引 EXPLAIN SELECT * FROM student WHERE MATCH(`name`)againstt('刘');
测试索引
-- 插入100万条数据 DELIMITER $$ -- 相当于标志(写函数之前必须要写) CREATE FUNCTION mock_data() RETURNS INT BEGIN DECLARE num INT DEFAULT 1000000; DECLARE i INT DEFAULT 0; WHILE i<num DO -- 插入语句 INSERT INTO app_user(`name`,email,phone,gender,`password`,age) VALUES(CONCAT('用户',i),'1933423120@qq.com' ,CONCAT('18',FLOOR(RAND()*((999999999-100000000)+100000000))) ,FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100)); SET i=i+1; END WHILE; RETURN i; END; SELECT mock_data(); SELECT * FROM app_user WHERE `name` ='用户9999'; EXPLAIN SELECT * FROM app_user WHERE `name` ='用户9999'; SELECT * FROM app_user; -- 索引命名: id_表名_字段名 -- create index 索引名 on 表(字段) CREATE INDEX id_app_user_name ON app_user(`name`); EXPLAIN SELECT * FROM app_user WHERE `name` ='用户9999';
创建索引后只查了一行(1 rows)(定位)执行效率明显提高了
执行耗时 : 0 sec
传送时间 : 0.001 sec
总耗时 : 0.001 sec
没创建索引之前之前是 992269 rows ,
执行耗时 : 1.423 sec
传送时间 : 0 sec
总耗时 : 1.424 sec
索引在小数据量的场景下用处不大,但在大数据的时候区别十分明显。
索引的数据结构
Hash 类型的索引
Btree:innodb 的默认数据结构
SQLyog 可视化管理
SQL 命令操作
用户表:mysql . user
本质:对这张表增删改查
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码'; CREATE USER Dylan IDENTIFIED BY '123456'; -- 修改密码(修改当前用户密码) SET PASSWORD = PASSWORD('111111'); -- 修改密码(修改指定用户密码) SET PASSWORD FOR Dylan = PASSWORD('111111'); -- 重命名 RENAME USER Dylan TO Dylan2 -- 用户授权 GRANT ALL PRIVILEGES 全部的权限 -- 除了给别人授权 GRANT ALL PRIVILEGES ON *.* TO Dylan2 -- 查看权限 SHOW GRANT FOR Dylan2 -- 查看指定用户的权限 SHOW GRANT FOR root@localhost -- root用户权限 -- 撤销权限 REVOKE 哪些权限,在哪个库撤销,给谁撤销 REVOKE ALL PRIVILEGES ON *.* FROM Dylan2; -- 删除用户 DROP USER Dylan2;
为什么要备份:
MySQL 数据库备份方式
直接拷贝物理文件
SQLyog(可视化工具) 中手动导出
命令行导出 : mysqldump
# mysqldump -h 主机 -u 用户名 -p 密码 数据库 表名 > 物理磁盘位置/文件名 mysqldump -h localhost -uroot -p123456 school student>d:/a.sql -- (student表) mysqldump -h localhost -uroot -p123456 school>d:/a.sql -- (整个库) # mysqldump -h 主机 -u 用户名 -p 密码 数据库 表名1 表名2 表名3 > 物理磁盘位置/文件名 -- (多张表) # 导入 -- 登录的情况下 切换到指定的数据库 source 备份文件 mysql -u 用户名 -p密码 库名<备份文件
备份数据库,防止数据丢失
糟糕的数据库设计:
良好的数据库设计
软件开发中,关于数据库的设计
设计数据库的步骤:(个人博客)
收集信息,分析需求
标识实体(把需求落地到每个字段)
标识实体之间的关系
为什么需要数据规范化?
三大范式
第一范式
要求数据库的每一列都是不可分割的原子项
第二范式
前提:满足第一范式
每张表只描述一件事情
第三范式
前提:满足第一范式和第二范式
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
为了规范数据库的设计
规范性和性能的问题
关联查询的表不得超过三张表
我们的程序会通过 数据库驱动 和数据库打交道!
JDBC
Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。
对于开发人员,我们只需要掌握 JDBC 接口的操作
java.sql
javax.sql
还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar
创建测试数据库 表
IDEA 创建一个普通项目
导入数据库驱动
新建lib目录 将 驱动 粘贴到里面(右键lib 标记为库)
/** * 第一个JDBC程序 * @author LY */ public class Demo01 { public static void main(String[] args) throws ClassNotFoundException, SQLException { //1.加载驱动 Class.forName("com.mysql.jdbc.Driver");//固定写法 加载驱动 //2. 用户信息和url String url="jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=utf8&useSSL=false"; String username="root"; String password="123456"; //3.连接成功,数据库对象 connection代表数据库 Connection connection = DriverManager.getConnection(url, username, password); //4.执行SQL的对象 Statement Statement statement = connection.createStatement(); //5. 执行SQL String sql="SELECT *FROM jdbctest.users"; ResultSet resultSet = statement.executeQuery(sql); while(resultSet.next()){ System.out.println("id="+resultSet.getObject("id")); System.out.println("name="+resultSet.getObject("name")); System.out.println("pwd="+resultSet.getObject("password")); System.out.println("email="+resultSet.getObject("email")); System.out.println("birthday="+resultSet.getObject("birthday")); } //6.释放连接 resultSet.close(); statement.close(); connection.close(); } }
自己产生的问题:
IDEA连接数据库问题(后面项目需要用到):
https://blog.csdn.net/liuqiker/article/details/102455077
【已解决】com.mysql.jdbc.exceptions.jdbc4.CommunicationsExcepti:Communications link failure ----mysql连接报错
最终查找原因解决办法如下;
第一种:
我之前使用的mysql版本是 5.7 驱动使用的是 com.mysql.jdbc.Driver
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
原因:useSSL=true时 数据库连接 安全认证不通过
解决办法:将useSSL true改为false (可以使用)
(我改为了false就不报错了)
第二种办法:
更换JDBC驱动:使用:com.mysql.cj.jdbc.Driver (需注意:需要指定时区serverTimezone:)
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
步骤总结:
1、加载驱动
2、连接数据库 DriverMannger
3、获得执行sql的对象 Statement
4、获得返回的结果集
5、释放连接
DriverManager
//DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //注册两次 Class.forName("com.mysql.jdbc.Driver");//固定写法 加载驱动 (推荐) Connection connection = DriverManager.getConnection(url, username, password); //connection代表数据库 //数据库设置自动提交 //事务提交 //事务回滚 connection.rollback(); connection.commit(); connection.setAutocommit();
URL
String url="jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=utf8&useSSL=false"; //mysql端口默认3306 //jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3;
Statement 、 PrepareStatement 执行SQL的对象
String sql='select * from users'//编写sql statement.executeQuery(sql);//查询操作 resultSet statement.execute(sql);//执行任何SQL(有个判断的过程,相对而言,效率比较低) statement.executeUpdate(sql);//更新、插入、删除,都用这个,返回一个受影响的行数
ResultSet 查询的结果集,封装了所有的查询结果
获得指定的数据类型
resultSet.getObject(); //在不知道列类型的情况下使用 resultSet.getString(); //知道列类型就使用对应的类型 resultSet.getInt(); resultSet.getFloat(); resultSet.getDate();
遍历、指针
resultSet.beforeFirst();//移动到最前面 resultSet.afterLast(); //移动到最后面 resultSet.next();//移动到下一个数据 resultSet.previous();//移动到前一行 resultSet.absolute(row)//移动到指定行
释放资源
//6.释放连接 resultSet.close(); statement.close(); connection.close();
jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过对这个对象向数据库发送增删改查语句即可。
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(增删改查语句导致了数据库几行数据发生了变化)。
Statement.executeQuery()方法用于向数据库发送查询语句,executeQuery()方法返回代表查询结果的ResultSet对象。
CRUD操作 -create
使用 executeUpdate(String sql) 方法完成数据添加操作,示例操作:
Statement st=conn.createStatement(); String sql="insert into user(...)values(...)"; int num=st.executeUpdate(sql); if(num>0){ System.out.println("插入成功!"); }
CRUD操作 -delete
使用 executeUpdate(String sql) 方法完成数据删除操作,示例操作:
Statement st=conn.createStatement(); String sql="delete from user where id=1"; int num=st.executeUpdate(sql); if(num>0){ System.out.println("删除成功!"); }
CRUD操作 -update
使用 executeUpdate(String sql) 方法完成数据修改操作,示例操作:
Statement st=conn.createStatement(); String sql="update user set name='张三' where name='李四'"; int num=st.executeUpdate(sql); if(num>0){ System.out.println("修改成功!"); }
CRUD操作 -select
使用 executeQuery(String sql) 方法完成数据查询操作,示例操作:
Statement st=conn.createStatement(); String sql="select * from user where id=1"; ResultSet resultSet = statement.executeQuery(sql); if(resultSet!=null){ System.out.println("查询成功!"); }
代码实现
1、提取工具类
package com.ly.utils; import java.io.InputStream; import java.sql.*; import java.util.Properties; /** * JDBC 工具类 * @author LY */ public class JdbcUtils { private static String driver=null; private static String url=null; private static String username=null; private static String password=null; static { try{ InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties"); Properties properties = new Properties(); properties.load(is); driver=properties.getProperty("driver"); url=properties.getProperty("url"); username=properties.getProperty("username"); password=properties.getProperty("password"); // 1.驱动只用加载一次 Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } //2. 获取连接 public static Connection getConnect() throws Exception{ return DriverManager.getConnection(url, username, password); } //3. 释放连接资源 public static void release (Connection conn, Statement st, ResultSet rs) throws Exception{ if(rs!=null){ rs.close(); } if(st!=null){ st.close(); } if(conn!=null){ conn.close(); } } }
2、编写增、删、改的方法 executeUpdate
package com.ly.utils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; /** * 测试类 * 测试插入(增加) * @author LY */ public class TestInsert { public static void main(String[] args) throws Exception { Connection connect=null; Statement statement=null; ResultSet resultSet=null; try { connect = JdbcUtils.getConnect(); statement = connect.createStatement(); String sql="insert into jdbctest.users values (5,'吕布','666666','lvbu@sina.com','1910-02-13')"; int num = statement.executeUpdate(sql); if(num>0){ System.out.println("插入数据成功!"); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(connect,statement,resultSet); } } }
package com.ly.utils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; /** * 测试删除 * @author LY */ public class TestDelete { public static void main(String[] args) throws Exception{ Connection connect=null; Statement statement=null; ResultSet resultSet=null; try { connect = JdbcUtils.getConnect(); statement = connect.createStatement(); String sql="delete from jdbctest.users where id=5"; int num = statement.executeUpdate(sql); if(num>0){ System.out.println("删除数据成功!"); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(connect,statement,resultSet); } } }
package com.ly.utils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; /** * 测试修改 * @author LY */ public class TestUpdate { public static void main(String[] args) throws Exception{ Connection connect=null; Statement statement=null; ResultSet resultSet=null; try { connect = JdbcUtils.getConnect(); statement = connect.createStatement(); String sql="update jdbctest.users set name='赵云' where name='吕布'"; int num = statement.executeUpdate(sql); if(num>0){ System.out.println("修改数据成功!"); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(connect,statement,resultSet); } } }
package com.ly.utils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; /** * 测试查询 * @author LY */ public class TestSelect { public static void main(String[] args) throws Exception { Connection connect=null; Statement statement=null; ResultSet resultSet=null; try { connect = JdbcUtils.getConnect(); statement = connect.createStatement(); String sql="SELECT *FROM jdbctest.users"; resultSet = statement.executeQuery(sql); while(resultSet.next()){ System.out.println("id="+resultSet.getInt("id")); System.out.println("name="+resultSet.getString("name")); System.out.println("pwd="+resultSet.getString("password")); System.out.println("email="+resultSet.getString("email")); System.out.println("birthday="+resultSet.getDate("birthday")); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(connect,statement,resultSet); } } }
SQL 注入
sql 存在漏洞,会被攻击导致数据泄露 (sql会被拼接)
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器器执行非授权的任意查询,从而进一步得到相应的数据信息。
示例:
package com.ly.utils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; /** * 测试SQL注入 * @author LY */ public class SqlInjection { public static void main(String[] args) throws Exception { //login("赵云","666666"); //SELECT* FROM users WHERE NAME='' OR '1=1' AND PASSWORD='666666'; //SELECT* FROM users WHERE NAME='' OR '1=1' AND PASSWORD='' or '1=1'; login("' or '1=1","' or '1=1"); } //登录业务 public static void login(String userName,String password) throws Exception{ Connection connect=null; Statement statement=null; ResultSet resultSet=null; try { connect = JdbcUtils.getConnect(); statement = connect.createStatement(); //SELECT* FROM users WHERE NAME='赵云' AND PASSWORD='666666'; String sql="SELECT *FROM jdbctest.users where name='"+userName+"'"+"and "+"password='"+password+"'" ; resultSet = statement.executeQuery(sql); while(resultSet.next()){ System.out.println("birthday="+resultSet.getObject("birthday")); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(connect,statement,resultSet); } } }
PreparedStatement 可以防止SQL 注入 并且效率更高!
1、新增
package com.ly.jdbctest2; import com.ly.utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.Date; /** * 测试PreparedStatement 插入 * @author LY */ public class TestInsert { public static void main(String[] args) throws Exception { Connection conn=null; PreparedStatement ps=null; try { conn = JdbcUtils.getConnect(); //区别 使用 ? 占位符代替参数 String sql="insert into jdbctest.users values (?,?,?,?,?)"; ps = conn.prepareStatement(sql);//预编译sql //手动给参数赋值 ps.setInt(1,5); ps.setString(2,"张飞"); ps.setString(3,"123456"); ps.setString(4,"zhngfei@sina.com"); //注意点 sql.Date 数据库 new java.sql.Date(new Date().getTime()) // util.Date Java new Date().getTime()(获得时间戳) ps.setDate(5,new java.sql.Date(new Date().getTime())); //执行 int i = ps.executeUpdate(); if(i>0){ System.out.println("增加成功!"); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(conn,ps,null); } } }
2、删除
package com.ly.jdbctest2; import com.ly.utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.Date; /** * PreparedStatement 测试删除 * @author LY */ public class TestDelete { public static void main(String[] args) throws Exception { Connection conn=null; PreparedStatement ps=null; try { conn = JdbcUtils.getConnect(); //区别 使用 ? 占位符代替参数 String sql="delete from jdbctest.users where name=?"; ps = conn.prepareStatement(sql);//预编译sql //手动给参数赋值 ps.setString(1,"张飞"); //执行 int i = ps.executeUpdate(); if(i>0){ System.out.println("删除成功!"); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(conn,ps,null); } } }
3、修改
package com.ly.jdbctest2; import com.ly.utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.Date; /** * preparedStatement 测试修改 * @author LY */ public class TestUpdate { public static void main(String[] args) throws Exception { Connection conn=null; PreparedStatement ps=null; try { conn = JdbcUtils.getConnect(); //区别 使用 ? 占位符代替参数 String sql="update jdbctest.users set name=? where name=?"; ps = conn.prepareStatement(sql);//预编译sql //手动给参数赋值 ps.setString(1,"法外狂徒"); ps.setString(2,"张三"); //执行 int i = ps.executeUpdate(); if(i>0){ System.out.println("修改成功!"); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(conn,ps,null); } } }
4、查询
package com.ly.jdbctest2; import com.ly.utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; /** * preparedStatement 测试查询 * @author LY */ public class TestSelect { public static void main(String[] args) throws Exception { Connection conn=null; PreparedStatement ps=null; ResultSet resultSet=null; try { conn = JdbcUtils.getConnect(); //区别 使用 ? 占位符代替参数 String sql="select * from jdbctest.users where name=?"; ps = conn.prepareStatement(sql);//预编译sql //手动给参数赋值 ps.setString(1,"法外狂徒"); //执行 resultSet = ps.executeQuery(); while (resultSet.next()){ System.out.println("id="+resultSet.getInt("id")); System.out.println("name="+resultSet.getString("name")); System.out.println("pwd="+resultSet.getString("password")); System.out.println("email="+resultSet.getString("email")); System.out.println("birthday="+resultSet.getDate("birthday")); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(conn,ps,resultSet); } } }
5、防止了SQL注入
package com.ly.jdbctest2; import com.ly.utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; /** * 测试SQL注入 * @author LY */ public class SqlInjection { public static void main(String[] args) throws Exception { //login("赵云","666666"); //SELECT* FROM users WHERE NAME='' OR '1=1' AND PASSWORD='666666'; //SELECT* FROM users WHERE NAME='' OR '1=1' AND PASSWORD='' or '1=1'; login("法外狂徒","123456"); } //登录业务 public static void login(String userName,String password) throws Exception{ Connection connect=null; PreparedStatement statement=null; ResultSet resultSet=null; try { connect = JdbcUtils.getConnect(); //SELECT* FROM users WHERE NAME='赵云' AND PASSWORD='666666'; String sql="SELECT *FROM jdbctest.users where name=? and password=?" ; statement = connect.prepareStatement(sql); statement .setString(1,userName); statement .setString(2,password); resultSet = statement.executeQuery(); while(resultSet.next()){ System.out.println("id="+resultSet.getObject("id")); System.out.println("name="+resultSet.getObject("name")); System.out.println("pwd="+resultSet.getObject("password")); System.out.println("email="+resultSet.getObject("email")); System.out.println("birthday="+resultSet.getObject("birthday")); } } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(connect,statement,resultSet); } } }
要么都成功,要么都失败
ACID 原则
原子性:要么都完成,要么都不完成
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交不可回滚,持久化到数据库了
隔离性的问题
脏读:一个事务读取了另一个没有提交的事务
不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
虚度(幻读):在一个事务内,读取到了别人插入的数据导致前后结果不一致
代码实现
1、开启事务
//关闭数据库的自动提交 自动会开启事务 conn.setAutoCommit(false);//开启事务
2、一组事务执行完毕,提交事务
//业务完毕,提交事务 conn.commit();
3、可以catch语句中显示定义回滚语句,但系统默认失败就会回滚
try { conn.rollback();//如果失败则回滚事务 } catch (SQLException ex) { ex.printStackTrace(); }
4、恢复数据库的自动提交(可以不用,因为系统默认恢复)、释放连接
//conn.setAutoCommit(true);//可以不用设置(系统默认恢复) JdbcUtils.release(conn,ps,rs);//释放连接
package com.ly.jdbctest; import com.ly.utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 测试事务(失败/成功的情况) * @author LY */ public class TestTransaction2 { public static void main(String[] args) { Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try { conn = JdbcUtils.getConnect(); //关闭数据库的自动提交 自动会开启事务 conn.setAutoCommit(false);//开启事务 String sql1="update jdbctest.account set money=money-100 where name='A'"; ps=conn.prepareStatement(sql1); ps.executeUpdate(); //就是好比中途出现了错误情况(如果没有这条语句,则执行成功) int x=1/0 //执行到这里会报错(下面就会回滚了) String sql2="update jdbctest.account set money=money+100 where name='B'"; ps=conn.prepareStatement(sql2); ps.executeUpdate(); //业务完毕,提交事务 conn.commit(); System.out.println("操作成功!"); } catch (Exception e) { // try { conn.rollback();//如果失败则回滚事务 //默认失败会回滚(系统隐式定义) } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); }finally { try { JdbcUtils.release(conn,ps,rs); } catch (Exception e) { e.printStackTrace(); } } } }
数据库连接—执行完毕------释放 (十分浪费系统资源)
池化技术:准备一些预先的资源,过来就连接准备好的
例子:银行 业务员(多个)–>等待–>服务 银行下班(关闭连接池)
常用连接数:
最小连接数:
最大连接数:
等待超时:100ms
编写连接池,实现一接口 DataSource
开源数据源实现
DBCP
C3P0
Druid: 阿里巴巴
使用了这些数据库连接池之后,我们在项目开发中就不需要连接编写数据库的代码了
DBCP (Database Connection Pool) 数据库连接池
用到的 jar 包
commons-dbcp-1.2.jar
commons-pool-1.6.jar
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
工具类
package com.ly.jdbctest3.utils; import com.ly.utils.JdbcUtils; import org.apache.commons.dbcp.BasicDataSourceFactory; import javax.sql.DataSource; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; public class JdbcUtils_DBCP { private static DataSource dataSource=null; static { try{ InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties properties = new Properties(); properties.load(is); //创建数据源 工厂模式 创建对象 dataSource = BasicDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } //2. 获取连接 public static Connection getConnect() throws Exception{ return dataSource.getConnection();//从数据源获取连接 } //3. 释放连接资源 public static void release (Connection conn, Statement st, ResultSet rs) throws Exception{ if(rs!=null){ rs.close(); } if(st!=null){ st.close(); } if(conn!=null){ conn.close(); } } }
C3P0
需要的 jar 包:
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar
工具类:
package com.ly.jdbctest3.utils; import com.mchange.v2.c3p0.ComboPooledDataSource; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; public class JdbcUtils_c3p0 { private static ComboPooledDataSource dataSource=null; static { try{ //代码配置 // dataSource= new ComboPooledDataSource(); // dataSource.setDriverClass(); // dataSource.setUser(); // dataSource.setPassword(); // dataSource.setJdbcUrl(); // dataSource.setMaxPoolSize(); //创建数据源 工厂模式 创建对象 dataSource = new ComboPooledDataSource("MySQL");//配置文件写法 } catch (Exception e) { e.printStackTrace(); } } //2. 获取连接 public static Connection getConnect() throws Exception{ return dataSource.getConnection();//从数据源获取连接 } //3. 释放连接资源 public static void release (Connection conn, Statement st, ResultSet rs) throws Exception{ if(rs!=null){ rs.close(); } if(st!=null){ st.close(); } if(conn!=null){ conn.close(); } } }
结论
无论使用什么数据源,本质还是一样的,DataSource接口不会变。方法就不会变
释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
工具类
package com.ly.jdbctest3.utils; import com.ly.utils.JdbcUtils; import org.apache.commons.dbcp.BasicDataSourceFactory; import javax.sql.DataSource; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; public class JdbcUtils_DBCP { private static DataSource dataSource=null; static { try{ InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties properties = new Properties(); properties.load(is); //创建数据源 工厂模式 创建对象 dataSource = BasicDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } //2. 获取连接 public static Connection getConnect() throws Exception{ return dataSource.getConnection();//从数据源获取连接 } //3. 释放连接资源 public static void release (Connection conn, Statement st, ResultSet rs) throws Exception{ if(rs!=null){ rs.close(); } if(st!=null){ st.close(); } if(conn!=null){ conn.close(); } } }
C3P0
需要的 jar 包:
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar
工具类:
package com.ly.jdbctest3.utils; import com.mchange.v2.c3p0.ComboPooledDataSource; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; public class JdbcUtils_c3p0 { private static ComboPooledDataSource dataSource=null; static { try{ //代码配置 // dataSource= new ComboPooledDataSource(); // dataSource.setDriverClass(); // dataSource.setUser(); // dataSource.setPassword(); // dataSource.setJdbcUrl(); // dataSource.setMaxPoolSize(); //创建数据源 工厂模式 创建对象 dataSource = new ComboPooledDataSource("MySQL");//配置文件写法 } catch (Exception e) { e.printStackTrace(); } } //2. 获取连接 public static Connection getConnect() throws Exception{ return dataSource.getConnection();//从数据源获取连接 } //3. 释放连接资源 public static void release (Connection conn, Statement st, ResultSet rs) throws Exception{ if(rs!=null){ rs.close(); } if(st!=null){ st.close(); } if(conn!=null){ conn.close(); } } }
结论
无论使用什么数据源,本质还是一样的,DataSource接口不会变。方法就不会变