Java 数据库学习

47 阅读6分钟

链接JBDC

注意:如果IDEA右侧没有Database的,看看你的IDEA是不是CE的,CE的和后面h2不兼容。 点击+号选择h2之后弹出下面的界面,填写URL。运行下面代码会有url,名字自己起。

public class Main1{  
    public static void main(String[] args) throws SQLException, ClassNotFoundException {  
        File projectDir = new File(System.getProperty("baseDir"), System.getProperty("user.dir"));  
        String jdbcUrl = "jdbc:h2:file:" + new File(projectDir, "xdmla").getAbsolutePath();  
        System.out.println(jdbcUrl);  
    }  
}

image.png

URL填写好之后,测试链接(Test Connection),然后Apply, OK, 生成数据库。

新增一个表

生成一个基础的user表,来举例子: password 不能是明文,只是来举例子的。

create table users(  
    id bigint primary key auto_increment,  
    name varchar(100),  
    password varchar(100),  
    tel varchar(20),  
    avatar varchar(100),  
    created_at timestamp,  
    updated_at timestamp  
)

image.png

  • create 创建
  • bigint 大数字
  • primary key 主键
  • auto_increment 自增
  • varchar 字符串(多长)
  • timestamp 时间戳

数据库SQL语句不区分大小写,命名风格是下划线分割(snake case)。图片中user是关键字,换成users,不然报错。

插入语句

insert into USERS (name, password, tel, avatar, created_at, updated_at)  
values ( '李四', '12345665432', '1301111111', 'http://www.baidu.com', now(), now())

image.png image.png

  • insert into 往表里插入
  • values 插入的值有哪些

删除数据

这个操作有危险,是删除users表中所有数据。

delete from USERS

指定删除哪条数据需要用where

delete from USERS where ID=1

image.png

修改数据

很多时候删除一条数据是危险的,用户找不回来了,我们在需求中一般用一个状态来表示。 接下来修改这个表,加入一个列:

alter table USERS add column status int default 1

这样就加入了status列,默认为1。 但是我想修改status数据为0:

update USERS set STATUS=0 where ID=2

image.png

这样status为0的数据就是用户不存在了,以后用户想要存在了就可以改这个字段

查询数据

基本的select用法,选择一条符合条件的数据

select id,NAME,TEL from USERS where AVATAR is null

上面代码的意思:选择 id name tel 从 users 表中,AVATAR 是 Null 的。

image.png 我想看某条数据有多少行

select count(*) from USERS where AVATAR is null

上面代码表示,AVATAR is null 的行数:

image.png

排序

想把数据排个序:

select * from USERS order by UPDATED_AT asc

order by 通过什么定制你想要的结果,asc是升序,desc是降序。 也可以先按照什么排序,再按照什么排序

select * from USERS order by UPDATED_AT, id, name asc

选出最大值

选出 id 最大的,当然字符串也能比较出最大的。

select max(id) from USERS

分页

limit(2) 每页两条数据

select * from USERS limit(2)

一对多的关系

用户和订单,每个用户多个订单。这时候需要外键(一个表的某个列是其他表的主键)。 注意:钱的存储数据格式是 decimal,十进制的表示。 创建一个订单的表:

create table orders  
(  
    id bigint primary key auto_increment,  
    name varchar(100),  
    user_id bigint,  
    created_at timestamp default now(),  
    updated_at timestamp default now(),  
    constraint order_pk primary key (id)  
);  
  
alter table ORDERS add column amount decimal default 0;
  
insert into ORDERS(name, user_id, AMOUNT)  
values ( '肥皂', 3, 10);  
insert into ORDERS(name, user_id, AMOUNT)  
values ( '水', 3, 15);  
insert into ORDERS(name, user_id, AMOUNT)  
values ( '水果', 2, 40 );

将users表中用户,下过单的都挑出来,按照注册时间倒序。

select * from USERS where id in (  
    select USER_ID from ORDERS  
) order by CREATED_AT desc;

上面代码表示从USERS表中 选出id 在 select USER_ID from ORDERS 这些id中。

image.png

用 java 如何查询

public class Main1{  
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        System.out.println(isCorrectPassword("1", "1"));  
        System.out.println(isCorrectPassword("李四", "1231231"));  
    }  
    // 判断用户名 密码 是否正确  
    public 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, "xdmla").getAbsolutePath();  
        Connection connection = DriverManager.getConnection(jdbcUrl);  

        Statement statement = connection.createStatement();  
        ResultSet resultSet = statement.executeQuery("select * from users where name = '" + username+"'" + "and password='" + password +"'");  
        return resultSet.next();  
    }  
}

当前是基础的代码进行查询,我们的密码当前有危险

SQL 注入

接下来进行攻破,攻破自己写的代码:

public static void main(String[] args) throws SQLException, ClassNotFoundException {
        System.out.println(isCorrectPassword("1", "1"));  
        System.out.println(isCorrectPassword("李四", "1231231"));
        System.out.println(isCorrectPassword("李四", "' or 1=1 --"));
}

第三条的打印数据本应该是false,才是我们期望的值,但是为true。

我们的sql最后的拼接是 ...password='' or 1=1 --,SQL注入:通过精心拼装的字符串攻破了你的数据库。

恐怖的是能够删除你数据库的记录....

预防 SQL 注入

public 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, "xdmla").getAbsolutePath();  
    try (  
        Connection connection = DriverManager.getConnection(jdbcUrl);  
        Statement statement = connection.prepareStatement("select * from users where name = ? and password = ?");  
    ) {  
        ResultSet resultSet = statement.executeQuery("select * from users where name = '" + username + "'" + "and password='" + password + "'");  
        return resultSet.next();  
    }  
}

connection.prepareStatement 能够预防SQL注入。

原理

原因是?相当于一个站位,当参数传进来时就会替换掉问号,而不会再次重新生成AST抽象语法树。

Docker 安装数据库

好处:

  • 兼容
  • 无残留
  • 统一、方便

去安装docker之后,运行:docker run --name java-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=****** -d mysql:8 密码自己设置

  • -p 3306:3306 : 将本地3306端口映射到docker的3306端口。本地访问3306之后自动访问到docker的3306端口上。

链接这个数据库

上面创建好了docker容器之后,本地点击 “+” 符号,采用MySQL。输入账号密码,看看你的端口号是不是3306。

image.png

创建使用数据库:mydb

show databases;  
create database mydb;  
show databases;  
use mydb;

安装 sequel pro 对本地数据库连接,展示本地数据库数据。可视化的对表进行增删改查操作。

数据持久化

现在我们的数据库是在docker上存储,我们本地是映射到docker上去的。 现在需要持久化,避免docker不存在了,数据还在。这就需要将docker映射到本地目录上。 由于MySQL版本最新的连接不上 sequel pro,所以我换成 postgressql。

docker run --name postgres -v `pwd`/pgdata:/var/lib/postgressql/data -p 3306:3306 -e POSTGRES_PASSWORD=qwe3518456 -d postgres:12

-v `pwd`/pgdata:/var/lib/postgressql/data 这个代码表示映射到本地绝对路径的pgdata 文件夹里 首先得创建这个文件夹,不然找不到。

数据库表设计原则

  • 每个实体一张表:用户/商品等
  • 每个实体一个主键
  • 按照业务创建索引
  • 每个关系用一张表联系

训练一下 select

limit 分页,接受两个参数,从第几个元素开始找、到底几个元素结束

image.png

group by xxx 选择xxx组,里面有多少个

image.png

select ORDERS.ID, ORDERS.USER_ID, ORDERS.ORDER_NUMBER, USERS.ID from ORDERS  
join USERS on USERS.ID = ORDERS.USER_ID

join xxx on 是一个加入操作,能够查找不同表的数据进行展示。能够展示交集,并集,或单独的左表,查找:

image.png

select count(distinct USER_ID) from ORDERS where ORDER_NUMBER = 1

上面代码表示:统计 ORDERS 表中 ORDER_NUMBER = 1 的 USER_ID 有多少个

image.png

总结

  • 链接JBDC
  • 学习SQL语句
  • 本地数据库持久化