一、JDBC入门
1. jdbc的概念
- JDBC(Java DataBase Connectivity:java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系型数据库提供统一访问,它是由一组用Java语言编写的类和接口组成的。
- JDBC的作用:可以通过java代码操作数据库
2. jdbc的本质
- 其实就是java官方提供的一套规范(接口)。用于帮助开发人员快速实现不同关系型数据库的连接
3. jdbc的快速入门程序
-
导入jar包
-
注册驱动
Class.forName("com.mysql.jdbc.Driver");
-
获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "root");
-
获取执行者对象
Statement stat = conn.createStatement();
-
执行sql语句,并接收返回结果
String sql = "SELECT * FROM user"; ResultSet rs = stat.executeQuery(sql);
-
处理结果
while(rs.next()) { System.out.println(rs.getInt("id") + "\t" + rs.getString("name")); }
-
释放资源
rs.close(); stat.close(); conn.close();
-
创建一个java项目:JDBC基础
-
将jar包导入,并添加到引用类库
-
import java.sql.*; public class Main { public static void main(String[] args) { Connection conn = null; Statement stat = null; ResultSet rs=null; try { Class.forName("com.mysql.cj.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456"); stat = conn.createStatement(); String sql = "SELECT * FROM user"; rs = stat.executeQuery(sql); while(rs.next()) { System.out.println(rs.getInt("id") + "\t" + rs.getString("name")+ "\t" + "\t" + rs.getInt("age") + "\t" + rs.getString("status")+ "\t" + rs.getString("gender")); } } catch (SQLException | ClassNotFoundException e) { throw new RuntimeException(e); } finally { try { if(rs!=null){ rs.close(); } } catch (SQLException e) { throw new RuntimeException(e); } try { if(stat!=null) { stat.close(); } } catch (SQLException e) { throw new RuntimeException(e); } try { if(conn!=null){ conn.close(); } } catch (SQLException e) { throw new RuntimeException(e); } } } }
1.1、使用配置文件读取连接信息
二、JDBC各个功能类详解
1. DriverManager
-
DriverManager:驱动管理对象
- 注册驱动(告诉程序该使用哪一个数据库驱动)
-
注册给定的驱动程序:static void registerDriver(Driver driver) (DriverManager的方法)
-
使用了Class.forName:Class.forName(“com.mysql.jdbc.Driver”)
- 通过了给forName指定了是mysql的驱动
- 它会帮助我们注册驱动,如下:
-
在com.mysql.jdbc.Driver类中存在静态代码块(通过查看源码发现)
//这是com.mysql.jdbc.Driver的静态代码块,只要使用这个类,就会执行这段代码 //而Class.forName("com.mysql.jdbc.Driver")就正好使用到了这个类 static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
-
- 注册驱动(告诉程序该使用哪一个数据库驱动)
-
获取数据库连接(获取到数据库的连接并返回连接对象)
- static Connection getConnection(String url, String user, String password);
- 返回值:Connection数据库连接对象
- 参数
- url:指定连接的路径。语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
- user:用户名
- password:密码
- static Connection getConnection(String url, String user, String password);
2.Connection
- Connection:数据库连接对象
- 获取执行者对象
- 获取普通执行者对象:Statement createStatement();
- 获取预编译执行者对象:PreparedStatement prepareStatement(String sql);
- 管理事务
- 开启事务:setAutoCommit(boolean autoCommit); 参数为false,则开启事务。
- 提交事务:commit();
- 回滚事务:rollback();
- 释放资源
- 立即将数据库连接对象释放:void close();
- 获取执行者对象
3.Statement
- Statement:执行sql语句的对象
- 执行DML语句:int executeUpdate(String sql);
- 返回值int:返回影响的行数。
- 参数sql:可以执行insert、update、delete语句。
- 执行DQL语句:ResultSet executeQuery(String sql);
- 返回值ResultSet:封装查询的结果。
- 参数sql:可以执行select语句。
- 释放资源
- 立即将执行者对象释放:void close();
- 执行DML语句:int executeUpdate(String sql);
4.ResultSet
- ResultSet:结果集对象
- 判断结果集中是否还有数据:boolean next();
- 有数据返回true,并将索引向下移动一行
- 没有数据返回false
- 获取结果集中的数据:XXX getXxx(“列名”);
- XXX代表数据类型(要获取某列数据,这一列的数据类型)
- 例如:String getString(“name”); int getInt(“age”);
- 释放资源
- 立即将结果集对象释放:void close();
- 判断结果集中是否还有数据:boolean next();
三、代码优化
1. 灵活变通SQL
-
这是我们写的SQL语句,但你是否觉得这样去写太死了,通常我们在操作数据库的时候都会写很多SQL语句,是非常灵活的,但是就下面这样的话确实是不太好
String sql = "insert into student values (1, 'zhansan')";
-
所以我们可以将代码改为下面这样,记录中的【学号】和【姓名】字段我们可以通过自己输入来进行控制
// 3.输入学号和姓名 System.out.println("请输入学号:"); Scanner sc1 = new Scanner(System.in); int id = sc1.nextInt(); System.out.println("请输入姓名:"); Scanner sc2 = new Scanner(System.in); String name = sc2.next(); // 4.构造一个 SQL 语句,来完成插入操作 //String sql = "insert into student values (1, 'zhansan')"; String sql = "insert into student values (" + id + ", '" + name + "')";
2. 防止SQL注入攻击
黑客可以通过代码直接拼接构造sql语句导致sql语句结构可被恶意篡改
对于上面这种危害,我们进行预防最靠谱的方案,就是使用PreparedStatement
中占位符替换的方式,来实现SQL的构造~
-
我们可以把SQL语句构造成下面这样,两个【?】就相当于是占位符
String sql = "insert into student values (?, ?)"; //通过占位符进行替换 String sql = "update student set name = ? where id = ?"; String sql = "delete from student where id = ?";
-
还记得我们上面使用到的那个
statement
对象吗,它可以用来描述SQL的情况,我们可以通过里面的【setInt()】和【setString()】方法来设置记录中的两个字段,从而将这个SQL语句构造完整
// jdbc中还需要搭配一个特定的对象,来搭配描述这里的sql情况 PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1, id); ps.setString(2, name);
Statement和PreparedStatement对比
- Statement存在SQL注入问题,PreparedStatement解决了SQL注入问题。
- Statement是编译一次执行一次,PreparedStatement是编译一次,可执行N次,PreparedStatement效率较高一些。
- PreparedStatement会在编译阶段做类型的安全检查。
【综上所述】:PreparedStatement使用较多,只有较少数的情况下需要使用Statement。
什么情况下必须使用Statement呢???
- 业务方面要求必须支持SQL注入的时候
- Statement支持SQL注入,凡是以业务方面要求是需要进行SQL语句拼接的,必须使用Statement。
四、JDBC的事物自动提交机制
JDBC事务机制
- JDBC中的事务是自动提交的,什么是自动提交?
- 只要执行任意一条DML语句,则自动提交一次,这是JDBC默认的事务行为。
- 但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须保证他们这些DML语句在同一个事务中同时成功或者同时失败。
手动提交机制
重点三行代码:
- connection.setAutoCommit(false);//开启事务
- connection.commit();//提交事务
- connection.rollback();//回滚事务
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* sql脚本:
* drop table if exists t_act;
* create table t_act(
* actno int,
* balance double(7,2) //注意:7表示有效数据的个数,2表示小数位的个数。
* );
* insert into t_act(actno,balance) values(111,20000);
* insert into t_act(actno,balance) values(222,0);
* commit;
* select * from t_act;
*
* 批量编辑快捷键:Alt+shift+insert
*/
public class JDBCTest11 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement ps = null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode?serverTimezone=UTC","root","user");
//将自动提交机制改为手动提交
connection.setAutoCommit(false);//开启事务
//3.获取预编译的数据库操作对象
String sql = "update t_act set balance = ? where actno = ?";
ps = connection.prepareStatement(sql);
//给?传值
ps.setDouble(1,10000);
ps.setInt(2,111);
int count = ps.executeUpdate();
//制造一个异常,这里坑定会出现空指针异常,
// 出现异常下边的代码将会不执行,直接进入catch语句块...
String s =null;
s.toString();
//给?传值
ps.setDouble(1,10000);
ps.setInt(2,222);
count += ps.executeUpdate();
System.out.println(count == 2 ? "转账成功" : "转账失败");
//程序能够走到这里说明以上程序没有异常,事务结束,手动提交数据
connection.commit();//提交事务
} catch (Exception e) {
//回滚事务
if (connection != null){
try {
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
e.printStackTrace();
}finally {
//6.释放资源
if (ps == null) {
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection == null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
五、JDBC工具类的封装
import java.sql.*;
/**
* JDBC工具类,简化JDBC编程。
*
*/
public class DBUtil {
/**
* 工具类中的构造方法都是私有的
* 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
*/
private DBUtil(){}
//静态代码块在类加载时执行,并且只执行一次
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接对象
* @return 连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode?serverTimezone=UTC","root","user");
}
/**
* 关闭资源
* @param connection 连接对象
* @param statement 数据库操作对象
* @param resultSet 结果集
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet){
if (resultSet == null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement == null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection == null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
六、JDBC实现模糊查询
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 这个程序两个任务
* 第一:测试DBUtil是否好用
* 第二:模糊查询怎么写?
*/
public class JDBCTest12 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
try {
//获取连接
connection = DBUtil.getConnection();
//获取预编译的数据库操作对象
//错误写法
/*String sql = "select ename from emp where ename like '_?%'";
ps = connection.prepareStatement(sql);
ps.setString(1,"A");*/
String sql = "select ename from emp where ename like ?";
ps = connection.prepareStatement(sql);
ps.setString(1,"_A%");
resultSet = ps.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getString("ename"));
}
} catch (Exception throwables) {
throwables.printStackTrace();
}finally {
//释放资源
DBUtil.close(connection,ps,resultSet);
}
}
}