JDBC入门
1 JDBC:Java DataBase Connectivity
1.1JDBC的概念
JDBC规范定义接口,具体的实现由各大数据库厂商来实现。
JDBC是Java访问数据库的标准规范,真正的怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的驱动。所以只需要会调用JDBC接口中的方法即可,数据库驱动由数据库厂商提供
- 使用JDBC的好处:
- 程序员如果要开发访问数据库的程序,只需要会调用JDBC接口中的方法即可,不用关注类如何实现的。
- 使用同一套Java代码,进行少量的修改就可以访问其他JDBC支持的数据库。
- 代码不依赖于任何的数据库
1.1.2 使用JDBC开发使用的包
会使用到的包 | 说明 |
---|---|
java.sql | 所有与JDBC访问数据库相关的接口和类 |
javax.sql | 数据库扩展,提供数据库额外的功能。 |
数据库的驱动 | 由各大数据库厂商提供,需要额外去下载,是对JDBC接口实现的类 |
1.2JDBC的核心API
接口或类 | 作用 |
---|---|
DriverManager类 | 1. 管理和注册数据库驱动;2. 得到数据库连接对象 |
Connection 接⼝ | ⼀个连接对象,可⽤于创建 Statement 和 PreparedStatement对象 |
Statement 接⼝ | ⼀个 SQL 语句对象,⽤于将 SQL 语句发送给数据库服务器。 |
PreparedStatement 接⼝ | ⼀个 SQL 语句对象,是 Statement 的⼦接⼝ |
ResultSet 接⼝ | ⽤于封装数据库查询的结果集,返回给客户端 Java 程序 |
1.3加载和注册驱动
加载和注册驱动的⽅法 | 描述 |
---|---|
Class.forName(数据驱动实现类) | 加载和注册数据库驱动,数据库驱动由数据库厂商实现; MySQL ⼚商: “com.mysql.jdbc.Driver” |
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//抛出类找不到的异常,注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
}
}
从JDBC3开始,目前已经普遍使用的版本,可以不用注册驱动而直接使用,Class.forName这句话可以省略
2.DriverManager类
2.1DriverManager作用
- 管理和注册驱动
- 创建数据库的连接
2.2类中的方法
DriverManager类中的静态方法 | 描述 |
---|---|
Connection getConnection(String url, String user,String password) | 通过连接字符串,⽤户名,密码来得到数据库的连接对象 |
Connection getConnection (String url, Properties info) | 通过连接字符串,属性对象来得到连接对象 |
2.3使用JDBC连接数据库的四个参数
JDBC连接数据库的四个参数 | 说明 |
---|---|
⽤户名 | 登录的⽤户名 |
密码 | 登录的密码 |
连接字符串 URL | 不同的数据库 URL 是不同的,mysql 的写法jdbc:mysql://localhost:3306/数据库[?参数名=参数值] |
驱动类的字符串名 | com.mysql.jdbc.Driver |
2.4连接数据库的URL地址格式
- 协议名:子协议://服务器名或IP地址:端口号/数据库名?参数=参数值
2.4.1MySQL写法
URL用于标识数据库的位置,程序员通过URL地址
JDBC程序连接哪个数据库,URL的写法为:
jdbc:mysql://localhost:3306/test?参数=参数值
注:jdbc:协议;
mysql:子协议;
localhost:3306: 主机:端口
test:子协议
2.4.2MySQL中可以简写
前提:必须是本地服务器,端口号是3306
jdbc:mysql:///数据库名
2.4.3乱码的处理
如果数据库出现乱码,可以指定参数:?characterEncoding=utf8,表示让数据库以UFT-8编码来处理数据。
jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8
3 Connection接口
流程:
- 导入jar包
- 加载驱动类
Class.forName(“com.mysql.jdbc.Driver”);
Class.forName(“com.mysql.cj.jdbc.Driver”);
3.1Connection作用
Connection接口,具体的实现类由数据库的厂商实现,代表一个连接对象。
3.2Connection方法
Connection接口中的方法:
Statement createStatement() -- 创建一条SQL语句对象
4 Statement接口
4.1 JDBC访问数据库的步骤
- 注册和加载驱动(可以省略)
- 获取连接
- Connection获取Statement对象
- 使用Statement对象执行SQL语句
- 返回结果集
- 释放资源
4.2 Statement作用
代表一条语句对象,用于发送SQL语句给服务器,用于执行静态SQL语句并返回它所生成结果的对象。
4.3 Statement中的方法
Statement接口中的方法 | 描述 |
---|---|
int executeUpdate(String sql) | 用于发送DML语句,增删改的操作,insert、update、delete 参数:SQL语句 返回值:返回对数据库影响的行数 |
ResultSet executeQuery(String sql) | 用于发送DQL语句,执行查询的操作select 参数:SQL语句 返回值:查询的结果集 |
4.4释放资源
- 需要释放的对象:ResultSet结果集,Statement语句,Connection连接
- 释放原则:先开的后关,后开的先关。ResultSet -> Statement -> Connection
- 放在哪个代码块中:finally块
4.5DDL操作
步骤:
- 创建连接对象
- 创建Statement语句对象
- 执行SQL语句:execute(sql)
- 返回影响的行数
- 释放资源
4.6DML操作
步骤:
- 创建连接对象
- 创建Statement语句对象
- 执行SQL语句:executeUpdate(sql)
- 返回影响的行数
- 释放资源
4.7执行DQL操作
-
作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录
-
接口中的方法
ResultSet 接⼝中的⽅法 | 描述 |
---|---|
boolean next() | 1. 游标向下移动 1 ⾏;2. 返回 boolean 类型,如果还有下⼀条记录,返回 true,否则返回 false |
数据类型 getXxx() | 1. 通过字段名,参数是 String 类型。返回不同的类型;2. 通过列号,参数是整数,从 1 开始。返回不同的类型 |
注:java.sql.Date、Time、Timestamp(时间戳),三个共同⽗类是:java.util.Date
4.7.3 需求
确保数据库中有3条以上的记录,查询所有的学员信息
- 步骤:
- 得到连接对象
- 得到语句对象
- 执行SQL语句得到结果集ResultSet对象
- 循环遍历取出每一条记录
- 输出的控制台上
- 释放资源
4.7.4关于ResultSet 接口中的注意事项
- 如果光标在第一行之前,使用rs.getXXX()获取列值,报错:Before start of result set
- 如果光标在最后一行,使用rs.getXXX()获取列值,报错:After end of result set
- 使用完毕以后要关闭结果集ResultSet,再关闭Statement,再关闭Connection
上述知识要点整合代码:
import java.sql.*;
public class JDBCDemo01 {
public static void main(String[] args) {
try {
//可能出现的异常:ClassNotFoundException
/*原因:
* 1.java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Drive 类名写错
* 2.jar包未导入
* */
Class.forName("com.mysql.cj.jdbc.Driver");
//3.可能出现的异常:获得数据库连接
/*SQLException
* 原因:1.jdbc:mysql: 其中任何一段字符串写错, ip地址协议错误
* 2. Unknown database xxxx 数据库名字错误 找不到这个数据库
* 3.SQLException: Access denied for user 'roo'@'localhost' (using password: YES) 数据库用户名或密码错误
* com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure localhost:330
* 原因:端口错误,防火墙阻拦
*ip地址出错,不会抛出异常,程序会阻塞,一直尝试连接
* */
String url = "jdbc:mysql://localhost:3306/java2106";
Connection connection = DriverManager.getConnection(url,"root","123456");
//System.out.println(connection);
//4.获得执行SQL语句的Statement对象
Statement statement = connection.createStatement();
//执行DDL 通常用execute
/*String sql = "create table test01(id int(6) PRIMARY KEY)";
boolean execute = statement.execute(sql);
System.out.println(execute);*/
/*//执行DML 通常用executeUpdate 如果增删改成功则返回1,增删改失败则返回0
String sql = "update test01 set id = 1";
int i = statement.executeUpdate(sql);
System.out.println(i);*/
//5.执行DQL获得 ResultSet
String sql = "select * from emp";
//执行可能发生的异常SQLSyntaxErrorException 一般是sql语句出现异常,根据具体信息修改异常
ResultSet rs = statement.executeQuery(sql);
//处理结果集 rs.next() boolean 移动到下一个位置,判断是否存在字段,如果存在则返回true
while (rs.next()){
int empno = rs.getInt("empno");
String ename = rs.getString("ename");
Date hiredate = rs.getDate("hiredate");
System.out.println(empno + " " + ename + " " + hiredate);
}
//上述代码使得next()指向了结果集的最后行,再下一个则会超出范围
//SQLException: After end of result set
/*int no = rs.getInt("empno");
String name = rs.getString("ename");
Date hireDate = rs.getDate("hiredate");
System.out.println(no + name + hireDate);*/
//关闭资源
rs.close();
statement.close();
connection.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
一些注意事项整理
import java.sql.*;
public class JDBCDemo02 {
public static void main(String[] args) {
try {
Connection connection = JDBCUtils.getConnection();
//4.获得执行SQL语句的Statement对象
Statement statement = connection.createStatement();
String sql = "select * from emp";
ResultSet rs = statement.executeQuery(sql);
//处理结果集 rs.next() boolean 移动到下一个位置,判断是否存在字段,如果存在则返回true
while (rs.next()){
/*int empno = rs.getInt("empno");
String ename = rs.getString("ename");
Date hiredate = rs.getDate("hiredate");
System.out.println(empno + " " + ename + " " + hiredate);*/
Emp emp = new Emp();
emp.setEmpno(rs.getInt("empno"));
}
//上述代码使得next()指向了结果集的最后行,再下一个则会超出范围
//SQLException: After end of result set
/*int no = rs.getInt("empno");
String name = rs.getString("ename");
Date hireDate = rs.getDate("hiredate");
System.out.println(no + name + hireDate);*/
//关闭资源
rs.close();
statement.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
5 数据库工具类JdbcUtils
自己创建工具类:
如果一个功能经常要用到,建议把这个功能做成一个工具类,可以再不同的地方重用。
5.1需求
出现很多的重复的代码,可以把这些公共代码抽取出来。
5.2创建类JbdcUtil包含3个方法
-
可以把几个字符串定义成常量:用户名,密码,URL,驱动类
-
得到数据库的连接:getConnection()
-
关闭所有打开的资源
close(Connection conn, Statement stmt),close(Connection conn, Statement stmt, ResultSet rs)
5.3步骤
-
得到用户从控制台上输入的用户名和密码来查询数据库
-
写一个登陆方法
a. 通过工具类得到连接
b. 创建语句对象,使用拼接字符串的方式生成SQL语句
c. 查询数据库,如果有记录则登陆成功,否则登录失败
d. 释放资源
5.4 SQL注入问题
用户输入的密码和SQL语句的字符串拼接。用户输入的内容作为SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入,要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。
6 PreparedStatement接⼝
6.1继承结构与作用
接口:Statement、Wrapper
所有已知子接口:
CallableStatement
preparedStatement是Statement接口的子接口,继承于父接口中所有的方法。它是一个预编译的SQL语句。
作用:
- 因为有预先编译的功能,提高SQL的执行效率
- 可以有效的防止SQL注入的问题,安全性更高。
6.2 Connection 创建 PreparedSatement 对象
Connection 接⼝中的⽅法 | 描述 |
---|---|
PreparedStatement prepareStatement(String sql) | 指定预编译的 SQL 语句,SQL 语句中使⽤占位符?创建⼀个语句对象 |
6.3 PreparedSatement 接⼝中的⽅法
PreparedStatement 接⼝中的⽅法 | 描述 |
---|---|
int executeUpdate() | 执⾏ DML,增删改的操作,返回影响的⾏数。 |
ResultSet executeQuery() | 执⾏ DQL,查询的操作,返回结果集 |
6.4 PreparedSatement 的好处
- prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引⽤着
预编译后的结果。可以多次传⼊不同的参数给 PreparedStatement 对象并执⾏。减少 SQL
编译次数,提⾼效率。 - 安全性更⾼,没有 SQL 注⼊的隐患。
- 提⾼了程序的可读性。
6.5 使⽤ PreparedStatement 的步骤
- 编写 SQL 语句,未知内容使⽤?占位:“SELECT * FROM user WHERE name=? AND
password=?”; - 获得 PreparedStatement 对象
- 设置实际参数:setXxx (占位符的位置,真实的值)
- 执⾏参数化 SQL 语句
- 关闭资源
PreparedStatement 中设置参数的⽅法 | 描述 |
---|---|
void setDouble(int parameterIndex, double x) | 将指定参数设置为给定 Java double值。 |
void setFloat(int parameterIndex, float x) | 将指定参数设置为给定 Java float 值。 |
void setInt(int parameterIndex, int x) | 将指定参数设置为给定 Java int 值。 |
void setLong(int parameterIndex, long x) | 将指定参数设置为给定 Java long 值。 |
void setObject(int parameterIndex, Object x) | 使⽤给定对象设置指定参数的值。 |
void setString(int parameterIndex, String x) | 将指定参数设置为给定 Java String 值。 |
import java.sql.*;
import java.util.Scanner;
public class LoginDemo02 {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
Connection connection = JDBCUtils.getConnection();
String sql = "select * from user where username=? and password=?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
//String sql = "select * from user where username = '" + username +"' and password = '"+ password +"'";
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()){
System.out.println("用户登陆成功");
}else {
System.out.println("用户名或密码错误");
}
resultSet.close();
preparedStatement.close();
connection.close();
}
}
7 JDBC 事务的处理
7.1 API介绍
Connection 接⼝中与事务有关的⽅法 | 说明 |
---|---|
void setAutoCommit(boolean | |
autoCommit) | 参数是 true 或 false。如果设置为 false,表示关闭⾃动提交,相当于开启事务 |
void commit() | 提交事务 |
void rollback() | 回滚事务 |
7.2 开发步骤
- 获取连接
- 开启事务
- 获取到 PreparedStatement
- 使⽤ PreparedStatement 执⾏两次更新操作
- 正常情况下提交事务
- 出现异常回滚事务
- 最后关闭资源
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo04 {
public static void main(String[] args) {
Connection connection = JDBCUtils.getConnection();//连接
String sql1 = "update customer set account = account - ? where cust_id = ?";//转出
String sql2 = "update customer set account = account + ? where cust_id = ?";//转入
try {
//事务手动提交设置
connection.setAutoCommit(false);
PreparedStatement statement1 = connection.prepareStatement(sql1);
PreparedStatement statement2 = connection.prepareStatement(sql2);
statement1.setInt(1,100);
statement1.setInt(2,1);
statement1.executeUpdate();
//出现异常
//int i = 1/0;
statement2.setInt(1,100);
statement2.setInt(2,2);
statement2.executeUpdate();
//转账入账的流程结束,提交事务,事务流程结束
connection.commit();
} catch (SQLException throwables) {
try {
//如果出现异常就对事务进行回滚,结束事务
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
import java.io.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtils {
private static String url;
private static String username;
private static String password;
private static String driver;
static {
Properties pro = new Properties();
try {
pro.load(JDBCUtils.class.getResourceAsStream("jdbc.properties"));
url = pro.getProperty("jdbc.url");
username = pro.getProperty("jdbc.username");
password = pro.getProperty("jdbc.password");
driver = pro.getProperty("jdbc.driver");
Class.forName(driver);
}catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
return DriverManager.getConnection(url,username,password);
} catch (SQLException throwables) {
throwables.printStackTrace();
return null;
}
}
}
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/java2106
jdbc.username = root
jdbc.password = 123456