一. 概念
概念理解:
1) API
全称为"应用程序编程接口", 把这个词理解成"一组类"/"一组方法", 都是现成的(别的大佬写好的), 可以直接进行调用, 就可以实现一些效果
对于java来说,
java提供了"标准库", 叫做标准库的API, 你只要安装了java,此时就能使用数据库中的类和方法
也可以使用其他大佬写好的类和方法, 叫第三方库的API, 就不是自带的了, 需要额外安装
2) SDK
全称叫"软件开发工具包", 有的库提供的API特别多, 形成了一系列的体系, 这种情况, 称为SDK
像java中用到的JDK, 其实就是一个SDK, 只不过给他起了一个专属的名字, java软件开发包
3) JDBC
mysql本身提供了一组API供程序猿使用
Oracle, SQL Server, SQLite....都提供了这样的API让程序员调用
这些数据库提供的API都大同小异, 但是细节上存在差异, 如果在项目中要用到不同的数据库, 就要学习多种API的使用
此时, java就提供了这样一套API, 可以让我们无缝切换各种数据库, 这一套API就是JDBC
上面的"接口转换"程序, 称为"数据库驱动", 就类似于电脑的转接头一样
二. JDBC的使用
准备工作:
JDBC是javan标准库中提供的, 你只要安装了JDK,就自带JDBC
但是, JDBC操作mysql就需要下载并导入mysql的驱动包
1. 下载驱动包
java来说, 日常开发中要用到大量的第三方库, 就有大佬, 把这些第三方库的安装包收集到一起, 称为"中央仓库"
中央仓库链接
在中央仓库中搜索mysql:
上面的是新版本,下面是旧版本, 我是用的是mysql5.7, 所以下载旧版本的, 点击进去, 找到对应的版本(最前面以为的大版本要求匹配即可, 后面的小版本区别不大):
我选择下载5.1.49, 点击进去:
点击方框jar, 开始下载
2. 导入项目
在项目中随便创建一个目录
假设命名为lib 把刚才的.jar文件拷贝到lib中(找到文件ctrl+C, 选中libCtrl+V)
右键目录, 选择Add as Library, 告诉IDEA,当前目录存放的是第三方库的目录
添加完成后, 可发现文件可以展开
编写代码(以新增操作为例):
要想编写jdbc, 还需要准备好数据库和数据表(虽然jdbc也能进行建表操作, 但一般都是提前建好的)
创建一个test表
第一步: 创建"数据源"
数据源, DataSourse, 意思是要操作的数据库数据在哪, 就需要设定好mysql服务器的位置, 要访问数据库的名字, 访问数据库的用户名和密码
//1. 创建"数据源"DataSource dataSource = new MysqlDataSource();((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false");((MysqlDataSource) dataSource).setUser("root");((MysqlDataSource) dataSource).setPassword("123456");
1) DataSource dataSource = new MysqlDataSource();
- DataSourse 是JDBC自带的接口, 接口是不能new实例的, 需要new一个实现了这个interface的class实例:MysqlDataSourse, 而这个类就来自于mysql的驱动包
为什么不这样写? MysqlDataSource dataSource = new MysqlDataSource();
带有向上转型的写法, 更便于代码和数据库之间"解耦合"
后续代码, 使用数据库时, 看到的都是DataSourse类型, 不知道DataSourse背后是Mysql还是Oracle还是其他的, 更便于进行数据库的切换
写程序要追求"高内聚" "低耦合"
耦合: 写代码的时候, 会分成很多模块, 如果一个模块修改了代码, 对别的模块影响很大, 这就叫"高耦合"
内聚: 代码中, 实现某个功能的时候, 如果这个功能的相关代码, 是几种放在一起的, 就认为是"高内聚", 如果是散落在项目的各个文件各个角落中, 就认为是"低内聚"
2) ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false");
- 要将dataSourse向下转型回来, 因为DataSourse中没有这些方法
- Url 唯一资源定位符
Mysql是一个 客户端 服务器 程序, 服务器保存数据的本体, 之前用的cmd黑框, 就是mysql自带的客户端, 咱们可也以通过java代码通过JDBC自己实现一个客户端, 咱们的客户端就要通过网络访问到数据库服务器, 才能进一步进行增删改查
而Url描述了服务器/服务器上的资源在网络中的位置 - jdbc:mysql 表示给jdbc中的mysql使用url
- 127.0.0.1 IP地址, 描述的是一个主机/电脑在网络中的位置(本机)
- 3306 端口号, 能够区分主机上的应用程序, 3306是mysql默认的端口号
- java 表示此时你要使用的数据库名
- characterEncoding=utf8 编码规则
- useSSL=false 关闭加密通信
3) ((MysqlDataSource) dataSource).setUser("root");
- root是mysql自带的用户, 管理员用户, 权限是最高的
4) ((MysqlDataSource) dataSource).setPassword("123456");
- 这里的密码和你装数据库时设置的密码要一致
第二步: 和数据库服务器建立连接
在进行 客户端-服务器 之间通信时, 常用的有两种模式: 1) 有连接(例如JDBC) 2) 无连接
//2. 和数据库服务器建立连接Connection connection = dataSource.getConnection();
- 在敲代码时注意
我们要选择的是java.sql的Connection, 不能选其他的 - 但是我们将这段代码敲上去时, 发现编译不通过
从错误信息, 我们看出, 调用这个方法, 要抛异常
第三步: 能够构造一个操作数据库的sql语句
- 字符串结构的sql, 往往还需要构造一个"语句对象"
Statement 仅仅是表示一个普通的语句对象
PreparedStatement 则是一个带有"预编译"功能的语句对象 - 一个字符串sql发送到数据库服务器上, 是要先对sql进行解析, 进行各种校验, 判定sql是否符合语法要求等, 才能执行, 而这个解析操作也是需要花费一定的开销的, 虽然开销不大, 但是mysql服务器要同时给多个客户端提供服务
- 为了减轻数据库服务器的负担, 就可以在客户端这边完成, 此时把解析后的结果发给服务器, 服务器直接执行即可, 所以我们使用PreparedStatement
第四步: 执行sql, 把刚才解析好的语句发给数据库服务器
//4. 执行sql, 把刚才解析好的语句发给数据库服务器int n = preparedStatement.executeUpdate();System.out.println("n = " + n);
- 这里使用的executeUpdate()方法, 是针对insert, update, delete
- 针对select ,我们使用executeQuery()方法
- 执行这个方法, 就会在内部, 给数据库发起请求, 请求中就包含了解析后的sql
- 等待数据库执行sql
- 过一会, 数据库执行完sql后, 返回响应
- 这个方法再获取到响应, 并且把返回的结果通过返回值体现出来, 返回值是int 类型, 表示这个操作影响了几行数据
第五步: 执行完毕, 进行收尾操作, 释放前面创建的各种资源
- 主要是释放 语句对象 和 连接对象, DataSourse不必释放
- 之前我们并没有怎么接触过资源回收, 那是因为java有垃圾回收机制(GC), 可以帮助我们回收不用的内存, 但是对于这种其他资源, java无能为力, 需要我们手动释放
- 要注意回收的顺序, 因为我们先建立连接对象, 后创建的语句对象, 所以要先释放语句对象, 再释放连接对象
执行代码:
执行成功后, 发现表中已经添加了这个数据
改进代码:
当我们插入内容时, 在代码中是写死的, 想要插入不同的数据, 就需要重新编译, 很多时候是希望能够在程序运行时, 让用户输入要插入的数据
所以我们可以这样写:
Scanner scanner = new Scanner(System.in);System.out.println("请输入学号:");int id = scanner.nextInt();System.out.println("请输入姓名:");String name = scanner.next();String sql = "insert into test values (" + id + ", '" + name + "')";PreparedStatement preparedStatement = connection.prepareStatement(sql);
虽然这样写可行, 但是并不推荐
原因1: 看起来很奇怪, 影响可读性
原因2: 不安全, 可能会引起sql注入攻击这样的漏洞
推荐的写法, 是通过PreparedStatement提供的api来完成动态内容的设置:
Scanner scanner = new Scanner(System.in);System.out.println("请输入学号:");int id = scanner.nextInt();System.out.println("请输入姓名:");String name = scanner.next();String sql = "insert into test values (?,?)";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setInt(1, id);preparedStatement.setString(2, name);
- sql语句中 ? 要占位置, 需要后续代码做出内容替换
- 当前是什么类型的数据, 就用set...方法
- set...()方法中的第一个参数, 表示替换第几个 ? , 下表从1开始
查询操作:
public static void main(String[] args) throws SQLException {//1. 创建"数据源"DataSource dataSource = new MysqlDataSource();//"jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false"((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("123456");//2. 建立连接Connection connection = dataSource.getConnection();//3. 创建sqlString sql = "select * from test";PreparedStatement preparedStatement = connection.prepareStatement(sql);//4. 执行sqlResultSet resultSet = preparedStatement.executeQuery();while(resultSet.next()){int id = resultSet.getInt("id");String name = resultSet.getString("name");System.out.println("id = " + id + "name = " + name);}//5. 回收资源resultSet.close();preparedStatement.close();connection.close();}
与新增操作不同的是:
- 第四步中, 调用的方法是executeQuery(), 用结果集合ResultSet接收
- 需要遍历结果集合, 使用while循环, 循环条件是resultSet.next(), 在第一行数据的前一行有一个光标, 每次循环, 光标向下移动
- 想要获得某一行中的某一列数据, 需要用get...()方法, 具体根据什么类型选择什么方法
- 第五步中, resultSet也需要回收, 注意顺序