DDL(Data Definition Language)
create table 建表语句
drop table 删表语句
alter table 修改表
基本SQL
- insert into - 增
- delete from - 删
- update - 改
- select - 查
使用JDBC访问
- Java Database Connection
- 连接串
- 用户名
- 密码,一般有这三样东西就可以连接数据库,类似这种
jdbc:h2:file:/Users/ories/Downloads/java-zhangbo-project/26/1/database-fix-exception-handling/xdml
- Statement
- PrepareStatement,和Statement的区别,这个可以防sql注入
- ResultSet
用户表是最常见的
- id是唯一的
create table user (id bigint primary key auto_increment)
- 创建用户表
create table user (id bigint primary key auto_increment,
name varchar(100),
password varchar(100),
tel varchar(20),
avatar varchar(100),
created_at timestamp,
update_at timestamp
)
- 重要的需要记住的知识
- SQL语句的关键词不需要区分大小写,
- 命名风格下划线,约定用下划线去进行区分大小写,camel case, snake case
- 数据库的字符串是单引号
- 数据库的注释是
--
插入语句
insert into user
(name, PASSWORD,TEL,AVATAR,CREATED_AT,UPDATE_AT)
values ( 'zhangsan', 'password123', '1234567890','http://123.com/1.jpg', now(), now() )
删除用户
delete from user where id=1
- 1 row affect表示影响了1行,如果update的时候是0 row affect就表示没有生效,没这条数据
- 很多情况下不用这么极端的删除,这种删除称为物理删除,数据被从数据库中抹去了
- 逻辑删除 数据还在数据库中,知识我们假装看不见而已
修改表
alter table user add status tinyint not null default 1
update user set STATUS = 0 where id = 2;
- delete和update非常危险一定要加where
数据库的查询快慢
- 一次查询差不多1ms
- 和数量有关
- 上亿之后才有必要优化,过早的优化是万恶之源
select 查询
select * from USER where AVATAR is not null
select count(*) from USER where AVATAR is not null
- 选出最大的id是哪个
Select *
Select count(*) count(1)
Select max/min/avg
Select limit 分页
select order by 排序
select is null/is not null
select where id in
用户表和订单表关联
- 方案一,缺点1.非常冗余,很多字段存了两遍2.怎么保证数据一致,用户表的手机改了,商品表手机没变

- 方案二,通过外键关联,外键的意思是一个表的某个列是其他表的主键
create table "order" (
id bigint auto_increment,
name varchar(100),
user_id bigint,
updated_at timestamp not null default now(),
created_at timestamp not null default now(),
constraint order_pk primary key (id)
)
- 钱这个东西一定不能用double或者float存,用big decimal,对应数据库decimal存
select * from user where id in (
select USER_ID from "order"
)
order by CREATED_AT ASC
- 新需求,订单编号 订单金额 用户名 用户头像,来源于两张表如何去查
如何用java的jdbc去查数据
package com.github.hcsp.exception;
import java.io.File;
import java.sql.*;
public class DatabaseReader {
public static void main(String[] args) throws SQLException {
System.out.println(isCorrectPassword("zhangsan", "password123"));
System.out.println(isCorrectPassword("zhangsan", "654321"));
}
private static boolean isCorrectPassword(String username, String password) throws SQLException {
File projectDir = new File(System.getProperty("basedir", System.getProperty("user.dir")));
String jdbcUrl = "jdbc:h2:file:" + new File(projectDir, "xdml").getAbsolutePath();
Connection connection = DriverManager.getConnection(jdbcUrl);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from user where name = '" + username + "'" + " and PASSWORD = '" + password + "'");
return resultSet.next();
}
}
- 永远不要相信用户传入的数据
- sql注入,攻击者通过精心设计的字符串,欺骗服务器,执行一些sql语句
System.out.println(isCorrectPassword("zhangsan", "' or 1=1 --"));
System.out.println(isCorrectPassword("zhangsan", "' or 1=1; delete from user where id=5; --"));
用完数据库一定要关系
- 两种东西要关闭,一种是connection,一种是statement
private static boolean isCorrectPassword(String username, String password) throws SQLException {
File projectDir = new File(System.getProperty("basedir", System.getProperty("user.dir")));
String jdbcUrl = "jdbc:h2:file:" + new File(projectDir, "xdml").getAbsolutePath();
try (Connection connection = DriverManager.getConnection(jdbcUrl); Statement statement = connection.createStatement()) {
String sql = "select * from user where name = '" + username + "'" + " and PASSWORD = '" + password + "'";
System.out.println(sql);
ResultSet resultSet = statement.executeQuery(sql);
return resultSet.next();
}
}
- 用PreparedStatement去防止sql注入,最终会被sql语句解析
package com.github.hcsp.exception;
import java.io.File;
import java.sql.*;
public class DatabaseReader {
public static void main(String[] args) throws SQLException {
System.out.println(isCorrectPassword("zhangsan", "' or 1=1; delete from user where id=5; --"));
}
private static boolean isCorrectPassword(String username, String password) throws SQLException {
File projectDir = new File(System.getProperty("basedir", System.getProperty("user.dir")));
String jdbcUrl = "jdbc:h2:file:" + new File(projectDir, "xdml").getAbsolutePath();
try (Connection connection = DriverManager.getConnection(jdbcUrl);
PreparedStatement statement = connection.prepareStatement("select * from user where name = ? and PASSWORD = ?")) {
statement.setString(1, username);
statement.setString(2, password);
ResultSet resultSet = statement.executeQuery();
return resultSet.next();
}
}
}
为什么第二种情况可以防止sql注入
- 第一种情况,写的sql最终会被sql引擎解析成ast

- 第二种情况,使用的时候已经被编译成了ast,参数传递进来的时候直接替换就行

Docker 方式安装书库与第三方客户端
- docker安装的好处百分之百兼容
- 百分百无残留
- 百分百统一,方便
- MySQL/PostgreSQL为例
docker内部就是一个独立的linux系统

- 运行docker,安装mysql5.7.27
docker run --name mysql -e MYSQL_ROOT_PASSWORD=123qweasd -d mysql:5.7.27
docker ps 查看docker运行的进程,
docker rm -f mysql 杀掉docker运行的进程
- 加上额外的-p参数,表示port端口,把docker容器的端口运行到机器的端口

docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123qweasd -d mysql:5.7.27
连接mysql的方法
- 方法一,sequel pro
- 方法二,idea自带的数据库连接
指定数据库,mysql的jdbc的连接
- 碰到问题,通过修改url成
jdbc:mysql://localhost:3306?autoReconnect=true&useSSL=false解决
- 创建数据库,
create database mydb;,执行一些命令
# show databases;
# create database mydb;
# show databases
# 使用mydb
# use mydb;
# 创建数据库user
# create table user (id int primary key, name varchar(20));
# 查询user
# select * from `user`;
- idea直接连接刚才创建的mydb
jdbc:mysql://localhost:3306/mydb?autoReconnect=true&useSSL=false
- 也可以用sql工具sequel pro去连接
用docker安装postgres
docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=123qweasd -d postgres:12
- docker rm -f postgres

- 现在启动的数据库的数据是不持久化的,docker重启了数据就没了
docker rm -f postgres
mkdir pgdata
docker run --name postgres -v `pwd`/pgdata:/var/lib/postgresql/data -p 5432:5432 -e POSTGRES_PASSWORD=123qweasd -d postgres:12
- mysql 持久化与之类似
docker run --name mysql -v pwd/mysql-data:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123qweasd -d mysql:5.7.27
- H2数据库不需要安装
squirrel sql的使用,支持所有jdbc数据库
- 要改下安装脚本,搜索
squirrelsql start script,stackover flow
- 总结先选择一个driver,相关驱动加入里面,创建数据库连接,比较丑,比较麻烦,但是支持所有jdbc,ide自带的数据库连接工具比较方便
SQL的高级SELECT语句与JOIN详解
- select 比较常用且比较容易被老板的需求影响
docker stop mysqldocker关掉数据库
数据库的表设计原则
- 每个实体一张表,(用户/商品)
- 每个关系用一张表联系,保持数据的一致性是很麻烦的
select * from user;
select count(*) from user;
select count(*) from user where ADDRESS='shanghai'
select id,name from user where ADDRESS='shanghai' order by id desc
select * from user limit <从第几个元素开始查找>,<最多返回几个元素>
select * from user limit 0,2
select ADDRESS, count(*) from user group by ADDRESS
select GOODS_ID, count(*) from `order` group by GOODS_ID
select GOODS_ID, count(*) as count from `order` group by GOODS_ID
select id, GOODS_ID, GOODS_NUM*GOODS_PRICE from "ORDER"
select GOODS_ID, SUM(GOODS_NUM*GOODS_PRICE) as TOTAL from "ORDER" group by GOODS_ID order by TOTAL desc;
select "ORDER".id,"ORDER".USER_ID,"ORDER".GOODS_ID, GOODS.NAME from "ORDER"
join GOODS
on "ORDER".GOODS_ID = GOODS.id;
- 默认inner join 内连,a和b都有
- left join只选择a链有
select "ORDER".id,"ORDER".USER_ID,"ORDER".GOODS_ID, GOODS.NAME from "ORDER"
left join GOODS
on "ORDER".GOODS_ID = GOODS.id;

select "ORDER".id,"ORDER".USER_ID,"ORDER".GOODS_ID, GOODS.NAME, user.name, user.TEL, user.ADDRESS
from "ORDER"
join GOODS
on "ORDER".GOODS_ID = GOODS.id
join USER
on "ORDER".USER_ID = USER_ID;
select "ORDER".id,"ORDER".USER_ID,"ORDER".GOODS_ID, GOODS.NAME, user.name, user.TEL, user.ADDRESS
from "ORDER"
join GOODS
on "ORDER".GOODS_ID = GOODS.id
left join USER
on "ORDER".USER_ID = USER.ID
where user.ADDRESS = 'shenzhen'

使用JDBC从数据库读取数据
- 写java sql之前先确保sql引擎没问题,再搬到java代码
select count(distinct USER_ID) from `ORDER` where GOODS_ID = 1;
select count(*) from user where id in
(
select USER_ID from `ORDER` where GOODS_ID = 1
)
select "ORDER".id, user.name as user_name, GOODS.NAME as goods_name, "ORDER".GOODS_ID, "ORDER".GOODS_NUM * "ORDER".GOODS_PRICE from "ORDER"
join GOODS
on "ORDER".GOODS_ID = GOODS.id
join USER
on "ORDER".USER_ID = USER.ID
select * from user order by ID desc limit (2-1) * 3, 2 * 3;
select GOODS_ID, GOODS.NAME, GOODS_PRICE * SUM(`ORDER`.GOODS_NUM) as GMV
from GOODS join "ORDER" on GOODS.ID = "ORDER".GOODS_ID GROUP BY GOODS.NAME order by GMV desc ;
课后练习
package com.github.hcsp.sql;
import java.io.File;
import java.math.BigDecimal;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class Sql {
public static class User {
Integer id;
String name;
String tel;
String address;
@Override
public String toString() {
return "User{" + "id=" + id + ", name='" + name + '\'' + ", tel='" + tel + '\'' + ", address='" + address + '\'' + '}';
}
}
public static int countUsersWhoHaveBoughtGoods(Connection databaseConnection, Integer goodsId) throws SQLException {
try (PreparedStatement statement = databaseConnection.prepareStatement("select count(distinct USER_ID) from `ORDER` where GOODS_ID = ?")) {
statement.setInt(1, goodsId);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
return resultSet.getInt(1);
}
}
return -1;
};
public static List<User> getUsersByPageOrderedByIdDesc(Connection databaseConnection, int pageNum, int pageSize) throws SQLException {
List<User> Users = new ArrayList<>();
try (PreparedStatement statement = databaseConnection.prepareStatement("select * from user order by ID desc limit ?, ?;")) {
statement.setInt(1, (pageNum - 1) * pageSize);
statement.setInt(2, pageNum * pageSize);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
User User = new User();
User.id = resultSet.getInt(1);
User.name = resultSet.getString(2);
User.address = resultSet.getString(4);
User.tel = resultSet.getString(3);
Users.add(User);
}
return Users;
}
}
public static class GoodsAndGmv {
Integer goodsId;
String goodsName;
BigDecimal gmv;
@Override
public String toString() {
return "GoodsAndGmv{" + "goodsId=" + goodsId + ", goodsName='" + goodsName + '\'' + ", gmv=" + gmv + '}';
}
}
public static List<GoodsAndGmv> getGoodsAndGmv(Connection databaseConnection) throws SQLException {
try (PreparedStatement statement = databaseConnection.prepareStatement("select GOODS_ID, GOODS.NAME, \n" +
" GOODS_PRICE * SUM(`ORDER`.GOODS_NUM) as GMV\n" +
"from GOODS join \"ORDER\" on GOODS.ID = \"ORDER\".GOODS_ID \n" +
"GROUP BY GOODS.NAME order by GMV desc ;")) {
ResultSet resultSet = statement.executeQuery();
List<GoodsAndGmv> GoodsAndGmvList = new ArrayList<>();
while (resultSet.next()) {
GoodsAndGmv GoodsAndGmv = new GoodsAndGmv();
GoodsAndGmv.goodsId = resultSet.getInt(1);
GoodsAndGmv.goodsName = resultSet.getString(2);
GoodsAndGmv.gmv = resultSet.getBigDecimal(3);
GoodsAndGmvList.add(GoodsAndGmv);
}
return GoodsAndGmvList;
}
}
public static class Order {
Integer id;
String userName;
String goodsName;
BigDecimal totalPrice;
@Override
public String toString() {
return "Order{" + "id=" + id + ", userName='" + userName + '\'' + ", goodsName='" + goodsName + '\'' + ", totalPrice=" + totalPrice + '}';
}
}
public static List<Order> getInnerJoinOrders(Connection databaseConnection) throws SQLException {
try (PreparedStatement statement = databaseConnection.prepareStatement("select \"ORDER\".id, user.name as user_name, GOODS.NAME as goods_name, \"ORDER\".GOODS_ID, \"ORDER\".GOODS_NUM * \"ORDER\".GOODS_PRICE from \"ORDER\"\n" +
" join GOODS\n" +
" on \"ORDER\".GOODS_ID = GOODS.id\n" +
" join USER\n" +
" on \"ORDER\".USER_ID = USER.ID")) {
return getResultSet(statement);
}
}
private static List<Order> getResultSet(PreparedStatement statement) throws SQLException {
ResultSet resultSet = statement.executeQuery();
List<Order> orders = new ArrayList<>();
while (resultSet.next()) {
Order order = new Order();
order.id = resultSet.getInt(1);
order.userName = resultSet.getString(2);
order.goodsName = resultSet.getString(3);
order.totalPrice = resultSet.getBigDecimal(4);
orders.add(order);
}
return orders;
}
public static List<Order> getLeftJoinOrders(Connection databaseConnection) throws SQLException {
try (PreparedStatement statement = databaseConnection.prepareStatement("select \"ORDER\".id, user.name as user_name, GOODS.NAME as goods_name, \"ORDER\".GOODS_ID, \"ORDER\".GOODS_NUM * \"ORDER\".GOODS_PRICE from \"ORDER\"\n" +
" left join GOODS\n" +
" on \"ORDER\".GOODS_ID = GOODS.id\n" +
" left join USER\n" +
" on \"ORDER\".USER_ID = USER.ID")) {
return getResultSet(statement);
}
}
public static void main(String[] args) throws SQLException {
File projectDir = new File(System.getProperty("basedir", System.getProperty("user.dir")));
String jdbcUrl = "jdbc:h2:file:" + new File(projectDir, "target/test").getAbsolutePath();
try (Connection connection = DriverManager.getConnection(jdbcUrl, "root", "Jxi1Oxc92qSj")) {
System.out.println(getUsersByPageOrderedByIdDesc(connection, 2, 2));
System.out.println(getInnerJoinOrders(connection));
}
}
}