基本概念
java DataBase Connectivity —> java数据库连接 —> java语言操作数据库
JDBC本质
其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
快速入门
步骤
- 导入驱动jar包
下载jar包
https://mvnrepository.com/ Mysql connecter java
https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.21
- 注册驱动
- 获取数据库的链接对象 Connection
- 定义sql
- 获取执行sql语句的对象 Statement
- 执行sql , 接收返回结果
- 处理结果
- 释放资源
实现一个操作



再创建一个空的项目

finish
弹出窗口
next
finish -> OK
创建一个包
为其命名cn.itcast.jdbc
tips: 如果创建出来的不是这样分开的包,点击右上角小齿轮 -> 取消勾选的Compact Middle Package


导入驱动jar包
连接什么数据库就用什么jar包,这里以MySQL为例
mysql-connector-java-8.0.21.jar
建一个文件夹libs方便管理jar包,因为做项目时有好多jar包
把jar包复制到libs里面,从计算机里面找到jar包,ctrl C复制到libs Ctrl V
弹出:

OK
总结
复制jar包到项目的libs目录下
右键—>add as Library
注册驱动
这里有一个ClassNotFound的异常 Alt+Enter抛出去就可以了
抛出之后删去ClassNotFound
获取数据库的链接对象

database输入错误 报错

Exception in thread “main” java.sql.SQLSyntaxErrorException: Unknown database ‘pet’
地域时间未设置 报错
Exception in thread “main” java.sql.SQLException: The server time zone value ‘�й���ʱ��’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the ‘serverTimezone’ configuration property) to use a more specifc time zone value if you want to utilize time zone support.
修改后:
这是在使用MySQL 8.0以上版本(MySQL连接驱动和版本都是8.0以上)的时候出现的问题错误
package cn.itcast.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.Statement;public class jdbcDemo1 {public static void main(String[] args) throws Exception {//导入jar包//注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//获取数据库的链接对象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myfirsttest?serverTimezone=Asia/Shanghai","root","123456");//定义sql语句String sql = "update account set balance = 500 where id = 1";//获取执行sql的对象StatementStatement stmt = conn.createStatement();//执行sqlint count = stmt.executeUpdate(sql);//处理结果System.out.println(count);//释放资源stmt.close();conn.close();}}结果:1Process finished with exit code 0
详解各个对象
- DriverManager:驱动管理对象
- Connection :数据库连接对象
- Statement:执行sql的对象
- ResultSet:结果集对象
- PreparedStatement:执行sql的对象
DriverManager
驱动管理对象
功能:注册驱动:告诉程序该使用哪一个数据库驱动jar包
static void registerDriver(Driver diver) :注册与给定的驱动程序DiverManager。
写代码使用:Class.forName(“com.mysql.cj.jdbc.Driver”);
通过查看源码发现:在com.mysql.cj.jdbc.Driver类中存在静态代码块
注释掉后发现程序依旧可以运行,why?
注释掉后被MySQL5+自动帮忙注册驱动了,所以MySQL5之后的驱动jar包可以省略注册驱动的步骤
获取数据库链接
方法
static Connection getConnection(String url , string user , String password )
参数:
url :指定连接的路径
语法:jdbc : mysql:// ip地址(域名): 端口号 / 数据库名称
🌰: jdbc:mysql:// localhost : 3306 / myfirsttest
细节:如果连接的是本机MySQL服务器,并且MySQL服务默认端口是3360,则url可简写为: jdbc:mysql:///数据库名称
user:用户名
password:密码
Connection
数据库链接对象
- 功能:
- 获取执行sql的对象
Statement createStatement():获取一个Statemen对象,用于将sql语句发送到数据库
PreparedStatement preparedStatement(String sql)
- 管理事务
- 执行sql
- boolean execute(String sql) : 可以执行任意的sql (了解)
- int executeUpdate(Strirng sql) : 执行DML(insert update delete)语句、DDL(create alter drop)语句
返回值:影响的行数
影响两行
可以通过这个影响的行数来判断DML语句是否执行成功 返回值>0,则成功,反之失败
c. ResultSet executeQuery (String sql) : 执行DQL (select)语句,返回一个结果及对象(后面讲
🍕练习:
account表 添加一条记录
注册驱动的时候发现一个异常,抓一下 try/catch一下

获取Connection对象时候再抓一个异常
抓完之后出现一个catch
释放资源
因为stmt在try作用域里面,相当于一个局部变量。
此时要提升它的作用域,把它提升到try的外面
package cn.itcast.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.Statement;/** account表 添加一条记录 insert 语句的一个练习* */public class JDBCDemo2 {public static void main(String[] args){Statement stmt = null;Connection conn = null;//不用导入jar包了,可省略try {//1. 注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.定义sqlString sql = "insert into account values(3,'王五','2020-10-01',3000) ";//3.获取Connection对象conn = DriverManager.getConnection("jdbc:mysql:///myfirsttest?serverTimezone=Asia/Shanghai","root","123456");//4.获取执行sql的对象Statementstmt = conn.createStatement();//5.执行sqlint count = stmt.executeUpdate(sql); //count 影响的行数//6.处理结果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 {//stmt.close(); 如果中间出错(如:密码错误),那么会返回一个空指针//7.释放资源//避免空指针异常if (stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}//复制粘贴一下 判断connif (conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}}结果:1添加成功
account表 修改记录
package cn.itcast.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.Statement;/** account表修改记录 王五金额修改为200* */public class JDBCDemo3 {public static void main(String[] args) {Connection conn = null;Statement stmt = null;try {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接对象conn = DriverManager.getConnection("jdbc:mysql:///myfirsttest?serverTimezone=Asia/Shanghai", "root", "123456");//3.定义sqlString sql = "update account set balance = 200 where id = 3";//4.获取执行sql对象stmt = conn.createStatement();//5.执行sqlint count = stmt.executeUpdate(sql);//6.处理结果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 {//释放资源 防止空指针异常if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}}}
account表 删除一套记录
复制上面的代码,改动一下第三步的语句
delete from account where id = 3
ResultSet
结果集对象,封装查询结果
就是把这些结果对象拿出来
游标(类似指针)
![]() |
游标默认指向表头(第一行) 此时不能取数据 |
|---|---|
作用过程涉及两类方法
next()
boolean类型的值,游标从当前位置向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是则返回false,反之true
getXxx(参数)
获取数据
一次获取某一行的某一列的数据
Xxx: 数据类型 eg:int getInt(),String getString()
参数:
1. Int 代表列的编号,从**1**开始 eg:getInt(1)1. String:代表列的名称 eg:getString("balance")
范例
package cn.itcast.jdbc;import java.sql.*;public class JDBCDemo6 {public static void main(String[] args) {Connection conn = null;Statement stmt = null;ResultSet rs = null;try {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接对象conn = DriverManager.getConnection("jdbc:mysql:///myfirsttest?serverTimezone=Asia/Shanghai", "root", "123456");//3.定义sqlString sql = "select * from account";//4.获取执行sql对象stmt = conn.createStatement();//5.执行sqlrs = stmt.executeQuery(sql);//6.处理结果//6.1让游标向下移动一行rs.next();//6.2获取数据int id = rs.getInt(1);String name= rs.getString("name");double balance = rs.getDouble(3);System.out.println(id+"---"+name+"---"+balance);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();} finally {//释放资源 防止空指针异常if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}}}}}结果:2---ls---2000.0
使用步骤
- 游标向下移动一行
- 判断是否有数据 : boolean next() 有数据true
获取数据
while(rs.next()) //循环判断结果集是否有数据{//6.2获取数据int id = rs.getInt(1);String name= rs.getString("name");double balance = rs.getDouble(3);System.out.println(id+"---"+name+"---"+balance);}
eg:
定义一个方法,查询account表的数据,将其封装为对象,然后装载集合,返回。打印定义account类
- 定义方法 public List
findAll(){} - 实现方法 select * from account ;

package cn.itcast.domain;//封装account表数据的JavaBeanpublic class account {private int id;private String name;private double balance;//右键-->Generate -->Ctrl全选public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}@Overridepublic String toString() {return "account{" +"id=" + id +", name='" + name + '\'' +", balance=" + balance +'}';}}
package cn.itcast.jdbc;import cn.itcast.domain.account;import java.sql.*;import java.util.*;/** 定义一个方法,查询account表的数据,将其封装为对象,然后装载集合,返回。* */public class JDBCDemo8 {public static void main(String[] args) {List<account> list = new JDBCDemo8().findAll();System.out.println(list);}//查询所有emp对象//@returnpublic List<account> findAll(){ResultSet re = null;Statement stmt = null;Connection conn = null;List<account> list = null;try {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取链接conn = DriverManager.getConnection("jdbc:mysql:///myfirsttest?serverTimezone=Asia/Shanghai","root","123456");//3.定义sqlString sql = "select * from account";//4.获取执行sql的对象stmt = conn.createStatement();//5.执行sqlre = stmt.executeQuery(sql);//6.遍历结果集,封装对象,装载集合account ac = null;list = new ArrayList<account>();while(re.next()){//获取数据int id = re.getInt("id");String name = re.getString("name");double balance = re.getDouble("balance");//创建account对象ac = new account();ac.setId(id);ac.setBalance(balance);ac.setName(name);//装载集合list.add(ac);}//list.forEach(System.out::println);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}finally {if (re != null){try {re.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();}}}return list;}}
PreparedStatement
Statement的衍生,执行SQL的对象(功能比Statement更强)
抽取JDBC工具类
JDBCUtils
分析
- 注册驱动也抽取
- 抽取一个方法获取连接对象
需求:不想传递参数(麻烦),还得保证工具类的通用性。
解决:配置文件
定义一个jdbc.properties配置文件,
其中记录:
url=
user=
password=
- 抽取一个方法释放资源

package cn.itcast.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;/*** 文件的读取,只需要读取一次即可拿到这些值.使用静态代码块*/static{//读取资源文件,获取值。try {//1.创建Properties集合类Properties pro = new Properties();//获取src路径下的文件的方式--->ClassLoader 类加载器,加载字节码文件径内存,且获取src下的文件路径ClassLoader classLoader = JDBCUtils.class.getClassLoader();URL res = classLoader.getResource("jdbc.properties");//URL 表示一个统一资源定位符 ↑ 定义了一个文件的绝对路径String path = res.getPath();System.out.println(path);//2.加载文件// pro里load方法load(Reader字符流 /InputStream字节流)//pro.load(new FileReader("src/jdbc.properties"));pro.load(new FileReader(path));//3.获取属性 赋值url = pro.getProperty("url");user = pro.getProperty("user");password = pro.getProperty("password");driver = pro.getProperty("driver");try {//4.注册驱动Class.forName(driver);} catch (ClassNotFoundException e) {e.printStackTrace();}} catch (IOException e) {e.printStackTrace();}}/*** 获取连接* @return 连接对象*/// 工具类有个特点,所有方法都是静态的,方便调用public static Connection getConnection() throws SQLException {return DriverManager.getConnection(url,user,password);}/*** 关闭/释放资源方法* 没有返回值*/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();}}}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();}}}}

url = jdbc:mysql:///myfirsttest?serverTimezone=Asia/Shanghaiuser = rootpassword = 123456driver = com.mysql.cj.jdbc.Driver
演示JDBC工具类
package cn.itcast.jdbc;import cn.itcast.domain.account;import cn.itcast.util.JDBCUtils;import java.sql.*;import java.util.ArrayList;import java.util.List;public class JDBCDemo8_1 {public static void main(String[] args) {List<account> list = new JDBCDemo8_1().findAll2();System.out.println(list);}//演示JDBC工具类//@returnpublic List<account> findAll2() {ResultSet re = null;Statement stmt = null;Connection conn = null;List<account> list = null;try {// //1.注册驱动// Class.forName("com.mysql.cj.jdbc.Driver");// //2.获取链接// conn = DriverManager.getConnection("jdbc:mysql:///myfirsttest?serverTimezone=Asia/Shanghai","root","123456");conn = JDBCUtils.getConnection();//3.定义sqlString sql = "select * from account";//4.获取执行sql的对象stmt = conn.createStatement();//5.执行sqlre = stmt.executeQuery(sql);//6.遍历结果集,封装对象,装载集合account ac = null;list = new ArrayList<account>();while (re.next()) {//获取数据int id = re.getInt("id");String name = re.getString("name");double balance = re.getDouble("balance");//创建account对象ac = new account();ac.setId(id);ac.setBalance(balance);ac.setName(name);//装载集合list.add(ac);}//list.forEach(System.out::println);} catch (SQLException e) {e.printStackTrace();}finally {// if (re != null){// try {// re.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();// }// }// }JDBCUtils.close(re, stmt, conn);return list;}}}
🍕练习
需求:
- 通过键盘录入用户名和密码
- 判断用户是否登录成功
select * from user where username = "" and password = "" ;
如果这个sql语句有查询结果,则成功。否则失败。
步骤:
- 创建数据表 user


package cn.itcast.jdbc;import cn.itcast.util.JDBCUtils;import com.sun.org.apache.bcel.internal.generic.Select;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Scanner;/*** 需求:* 1. 通过键盘录入用户名和密码* 2. 判断用户是否登录成功*/public class JDBCDemo9 {public static void main(String[] args) {//1.键盘录入,接受用户名和密码Scanner sc = new Scanner(System.in);System.out.println("username:");String username = sc.nextLine();System.out.println("password:");String password = sc.nextLine();//2.调用方法boolean flag = new JDBCDemo9().login(username,password);//3.判断结果,输出不同语句if (flag){//登陆成功System.out.println("登陆成功!");}else {System.out.println("用户名或密码错误!");}}/*** 登陆方法*/public boolean login(String username,String password){if (username == null || password == null){return false;}//连接数据库判断是否登录成功Connection conn = null;Statement stmt= null;ResultSet re= null;//1.获取连接try {conn = JDBCUtils.getConnection();//2.定义sqlString sql = "select * from user where username = '"+username+"' and password = '"+password+"'";//3.获取执行sql对象stmt = conn.createStatement();//执行查询re = stmt.executeQuery(sql);//5.判断return re.next();//如果有下一行,返回true} catch (SQLException e) {e.printStackTrace();}finally {JDBCUtils.close(re,stmt,conn);}return false;}}
sql注入问题:、
在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性的问题。
🌰随便输入username,然后输入password:a’ or ‘a’ = ‘a ,却登陆成功
如何解决?
使用 PreparedStatement 对象来解决
预编译的sql:参数使用 ? 作为占位符
步骤:
- 导入驱动jar包
- 注册驱动
- 获取数据库的链接对象 Connection
- 定义sql
- 注意:sql参数使用?作为占位符
- 🌰:
select * from user where username = ? and password = ? ;
- 🌰:
- 注意:sql参数使用?作为占位符
- 获取执行sql语句的对象
PreparedStatement Connection.prepareStatement(String sql) - 给“?”赋值
- 方法:`setXxx(参数1 , 参数2)
- 参数1:?的位置编号 从1开始
- 参数2:?的值
- 方法:`setXxx(参数1 , 参数2)
- 执行sql , 接收返回结果
- 处理结果
-
🧐tip
后期都会使用PreparedStatement来完成增删改查的所有操作
它可以防止sql注入问题
效率更高事务
一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
操作
开启事务
- 提交事务
- 回滚事务
使用Connection
- 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
- 在执行sql之前开启事务
- 提交事务:commit()
- 当所有sql都执行完提交事务
- 回滚事务:rollback()
- 在catch中回滚事务 ```java package cn.itcast.jdbc;
import cn.itcast.util.JDBCUtils; import com.mysql.cj.x.protobuf.MysqlxCrud;
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException;
/**
事务操作 */ public class JDBCDemo10 { public static void main(String[] args) {
Connection conn = null;PreparedStatement pstm1 = null;PreparedStatement pstm2 = null;try {//1.获取连接conn = JDBCUtils.getConnection();//开启事务conn.setAutoCommit(false);//2.定义sql//2.1 张三 -500String sql1 = "update account set balance = balance - ? where id = ?";//2.2 李四 +500String sql2 = "update account set balance = balance + ? where id = ?";//3.获取执行sql对象pstm1 = conn.prepareStatement(sql1);pstm2 = conn.prepareStatement(sql2);//4.设置参数pstm1.setDouble(1,500);pstm1.setInt(2,1);pstm2.setDouble(1,500);pstm2.setInt(2,2);//5.执行sql
// 手动制造异常int i = 3/0;pstm1.executeUpdate();pstm2.executeUpdate();//提交事务conn.commit();} catch (Exception e) { //事务回滚时候抓大一点的异常//事务回滚try {if (conn!=null){ //conn有可能为nullconn.rollback();}} catch (SQLException ex) {ex.printStackTrace();}e.printStackTrace();}finally {JDBCUtils.close(pstm1,conn);JDBCUtils.close(pstm2,null);}}
}
``` account表没有变化



