《JDBC高级》笔记
junit使用Scanner在idea中卡死的设置
2017版,可以直接编辑这个文件:安装目录
C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.3\bin\idea64.exe.vmoptions
在最后面添加1行:
-Deditable.java.test.console=true
2019版选择菜单
在最后加上
-Deditable.java.test.console=true
回顾
-
能够使用Connection接口
Connection接口中的方法 描述 Statement createStatement() 创建一个语句对象,不带SQL语句 PreparedStatement prepareStatement(String sql) 创建一个预编译语句对象,指定SQL语句 -
能够使用Statement接口
Statement接口中的方法 描述 boolean execute(String sql) 作用:执行任意的SQL语句
返回值:如果有结果集返回true,否则返回falseint executeUpdate(String sql) 作用:执行增删改
返回值:返回影响的行数ResultSet executeQuery(String sql) 作用:执行查询操作
返回值:查询到的结果集 -
能够使用ResultSet接口
ResultSet接口中的方法 描述 boolean next() 1. 向下移动一行游标
2. 判断当前所指是否有记录,有返回true,没有就返回false数据类型 getXxx(参数) 获取数据,通过以下两种方式:
1. 通过列名
2. 通过列号 -
能够使用JDBC操作事务
Connection接口中与事务有关的方法 说明 void setAutoCommit(boolean autoCommit) 设置为false,表示手动提交事务 void commit() 提交事务 void rollback() 事务回滚 -
能够通过PreparedStatement完成增、删、改、查
方法没有SQL语句
PreparedStatement接口中的方法 | 描述 |
---|---|
int executeUpdate() | 执行增删改操作 |
ResultSet executeQuery() | 执行查询的操作 |
学习内容
- MySQL中DCL语句的使用
- JDBC处理CLOB和BLOB类型
- JDBC中元数据
- 制作通用的JDBC工具类
学习目标
- 能够使用DCL处理MySQL中的用户
- 能够说出什么是数据库元数据
- 能够自定义数据库框架,实现增加,删除,更新方法
DCL:创建用户,授权,撤销权限
目标
- 创建mysql的用户
- 给用户授权
- 撤销权限
DCL(Data Control Language)
回顾:mysql有四种语句
- DDL 建库,建表,建索引
- DML 增删改操作
- DQL 查询操作
- DCL 对数据库的用户进行管理
我们现在默认使用的都是root用户,超级管理员,拥有全部的权限。但是,一个公司里面的数据库服务器上面可能同时运行着很多个项目的数据库。所以,我们应该可以根据不同的项目建立不同的用户,分配不同的权限来管理和维护数据库。
创建用户
语法
create user '用户名'@'主机名' identified by '密码'
说明
参数 | 注:用户名、主机名和密码都应该加上单引号 |
---|---|
‘用户名’ | 要创建的用户名 |
‘主机名’ | 限制这个用户只能在哪台主机上登录,如果本地用户使用localhost, 如果想让用户可以在任意的主机上登录,使用通配符% |
‘密码’ | 设置用户的密码,可以为空,为空用户就不需要密码 |
操作
-
创建user1用户,只能在localhost这个服务器登录mysql服务器,密码为123
-
创建user2用户可以在任何电脑上登录mysql服务器,密码为123
使用其它用户可以登录
注:创建的用户名都在mysql数据库中的user表中可以查看到,密码经过了加密。
-- 可以在mysql这个数据库中
use mysql;-- 查询user表
select * from user;
给用户授权
用户创建之后,没什么权限!需要给用户授权
语法
grant 权限 on 数据库名.表名 to '用户名'@'主机名'
说明
参数 | 说明 |
---|---|
权限 | 授予用户的权限,有如下权限:CREATE, ALTER, SELECT,INSERT,UPDATE, DELETE。如果要授予所有的权限可以使用ALL |
数据库名.表名 | 对哪个数据库中哪个表指定权限,库名或表名都可以使用*表示所有库或表。 如:day21.* 表示day21库下所有的表 如:*.* 所有数据库的所有表 |
‘用户名’@‘主机名’ | 给哪个用户权限,要加单引号。注:必须与创建用户的时候名字相同 |
操作
-
给user1用户分配对test这个数据库操作的权限:创建表,修改表,插入记录,更新记录,查询
-
给user2用户分配所有权限,对所有数据库的所有表
使用user1登录操作
撤销授权
语法
revoke 权限 on 数据库名.表名 from '用户名'@'主机名'
参数
参数 | 说明 |
---|---|
权限 | 要撤销的用户权限,有如下权限:CREATE, ALTER, SELECT,INSERT,UPDATE, DELETE。如果要撤销所有的权限可以使用ALL |
数据库名.表名 | 对哪个数据库中哪个表撤销权限,库名或表名都可以使用*表示所有库或表。 |
‘用户名’@‘主机名’ | 给哪个用户权限,要加单引号。注:必须与创建用户的时候名字相同 |
操作
撤销user1用户对test数据库所有表的操作的权限
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l4ycT8HX-1592211745839)(assets/image-20200512094731862.png)]
注:用户名和主机名要与创建时相同,各自要加上单引号
小结
-
创建用户:
create user '用户名'@'主机名' identified by '密码'
-
给用户授权:
grant 权限 on 数据库名.表名 to '用户名'@'主机名'
-
撤销权限:
revoke 权限 on 数据库名.表名 from '用户名'@'主机名'
DCL:删除用户,修改密码
目标
- 删除用户
- 修改管理员密码
- 修改自己密码
删除用户
语法
删除用户 |
---|
drop user ‘用户名’@‘主机名’ |
具体操作
- 删除user2
- 删除后user2不能再登录
修改管理员密码
语法
这是一条bin目录下可执行文件:mysqladmin.exe
mysqladmin -u用户名 -p password 新密码 |
---|
回车后要输入原密码,才能更改成功。如果原密码不正确,则修改失败 |
操作
- 将root管理员的新密码改成123456
- 要求输入旧密码
- 使用新密码登录
普通用户修改自己的密码
语法
set password=password('新密码')
注:用户登录mysql后操作
操作
- 以user1登录
- 修改user1的密码为abc
- 重新以新的密码登录
小结
- 删除用户:drop user ‘用户名’@‘主机名’
- 修改管理员密码:mysqladmin -u用户名 -p password 新密码
- 修改自己密码:set password=password(‘新密码’)
CLOB和BLOB概述
为什么要CLOB和BLOB
如果我们要向数据库中保存图片,或者要存入大量的文字到某个字段。要怎么做呢?
这时就需要使用到CLOB和BLOB类型。比如:我们要将一张美女存到MySQL的某个字段中,下次要使用的时候,再从数据库中读出来。
这是实际开发过程中常常需要用到的功能,要如何实现呢?
LOB数据类型概述
基本概念
什么是LOB数据类型:Large Object 大对象数据类型,用来存储数据量大的字符和字节数据
LOB分类
-
CLOB:Character Large Object 大的字符数据类型,存大量的文本数据。如:文字,书,文章
-
BLOB:Binary Large Object 大的字节数据类型,存二进制的数据。如:音乐,图片,视频
MySQL的处理类型:
-
CLOB在MySQL中对应的是TEXT类型
-
BLOB类型在MySQL中就是BLOB
TEXT类型在MySQL的数据类型中又细分成4种,它们的使用方式是一样的,唯一的区别就是存储的字符数据长度不同,具体长度见下表。
类型 | 最长 |
---|---|
TINYTEXT | 256 字节 |
TEXT | 64KB |
MEDIUMTEXT | 16MB |
LONGTEXT | 4GB |
BLOB类型也同样会成4种,它们的使用方式也是一样的,唯一的区别就是存储的字节数据长度不同,具体长度见下表。
类型 | 最长 |
---|---|
TINYBLOB | 256字节 |
BLOB | 64K |
MEDIUMBLOB | 16M |
LONGBLOB | 4G |
对CLOB的写入和读取操作
在JDBC中有两个接口对应数据库中的BLOB和CLOB类型,java.sql.Blob和java.sql.Clob。和平常使用数据库一样你可以直接通过resultSet.getBlob()方法来获取该接口的对象。
与普通的查找唯一不同的是得到Blob或Clob的对象后,我们并没有得到任何数据,需要再调用这两个接口中的方法得到数据。
如:varchar(10) -> getString() 得到数据
如:getBlob() -> Blob对象 -> 调用Blob中方法得到数据
CLOB的写入
在JDBC中如果要写入CLOB到数据库中,需要使用到PreparedStatement中的方法:
PreparedStatement中的方法 | 说明 |
---|---|
void setClob(int parameterIndex, Reader reader) | 设置CLOB的占位符,参数: 1. 占位符的位置 2. 字符流对象,可以使用文件字符流 |
案例需求
有一张员工表,包含id主键,name姓名,resume简历,photo照片。其中主键自动增长,姓名使用varchar类型,resume使用TEXT类型,photo使用BLOB类型。
向员工表中添加一条记录,从本地读取一个文本文件,将文本文件中的内容写入到数据库的TEXT类型的字段中。
表结构如下
文件中数据的准备
在d盘根目录下准备一个文本文件,命名为 “李白.txt”,输入一些文字内容。注意:文本文件的编码要与MySQL的编码一致,如果在MySQL中编码是UTF-8,则文本文件的编码也应该是UTF-8。不然会出现文件写入失败的情况。
创建自定义的文件
- 选择菜单
- 指定细节
开发步骤
- 读取本地文件,返回输入字符流
FileReader reader = new FileReader(new File(“d:/李白.java”));
- 与操作普通类型一样,只不过使用的是字符流
stmt.setClob(1,reader);
案例代码
- 创建表的代码
create table employee(id int primary key auto_increment, -- 主键name varchar(20), -- 员工姓名resume text, -- 简历,使用text类型,最大64Kphoto mediumblob -- 照片,使用blob类型,最大16M
);
- JDBC代码:插入记录:名字和介绍2列
package com.itheima;import com.itheima.utils.JdbcUtils;
import org.junit.Test;import java.io.FileNotFoundException;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;/****/
public class Demo1 {/*** 向员工表中添加一条记录,从本地读取一个文本文件,将文本文件中的内容写入到数据库的TEXT类型的字段中。*/@Testpublic void testInsertClob() throws SQLException, FileNotFoundException {//0.读取文件,获取FileReaderFileReader reader = new FileReader("D:\\李白.txt");//1.创建连接Connection connection = JdbcUtils.getConnection();//2.创建语句对象PreparedStatement preparedStatement = connection.prepareStatement("insert into employee (name,resume) values (?,?)");//3.设置占位符,其中clob类型通过文件读取preparedStatement.setString(1, "全智贤");//传入clob类型preparedStatement.setClob(2, reader);//4.执行插入操作int row = preparedStatement.executeUpdate();//5.关闭连接JdbcUtils.close(connection,preparedStatement);System.out.println("插入1条新的记录");}}
效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFk9HNGv-1592211745884)(assets/image-20200512104426331.png)]
小结
设置CLOB类型使用哪个方法?
setClob(占位符,Reader 字符流)
CLOB的读取
案例需求:
通过员工的名字,读取某员工的个人简历到d:/name.txt文本文件中
案例步骤:
从数据库中读取CLOB类型的数据有两种实现方式
- 读取方式:
1) 方式一:从结果集中当做字符串直接取出数据
rs.getString("列名");
2) 方式二:从结果集中返回字符输入流的形式
//得到java.sql.Clob接口
Clob clob =rs.getClob("列名");
//得到一个Reader对象
Reader reader =clob.getCharacterStream();
//将Reader写到一个Writer中
案例代码:
通过名字:查询名字和介绍两列
/*
通过名字:查询名字和介绍两列*/
@Test
public void testQueryClob() throws Exception {//1.创建连接Connection connection = JdbcUtils.getConnection();//2.创建预编译语句对象PreparedStatement preparedStatement = connection.prepareStatement("select name, resume from employee where name=?");//2.5 设置参数preparedStatement.setString(1, "全智贤");//3.查询数据得到结果集ResultSet resultSet = preparedStatement.executeQuery();//4.读取结果中列if (resultSet.next()) {//4.1 直接显示clob的数据String name = resultSet.getString("name");String resume = resultSet.getString("resume"); //直接读取字符串System.out.println("名字:" + name);System.out.println("简历:" + resume);//4.2 将clob的数据写成文件Clob clob = resultSet.getClob("resume"); //得到clob类型Reader reader = clob.getCharacterStream(); //得到字符输入流//写成一个文件FileWriter fileWriter = new FileWriter("d:/" + name + ".txt");//写成文件IOUtils.copy(reader, fileWriter);//关闭文件流fileWriter.close();reader.close();}//5.关闭连接JdbcUtils.close(connection, preparedStatement, resultSet);
}
小结
读取CLOB使用哪个方法?
1. 直接做为字符串读取:getString("列名")
2. 先读取到Clob类型 -> Reader -> Writer 写成一个文件getClob("列名") -> getCharacterStream()
对BLOB的写入和读取操作
CLOB只能用于对大文本进行操作,如果要将图片、音乐、视频等二进制的数据写入到数据库中,则需要使用BLOB类型。
BLOB的写入
要写入BLOB的类型,需要用到PreparedStatement中的方法:
PreparedStatement中的方法 | 说明 |
---|---|
void setBlob(int parameterIndex, InputStream inputStream) | 替换占位符中blob的类型,参数: 1. 占位符位置 2. 字节输入流 |
案例需求:
将本地的一张图片d:/p1.jpg更新到上面的员工信息的photo列中,照片类型使用MEDIUMBLOB类型,因为照片的有可能大于64K。
案例步骤:
1) 读取本地的二进制文件,返回输入字节流
FileInputStream fis =new FileInputStream("d:/p1.jpg");
2) 与操作普通类型一样,只不过使用的是字节流
stmt.setBlob(1, fis);
案例代码:
通过名字更新照片
package com.itheima;import com.itheima.utils.JdbcUtils;
import org.junit.Test;import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;/*** 写入和读取Blob类型*/
public class Demo2 {@Testpublic void updatePhoto() throws Exception {//0.读取文件输入流FileInputStream inputStream = new FileInputStream("d:/p1.jpg");//1.创建连接对象Connection connection = JdbcUtils.getConnection();//2.得到预编译语句对象PreparedStatement preparedStatement = connection.prepareStatement("update employee set photo=? where name=?");//3.替换占位符preparedStatement.setBlob(1, inputStream);preparedStatement.setString(2, "全智贤");//4.执行更新操作int row = preparedStatement.executeUpdate();//5.关闭连接JdbcUtils.close(connection, preparedStatement);inputStream.close();System.out.println("更新了" + row + "行记录");}}
小结
设置BLOB的占位符使用哪个方法?
setBlob(占位符, InputStream 输入流)
BLOB的读取
将图片写入到数据库中以后,如何读取出来再保存成文件呢?需要使用到字节输入流。请看下面的案例:
案例需求:
通过名字,读取某员工的姓名和照片,使用字节输出流写出照片到硬盘,文件名是name.jpg,文件写完以后,关闭输入输出流。
开发步骤:
BLOB的读取有两种方式:
- 方式一:从结果集中直接读取字节流
InputStream is =rs.getBinaryStream("列名");
- 方式二:返回Blob接口,从Blob中读取字节流
InputStream in =rs.getBlob("列名").getBinaryStream();
案例代码:
通过名字查询名字和照片,并且把照片写成文件
//通过名字查询名字和照片,并且把照片写成文件
@Test
public void queryPhoto() throws Exception {//1.创建连接对象Connection connection = JdbcUtils.getConnection();//2.创建预编译语句对象PreparedStatement preparedStatement = connection.prepareStatement("select name,photo from employee where name=?");//3.设置占位符preparedStatement.setString(1, "全智贤");//4.执行查询操作ResultSet resultSet = preparedStatement.executeQuery();if (resultSet.next()) {String name = resultSet.getString("name");InputStream photo = resultSet.getBinaryStream("photo"); //字节输入流//5.创建字节文件输出流FileOutputStream fos = new FileOutputStream("d:/" + name + ".jpg");//6.把字节输入流写成文件IOUtils.copy(photo, fos);//关闭流fos.close();photo.close();}//7.释放资源JdbcUtils.close(connection, preparedStatement, resultSet);System.out.println("写入文件成功");
}
MySQL数据库对文件大小的限制
-
出现问题:
如果我们插入一个大于1M的图片,结果发现出现错误:
Packet for query is toolarge (1594841 > 1048576).
You can change thisvalue on the server by setting the max_allowed_packet’ variable.
-
分析问题:
MySQL数据库默认情况下,由于max_allowed_packet变量的限制,只能存储不超过1M的文件
-
解决方案:
修改mysql目录下的my.ini文件, 选择[mysqld] 中修改或添加max_allowed_packet变量,然后重启MySQL即可。假如要设置的大小为5M,则修改如下: max_allowed_packet=5M
CLOB和BLOB的小结
- CLOB:
- 读取的方法:
- 写入的方法:
- BLOB:
- 读取的方法:
- 写入的方法:
元数据的基本概述
目标
元数据的概念和分类
元数据的基本概述
什么是元注解?
给注解加注解的,称为元注解。
什么是元数据:
用来描述数据的数据,比如创建表的代码,用来描述表的结构,描述这一列数据的信息
JDBC中元数据分类
三种元数据 | 说明 | 如何得到元数据 |
---|---|---|
数据库元数据 | 描述数据库一些信息,如:数据库驱动版本号,数据库版本号等 | 通过连接对象Connection |
参数元数据 | 描述占位符参数的一些信息,如:一共有几个参数 | 通过PreparedStatement |
结果集元数据 | 描述结果集中信息,如:一共有多少列,每列的数据类型是什么 | 通过ResultSet |
参数元数据:ParameterMetaData
目标
参数元数据的API和案例
获取ParameterMetaData
PreparedStatement接口中的方法 | 说明 |
---|---|
ParameterMetaData getParameterMetaData() | 通过预编译的语句获取参数元数据 |
接口中的方法
因为是接口,所以需要数据库厂商驱动的支持,mysql对参数元数据的支持不是太理想,但不影响今天的使用
ParameterMetaData接口中的方法 | 说明 |
---|---|
int getParameterCount() | 获取参数的个数 |
String getParameterTypeName(int param) | 获取某一列参数的数据类型,参数就是列号,从1开始 只能获取varchar类型的数据 |
案例:得到SQL语句参数的元数据
准备表
步骤
- 得到连接对象
- 得到语句对象,SQL语句是向学生表中插入一条记录
- 得到参数元数据
- 输出参数的个数 getParameterCount()
- 得到第1个参数的类型名字 getParameterTypeName()
- 执行SQL语句,插入1条记录
执行效果
实现代码
package com.itheima;import com.itheima.utils.JdbcUtils;import java.sql.*;/*** 参数元数据*/
public class Demo3 {public static void main(String[] args) throws SQLException {//1. 得到连接对象Connection connection = JdbcUtils.getConnection();//2. 得到语句对象,SQL语句是向学生表中插入一条记录PreparedStatement preparedStatement = connection.prepareStatement("insert into student (name, sex, birthday) values (?,?,?)");preparedStatement.setString(1,"猪八戒");preparedStatement.setInt(2, 1); //使用整数preparedStatement.setDate(3, Date.valueOf("2000-11-11"));//3. 得到参数元数据ParameterMetaData metaData = preparedStatement.getParameterMetaData();//4. 输出参数的个数 getParameterCount()System.out.println("一共有多少个参数:" + metaData.getParameterCount());//5. 得到第1个参数的类型名字 getParameterTypeName()System.out.println("第1个参数的类型:" + metaData.getParameterTypeName(1));//6. 执行SQL语句,插入1条记录int row = preparedStatement.executeUpdate();System.out.println("插入了一条记录:" + row);//关闭连接JdbcUtils.close(connection, preparedStatement);}}
注意事项
默认情况下:
MySQL驱动对参数元数据的数据类型支持不理想,需要如下配置才能得到参数的MySQL数据类型,而且只能得到VARCHAR类型,如果不配置这个参数则会出现异常。 |
---|
jdbc:mysql://localhost:3306/数据库名?generateSimpleParameterMetadata=true |
修改工具类,修改URL的参数
小结
ParameterMetaData接口中的方法 | 说明 |
---|---|
int getParameterCount() | 获取参数的个数 |
String getParameterTypeName(int param) | 获取指定列的参数类型,但mysql驱动支持不太理想 |
结果集元数据:ResultSetMetaData
目标
结果集元数据的使用和案例
作用
通过结果集元数据获取结果集中列名和列的类型,一共有多少列等信息
如何获取ResultSetMetaData
ResultSet接口中的方法 | 说明 |
---|---|
ResultSetMetaData getMetaData() | 通过结果集对象获取结果集元数据 |
接口中的方法
ResultSetMetaData接口中的方法 | 说明 |
---|---|
int getColumnCount() | 获取结果集中有多少列 |
String getColumnName(int column) | 获取指定列的列名 |
String getColumnTypeName(int column) | 获取指定列的数据类型,返回的是字符串的类型名 |
案例:结果集元数据的使用
步骤
- 创建连接对象
- 获取预编译的语句对象
- 查询学生信息,得到结果集对象
- 通过结果集对象得到结果集元数据对象
- 得到一共有多少列
- 循环输出每一列的名字和列的类型
- 释放资源关闭连接
代码
package com.itheima;import com.itheima.utils.JdbcUtils;import java.sql.*;/*** 结果集元数据的使用*/
public class Demo4 {public static void main(String[] args) throws SQLException {//1. 创建连接对象Connection connection = JdbcUtils.getConnection();//2. 获取预编译的语句对象PreparedStatement preparedStatement = connection.prepareStatement("select * from student where id=?");preparedStatement.setInt(1, 1);//3. 查询学生信息,得到结果集对象ResultSet resultSet = preparedStatement.executeQuery();//4. 通过结果集对象得到结果集元数据对象ResultSetMetaData metaData = resultSet.getMetaData();//5. 得到一共有多少列int columnCount = metaData.getColumnCount();System.out.println("一共有" + columnCount + "列");//6. 循环输出每一列的名字和列的类型for (int i = 0; i < columnCount; i++) {//列号从1开始String columnName = metaData.getColumnName(i + 1);//每一列的数据类型String columnTypeName = metaData.getColumnTypeName(i + 1);System.out.println("第" + (i+1) + "列的名字是:" + columnName + ",数据类型是:" + columnTypeName);}//7. 释放资源关闭连接JdbcUtils.close(connection, preparedStatement, resultSet);}
}
效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5gaBnExG-1592211745902)(assets/image-20200512141332307.png)]
小结
ResultSetMetaData接口中的方法 | 说明 |
---|---|
int getColumnCount() | 获取结果集中一共有多少列 |
String getColumnName(int column) | 获取指定列的名字 |
String getColumnTypeName(int column) | 获取指定列的数据类型 |
工具类:增删改的通用方法
需求
修改JdbcUtils工具类,使用元数据做一个通用的增删改的方法
分析
所有实体的增删改操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,因此可以把增删改的操作所有相同代码抽取到工具类的一个方法中,并定义参数接收变化的SQL语句。
增删改操作的不同点:
- SQL语句不同
- 占位符的参数列表不同
public static int update(String sql, Object... params)
步骤
- 得到连接对象
- 得到预编译语句对象,有占位符
- 得到参数元数据
- 得到有几个占位符
- 使用循环替换设置占位符的值
- 执行executeUpdate()方法
- 自动释放资源
- 返回影响的行数
代码
package com.itheima.utils;import java.sql.*;/*** 访问数据库的工具类*/
public class JdbcUtils {//可以把几个字符串定义成常量:用户名,密码,URL,驱动类private static final String USER = "root";private static final String PWD = "root";private static final String URL = "jdbc:mysql://localhost:3306/day23";private static final String DRIVER = "com.mysql.jdbc.Driver";/*** 注册驱动,为了兼容1.5以前的版本*/static {try {Class.forName(DRIVER);} catch (ClassNotFoundException e) {e.printStackTrace();}}/*** 得到数据库的连接*/public static Connection getConnection() throws SQLException {return DriverManager.getConnection(URL, USER, PWD);}/*** 关闭连接* 查询调用这个方法*/public static void close(Connection connection, Statement statement, ResultSet resultSet) {try {if (resultSet != null) {resultSet.close();}} catch (SQLException e) {e.printStackTrace();}try {if (statement != null) {statement.close();}} catch (SQLException e) {e.printStackTrace();}try {if (connection != null) {connection.close();}} catch (SQLException e) {e.printStackTrace();}}/*** 关闭连接* 增删改没有结果集*/public static void close(Connection connection, Statement statement) {//直接调用上面的方法close(connection, statement, null);}/*** 通用的增删改的方法* @param sql 要执行的SQL语句* @param params 替换占位符的真实地址,在方法内部是一个数组(从0开始)* @return 影响的行数*/public static int update(String sql, Object... params) {Connection connection = null;PreparedStatement ps = null;int row = 0; //影响的行数try {//1.创建连接对象connection = getConnection();//2.创建预编译的语句对象ps = connection.prepareStatement(sql);//2.5 得到参数元数据ParameterMetaData metaData = ps.getParameterMetaData();int count = metaData.getParameterCount(); //获取有几个参数//3.替换占位符为真实的值for (int i = 0; i < count; i++) {ps.setObject(i + 1, params[i]); //一定是个对象类型}//4.执行SQL语句row = ps.executeUpdate();} catch (SQLException e) {e.printStackTrace();} finally {close(connection, ps); //调用自己的方法}return row;}
}
使用工具类
package com.itheima;import com.itheima.utils.JdbcUtils;/*** 使用工具类来实现增删改*/
public class Demo5UseUtils {public static void main(String[] args) {//1.使用工具类实现添加的操作//JdbcUtils.update("insert into student (name,sex,birthday) values (?,?,?)", "白骨精", 0, "1999-09-09");//2.使用工具类实现修改的操作//JdbcUtils.update("update student set name=?, sex=?, birthday=? where id=?", "唐僧", 0, "1992-05-01", 7);//3.使用工具类实现删除的操作JdbcUtils.update("delete from student where id=?", 5);}}
小结
DML增删改操作的SQL不同点有
- SQL语句不同
- 占位符的个数不同,代替占位符的数据类型不同
工具类:通用的查询方法
目标
实现通用的查询方法
回顾:反射给属性赋值
相关的方法
通过类对象,创建一个对象的实例
类对象.getConstructor().newInstance()
获取构造方法,再实例化
Class类对象的方法 | 说明 |
---|---|
Field[] getDeclaredFields() | 获取所有的成员变量,包含私有的,返回一个字段类型的数组 |
Field类中的方法 | 说明 |
---|---|
String getName() | 获取属性的名字 |
void set(Object obj, Object value) | 给属性设置值 参数1:对象名,给哪个对象赋值 参数2:要赋的值 |
void setAccessible(true) | 给私有的属性赋值需要使用暴力反射 |
分析
通用的查询方法,不同点:
- sql语句不同
- 参数个数不同
- List集合中的对象不同
步骤
-
获取数据库连接
-
创建预编译sql语句对象
-
给SQL语句设置参数,代替占位符
-
执行sql,得到结果集
-
遍历结果集
-
通过反射创建一个对象
-
得到实体类中所有的属性,给每个字段赋值
-
把实体对象添加到集合中
-
释放资源
-
返回集合对象
代码
package com.itheima.utils;import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;/*** 访问数据库的工具类*/
public class JdbcUtils {//可以把几个字符串定义成常量:用户名,密码,URL,驱动类private static final String USER = "root";private static final String PWD = "root";private static final String URL = "jdbc:mysql://localhost:3306/day23?generateSimpleParameterMetadata=true";private static final String DRIVER = "com.mysql.jdbc.Driver";/*** 注册驱动,为了兼容1.5以前的版本*/static {try {Class.forName(DRIVER);} catch (ClassNotFoundException e) {e.printStackTrace();}}/*** 得到数据库的连接*/public static Connection getConnection() throws SQLException {return DriverManager.getConnection(URL, USER, PWD);}/*** 关闭连接* 查询调用这个方法*/public static void close(Connection connection, Statement statement, ResultSet resultSet) {try {if (resultSet != null) {resultSet.close();}} catch (SQLException e) {e.printStackTrace();}try {if (statement != null) {statement.close();}} catch (SQLException e) {e.printStackTrace();}try {if (connection != null) {connection.close();}} catch (SQLException e) {e.printStackTrace();}}/*** 关闭连接* 增删改没有结果集*/public static void close(Connection connection, Statement statement) {//直接调用上面的方法close(connection, statement, null);}/*** 通用的增删改的方法* @param sql 要执行的SQL语句* @param params 替换占位符的真实地址,在方法内部是一个数组(从0开始)* @return 影响的行数*/public static int update(String sql, Object... params) {Connection connection = null;PreparedStatement ps = null;int row = 0; //影响的行数try {//1.创建连接对象connection = getConnection();//2.创建预编译的语句对象ps = connection.prepareStatement(sql);//2.5 得到参数元数据ParameterMetaData metaData = ps.getParameterMetaData();int count = metaData.getParameterCount(); //获取有几个参数//3.替换占位符为真实的值for (int i = 0; i < count; i++) {ps.setObject(i + 1, params[i]); //一定是个对象类型}//4.执行SQL语句row = ps.executeUpdate();} catch (SQLException e) {e.printStackTrace();} finally {close(connection, ps); //调用自己的方法}return row;}/*** 通用的查询方法* @param sql 要执行的SQL语句* @param clazz 要实例化的类型,如:Student.class, Employee.class* @param params 替换占位符的真实值* @return 封装好的集合对象*/public static <T> List<T> query (String sql, Class<T> clazz, Object...params) {Connection connection = null;PreparedStatement ps = null;ResultSet resultSet = null;List<T> list = new ArrayList<>();try {//1.创建连接对象connection = getConnection();//2. 创建预编译的语句对象ps = connection.prepareStatement(sql);//2.5 得到参数元数据ParameterMetaData metaData = ps.getParameterMetaData();int count = metaData.getParameterCount(); //获取有几个参数//3.替换占位符为真实的值for (int i = 0; i < count; i++) {ps.setObject(i + 1, params[i]); //一定是个对象类型}//4.执行查询操作resultSet = ps.executeQuery();//5.遍历整个结果集,封装到集合中,每个元素是一个对象while(resultSet.next()) {//每条记录封装成一个对象,创建一个对象T obj = clazz.getConstructor().newInstance();//先得到实体类中有哪些属性Field[] fields = clazz.getDeclaredFields(); //得到所有成员变量,包含私有的//遍历每个成员变量,进行赋值for (Field field : fields) {//私有的要暴力field.setAccessible(true);//列名=属性名String name = field.getName();//要赋值的对象,值field.set(obj, resultSet.getObject(name)); //从结果集中获取数据}//6.添加到集合中list.add(obj);}}catch (Exception ex) {ex.printStackTrace();}finally {close(connection, ps, resultSet);}return list;}
}
测试查询方法
package com.itheima;import com.itheima.entity.Account;
import com.itheima.entity.Student;
import com.itheima.utils.JdbcUtils;import java.util.List;public class Demo6List {public static void main(String[] args) {//使用工具类查询所有的女生//List<Student> students = JdbcUtils.query("select * from student where sex=?", Student.class, 0);//students.forEach(student -> System.out.println(student));//查询所有的账户List<Account> accounts = JdbcUtils.query("select * from account", Account.class);accounts.forEach(account -> System.out.println(account));}
}
学习总结
-
能够使用DCL处理MySQL中的用户
-- 创建用户 create user '用户名'@'主机名' identified by '密码' -- 授权 grant 权限 on 数据库名.表名 to '用户名'@'主机名' -- 撤销权限 revoke 权限 on 数据库名.表名 from '用户名'@'主机名' -- 删除用户 drop user '用户名'@'主机名' -- 修改自己密码 set password=password('密码')
-
CLOB写入
PreparedStatement中的方法 说明 void setClob(int parameterIndex, Reader reader) 参数1:占位符
参数2:字符输入流 -
CLOB的读取
- 方式一:从结果集中当做字符串直接取出数据
getString("列名")
- 方式二:从结果集中返回字符输入流的形式
getClob().getCharacterStream();
-
BLOB写入
PreparedStatement中的方法 说明 void setBlob(int parameterIndex, InputStream inputStream) 参数1:占位符
参数2:字节输入流 -
BLOB的读取
- 方式一:从结果集中直接读取字节流
getBinaryStream()
- 方式二:返回Blob接口,从Blob中读取字节流
getBlob().getBinaryStream()
-
能够说出什么是数据库元数据
PreparedStatement接口中的方法 说明 ParameterMetaData getParameterMetaData() 通过预编译对象得到参数元数据 ParameterMetaData接口中的方法 说明 int getParameterCount() 获取参数的个数 String getParameterTypeName(int param) 获取指定列的类型 ResultSet接口中的方法 说明 ResultSetMetaData getMetaData() 通过结果集获取结果集元数据 ResultSetMetaData接口中的方法 说明 int getColumnCount() 获取一共有多少列 String getColumnName(int column) 获取指定列的名字 String getColumnTypeName(int column) 获取指定列的类型 -
能够自定义数据库框架,实现增加,删除,更新方法
作业
-
完成今天上课案例的CLOB和BLOB的读取和写入
| 说明 |
| --------------------------------------------------------- | ------------------------------------ |
| void setBlob(int parameterIndex, InputStream inputStream) | 参数1:占位符
参数2:字节输入流 | -
BLOB的读取
- 方式一:从结果集中直接读取字节流
getBinaryStream()
- 方式二:返回Blob接口,从Blob中读取字节流
getBlob().getBinaryStream()
-
能够说出什么是数据库元数据
PreparedStatement接口中的方法 说明 ParameterMetaData getParameterMetaData() 通过预编译对象得到参数元数据 ParameterMetaData接口中的方法 说明 int getParameterCount() 获取参数的个数 String getParameterTypeName(int param) 获取指定列的类型 ResultSet接口中的方法 说明 ResultSetMetaData getMetaData() 通过结果集获取结果集元数据 ResultSetMetaData接口中的方法 说明 int getColumnCount() 获取一共有多少列 String getColumnName(int column) 获取指定列的名字 String getColumnTypeName(int column) 获取指定列的类型 -
能够自定义数据库框架,实现增加,删除,更新方法