• MySQL中级、Java中操作MySQL数据库、SQL语句、事务、jdbc、数据连接池(c3p0、druid)、Spring JDBC等


    1.数据库的概念:

    存放数据的仓库,用来按照特定的结构去组织和管理我们的数据,有数据库就可以更加方便的储存数据,英文名:DataBase,简称:DB.

    数据库只是存储数据的一种手段,最终数据是存放在硬盘中的,只是存放的格式不同于文本文件

    2.常见数据库软件:

    Oracle: 收费的大型数据库,Oracle 公司的产品。Oracle 收购 SUN 公司,收购 MYSQL
    DB2 : IBM 公司的数据库产品,收费的。常应用在银行系统中.
    SQLServer: MicroSoft 公司收费的中型的数据车。C#、.net 等语言常使用。
    SyBase:已经淡出历史舞台。提供了一个非常专业数据建模的工具 PowerDesigner。
    SQLite:嵌入式的小型数据库,应用在手机端。
    常用数据库:MYSQL,Oracle
    在 web 应用中,使用的最多的就是 MySQL 数据库,原因如下:
    1)开源、免费
    2)功能足够强大,足以应付 web 应用开发(最高支持千万级别的并发访问)

    3.MySQL安装:

    windows安装MySQL很简单,可以参考阅读:https://blog.csdn.net/weixin_46758988/article/details/116368575

    4.SQL:

    SQL全称Structured Query Language,意为结构化查询语言。说白了就是定义了操作所有关系型数据库的语言。每一种数据库的操作存在一定的差异,这个差异可以称为方言。

    SQL语法:

    1.SQL语句可以单行或多行书写,以分号结尾。

    -- 单行sql,查询数据库:
    SHOW DATABASES;
    # 多行sql:
    SELECT *
    FROM user;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.SQL语句不区分大小写,但是关键字建议大写。

    show databases;
    SHOW databases;
    
    • 1
    • 2

    3.注释:

    -- 单行注释,横线后面空格不能省略
    # 单行注释,空格可省略
    /*多行注释*/
    
    • 1
    • 2
    • 3
    5.SQL分类:

    sql可以大致分四类:

    1.DDL:操作数据库和表,如:

    #操作数据库:
    # 1.C(Create)创建
    CREATE DATABASE db1;#创建一个db1数据库
    CREATE DATABASE IF NOT EXISTS db2;#如果没有数据库db2就创建一个,否则不创建
    CREATE DATABASE IF NOT EXISTS db3 CHARACTER SET gbk;#如果没有数据库db3就创建一个,否则不创建,创建时并指定字符编码为gbk
    CREATE DATABASE db4 CHARACTER SET gbk;#创建一个数据库db4并指定字符编码集为gbk
    # 2.R(Retrieve)查询
    SHOW DATABASES;#查看数据
    SHOW CREATE DATABASE mysql;#查数据库mysql的创建信息(创建字符集,创建语句)
    # 3.U(Update)修改
    ALTER DATABASE db3 CHARACTER SET utf8;#修改db3数据库字符编码集为utf-8,且记这里是utf8
    # 4.D(Delete)删除
    DROP DATABASE db2;#删除数据库db2,谨慎删除。
    DROP DATABASE IF EXISTS db1;#如果数据库db1存在就删除
    # 5.使用数据库
    SELECT DATABASE();#查看当前正在使用的数据库
    USE db1;#使用数据库db1
    # 操作表:
    # 1.新增
    CREATE TABLE user_root(id INT, age INT); #给当前数据库新增一个user_root表,并给id和age两个字段,每个字段都需要指定类型
    CREATE TABLE stu LIKE user_root; #复制user_root表并命名为stu(创建一个表,让它跟user_root表一样)
    ALTER TABLE users ADD nickname VARCHAR(10); #给users表新增一列nickname,数据类型为可变字符varchar10个
    # 2.查询
    SHOW TABLES; # 查询当前使用的数据库中的所有表名称
    SHOW TABLES FROM db1; # 查询数据库db1中所有的表
    DESC user_tab; # 查询user_tab表结构
    SHOW CREATE TABLE users; #查询users表的结构信息
    # 3.删除
    DROP TABLE stu; #删除stu表
    DROP TABLE IF EXISTS user_root; #如果user_root表存在就删除
    ALTER TABLE users DROP sex; #删除users表中的sex字段
    # 4.修改
    ALTER TABLE user_root RENAME TO users; #将user_root表名称修改为users;
    ALTER TABLE users CHARACTER SET utf8; #修改users表的字符集为utf-8编码
    ALTER TABLE users CHANGE gender sex INT; #修改users表中gender字段为sex字段并设置类型为int
    ALTER TABLE users MODIFY sex VARCHAR(10); #修改users表中sex字段数据类型为varchar长度为10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    DML:操作表中的数据,对表中数据进行增删改

    # 1.新增数据
    INSERT INTO users(id,age,nickname) VALUES (0,12,'苦海123'); #给users表中新增一条数据,全部字段给值(0,12,'苦海123')可以在后面继续用逗号隔开,如:(0,12,'苦海123'),(1,22,'123')表示一次插入多条数据
    INSERT INTO users(id,nickname) VALUES (0,'苦海123'); #给users表中新增一条数据,只给id和nickname
    INSERT INTO users VALUES (0,15,'苦海123');#给users表中新增一条数据,如果给所有字段都给值,那么前面的字段可以省略,直接给值
    # 2.删除数据
    DELETE FROM users;#删除表中所有数据,不推荐这种方式,因为很耗费性能,可以使用下面方式代替删除表中所有数据,如:
    TRUNCATE TABLE users; #表示删除users表(表被删除了,数据也就没了),再创建一个一模一样的表
    DELETE FROM users WHERE id = 1; #删除users表中id为1的数据
    DELETE FROM users WHERE id IN (2,3);#删除users表中id存在后面括号中的值的数据
    # 3.修改数据
    UPDATE users SET age=15,nickname='kuhai123'; #修改users表中所有数据的age字段值为15,nickname值为kuhai123
    UPDATE users SET age=18,nickname='kuhai123' WHERE id = 1;#修改users表中id为1的数据age字段值为18,nickname值为kuhai123
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    SQL:查询表中的数据

    查询数据的语句结构:select 字段 from 表名 where 筛选条件 group bu 分组字段 having 分组之后的条件 order by 排序 limit 分页限定

    基础查询:
    SELECT * FROM student; #查询student表中所有字段对应的值
    
    SELECT nams,age FROM student; #查询student表中nams和age字段对应的数据
    
    SELECT DISTINCT address FROM student; #查询student表中address字段对应的数据并去重,这里的字段可以写多个,也可以写*,但是业务一般都是查一个字段,比如学生都来自哪里,DISTINCT表示去重
    
    SELECT nams,math,english, math + english AS totalsco FROM student; #查询student表中的nams、math、math + english求和重命名为totalsco展示,AS可以用空格代替
    SELECT nams,math + english FROM student; #查询nams字段、math + english求和并作为math + english字段展示所有数据
    
    SELECT nams,math + IFNULL(english,0) FROM student;#求和的过程中可能会出现某个字段对应的值为null,此时可以使用IFNULL函数替换,这个函数第一个值表示字段,第二个值表示要替换的数值
    
    SELECT * FROM student WHERE id <> 1;#查询student表中id不等于1的值,在sql中不等于用<>或!=表示
    
    SELECT * FROM student WHERE id BETWEEN 2 AND 5;#查询student表中id在2和5之期间的数据,这里2和5都是可以取到的。
    
    SELECT * FROM student WHERE id IN (1,2,5);#查询student表中id包含在1,2,5中的数据
    
    SELECT * FROM student WHERE nams LIKE '%李%';#查询student中nams字段包含李的数据,这里是模糊查询,其中百分号表示模糊匹配,李字前后都写百分号表示包含李字都会被匹配到,如果只在李字后面写,则只会匹配李字开头的
    SELE CT * FROM student WHERE nams LIKE '李%';#查询到student表中nams字段值李字开头的数据
    SELECT * FROM student WHERE nams LIKE '%李';#匹配到student表中nams字段值李字结尾的所有数据
    SELECT * FROM student WHERE nams LIKE '%华_';#匹配到student表中nams字段值华后面一个字的数据,其中下划线表示占1个字符
    SELECT * FROM student WHERE nams LIKE '__';#匹配到student表中nams字段值为2个字符的数据,其中下划线表示占位,一个下划线表示占一个字符串的位置
    
    SELECT * FROM student WHERE english IS NULL;#查询student表中englis字段值为null的数据,这里不能使用=做判断,也不可以使用!=判断不为null的数据,判断不是NULL用IS NOT NUll;
    
    SELECT * FROM student WHERE math > 90 AND english > 90;#查询student表中math和english都大于90的数据,这里的AND也可以使用&&代替,如:
    SELECT * FROM student WHERE math > 90 && english > 90;
    
    SELECT * FROM student WHERE math > 90 OR english > 90;#查询student表中math大于90或english大于90的数据,这里的OR也可以使用||代替,如:
    SELECT * FROM student WHERE math > 90 || english > 90;
    
    SELECT * FROM student ORDER BY math DESC, english DESC;#按照math字段对数据进行降序排序,如果math相同则再按照english字段降序排序,排序通过order by实现,多个条件用逗号隔开
    
    聚合函数:将一列数据作为一个整体,进行纵向计算=-
    SELECT COUNT(nams) FROM student;#统计student表中有多少条数据,count用来计算一个表中有多少数据,括号里面一般给一个字段,也可以给星号,但是不推荐,因为查星号毫无意义,需要强调的是count统计的是非null的值,所有如果nams字段对应的值有为null的数据,那么统计的结果并不等于表中所有数据的数量
    SELECT MAX(math) FROM student;#查询student表中math字段对应的最大值
    SELECT MIN(math) FROM student;#查询student表中math字段对应的最小值
    SELECT AVG(math) FROM student;#查询student表中math字段对应的平均值
    SELECT SUM(math) FROM student;#查询student表中math字段对应的和
    
    分组查询:将一类属性相同的数据分成一组然后做统计,分组查询的字段必须是分组字段或者聚合函数,其他值都没有任何意义
    SELECT sex,AVG(math),SUM(math),COUNT(id) FROM student GROUP BY sex;#统计student表中math字段对应值按sex字段分类,各类数据的平均值,求和,个数统计(一句话:统计student表中男女的平均值,求和,个数统计)
    
    SELECT sex,AVG(english),SUM(english),COUNT(id) FROM student WHERE english > 60 GROUP BY sex;#统计student表中english值大于60的数据,english字段对应值按sex字段分类,各类数据的平均值,求和,个数统计
    
    SELECT sex,AVG(english),SUM(english),COUNT(id) FROM student WHERE english > 60 GROUP BY sex HAVING COUNT(id) >2;#查询student表中english大于60的数据并按sex分组统计平均值,求和,统计个数,并留下分组后个数大于2的分组,需要注意的是where在分组之前限定,而having在分组后进行限定,where后不能跟聚合函数,而having后可以跟聚合函数
    
    SELECT * FROM student LIMIT 2,4;#表示越过2条查询4条数据,limit在mysql数据库中做分页查询,limit在SQLServer等数据库中不支持。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    约束:

    约束指对表中的数据进行限定,保证数据的正确性,有效性和完整性。约束可分为:主键约束primary key、非空约束not null、唯一约束unique、外键约束foreign key

    #非空约束:值不能为null
    CREATE TABLE stu(id INT, nams VARCHAR(20) NOT NULL); #创建一个stu表,对nams字段进行约束不能为null
    ALTER TABLE stu MODIFY nams VARCHAR(20);#当一个表中字段被进行了约束,可以通过此方法对其重新定义字段约束(取消某种约束,唯一约束、主键约束、除外)
    #唯一约束:不能有重复的值出现
    ALTER TABLE stu MODIFY id INT UNIQUE;#对stu表中id字段进行唯一约束,使其字段的值不能重复,此约束和上面一样,在创建时也可以添加,任意约束都可以在创建和修改中添加,只需要用空格隔开,null值不划分到重复中,需要注意。
    ALTER TABLE stu DROP INDEX id;#取消对stu表中id键的唯一约束,这里需要注意,主键不能通过修改取消。
    
    #主键约束:值非空且唯一,主键一张表只能有一个,主键是表中唯一标识
    ALTER TABLE stu MODIFY id INT PRIMARY KEY;#修改stu表中id为主键,在创建的时候也可以指定主键
    ALTER TABLE stu DROP PRIMARY KEY; #删除主键
    ALTER TABLE stu MODIFY id INT PRIMARY KEY AUTO_INCREMENT;#如果一个表中主键是唯一且是int类型,此时可以给主键加AUTO_INCREMENT,使其自动递增,当然可以在新增数据的时候可以指定值,指定值后,后面的值会在新的基础上增加1
    ALTER TABLE stu MODIFY id INT;#删除自动递增
    
    #外键约束:当两个表中有关联时,如:员工表中地址关联pid和地址表中id做关联时,如果员工表中有数据关联了地址表中数据,此时如果删除了地址表中被关联的数据就会用问题,此时可以借助外键约束来解决这个问题,当对某个表做外键约束时,关联的表中数据被删除时,如果这个关联的表中被删除的数据被有外键约束的表所引用,那么就提示不能被删除,也删除不掉,插入数据时,如果外键关联的数据不存在,则也添加不成,但是可以为NULL
    CREATE TABLE emplay (id INT PRIMARY KEY AUTO_INCREMENT, nams VARCHAR(30),dep_id INT, CONSTRAINT emp_dep_id FOREIGN KEY (dep_id) REFERENCES department(id));#创建表时做外键约束,创建一个emplay表,CONSTRAINT表示限制,限制名定义称为了emp_dep_id,FOREIGN KEY表示外键为emplay表中的dep_id,REFERENCES表示引用为department表中的主键id,一般都是关联主键id。
    ALTER TABLE emplay DROP FOREIGN KEY emp_dep_id; #删除emplay表的外键约束emp_dep_id
    ALTER TABLE emplay ADD CONSTRAINT emp_dep_id FOREIGN KEY (dep_id) REFERENCES department(id);#给已经创建好的表添加外键约束
    
    级联操作:当一个表有外键约束时,所引用的表中主键被修改时,此时所关联的数据就会发生错乱,当然这也是不能被修改的,如果想要修改,可以将表中关联id设置为NULL,之后在修改应用表中的id,然后将表中为NULL的值再改成新的id,当然这是比较麻烦且很笨的方式, 此时就可以借助级联操作。
    如果想要清楚的看到表之间的关联关系,可以借助sqlyog中的架构设计器,可以清除得看到各表间用线连接的视图。
    ALTER TABLE emplay ADD CONSTRAINT emp_dep_id FOREIGN KEY (dep_id) REFERENCES department(id) ON UPDATE CASCADE;#如果想要在修改了关联表中数据id,数据表中也同步自动更改,可以在创建表,外键时添加N UPDATE CASCADE,表示自动同步更新关联id
    ALTER TABLE emplay ADD CONSTRAINT emp_dep_id FOREIGN KEY (dep_id) REFERENCES department(id) ON DELETE CASCADE;#删除级联
    #级联比较耗性能,而且不便于操作,使用需谨慎。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    DCL:授权,是否有权操作数据库,可以对用户进行管理,授权是指对某一个用户进行权限的处理,此时就要操作mysql这个数据库:

    1.管理用户:

    USE mysql;#切换到mysql数据库:
    SELECT * FROM USER;#查询user表:
    #2.创建用户:
    CREATE USER 'kuhai123'@'localhost' IDENTIFIED BY '123456';#CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';这里创建的用户指定了主机,如果想要用户在任意的主机上面都能访问数据库,那么就要将localhost改为百分号%,表示通配符。
    #3.删除用户:
    DROP USER 'kuhai123'@'localhost';#DROP USER '用户名'@'主机名';
    #4.修改用户信息:(修改用户的密码)
    UPDATE USER SET PASSWORD = PASSWORD('sfs123') WHERE USER = 'kuhai123';#PASSWORD函数可以对密码进行加密处理,这里可能应版本问题导致报错,此时可以使用:
    ALTER USER 'kuhai123'@'localhost' IDENTIFIED WITH mysql_native_password BY '123123123fgv';#其中kuhai123为用户名,localhost为主机名,123123123fgv为新密码。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    修改root用户密码:

    #修改root用户的密码:想要修改其他用户的密码,首先你得知道root用户的密码,但是有的时候难免密码是会被忘记的,此时可以使用以下方式修改密码:
    #1.以管理员身份进入cmd终端:
    net stop mysql #停止mysql服务
    #2.使用无验证方式启动mysql服务:
    mysqld --skip-grant-tables #终端继续执行此命令无验证开启mysql服务,这里可能开启失败,导致后面无法进入,此时可以尝试使用命令:mysqld --console --skip-grant-tables --shared-memory 无验证开启myslq服务
    #3.当执行完第二步后,此时光标会卡在终端,此时新打开一个终端(无需管理员身份),继续输入命令:mysql -uroot -p,此时会提示输入密码,直接回车无需输入
    #4.回车后提示进入mysql的提示语,此时表示进入mysql数据库成功。
    #5.切换数据:use mysql;
    #6.修改新密码:alter user root@localhost identified by '123456'; 此时可能提示修改失败,那么就需要先刷新一下权限:flush privileges; 然后在执行:alter user root@localhost identified by '123456'; 之后会提示修改成功的提示语。
    #7.关闭两个终端即可。
    #8.以管理员权限打开终端输入命令启动mysql服务:net start MySQL
    #9.终端继续使用命令:mysql -uroot -p回车输入新的密码登录即可,SQLyog用新密码登录root账户可能会失败,失败请参考博客 https://blog.csdn.net/weixin_46758988/article/details/116368575 中MySQL数据可视化工具部分。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.权限管理:

    #1.查询权限:
    SHOW GRANTS FOR 'kuhai123'@'localhost';#SHOW GRANTS FOR '用户名'@'主机名';其中权限分别表示:USAGE(表示只能登录,使用其它数据库是不可以的)、SELECT(查询权限)
    #2.授予权限:
    GRANT SELECT ON db3.account TO 'kuhai123'@'localhost';#给localhost主机下的kuhai123用户授予db3数据库中account表的查询权限,此时使用kuhai123登录数据库后,只能看到db3一个数据库,且此数据库中只能看到一个account表,且只能查看数据,不能对数据修改删除等操作
    如果想要授权多个权限,则用逗号隔开即可,如:GRANT SELECT,DELETE,UPDATE,INSERT ON db3.account TO 'kuhai123'@'localhost';
    给某个用户一次性授权所有数据库和所有表:GRANT ALL ON *.* TO 'kuhai'@'localhost';#如果刚开始已经单个授予某些表,那么使用此方式授权会有部分库和表授权不上的情况。新用户一次性授权可授权全部库和表。
    #3.撤销权限:
    REVOKE SELECT ON db3.account FROM 'kuhai123'@'localhost'; #撤销localhost主机下kuhai123用户表对db3数据库下account表的查询权限。
    REVOKE ALL ON *.* FROM 'kuhai'@'localhost';#撤销localhost主机下kuahi用户的所有数据库和表的权限,注意,系统的部分数据库是无法撤销的如:information_schena
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    6.数据库设计范式:

    设计数据库时,需要遵循的一些规范被称为数据库设计的范式。它分为三类,分别是:

    第一范式:每一列都是不可分割的原子数据项,意思就是说每个字段只能存一种数据,如:姓名和性别推荐存两个字段,不推荐存一个字段。

    第二范式:在第一范式的基础上,非码属性必须完全依赖于候选码,分解理解:1.函数依赖:如果通过a属性的值可以确定唯一b的属性值,则称b依赖于a,如:通过学号可以确定姓名。2.完全函数依赖:如果a是一个属性组(学号+课程名称)确定一个属性值(该课程学分)3.部分函数依赖: 如果a是一个属性组,b属性的确定只需要依赖于a属性组中某个属性值即可,如:学号+课程名称属性组中,只通过学号就可以确定姓名 4.传递函数依赖:如果通过a属性或属性组的值可以确定唯一b属性的值,在通过b属性或属性组的值可以确定唯一c属性的值,则称c传递函数依赖于a如:学号依赖于系名,系名依赖于系主任 5.码:一张表中一个属性或属性组,被其他所有属性或属性组完全依赖,则称这个属性或属性组为该表的码,跟主键差不多一个意思。

    第三范式:在第二范式的基础上,任何非主属性不依赖于其它非主属性(在第二范式的基础上消除传递依赖)

    7.数据库备份和还原

    数据库的备份是防止数据异常导致数据丢失的情况出现。数据库备份的方式有两种:

    1.通过命令行:mysqldump -u 用户名 -p 密码 > 保存的路径,如:

    mysqldump -uroot -ptgbyhn123tgbyhn db1 > E://db1.sql
    
    • 1

    还原:登录数据库、创建数据库、使用数据库、执行文件(source 文件路径)

    CREATE DATABASE db1;
    USE db1;
    souurce E://db1.sql;命令行执行
    
    • 1
    • 2
    • 3

    提示: 在备份时明文输入密码会提示Warning. Using a password on the command line interface can be insecure.告诉这样不安全,但是数据是备份成功的,如果需要改这个提示,win则需要修改my.cnf配置文件夹,Linux则需要新建一个密码和用户名的配置文件,具体百度即可。

    2.通过图像化工具sqlyog:

    备份:鼠标移动到某个数据库上右击–备份导出–备份数据库

    还原:鼠标移动到数据库虚拟机上root@127.0.0.1右击 – 执行sql脚本(备份的sql脚本)

    8.多表查询:

    笛卡尔积:当查询一个表时有2条数据,当查询另一个表时有4条数据,当同时对这两个表进行查询时就会有8条数据(两表数据条数之积),然而连个表是有关系的,a表中有个关联键是关联了b表的主键,按道理查到的数据条数是a表条数数量才对,但是通过如下方式查询到的数据条数不对,且这种现象被称为笛卡尔积

    SELECT * FROM emplay, department;
    
    • 1

    解决笛卡尔积现象:

    多表查询可以解决笛卡尔积现象,多表查询分类:

    内连接查询:

    1.隐式内连接:使用where条件进行筛选无用数据:
    SELECT * FROM emplay, department WHERE emplay.dep_id = department.id; #通过emplay表中的dep_id等于department表中的id可以选择出有用的数据,这里查询到的是emplay表中所有的数据,并查到emplay表中所有数据分别对应在department表中的字段
    2.显式内连接:通过inner join on关键字进行数据的查询,inner可以省略:
    SELECT * FROM emplay INNER JOIN department ON emplay.dep_id = department.id;# 查询emplay表中的数据,并加入department表中id等于emplay表中dep_id的数据
    
    • 1
    • 2
    • 3
    • 4

    外连接查询:

    1.左外连接:通过LEFT OUTER JOIN ON关键字查询数据,左外连接的含义是查询左边表的所有数据,以及它们的交际部分,on后面等号不满足的字段用null显式,此处OUTER可以省略掉。
    SELECT * FROM emplay LEFT OUTER JOIN department ON emplay.dep_id = department.id;#查询到emplay表中所有的数据,即使emplay表中dep_id的值为null时,也是查询到所有,只是emplay表中dep_id为null的此条数据查询到后面department表中的数据都为null
    2.右外连接:通过RIGHT OUTER JOIN ON关键字查询数据,右外连接的含义是查询右边表的所有数据,以及它们的交际部分,on后面等号不满足的字段用null显式,此处OUTER可以省略掉,和左外连接查询时相对的,方向换掉一样。
    SELECT * FROM emplay RIGHT OUTER JOIN department ON emplay.dep_id = department.id;
    
    • 1
    • 2
    • 3
    • 4

    子查询:查询中嵌套查询,称嵌套查询为子查询。

    SELECT * FROM emplay WHERE salary = (SELECT MAX(salary) FROM emplay);#查询emplay表中salary最大值所对应的数据,按之前的方式一步是无法实现,可以分两步实现:1.查询最大值 2.使用where查询最大值所对应的数据,但是在很多时候分两条语句查询在后端处理时又比较繁琐,此时可以利用子查询,先查询括号中的值,然后通过where再筛选数据。
    
    1.子查询的结果是单行单列的:子查询的结果作为前面条件查询的条件,判断符多为:>、<、=、>=、<=等。
    SELECT * FROM emplay WHERE salary = (SELECT MAX(salary) FROM emplay);
    2.子查询的结果是多行单列的:判断符号一般为in
    SELECT * FROM emplay WHERE emplay.dep_id IN (SELECT id FROM department WHERE department.dep_name = '财务部' OR department.dep_name = '研发部');
    3.子查询的结果是多行多列的:如果子查询到的结果是多行多列的,此时子查询到的结果可以作为一张虚拟表hightSalary
    SELECT * FROM department, (SELECT * FROM emplay WHERE emplay.salary > 6000) hightSalary WHERE department.id = hightSalary.dep_id;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    练习:

    1.查询所有员工信息。查询员工编号,员工姓名,工资,职务名称,职务描述
    SELECT e.id,e.ename,e.salary,j.jname,j.description FROM emp e JOIN job j ON  e.job_id = j.id;
    SELECT e.id,e.ename,e.salary,j.jname,j.description FROM emp e, job j WHERE  e.job_id = j.id;
    
    2.查询员工编号,员工姓名,工资,职务名称,职务描述,部门名称部门位置
    SELECT e.id,e.ename,e.salary,j.jname,j.description,d.dname,d.loc FROM emp e, job j, dept d WHERE  e.job_id = j.id AND e.dept_id = d.id;
    SELECT e.id,e.ename,e.salary,j.jname,j.description,d.dname,d.loc FROM emp e JOIN job j ON  e.job_id = j.id JOIN dept d ON e.dept_id = d.id;
    
    3.查询员工姓名,工资,工资等级
    SELECT e.ename,e.salary, s.grade FROM salarygrade s, emp e WHERE e.salary >= s.losalary AND e.salary <= s.hisalary;
    SELECT e.ename,e.salary, s.grade FROM salarygrade s JOIN emp e ON e.salary >= s.losalary AND e.salary <= s.hisalary;
    SELECT e.ename,e.salary, s.grade FROM salarygrade s JOIN emp e ON e.salary BETWEEN s.losalary AND s.hisalary;
    SELECT e.ename,e.salary, s.grade FROM salarygrade s, emp e WHERE e.salary BETWEEN s.losalary AND s.hisalary;
    
    4.查询员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
    SELECT e.ename,e.salary,s.grade,d.dname,d.loc,j.jname,j.description FROM salarygrade s, emp e,dept d,job j WHERE e.dept_id = d.id AND e.job_id = j.id AND e.salary >= s.losalary AND e.salary <= s.hisalary;
    SELECT e.ename,e.salary,s.grade,d.dname,d.loc,j.jname,j.description FROM salarygrade s JOIN emp e ON e.salary >= s.losalary AND e.salary <= s.hisalary JOIN dept d ON e.dept_id = d.id JOIN job j ON j.id = e.job_id;
    SELECT e.ename,e.salary,s.grade,d.dname,d.loc,j.jname,j.description FROM salarygrade s, emp e,dept d,job j WHERE e.dept_id = d.id AND e.job_id = j.id AND e.salary BETWEEN s.losalary AND s.hisalary;
    SELECT e.ename,e.salary,s.grade,d.dname,d.loc,j.jname,j.description FROM salarygrade s JOIN emp e ON e.salary BETWEEN s.losalary AND s.hisalary JOIN dept d ON e.dept_id = d.id JOIN job j ON j.id = e.job_id;
    
    5.查询出部门编号、部门名称、部门位置、部门人数
    SELECT d.id,d.dname,d.loc,grouptotal.totalnum FROM dept d,(SELECT dept_id,COUNT(id) totalnum FROM emp GROUP BY dept_id) grouptotal WHERE d.id = grouptotal.dept_id;
    SELECT d.id,d.dname,d.loc,grouptotal.totalnum FROM dept d JOIN (SELECT dept_id,COUNT(id) totalnum FROM emp GROUP BY dept_id) grouptotal ON d.id = grouptotal.dept_id;
    
    6.查询所有员工的姓名及其直接上级的姓名,没有领导的员工也需要查询
    SELECT e1.id,e1.ename,e1.mgr,e2.id,e2.ename FROM emp e1, emp e2 WHERE e1.mgr = e2.id;#只查询有领导的也查
    SELECT e1.id,e1.ename,e1.mgr,e2.id,e2.ename FROM emp e1 LEFT JOIN emp e2 ON e1.mgr = e2.id; #没有领导的也查
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    9.事务:

    1.事务的基本介绍:如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败;事务的操作可分为:开启事务(start transaction)、回滚(rollback)、提交(commit);事务可以确保一套业务安全的完成;mysql数据库中事务默认自动提交(一条DML增删改语句就可以将数据库中的数据真实的修改掉,而Oracle数据库默认是不会自动提交的)

    #1.开启事务:在多个业务的最开始部分开启事务
    START TRANSACTION;
    
    #下面写每个业务
    UPDATE account SET balance = balance - 500 WHERE id = 1;
    出错。。
    UPDATE account SET balance = balance + 500 WHERE id = 2;
    
    #2.提交事务:前面业务执行成功,则提交事务
    COMMIT;
    
    #3.回滚事务:前面业务如果失败,则回滚到最初的状态
    ROLLBACK;
    
    #4.查询是否开启自动提交:1代表自动提交,0代表不自动提交
    SELECT @@autocommit;
    
    #5.修改默认提交方式:当提交方式改为0时表示需要手动提交,也就是说每次执行完增删改语句后,不COMMIT提交的话,其他地方查询的结果为未执行增删改前的数据,再次查询自己也是未更新前的数据
    SET @@autocommit = 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.事务的四大特征:

    1.原子性:是不可分割的最小操作单位,要么同时失败,要么同时成功
    2.持久性:当事务提交或回滚后,数据库会持久化保存数据
    3.隔离性:多个事务之间,相互独立。
    4.一致性:事务操作前后,数据总量不变
    
    • 1
    • 2
    • 3
    • 4

    3.事务的隔离级别:

    3. 事务的隔离级别(了解)
    	* 概念:多个事务之间隔离的,相互独立的。但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
    	* 存在问题:
    		1. 脏读:一个事务,读取到另一个事务中没有提交的数据
    		2. 不可重复读(虚读):在同一个事务中,两次读取到的数据不一样。
    		3. 幻读:一个事务操作(DML)数据表中所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改。
    	* 隔离级别:
    		1. read uncommitted:读未提交,没有提交的数据,另一个事务也是可以读到
    			* 产生的问题:脏读、不可重复读、幻读
    		2. read committed:读已提交,只有提交的数据,另一个事务才可以读取到 (Oracle默认隔离级别)
    			* 产生的问题:不可重复读、幻读
    		3. repeatable read:可重复读 (MySQL默认隔离级别)
    			* 产生的问题:幻读
    		4. serializable:串行化
    			* 可以解决所有的问题
    
    		* 注意:隔离级别从小到大安全性越来越高,但是效率越来越低
    		* 数据库查询隔离级别:mysql版本不同查询语句不同,具体按实际情况操作
    			* select @@tx_isolation;SELECT @@transaction_isolation;
    		* 数据库设置隔离级别:
    			* set global transaction isolation level  级别字符串(上面4种);
    			SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
    			
    		* 演示:
    		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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    10.JDBC:

    1.jdbc的感念:其全称:Java DataBase Connectivity,意为Java数据库连接,Mysql、Oracle、DB2等各个数据库之间都是存在着差异的,使用JDBC可以使用一套代码就能操作不同的关系型数据库,它定义了一套操作所有关系型数据库的规则,即接口,而下面各个数据库之间又有各自对应的实现类(也就是继承与多态,Java操作数据库使用的父类中的方法,而实际操作不同数据库时,实际是new了不同的子类,子类又继承于父类的规则,各个数据库厂商提供了遵循同一套接口的jar包),来达到同一个接口不同实现类对象,操作不同的关系型数据库,这类实现类被称为数据库驱动。

    2.jdbc操作mysql快速入门:

    先打开idea新建一个项目,这里我命名为:jdbcConnectMysqlDemo,文件下面继续创建一个模块并命名为jdbcdemo,在jdbcdemo下的src目录上面鼠标右击创建一个包,这里我起名为:cn.kuhai.jdbc,继续在cn.kuhai.jdbc包上鼠标右击创建一个类,这里我命名为:JdbcDemo.java

    // 1.导入jdbc操作mysql的jar包到项目:到官方https://dev.mysql.com/downloads/file/?id=477058,找到某个版本下载,这里我使用的版本为:8.0.11,老版本可能有漏洞,但是不影响学习,实际开发中或学习中遇到问题在解决就好了。
    // 2.注册驱动: 
    // 3.获取数据库连接对象Connection:
    // 4.定义sql:
    // 5.获取指定sql语句的对象Statement:
    // 6.执行sql,接收返回结果:
    // 7.处理结果:
    // 8.释放资源:
    
    //具体操作实例如下:
    package cn.kuhai.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.Statement;
    
    // JDBC快速入门:
    public class JdbcDemo {
        public static void main(String[] args) throws Exception {
            // 1.导入mysql数据库驱动jar包:导入前先在jdbcdemo模块下新建一个libs文件夹(目录),用来存放依赖,并将mysql-connector-java-8.0.11文件夹下的mysql-connector-java-8.0.11.jar包复制到libs文件夹中,鼠标右击libs文件夹并添加为库...
            // 2.注册驱动:
            Class.forName("com.mysql.cj.jdbc.Driver"); // 部分低版本注册表为:com.mysql.jdbc.Driver
            // 3.获取数据库的连接对象:DriverManager类下的getConnection方法用来连接数据库,url参数格式为:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2...,问号传参可以传递字符编码、时区等参数,其中jdbc:mysql表示为jdbc的mysql协议;后面两个参数分别为用户名和密码
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3?serverTimezone=GMT&useSSL=false","root","123456"); // serverTimezone=GMT设置时区,用来解决时区未知异常;useSSL=false禁用SSL连接,用来解决高版本mysql中默认建立SSL连接,当useSSL的值为true时需要提供证书,将verifyServerCertificate属性设置为“false”。
            // 4.定义一个sql语句,用来操作数据库:
            String updateSql = "update account set balance = 2000"; // 修改account表中balance的值为2000
            // 5.获取执行sql语句的对象Statement:Connection对象下的createStatement方法可以创建一个执行sql的对象
            Statement stmt = connection.createStatement();
            // 6.执行SQL:Statement对象下有很多方法,其中增删改查各不一样,executeUpdate用来更新数据,此方法返回一个int类型的值
            int count = stmt.executeUpdate(updateSql);
            // 7.处理结果,这里暂时没有什么业务可写,就打印看看是什么结果:
            System.out.println(count); // 2 ,修改了2条数据,这里返回了2
            // 8.释放资源:这里要释放Statement资源和Connection,实际项目中如果项目一直是在运行的,Connection可能要一直为程序服务,此时可能就会把Connection单独封装进行抽离到单独的文件中供全局项目使用,此时可能都没必要释放Connection
            stmt.close();
            connection.close();
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    3.对JDBC各个类和接口的详细介绍:

    //1.DriverManager:驱动管理对象,它有两个功能:
    	//1-1.注册驱动:static void registerDriver(Driver driver):注册给定的驱动程序DriverManager,在上面代码中做快速连接mysql的时候并未使用此方法,而是通过加载字节码文件的方式注册的:Class.forName("com.mysql.cj.jdbc.Driver"),经查看com.mysql.cj.jdbc.Driver的源码可以发现里面有一段静态代码块:(静态代码块会自动执行,因此当字节码文件加载到内存中时会自动执行一段registerDriver代码进行注册驱动,通过加载字节码文件的方式注册驱动只是为了代码简洁)
    	static {
            try {
                java.sql.DriverManager.registerDriver(new Driver());
            } catch (SQLException E) {
                throw new RuntimeException("Can't register driver!");
            }
        }
    	//1-2.获取数据库连接:
    	//static Connection getConnection(String url,String user,String password)
            //url:指定连接的路径:url参数格式为:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2...,如果是本地ip且端口为3306,则可简写为:jdbc:mysql:///数据库名称?参数键值对
    //2.Connection: 数据库连接对象:它有几个常用功能:
    	//2-1.获取执行sql的对象:createStatement和PreparedStatement都用来获取执行sql的对象,
    	//2-2.管理事务:
    		//开启事务:void setAutoCommit(boolean autoCommit),该方法的参数设置为false则开启事务
    		//提交事务:commit()
    		//回滚事务:rollback()
    //3.Statement:执行SQL的对象,它用来执行静态sql语句
    	//3-1.boolean execute(String sql):一般不怎么用,返回一个执行sql布尔值
    	//3-2.int executeUpdate(String sql):执行DML(insert、update、delete)语句,DDL(create、alter、drop)在java中一般不使用executeUpdate执行DDL语句对表和库进行操作,返回值表示影响的行数,如果大于0则表示执行成功。
    	//3-3.ResultSet executeQuery(String sql):执行DQL(select)语句,返回结果集
    //4.ResultSet: 结果集对象,封装查询结果
    	// 4-1.next()方法用来移动光标,向下移动一行光标。
    	// 4-2.getXxx()获取数据,Xxx代表数据类型,如getInt(),获取int类型的值、此方法接收一个int或string类型的值,当为int时表示获取哪一列的值(这个值从1开始),当为string时,表示获取哪个字段的值。
    //5.PreparedStatement: 还是执行SQL的对象,只是功能比Statement强大,它用来执行动态sql
    
    package cn.kuhai.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class JdbcDemo2 {
        public static void main (String[] args){
            // 防止后面try/catch中无法访问到变量,这里先将变量提升一下:
            Connection connection = null;
            Statement stmt = null;
            try {
                // 每个模块导入一次mysql的jar包即可,后面再无需导入,因此这里就不需要在导入了
                // 1.注册驱动:
                Class.forName("com.mysql.cj.jdbc.Driver");
                // 2.定义sql: 往accout表中插入一条数据
                String sql = "insert into account values (null, 'kuhai12', 2000)";
                // 3.获取Connection对象,并使用其getConnection方法连接数据库:
                connection =DriverManager.getConnection("jdbc:mysql://localhost:3306/db3?serverTimezone=GMT&useSSL=false","root","123456");
                // 4.获取执行sql的Statement对象:
                stmt = connection.createStatement();
                // 5.执行sql:调用Statement对象的executeUpdate执行DML语句
                int count = stmt.executeUpdate(sql);
                // 查看执行结果,这里为影响行数:
                if (count > 0) {
                    System.out.println("执行成功");
                } else {
                    System.out.println("执行失败");
                }
                System.out.println(count);
                // 6.处理异常:
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                // 刚开始stmt为null,此时调用close会抛异常,此时应该先做判断,不等于null时再去调用close方法
                if (stmt != null) {
                    try {
                        stmt.close();
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
                // connection同stmt变量一样处理:
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
    }
    
    // 处理结果集:
    package cn.kuhai.jdbc;
    
    import java.sql.*;
    
    // JDBC快速入门:
    public class JdbcDemo3 {
        public static void main(String[] args) {
            ResultSet result = null;
            Statement stmt = null;
            Connection connection = null;
            // 1.第二次使用,无需再导包
            try {
                // 2.注册驱动:
                Class.forName("com.mysql.cj.jdbc.Driver");
                // 3.获取数据库的连接对象:
                connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3?serverTimezone=GMT&useSSL=false","root","123456"); // serverTimezone=GMT设置时区,用来解决时区未知异常;useSSL=false禁用SSL连接,用来解决高版本mysql中默认建立SSL连接,当useSSL的值为true时需要提供证书,将verifyServerCertificate属性设置为“false”。
                // 4.定义一个sql语句:
                String selectSql = "select * from account"; // 查询account表中所有数据
                // 5.获取执行sql语句的对象Statement:
                stmt = connection.createStatement();
                // 6.执行SQL:Statement对象下有很多方法,executeQuery用来查询ResultSet类型的结果集:
                result = stmt.executeQuery(selectSql);
                // 光标向下移动一行:查询第一条数据前,应该先移动一次光标,查下一条时也需要先移动光标,当下一行没有数据时,再移动会抛异常,首次不移动也会报错
                // result.next();
                // // 获取数据:
                // int id = result.getInt(1);
                // String names = result.getString("NAME");
                // Double balance = result.getDouble("balance");
                // System.out.println(id + "-------" + names +  "------" + balance);
                // result.next();
                // // 获取数据:
                // int id2 = result.getInt(1);
                // String names2 = result.getString("NAME");
                // Double balance2 = result.getDouble("balance");
                // System.out.println(id2 + "-------" + names2 +  "------" + balance2);
                // 正确使用步骤:next返回一个布尔值,表示是否有数据
                while (result.next()) {
                    int id = result.getInt(1);
                    String names = result.getString("NAME");
                    Double balance = result.getDouble("balance");
                    System.out.println(id + "-------" + names +  "------" + balance);
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            } finally {
                if (stmt != null) {
                    try {
                        stmt.close();
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
                // connection同stmt变量一样处理:
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    定义一个方法,查询emp表中的数据,并将查询到的数据封装为一个个对象,然后装入集合中,并返回

    package cn.domai;
    
    import java.util.Date;
    
    // 封装emp表中对应字段对象的类:字段名称不一定和数据库表中一样,但是推荐一样
    public class Emp {
        private int id;
        private String ename;
        private int job_id;
        private Date joindate;
        private int mgr;
        private double salary;
        private double bounds;
        private int dept_id;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getEname() {
            return ename;
        }
    
        public void setEname(String ename) {
            this.ename = ename;
        }
    
        public int getJob_id() {
            return job_id;
        }
    
        public void setJob_id(int job_id) {
            this.job_id = job_id;
        }
    
        public Date getJoindate() {
            return joindate;
        }
    
        public void setJoindate(Date joindate) {
            this.joindate = joindate;
        }
    
        public int getMgr() {
            return mgr;
        }
    
        public void setMgr(int mgr) {
            this.mgr = mgr;
        }
    
        public double getSalary() {
            return salary;
        }
    
        public void setSalary(double salary) {
            this.salary = salary;
        }
    
        public double getBounds() {
            return bounds;
        }
    
        public void setBounds(double bounds) {
            this.bounds = bounds;
        }
    
        public int getDept_id() {
            return dept_id;
        }
    
        public void setDept_id(int dept_id) {
            this.dept_id = dept_id;
        }
    
        @Override
        public String toString() {
            return "Emp{" +
                    "id=" + id +
                    ", ename='" + ename + '\'' +
                    ", job_id=" + job_id +
                    ", joindate=" + joindate +
                    ", mgr=" + mgr +
                    ", salary=" + salary +
                    ", bounds=" + bounds +
                    ", dept_id=" + dept_id +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    package cn.kuhai.jdbc;
    
    import cn.domai.Emp;
    
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    public class JdbcDemo4 {
        public static void main(String[] args){
            // List list = findAll(); // 下面方法申明为静态方法,这里可以直接调用:
            List<Emp> list = new JdbcDemo4().findAll();
            System.out.println(list);
        }
        /*
        * 查询所有emp对象:
        * @return
        * */
        public static List<Emp> findAll(){
            ResultSet results = null;
            Statement stmt = null;
            Connection connection = null;
            Emp emp = null; // 定义一个每个对象共用的寄存器地址:
            List<Emp> list = new ArrayList<Emp>();
            try {
                // 1.注册驱动:可写可不写,mysql5后自动载入,因为包中有处理
                Class.forName("com.mysql.cj.jdbc.Driver");
                // 2.获取连接:
                connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3?serverTimezone=GMT&useSSL=false","root","123456");
                // 3.定义sql:
                String sql = "select * from emp;";
                // 4.获取执行sql语句的对象Statement:
                stmt = connection.createStatement();
                // 5.执行sql查询数据:
                results = stmt.executeQuery(sql);
                // 6.遍历结果集,封装对象并装到集合中:
                while(results.next()){
                    int id = results.getInt("id");
                    String ename = results.getString("ename");
                    int job_id = results.getInt("job_id");
                    int mgr = results.getInt("mgr");
                    Date joindate = results.getDate("joindate");
                    Double salary = results.getDouble("salary");
                    Double bonus = results.getDouble("bonus");
                    int dept_id = results.getInt("dept_id");
                    // 创建对象:
                    emp = new Emp();
                    // 给对象设置值
                    emp.setId(id);
                    emp.setEname(ename);
                    emp.setJob_id(job_id);
                    emp.setMgr(mgr);
                    emp.setJoindate(joindate);
                    emp.setSalary(salary);
                    emp.setBounds(bonus);
                    emp.setDept_id(dept_id);
                    // 将对象加入集合:
                    list.add(emp);
                    System.out.println(emp.toString());
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                if (stmt != null) {
                    try {
                        stmt.close();
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
                // connection同stmt变量一样处理:
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return list;
        };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    jdbc公共代码抽离封装:

    定义一个数据库信息配置文件jdbcConfig.properties:

    url=jdbc:mysql://localhost:3306/db3?serverTimezone=GMT&useSSL=false
    user=root
    password=123456
    driver=com.mysql.cj.jdbc.Driver
    
    • 1
    • 2
    • 3
    • 4

    JDBCutils工具类:

    package cn.util;
    
    import java.io.FileReader;
    import java.io.IOException;
    import java.net.URL;
    import java.sql.*;
    import java.util.Properties;
    
    // 这是一个对jdbc公共代码块的抽离:
    public class JDBCutils {
        // 定义变量:
        private static String url;
        private static String user;
        private static String password;
        private static String driver;
        // 1.定义数据库配置文件,并注册驱动:
        // 1-1.在src目录下创建jdbcConfig.properties配置文件,配置数据库相关配置和驱动配置
        // 1-2.定义静态代码块:自动执行一次文件,只执行一次操作获取配置文件中的值:
        static {
            try {
                // 1-2-1.创建Properties集合类来读取文件:Properties类可以读取文件
                Properties pro = new Properties();
                // 1-2-2.加载文件:这里文件路径写成src/jdbcConfig.properties是获取不到的,除非绝对路径,当然绝对路径是不合理的,这里最好的方法是通过ClassLoader这个类,它提供了获取src路径下的文件的类加载器:
                ClassLoader classLoader = JDBCutils.class.getClassLoader();
                URL urlObj = classLoader.getResource("jdbcConfig.properties"); // 它是以src为基础的,所以值传src后面的路径即可,返回一个URL对象,URL对象下的getPath可以获取到相对路径
                String paths = urlObj.getPath();
                pro.load(new FileReader(paths));
                // 1-2-3.获取配置文件中的数据:
                url = pro.getProperty("url");
                user = pro.getProperty("user");
                password = pro.getProperty("password");
                url = pro.getProperty("url");
                driver = pro.getProperty("driver");
                // 1-2-4.注册驱动:
                Class.forName(driver);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            ;
        }
        // 1.获取连接:抽取可能会用到数据库路径,密码,用户等等参数,每次调用getConnection方法都要传入这几个参数,太麻烦了,这里可以使用另一种方式:通过配置文件的方法,具体如下:
        public static Connection getConnection(){
            try {
                return DriverManager.getConnection(url,user,password);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        };
        // 2.释放资源:
        public static void close(ResultSet rs, Statement stmt, Connection conn){
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    测试JDBCutils工具类:

    package cn.kuhai.jdbc;
    
    import cn.domai.Emp;
    import cn.util.JDBCutils;
    
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    public class JdbcDemo5 {
        public static void main(String[] args){
            // List list = findAll(); // 下面方法申明为静态方法,这里可以直接调用:
            List<Emp> list = new JdbcDemo5().findAll();
            System.out.println(list);
        }
        /*
         * 使用封装的jdbc工具类,查询所有emp对象:
         * @return
         * */
        public static List<Emp> findAll(){
            ResultSet results = null;
            Statement stmt = null;
            Connection connection = null;
            Emp emp = null; // 定义一个每个对象共用的寄存器地址:
            List<Emp> list = new ArrayList<Emp>();
            try {
                // // 1.注册驱动:可写可不写,mysql5后自动载入,因为包中有处理
                // Class.forName("com.mysql.cj.jdbc.Driver");
                // // 2.获取连接:
                // connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3?serverTimezone=GMT&useSSL=false","root","123456");
    
                // 使用jdbc工具类代替上面代码:
                connection = JDBCutils.getConnection();
                // 3.定义sql:
                String sql = "select * from emp;";
                // 4.获取执行sql语句的对象Statement:
                stmt = connection.createStatement();
                // 5.执行sql查询数据:
                results = stmt.executeQuery(sql);
                // 6.遍历结果集,封装对象并装到集合中:
                while(results.next()){
                    int id = results.getInt("id");
                    String ename = results.getString("ename");
                    int job_id = results.getInt("job_id");
                    int mgr = results.getInt("mgr");
                    Date joindate = results.getDate("joindate");
                    Double salary = results.getDouble("salary");
                    Double bonus = results.getDouble("bonus");
                    int dept_id = results.getInt("dept_id");
                    // 创建对象:
                    emp = new Emp();
                    // 给对象设置值
                    emp.setId(id);
                    emp.setEname(ename);
                    emp.setJob_id(job_id);
                    emp.setMgr(mgr);
                    emp.setJoindate(joindate);
                    emp.setSalary(salary);
                    emp.setBounds(bonus);
                    emp.setDept_id(dept_id);
                    // 将对象加入集合:
                    list.add(emp);
                    System.out.println(emp.toString());
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                //if (stmt != null) {
                //    try {
                //        stmt.close();
                //    } catch (SQLException e) {
                //        throw new RuntimeException(e);
                //    }
                //}
                 connection同stmt变量一样处理:
                //if (connection != null) {
                //    try {
                //        connection.close();
                //    } catch (SQLException e) {
                //        throw new RuntimeException(e);
                //    }
                //}
    
                // 使用封装工具类JDBCutils中的close代替上面代码
                JDBCutils.close(results,stmt,connection);
            }
            return list;
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    PreparedSta防止sql注入:

    此接口用来解决sql注入问题,将来一系列的增删改查都会用PreparedSta来实现,那么,什么是sql注入呢,比如一个登录接口的SQL如下:

    String sql = 'select * from user where username = ' + username and 'password = ' + password
    如果给最后一个变量password传入 'xxx or 'a' = 'a',此时前面xxx和前面sql拼接起来用户名和密码不匹配是查不到数据的,当在后面继续拼接 or 'a' = 'a'时,此sql为恒成立,那么就会查到所有用户的信息,此时就不安全了,为了解决这个问题,sun公司提供了PreparedSta来解决sql注入的问题,它是一个预编译sql对象。
    
    • 1
    • 2

    其实和之前拼接sql的区别主要是:将变量通过?占位,然后在执行sql前进行处理,如:

    // 1.使用PreparedSta解决sql注入问题,第一步是将所有变量改为?进行占位:
    String sql = 'select * from user where username = ? and password = ?';
    // 2.使用Connection的prepareStatement()方法将定义含有占位的sql传入此方法,返回一个PreparedStatement对象,PreparedStatement对象也是要被释放的。
    PreparedStatement preStmt = connection.prepareStatement(sql);
    // 3.给占位符赋值:PreparedStatement对象有系列方法:setXXX,其中XXX表示类型,该类方法接收两个参数,第一个为第几个占位符,第二个参数为该占位所用到的变量,如:
    preStmt.setString(1, username);
    preStmt.setString(2, password);
    // 4.使用PreparedStatement对象空参数方法:executeQuery执行sql
    results = preStmt.executeQuery();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    JDBC管理事务:

    JDBC中开启事务用Connection类的方法:setAutoCommit(false);提交事务用Connection的commit方法,回滚事务用Connection的rollback方法,其使用步骤是:在执行sql前开启事务,在执行sql完提交事务,在异常中回滚事务,如:

    package cn.kuhai.jdbc;
    
    import cn.domai.Emp;
    import cn.util.JDBCutils;
    
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    public class JdbcDemo6 {
        public static void main(String[] args){
            ResultSet results = null;
            PreparedStatement pstmt1 = null;
            PreparedStatement pstmt2 = null;
            Connection connection = null;
            try {
                // 获取数据库连接:
                connection = JDBCutils.getConnection();
                // 1.开启事务:
                connection.setAutoCommit(false);
                // 定义两条转账的sql:
                String sql1 = "update account set balance = balance - ? where id = ?";
                String sql2 = "update account set balance = balance + ? where id = ?";
                // 获取执行sql的PreparedStatement对象:
                pstmt1 = connection.prepareStatement(sql1);
                pstmt2 = connection.prepareStatement(sql2);
                // 设置参数创建有效的sql:
                pstmt1.setDouble(1, 500);
                pstmt1.setInt(2, 1);
                pstmt2.setDouble(1, 500);
                pstmt2.setInt(2, 2);
                // 执行SQL:
                pstmt1.executeUpdate();
                // 手动给个异常,看看事务是否生效:发现设置这个错误代码后,数据库的数据没有修改
                // int num = 10 / 0;
                pstmt2.executeUpdate();
                // 2.提交事务:
                connection.commit();
            } catch (Exception e) {
                try {
                    // 3.当有异常抛出时,回滚事务:
                    if (connection != null) connection.rollback();
                } catch (SQLException ex) {
                    throw new RuntimeException(ex);
                }
                throw new RuntimeException(e);
            }
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    数据库连接池: 每次操作数据库之后都将资源释放掉了,其实每次申请资源是比较耗时的,此时可以使用数据库连接池来解决这个问题。数据库连接池是指一容器(集合)中有多个操作数据的元素,每个元素每次操作完数据库后并不会被释放,而是归还给池子了,供下一次操作数据库时使用。

    实现数据库连接池:

    C3P0:是一个比较老的数据库连接池,这里只是学习一下使用数据库连接池的步骤,实际中可能使用阿里的Druid比较多,c3p0包下载因仓库在国外,下载可能需要翻墙,或者百度找别人下载好的即可。

    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    public class C3p0Demo {
        public static void main(String[] args) throws SQLException {
            // 1.导包:将c3p0-0.9.5.5.jar和mchange-commons-java-0.2.19.jar两个包放到项目下libs文件夹中外,还要导入之前用过的数据库驱动jar包mysql-connector-java-8.0.11.jar,选中libs下所有jar包,鼠标右击添加到库...
            // 2.配置c3p0-config.xml配置文件,下面会有配置实例:(特别提醒:在高版本数据库驱动jar包下,c3p0的版本也要相对跟上,第一条经测试是连接成功的,还有一点就是,可能会报异常,此时可能连接地址还需要配置:?characterEncoding=utf-8&serverTimezone=UTC)
            // 3.创建数据库连接池对象:
            DataSource ds = new ComboPooledDataSource(); // 可接收一个c3p0-config.xml数据库配置的名称,用于连接对应名称配置的数据库,不传则连接默认配置的
            // 4.获取连接对象(这里可以获取maxPoolSize设置的最大个数个,超出最大数量且过响应时间就会报异常):
            Connection connection = ds.getConnection();
            System.out.println(connection);
    
            // 5.归还连接对象到池中:close在数据连接池中并非关闭释放资源,而是将数据库对象归还给池中,此时它还是存在于池中的,并且还是可以继续使用的。
            connection.close();
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    c3p0-config.xml配置文件如下:

    
    <c3p0-config>
    
      <default-config>
        <property name="driverClass">com.mysql.cj.jdbc.Driverproperty>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/db3?characterEncoding=utf-8&serverTimezone=UTCproperty>
        <property name="user">rootproperty>
        <property name="password">123456property>
        <property name="initialPoolSize">5property>
        <property name="checkoutTimeout">3000property>
        <property name="idleConnectionTestPeriod">30property>
        <property name="maxIdleTime">30property>
        <property name="maxPoolSize">100property>
        <property name="minPoolSize">10property>
        <property name="maxStatements">200property>
      default-config>
    
    
      <named-config name="kuhai123C3p0">
        <property name="driverClass">com.mysql.cj.jdbc.Driverproperty>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/db4?characterEncoding=utf-8&serverTimezone=UTCproperty>
        <property name="user">rootproperty>
        <property name="password">123456property>
        <property name="initialPoolSize">5property>
        <property name="checkoutTimeout">3000property>
        <property name="idleConnectionTestPeriod">30property>
        <property name="maxIdleTime">30property>
        <property name="maxPoolSize">100property>
        <property name="minPoolSize">10property>
        <property name="maxStatements">200property>
      named-config>
    c3p0-config>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    **Druid:**数据库连接池,是阿里巴巴提供的,速度和性能都是比较客观的,用法和c3p0基本一致,后面项目中也是推荐使用Druid连接池,其下载地址为:https://repo1.maven.org/maven2/com/alibaba/druid/ 可根据自己数据库驱动器的版本选择不同的版本。

    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.util.Properties;
    
    public class DruidDemo {
        // druid使用步骤
        public static void main(String[] args) throws Exception {
            // 1.在项目根目录下新建libs文件夹,并将druid-1.2.9.jar包,外加数据库驱动jar包:mysql-connector-java-8.0.11.jar复制到libs文件夹中,选中这两个包,鼠标右击添加为库...
            // 2.定义配置文件:以properties形式配置、可以任意名称,任意目录下、需要手动加载文件(这里我也是放到src下面了,并命名为:druid.properties)
            InputStream ins = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties"); // getClassLoader获取当前类的类加载器,getResourceAsStream然后在加载器中加载某个资源,并以流的形式返回
            Properties pro = new Properties(); // 用于获取配置文件:druid.properties的Properties对象
            pro.load(ins); // 加载配置文件
            // 3.获取数据库连接池对象:通过工厂类获取(DruidDataSourceFactory)
            DataSource ds = DruidDataSourceFactory.createDataSource(pro);
            // 4.获取连接:getConnection
            Connection connection = ds.getConnection();
            System.out.println(connection);
        };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    druid.properties配置文件:

    driverClassName=com.mysql.cj.jdbc.Driver
    # 如果不传参数,根据默认配置会报异常的,所以这里推荐将serverTimezone=GMT&useSSL=false配置上
    url=jdbc:mysql://localhost:3306/db3?serverTimezone=GMT&useSSL=false
    username=root
    password=123456
    # 初始化连接数量
    initialSize=5
    # 最大连接数
    maxActive=10
    maxWait=3000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    druid工具类(JdbcDruidPool)封装:实际开发中连接池也不是在每一个模块或类中都会创建,而是在项目中创建一个连接池(如果是同一个数据库配置的话,一般是一个连接池就好了),如:

    JdbcDruidPool工具类:

    package utils;
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    // 封装一个数据库连接池工具类:在src下面新建一个文件夹(软件包)并命名为:utils,在utils包下新建一个JdbcDruidPool数据库连接池类
    public class JdbcDruidPool {
        // 1.定义一个成员变量DataSource
        private static DataSource ds;
        // 利用静态代码块自动加载配置文件
        static {
            try {
                // 2.加载配置文件:
                Properties pro = new Properties();
                pro.load(JdbcDruidPool.class.getClassLoader().getResourceAsStream("druid.properties")); // 加载配置文件
                // 3.获取DataSource并赋值给ds成员变量:
                ds = DruidDataSourceFactory.createDataSource(pro);
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        /*
        * 4.获取连接对象(从连接池中取一个连接对象)的方法:
        * */
        public static Connection getConnection() throws SQLException {
            return ds.getConnection();
        };
    
        /*
        * 5.释放资源(归还连接对象)的方法:
        * */
        public static void close(Statement stmt, Connection conn){
            // if (stmt != null) {
            //     try {
            //         stmt.close();
            //     } catch (SQLException e) {
            //         e.printStackTrace();
            //     }
            // }
            // if (conn != null) {
            //     try {
            //         conn.close();
            //     } catch (SQLException e) {
            //         e.printStackTrace();
            //     }
            // }
    
            // 调用close重置方法优化上面代码块:
            close(null, stmt, conn);
        };
        // 重置close方法
        public static void close(ResultSet rs, Statement stmt, Connection conn){
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        };
        /*
        * 6.获取连接池方法:
        * */
        public static DataSource getDataSource(){
            return ds;
        };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    测试JdbcDruidPool工具类:

    import utils.JdbcDruidPool;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.util.Properties;
    
    public class DruidUtilDemo {
        public static void main(String[] args){
            Connection connection = null;
            PreparedStatement pstmt = null;
            // 使用druid封装的工具类给数据库新增一条数据:
            try {
                // 1.获取连接:
                connection = JdbcDruidPool.getConnection();
                // 2.定义sql:
                String sql = "insert into account values(null,?,?)";
                // 3.获取PreparedStatement对象并初始化sql语句:
                pstmt = connection.prepareStatement(sql);
                pstmt.setString(1, "苦海123");
                pstmt.setDouble(2, 299);
                // 4.执行sql语句,并接收返回结果:
                int count = pstmt.executeUpdate();
                // 5.处理数据及业务
                System.out.println(count); // 1
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                // 6.释放资源归还数据库连接对象
                JdbcDruidPool.close(pstmt, connection);
            }
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    Spring JDBC: Spring框架对JDBC的简单封装,提供了JDBCTemplate对象,简化JDBC的开发,上面DruidUtilDemo中尽管封装了JdbcDruidPool工具类,但是还是很不方便,因为一些prepareStatement等的操作。使用JdbcTemplate可以简化这些操作,如下:

    使用案例:提示,此demo用到前面封装的JdbcDruidPool数据库连接池工具类、封装emp表中对应字段对象的类Emp

    import org.junit.Test;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.RowMapper;
    
    import javax.sql.DataSource;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    
    public class SpringJdbcTest {
        // 2.获取JDBCTemplate对象:
        private JdbcTemplate tempJdbc = new JdbcTemplate(JdbcDruidPool.getDataSource()); // JdbcTemplate方法接收一个DataSource,这里使用之前封装好的JdbcDruidPool数据库连接池工具类获取DataSource,之后在传给JdbcTemplate方法
    
        // 使用Junit单元测试,可以让方法独立执行(不借助main方法)
        @Test //加入@Test注解,此方法就称为Junit单元测试方法,这里需要导入Junit4包,否则报异常,alt+enter,之所以写单元测试方法,是因为方便学习,实际开发中不建议这么写,一般是由测试人员写单元测试方法进行测试的
        public void test1(){ // 点击编辑器左侧绿色箭头即可执行方法
            // 使用JdbcTemplate的基本步骤:
            // 1.在项目下新建一个libs文件夹,去官网https://commons.apache.org/proper/commons-logging/download_logging.cgi下载二进制commons-logging-1.2.jar包、以及一些其他的包如下:
            // spring-beans-5.1.10.RELEASE.jar、spring-core-5.1.10.RELEASE.jar、spring-jdbc-5.1.10.RELEASE.jar、spring-tx-5.1.10.RELEASE.jar,最后别忘了数据库驱动器mysql-connector-java-8.0.11.jar和数据库连接池druid-1.2.9.jar,将所有的jar包都放到libs文件夹中,然后添加为库...
    
            // 3.定义sql: 修改emp表中所有用户的salary值为1000
            String sql = "update emp set salary = 1000";
            // 4.执行SQL:
            int count = tempJdbc.update(sql);
            System.out.println(count); // 14
        };
    
        @Test // 在定义一个方法,用于执行另一个业务,后面提供给前端的每一个接口都有可能以这种形式书写,这里只是用单元测试方法做练习:
        public void test2(){
            // 定义sql:新增一条新用户:
            String sql = "INSERT INTO `db3`.`emp` (`id`, `ename`, `job_id`, `mgr`, `joindate`, `salary`, `bonus`, `dept_id`) VALUES (?, ?, '4', '2000', '2023-09-08', '100.00', '0.00', '10')";
            int count = tempJdbc.update(sql,1017, "方淑奇"); // 可以直接给问号赋值,无需多余的setXXX赋值就可以解决SQL注入问题
            System.out.println(count); // 1
        };
    
        @Test
        public void test3(){
            // 定义sql:删除id大于等于1015的数据
            String sql = "delete from emp where id >= ?";
            int count = tempJdbc.update(sql,1015);
            System.out.println(count); // 3,因为id大于等于1015的数据有3条
        };
    
        @Test
        public void test4(){
            // 查询emp表中的数据,并将其结果封装为Map集合
            String sql = "select * from emp where id = ?";
            Map<String, Object> map = tempJdbc.queryForMap(sql,1012); // queryForMap可以将查询到的结果封装为Map对象,前提是查到数据是一条才行,如果有多条或0条数据的话就会异常。
            System.out.println(map); // {id=1012, ename=李逵, job_id=4, mgr=1006, joindate=2001-12-03, salary=1000.00, bonus=null, dept_id=30}
        };
    
        @Test
        public void test5(){
            // 查询emp表中的数据,并将其结果封装为List,List下每个元素都是Map集合
            String sql = "select * from emp where id < ?";
            List<Map<String,Object>> list = tempJdbc.queryForList(sql,1003); // queryForList可以将查询到的结果封装为Map对象然后装进一个List集合中。
            System.out.println(list); // [{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=1000.00, bonus=null, dept_id=20}, {id=1002, ename=卢俊义, job_id=3, mgr=1006, joindate=2001-02-20, salary=1000.00, bonus=3000.00, dept_id=30}]
        };
    
        // 实际开发中,一般在List集合中并不会装载Map集合,而是将查到的结果转为Java对象,然后在装载到List集合
        @Test
        public void test6(){
            // 查询emp表中的数据,并将其结果封装为List,List下每个元素都是Java对象,实际开发中也不会这么写,因为太麻烦了,一般使用test7这种使用自带的方法生成,使用Spring提供的方法,泛型接收一个字段对应的类
            String sql = "select * from emp where id < 1003";
            List<Emp> list = tempJdbc.query(sql,new RowMapper<Emp>() { // query方法第一个参数可以new一个RowMapper,后面泛型接收一个定义的数据库字段的类
                // 重新mapRow,用于获取每一行中的值,然后再将值设置给一个对象(Emp),之后将这个对象return出去,最后就是一个List集合下装的java对象
                @Override
                public Emp mapRow(ResultSet resultSet, int i) throws SQLException {
                    // 获取需要返回的字段:
                    int id = resultSet.getInt("id");
                    String ename = resultSet.getString("ename");
                    int job_id = resultSet.getInt("job_id");
                    int mgr = resultSet.getInt("mgr");
                    Date joindate = resultSet.getDate("joindate");
                    Double salary = resultSet.getDouble("salary");
                    Double bonus = resultSet.getDouble("bonus");
                    int dept_id = resultSet.getInt("dept_id");
    
                    // new一个Emp对象,并一一设置值:
                    Emp emp = new Emp();
                    emp.setId(id);
                    emp.setEname(ename);
                    emp.setJob_id(job_id);
                    emp.setMgr(mgr);
                    emp.setJoindate(joindate);
                    emp.setSalary(salary);
                    emp.setBounds(bonus);
                    emp.setDept_id(dept_id);
                    return emp;
                }
            });
            System.out.println(list); // [Emp{id=1001, ename='孙悟空', job_id=4, joindate=2000-12-17, mgr=1004, salary=1000.0, bounds=0.0, dept_id=20}, Emp{id=1002, ename='卢俊义', job_id=3, joindate=2001-02-20, mgr=1006, salary=1000.0, bounds=3000.0, dept_id=30}]
        };
    
        // 查列表数据终极版:
        @Test
        public void test7(){
            String sql = "select * from emp where id < 1003";
            List<Emp> list = tempJdbc.query(sql,new BeanPropertyRowMapper<Emp>(Emp.class)); // BeanPropertyRowMapper方法泛型接收一个字段类,参数接收这个类的class字节码,这样可以省略很多代码而实现同样的效果
            System.out.println(list); // [Emp{id=1001, ename='孙悟空', job_id=4, joindate=2000-12-17 08:00:00.0, mgr=1004, salary=1000.0, bounds=0.0, dept_id=20}, Emp{id=1002, ename='卢俊义', job_id=3, joindate=2001-02-20 08:00:00.0, mgr=1006, salary=1000.0, bounds=0.0, dept_id=30}]
        };
    
        // 查询总记录条数:
        @Test
        public void test8() {
            String sql = "select count(id) from emp";
            Long total = tempJdbc.queryForObject(sql,Long.class); // queryForObject一般用于查询聚合函数
            System.out.println(total); // 14,总共14条数据
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:810665436@qq.com联系笔者删除。
    笔者:苦海123
    其它问题可通过以下方式联系本人咨询:
    QQ:810665436
    微信:ConstancyMan

  • 相关阅读:
    Android -- 每日一问:两个 Fragment 之间如何进行通信 ?
    QProperty 理解与示例
    Python多平台word转pdf
    【LeetCode热题100】--33.搜索旋转排序数组
    如何下不可选中的文章
    Python 框架学习 Django篇 (十) Redis 缓存
    437-C++基础语法(81-90)
    C语言初阶-指针详解-庖丁解牛篇
    R语言清洗与处理数据常用代码段
    Java项目:ssm在线选课管理系统
  • 原文地址:https://blog.csdn.net/weixin_46758988/article/details/132786172