文章目录
- 前言:Java操作数据库有很多技术概要
- 零、JDBC是什么?
- 一、JDBC快速入门(快速了解)
- 二、JDBC详解(详解JDBC中的对象类)
- 三、JDBC具体演示(练习)
- 四、JDBC管理事务
- 五、JDBC程序的优化(上面的所有都是为下面的这两个做准备,上面的代码你可以不会写,但下面这两个你会了就行)
前言:Java操作数据库有很多技术概要
Java操作数据库有很多技术
- 前几年使用比较多的就是JDBC这套san公司定义的规则
- 近几年再JDBC的规则上又衍生出来了一些流行的框架。例如Mybatis这些
这些我们都需要学习,但所有的都是基于JDBC的,所以我们先来学习单独使用JDBC是怎么做的,Mybatis这些操作数据库的框架能做的JDBC肯定也能做,只是代码复杂一点。所以我们先打好JDBC的基础再来学Mybatis这些框架就会更加简单了
零、JDBC是什么?
- JDBC
- 概念:Java DataBase Connectivity, Java 数据库连接, Java语言操作数据库
- JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动iar包中的实现类
其实就是sun公司提供了一套接口,可以通过这套接口去操作具体的数据库。各个数据库厂商就根据这套接口规则提供对应的实现类个Java程序员使用
一、JDBC快速入门(快速了解)
参考视频
下面是一个简单的快速入门,后面会详细讲。
- 步骤:
- (1)导入驱动jar包
mysql-connector-j-8.0.31.jar(网上一搜就能搜到,注意与MySQL版本对应起来) - (2)注册驱动
- (3)获取数据库连接对象 Connection
- (4)定义sql
- (5)获取执行sql语句的对象 Statement
- (6)执行sql,接收返回结果
- (7)处理结果
- (8)释放资源
- (1)导入驱动jar包
package cn.hjblogs.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author JUHE
* @version 1.0
* JBDC快速入门
*/
public class JbdcDemo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1. 导入驱动jar包(手动导jar包即可)
// 2. 注册驱动,不同版本的MySQL驱动注册方式不同,但基本差不多
Class.forName("com.mysql.cj.jdbc.Driver"); // 其实就是干了一件事:使用全类名将一个类加载进内存了
// 3. 获取数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db01", "root", "1234"); // 连接上了本地的db01数据库
// 4. 定义sql
String sql = "update tb_emp set name = '迪迦奥特曼' where id = 1"; // 将数据库中id为1的name改为迪迦奥特曼
// 5. 获取执行sql语句的对象
Statement stmt = conn.createStatement();
// 6. 执行sql
int count = stmt.executeUpdate(sql);
// 7. 处理结果
System.out.println(count); // 执行成功会返回1
// 8. 释放资源
stmt.close();
conn.close();
}
}
执行成功上面代码会发现数据库内数据的确修改过来了,所以我们就实现了利用Java代码操作数据库
二、JDBC详解(详解JDBC中的对象类)
我们主要要学习下面5个对象:
- DriverManager:驱动管理对象
- Connection:数据库连接对象
- Statement:执行sql的对象
- Resultset:结果集对象
- Preparedstatement:执行sql的对象
其实这五个对象都是java.sql包下的五个接口或类(如果是接口就要对应数据库的厂商提供了),这就是sun公司规定的jdbc这套规则,我们实际new出来的是具体的数据库厂商写的实现类。例如这里我们的mysql-connector-j-8.0.31.jar下的就是MySQL厂商配套写的jdbc这套规则下的接口的实现类。
1 DriverManager:驱动管理对象
- DriverManager:驱动管理对象,是一个类
- 功能:
- 注册驱动
- 获取数据库连接
- 功能:
(1)DriverManager功能1:注册驱动(告诉程序该使用哪一个数据库驱动jar)-- Class.forName(“com.mysql.cj.jdbc.Driver”)
-
注册驱动:告诉程序该使用哪一个数据库驱动jar
static void registerDriver(Driver driver):注册与给定的驱动程序 DriverManager。
写代码使用: Class.forName(“com.mysql.cj.jdbc.Driver”); // 其实就是干了一件事:使用全类名将一个类加载进内存了 -
QS1:上面两种注册驱动程序有什么区别?
通过查看源码发现: 在com.mysql.cj.jdbc.Driver类中存在静态代码块,其实也是执行了static void registerDriver(Driver driver)
这下就清晰了
-
QS1: 注册驱动是在干什么?
告诉程序该使用哪一个数据库驱动jar包。因为jbdc里面我们都是在面向接口编程(多态创建实现类对象),要一套代码通用所有类型的关系数据库,我们就必须告诉程序我们使用的是哪一个jar包 new出接口的实现类对象
【注】:MySQL5后面版本不需要Class.forName(“com.mysql.cj.jdbc.Driver”);单独注册驱动了,可以省略这行代码,底层封装成自动注册驱动了
(2)DriverManager功能2:获取数据库连接对象 – Connection conn = DriverManager.getConnection(‘url’, “数据库用户名”, “密码”); // 连接上了本地的db01数据库
- 方法: static Connection getConnection(String url,String user , String password)
- 参数:
- url: 指定连接的路径(每种不同数据库这里的连接路径有点不同,这里我们只介绍MySQL的)
语法: jdbc:mysql://ip地址(域名):端口号/数据库名称
例子: jdbc:mysql://localhost:3306/db03
细节: 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为: jdbc:mysql:///数据库名称 - user: 数据库用户名
- password: 密码
- url: 指定连接的路径(每种不同数据库这里的连接路径有点不同,这里我们只介绍MySQL的)
- 参数:
// 3. 获取数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db01", "root", "1234");
// 连接上了本地的db01数据库
2 Connection:数据库连接对象(获取可以执行SQL的对象和管理事务)
【注】:查看官方文档会发现这是一个接口
(1)Connection功能1:获取可以执行SQL的对象Statement 或者 Preparedstatement
(2)Connection功能2:管理事务
- 功能:
- (1)获取执行sql 的对象:
下面是获取可以执行SQL对象Statement 、Preparedstatement 的两个方法,这两个对象有什么区别后面会讲- Statement createStatement()
- Preparedstatement prepareStatement(String sql)
- (2)管理事务
- 开启事务: void setAutoCommit(boolean autoCommit): 调用该方法设置参数为false,即开启事务
- 提交事务: void commit()
- 回滚事务: void rollback()
怎么使用这些方法,后面练习中会具体演示。
- (1)获取执行sql 的对象:
3 Statement:执行sql的对象(由于存在SQL注入不安全问题所以我们后期实际开发中都是使用PreparedStatement)
【注】:查官方文档,可以发现这是一个接口
这个对象里面有几个方法是要重点掌握的
- 执行SQL:
- boolean execute(String sql): 可以执行任意的sql (了解即可)
这个不常用了解即可,主要是下面这两个非常常用要掌握 - int executeUpdate(String sql): 执行DMl (insert、update、delete)语句、DDL(create、alter、drop)语句
返回值: 影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。 - ResultSet executeQuery(String sql) : 执行DQL(select)语句
- boolean execute(String sql): 可以执行任意的sql (了解即可)
4 Resultset:结果集对象
- Resultset: 结果集对象,封装查询结果
我们同样学习两个方法就会了-
next(): 游标向下移动一行
游标,理解成指针即可,我们在SQL中select查询返回的是一个表格,这个指针一开始就是指向第一行(这个第一行指的是表头的那一行,没有数据的,表头是字段列名的哪行)
-
getXxx(参数): 获取数据
- Xxx: 代表数据类型。如: int getInt(), String getString()
- 参数:
(1)int: 代表列的编号,从1开始 。如: getString(1)
(2)String: 代表列名称。如: getDouble(“字段”)
-
具体的使用demo见【三、JDBC具体演示(练习)】中
5 PreparedStatement:执行sql的对象,解决Statement的SQL注入不安全问题(后期开发中,都是使用这个更加安全)
public interface PreparedStatement extends Statement 可以看出 PreparedStatement接口继承了Statement接口
- PreparedStatement: 执行sql的对象
- 1.SQL注入问题: 在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
- 输入用户随便,输入密码: a’ or ‘a’ = 'a
- sql: select * from tb_emp where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’= ‘a’
- 具体这个SQL注入注入问题是什么意思见下面的【登入练习:通过键盘录入判断员工是否登录成功(判断:username & password)】这块解释
- 2.解决sql注入问题: 使用PreparedStatement对象来解决
- 3.预编译的SQL: 参数使用 ? 作为占位符
- 4.步骤:
- 1.导入驱动jar包 mysq1-connector-java-5.1.37-bin.jar
- 2.注册驱动
- 3.获取数据库连接对象 Connection
- 4.定义sql
注意: sql 的参数使用 ? 作为占位符。如: select * from tb_emp where username =? and password =?; - 5.获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
- 6.给 ? 赋值:
方法: setXxx(参数1,参数2)
参数1: ? 的位置编号 从1 开始
参数2: ? 的值 - 7.执行sql,接收返回结果,不需要传递sql语句
- 8.处理结果
- 9.释放资源
- 1.SQL注入问题: 在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
具体的演示还是看下面的练习【登入练习:通过键盘录入判断员工是否登录成功(判断:username & password)】
三、JDBC具体演示(练习)
1 执行DMl (insert、update、delete)语句和 执行DDL(create、alter、drop)语句
(1)tb_emp表insert一条记录(模版)
INSERT INTO tb_emp(username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time)
VALUES (‘cxx’, ‘cxxwxhn’, ‘曹潇潇’, 2, ‘cxx.jpg’, 4, ‘2000-01-01’, 2, now(), now());
package cn.hjblogs.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JbdcDemo1 {
public static void main(String[] args){
// 1. 导入驱动jar包(手动导jar包即可)
// 2. 注册驱动,不同版本的MySQL驱动注册方式不同,但基本差不多
Statement stmt = null;
Connection conn = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 3. 定义sql
//tb_emp表insert一条记录
// INSERT INTO tb_emp(username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time) VALUES ('cxx', 'cxxwxhn', '曹潇潇', 2, 'cxx.jpg', 4, '2000-01-01', 2, now(), now());
String sql = "INSERT INTO tb_emp(username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time)\n" +
"VALUES ('cxx', 'cxxwxhn', '曹潇潇', 2, 'cxx.jpg', 4, '2000-01-01', 2, now(), now())"; // 将数据库中id为1的name改为迪迦奥特曼
// 4. 获取数据库连接对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db01", "root", "1234"); // 连接上了本地的db01数据库
// 5. 获取执行sql语句的对象
stmt = conn.createStatement();
// 6. 执行sql
int count = stmt.executeUpdate(sql); // 返回值是影响的行数
System.out.println(count);
if (count > 0){
System.out.println("添加成功!");
}else {
System.out.println("添加失败!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
// 7. 释放资源
// stmt.close();
// conn.close();
// 为了避免空指针异常,所以要进行非空判断
if (stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
【注】:上面逻辑还可以进一步优化,例如数据库表中有些字段设置了唯一约束,重复添加还是会报错的,我们可以进一步优化这个问题,但是我们这里仅仅用于学习就先这样了。
(2)tb_emp表update一条记录
将SQL语句改成下面这个即可
String sql = "update tb_emp set name = '曹潇潇2' where username = 'cxx'";
(3)tb_emp表delete一条记录
将SQL语句改成下面这个即可
String sql = "delete from tb_emp where username = 'cxx'";
(4)执行DDL(create、alter、drop)语句
虽然实际中DDL语句在JDBC中基本不会用到但我们还是可以演示一下:创建一个简单的Student表吧!
将SQL语句改成下面这个即可
String sql = "create table Student(id int, name varchar(20))";
需要注意的是,DDL语句是没有影响行数的,所以模版中返回的count影响行数是0。
2 Resultset: 结果集对象,封装查询结果(练习)
- Resultset: 结果集对象,封装查询结果
我们同样学习两个方法就会了-
next(): 游标向下移动一行,返回true就是有数据,返回false就是没有数据
游标,理解成指针即可,我们在SQL中select查询返回的是一个表格,这个指针一开始就是指向第一行(这个第一行指的是表头的那一行,没有数据的,表头是字段列名的哪行)
-
getXxx(参数): 获取数据
- Xxx: 代表数据类型。如: int getInt(), String getString()
- 参数:
(1)int: 代表列的编号,从1开始 。如: getString(1)
(2)String: 代表列名称。如: getDouble(“字段”)
-
(1)Resultset: 结果集对象基本使用
public class JbdcDemo1 {
public static void main(String[] args){
// 1. 导入驱动jar包(手动导jar包即可)
// 2. 注册驱动,不同版本的MySQL驱动注册方式不同,但基本差不多
Statement stmt = null;
Connection conn = null;
ResultSet res = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 3. 定义sql
String sql = "select * from tb_emp";
// 4. 获取数据库连接对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db01", "root", "1234"); // 连接上了本地的db01数据库
// 5. 获取执行sql语句的对象
stmt = conn.createStatement();
// 6. 执行sql
res = stmt.executeQuery(sql);
// 7. 处理结果(解析res)
res.next();
int id = res.getInt(1);
String name = res.getString("name");
String username = res.getString("username");
String password = res.getString("password");
System.out.println("id: " + id + ", name: " + name + ", username: " + username + ", password: " + password);
// id: 1, name: 迪迦奥特曼, username: jinyong, password: 123456
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7. 释放资源
// stmt.close();
// conn.close();
// 为了避免空指针异常,所以要进行非空判断
if (res != null){
try {
res.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();
}
}
}
}
}
(2)Resultset: 结果集对象,遍历结果集(模版)— 使用while循环
package cn.hjblogs.jdbc;
import java.sql.*;
/**
* @author JUHE
* @version 1.0
* JBDC快速入门
*/
public class JbdcDemo1 {
public static void main(String[] args){
// 1. 导入驱动jar包(手动导jar包即可)
// 2. 注册驱动,不同版本的MySQL驱动注册方式不同,但基本差不多
Statement stmt = null;
Connection conn = null;
ResultSet res = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 3. 定义sql
String sql = "select * from tb_emp";
// 4. 获取数据库连接对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db01", "root", "1234"); // 连接上了本地的db01数据库
// 5. 获取执行sql语句的对象
stmt = conn.createStatement();
// 6. 执行sql
res = stmt.executeQuery(sql);
// 7. 处理结果(解析res)
while (res.next()){
int id = res.getInt("id");
String name = res.getString("name");
String username = res.getString("username");
String password = res.getString("password");
System.out.println("id: " + id + " name: " + name + " username: " + username + " password: " + password);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7. 释放资源
// stmt.close();
// conn.close();
// 为了避免空指针异常,所以要进行非空判断
if (res != null){
try {
res.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