Java学习笔记-Day60 MySQL高级(一)
一、连接数据库
连接数据库的方式:
(1)DOS命令窗口
(2)MySQL命令窗口
(3)Navicat软件
连接数据库的命令:
命令格式:mysql -h hostname -u username -p
- -h 指定要连接的MySQL数据库所在的主机,如果是本机,可以省略。
- -u 指定登录的用户名,如果不指定,默认是操作系统的登录用户名。
- -p 指定密码。可以在-p之后直接输入密码,但是这种方式密码将显示出来。也可以先不输入密码,回车之后系统会提示输入密码,此时再输入密码,密码将会使用*显示。
正确连接后将出现欢迎界面。
二、函数
1、字符串函数
函数 | 功能 |
---|---|
concat(s1,s2,…sn) | 连接s1,s2,…,sn为一个字符串 |
insert(str,x,y,instr) | 将字符串str从第x位置开始,y个字符长的子串替换为字符串instr |
lower(str) | 将字符串str中字符变为小写 |
upper(str) | 将字符串str中字符变为大写 |
left(str,x) | 返回字符串str最左边的x个字符 |
right(str,x) | 返回字符串str最右边的x个字符 |
lpad(str,n,pad) | 用字符串pad对str最左边进行填充,直到长度为n个字符长度 |
rpad(str,n,pad) | 用字符串pad对str最右边进行填充,直到长度为n个字符长度 |
ltrim(str) | 去掉字符串str左侧的空格 |
rtrim(str) | 去掉字符串str右侧的空格 |
repeat(str,x) | 返回str重复x次的结果 |
replace(str,a,b) | 用字符串b替换字符串str中所有出现的字符串a |
strcmp(s1,s2) | 比较字符串s1和s2 |
trim(str) | 去掉字符串行尾和行头的空格 |
substring(str,x,y) | 返回从字符串str的x位置起y个字符长度的字符串 |
2、数值函数
函数 | 功能 |
---|---|
abs(x) | 返回x的绝对值 |
ceil(x) | 返回大于x的最小整数值 |
floor(x) | 返回小于x的最大整数值 |
mod(x,y) | 返回x/y的模 |
rand() | 返回0~1内的随机值 |
round(x,y) | 返回参数x的四舍五入的有y位小数的值 |
truncate(x,y) | 返回数字x截断为y |
3、日期与时间函数
函数 | 功能 |
---|---|
curdate() | 返回当前日期 |
curtime() | 返回当前时间 |
now() | 返回当前的日期和时间 |
sysdate() | 返回返回当前日期时间 |
unix_timestamp(date) | 返回日期date的unix时间戳 |
from_unixtime | 返回unix时间戳的日期值 |
week(date) | 返回日期date为一年中的第几周 |
year(date) | 返回日期date的年份 |
hour(time) | 返回time的小时值 |
minute(time) | 返回time的分钟值 |
monthname(date) | 返回日期date的月份名 |
date_format(date,fmt) | 返回按字符串fmt格式化日期datee值 |
date_add(date,interval expr type) | 返回一个日期或时间值加上一个时间间隔的时间值 |
datediff(expr,expr2) | 返回起始时间expr和结束时间expr2之间的天数 |
timediff(time1,time2) | 返回两个时间相减得到的差值 (time1 - time2) |
timestampdiff(interval,datetime1,datetime2) | 返回datetime2-datetime1的差值,结果单位有interval参数给出 |
4、流程函数
函数 | 功能 |
---|---|
if(value,t,f) | 如果value是真,返回t;否则返回f |
ifnull(value1,value2) | 如果value1不为空,返回value1,否则value2 |
case when[value1] then [result]…else [default] end | 如果value1是真,返回result,否则返回default |
case [expr] when [value1] then [result1] … else [default] end | 如果expr等于value1,返回result1,否则返回default |
5、常用函数
函数 | 功能 |
---|---|
DATABASE() | 返回当前数据库名 |
VERSION() | 返回当前数据库版本 |
USER() | 返回当前登录用户名 |
INET_ATON(IP) | 返回IP地址的数字表示 |
INET_NTOA(num) | 返回数字代表的IP地址 |
PASSWORD(str) | 返回字符串str的加密版本 |
MD5() | 返回字符串str的MD5值 |
6、其它函数
函数 | 功能 |
---|---|
GREATEST(value1,value2,…) | 获取最大值 |
LEAST(value1,value2,…) | 获取最小值 |
COALESCE(value1,value2,…) | 取第一个非null值的字段 (coalesce(age,sex):有年龄就取年龄,没有年龄,就取性别) |
ISNULL(expr) | 如果值为空,就返回1 |
三、字符串加密
MD5(Message-Digest Algorithm 5)加密是一种不可逆的加密规则,用于确保信息的完整。任意长度的数据经过MD5加密后得到的值的长度都是固定的(16位或32位),并且对原数据修改一个字符对于加密后的值都有很大的变动。
数据库存放的密码是以密文的形式存在,用户输入的密码通过MD5加密成密文,再将此密文与数据库中的密码进行对比,如果相同即密码正确。
- MD5Util.java (自定义MD5算法)
package com.etc.bs.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 自定义MD5算法实现加密
* @author Administrator
*
*/
public class MD5Util {
/**
* 加密算法
* @param str
* @return
*/
public static String getEncodeByMd5(String str) {
try {
int i;
// 得到MessageDigest的对象
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
byte b[] = md.digest();
// 创建StringBuffer对象
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
// 返回结果为32位加密
return buf.toString();
// 返回结果为16位的加密
// return buf.toString().substring(8, 24);
} catch (NoSuchAlgorithmException e) {
// e.printStackTrace();
return null;
}
}
}
- 使用自定义MD5算法实现加密
String pwd = MD5Util.getEncodeByMd5(pwd);
四、MySQL存储引擎
MySQL5.7支持的存储引擎包括:
- InnoDB
- MyISAM
- MEMORY
- CSV
- BLACKHOLE
- ARCHIVE
- MERGE
- FEDERATED
- EXAMPLE
- NDE
- 其中InnoDB和NDB提供事务安全表,其他存储引擎都是非事务安全表。
MySQL5.5之前默认存储引擎是MyISAM,5.5版本之后改为InnoDB。
(1)修改默认存储引擎,在参数文件中设置:default_storage_engine
(2)查看当前的默认引擎:show variables like 'default_storage_engine'
(3)查询当前数据库支持的存储引擎:show engines \G
, 其中Support不同值的含义分别为:
- DEFAULT:支持并启用,并且为默认引擎
- YES:支持并启用
- NO:不支持
- DISABLED:支持,但是数据库启动的时候被禁用
(4)在创建新表的时候,可以通过增加engine关键字设置新建表的存储引擎。
(5)可以使用alter table语句,将一个已经存在的表修改成其他的存储引擎。
注意:修改表的存储引擎需要锁表并复制数据,对于线上环境的表进行这个操作非常危险,除非你非常了解可能造成的影响,否则在线上环境请使用其他方式(借助OSC工具)
1、MyISAM
MyISAM既不支持事务,也不支持外键,对事务完整性没有要求或者以SELECT、INSERT为主的应用可以使用这个引擎来创建表。可以用于只需要展示数据场景。
2、InnoDB
InnoDB提供了具有提交、回滚和崩溃恢复能力的事务安全保障,同时提供了更小粒度和更强的并发能力,拥有自己独立的缓存和日志。
对比MyISAM存储引擎,InnoDB会占用更多的磁盘空间以保留数据和索引。InnoDB支持事务和外键。
不同于使用其他存储引擎的表的特点:
(1)自动增长列
(2)外键约束
(3)主键和索引
- 满足唯一和非空约束;
- 优先考虑使用最经常被当作查询条件的字段或者自增字段
- 字段值基本不会被修改
- 使用尽可能短的字段
(4)存储方式
- 共享表空间存储
- 多有空间存储
五、SQL注入
结构化查询语言(SQL)是一种用来和数据库交互的文本语言。
SQL注入就是利用某些数据库的外部接口将用户数据插入到实际的数据库操作语言当中,从而达到入侵数据库乃至操作系统的目的。它的产生主要是由于程序对用户输入的数据没有进行严格的过滤,导致非法数据库查询语句的执行。
SQL注入攻击的危害:
- 读取、修改或删除数据库数据。
- 获取数据库中的敏感数据。
- 获取数据库管理员的权限。
(1)' or 1=1 --
(2)' or 1=1 /*
(3)' or 1=1 #
因为在SQL语句中,–、/*、# 都可以将后面的语句注释掉,在1=1(条件为真)恒成立的情况下,不用考虑用户名和密码是否正确。
//SQL注入(Statement)
String sql = "SELECT * FROM users WHERE uname = '"+uname+"' and upwd = '"+upwd+"';";
当 uname 为 ' or 1=1 --
,而upwd为123456,sql语句就会变为 SELECT * FROM users WHERE uname = '' or 1=1 -- ' and upwd = '123456';
,此时SQL语句就会通过。
解决方法:Statement(不安全)使用拼接字符串来传递参数的,而PrepareStatement(安全)是通过 ?占位符来传递参数,就不会出现SQL注入。
六、事务
1、事务的概念
事务概念:在单个会话期间,执行一系列有序的数据库操作。
执行sql语句时默认是隐式事务(自动提交)。
特性( A C I D ):
(1)原子性 ( A ):事务的所有步骤必须成功完成,否则,任何步骤都不会被提交。
(2)一致性 ( C ):事务的所有步骤必须成功完成,否则,所有数据都会被恢复到事务开始前的状态。
(3)隔离性 ( I ):未完成事务的所做得步骤必须与系统隔离,直到认为事务完成为止。
(4)持久性 ( D ):所有的数据都以某种形式保存,确保系统出现故障时,可以恢复到原始的状态。
事务只针对采用innodb存储引擎的表,对于采用myisam存储引擎的表不支持。
2、事务的实现
事务的实现步骤:
(1)开启事务:start transaction;
/ begin;
(2)执行语句
(3)提交事务:commit;
/ 回滚事务:rollback;
Java中实现事务:实现边界
(1)设置手动提交:conn.setAutoCommit(false);
(2)提交事务:conn.commit();
(3)回滚事务:conn.rollback();
注意:事务处理的conn对象必须是同一个对象。
应用场景:用户转账功能
- TestTransfer.java
package com.etc.bs.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestTransfer {
//数据库连接的url
private static final String url = "jdbc:mysql://localhost:3306/blogdb?useSSL=FALSE&serverTimezone=Asia/Shanghai";
//数据库连接的user
private static final String user = "root";
//数据库连接的password
private static final String password = "root";
public static void main(String[] args) {
transfer(1,2,50);
}
/**
* 转账功能
* @param payid 付款账户id
* @param receiptid 收款账户id
* @param balance 金额
*/
public static void transfer(int payid,int receiptid,double balance) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
String sql1="update users set ubalance=ubalance+? where userid=?;";
String sql2="update users set ubalance=ubalance-? where userid=?;";
try {
// 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 创建数据库连接
conn = DriverManager.getConnection(url, user, password);
// 设置手动提交事务(开始事务)
conn.setAutoCommit(false);
// 创建prepareStatement对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
// 给空格占位符赋值
setParameters(pstmt1, balance,receiptid);
setParameters(pstmt2, balance,payid);
// 执行具体操作,获取结果集
int n1 = pstmt1.executeUpdate();
int n2 = pstmt2.executeUpdate();
System.out.println(n1+"----"+n2);
// 判断sql是否都成功执行
if(n1>0 && n2>0) {
//提交事务
conn.commit();
System.out.println("转账成功!");
}else {
//回滚事务
System.out.println("事务自动回滚!");
conn.rollback();
}
} catch (Exception e) {
//处理异常
try {
//回滚事务
System.out.println("发生异常,事务自动回滚!");
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
//关闭资源
try {
pstmt1.close();
pstmt2.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 设置PreparedStatement的SQl语句的参数
*
* @param pstmt PreparedStatement对象
* @param params sql语句的参数
* @throws SQLException SQLException异常
*/
private static void setParameters(PreparedStatement pstmt, Object... params) throws SQLException {
if (params != null) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
}
}
}