文章目录
1、JDBC是什么?
Java语言可以作为一个客户端去连接一些常见的数据库产品,那么这个时候为了JAVA生态的发展,JAVA就指定了连接数据库的这么一套标准,这套标准就是JDBC。
JDBC(Java Database Connection),具体来说,JDBC的具体体现是什么呢?其实就是JAVA定义了一套连接管理数据库的这么一套标准接口。
对于开发者来说,假如我们切换了一下数据库,那么这个时候我们下面的代码就得去做大量的修改,这样对于Java语言的发展其实是很不利的。在这种情况下,Java就推出了一个去连接数据库的标准,这个标准就是JDBC。说白了,JDBC其实就是一套连接数据库的接口。
2、第一个JDBC
数据库访问过程:
•客户端与Mysql服务器之间建立连接
•客户端向Mysql服务器发送数据库请求
•Mysql服务器处理客户端请求,并返回结果给客户端
•客户端接受Mysql服务器的响应,并按照自己的业务逻辑做响应处理。
•释放相关资源。
Java去连接数据库也是严格遵循这个数据库的访问过程的。
2.1 第一步:导包
我们需要导入MySQL的驱动包,我们导入了这个MySQL的驱动包以后,就能使用这个驱动包里面代码。具体的原理是我们自己写的Java程序在运行的时候,会去把MySQL驱动包里面的代码进行类加载,加载到我们的JVM内存里面去,然后我们就可以使用导包里面的代码了。
我们需要去maven仓库管理网站 去下载对应的Jar包。
下载完了以后,我们会得到一个.jar
文件,我们可以把这个文件添加到我们项目的依赖里面去,如何添加呢?
右键对应的文件夹或者是文件,然后点击下面的add as library
即可。
2.2 第二步:编写代码
2.2.1 修改
// 这个方法其实就是去连接数据库,操作数据库
// 1. 加载驱动
DriverManager.registerDriver(new Driver());
// 2. 获取连接
Connection connection = DriverManager.getConnection(url, username, password);
// 3. 执行sql语句 connection只是一个连接对象,不能够直接执行sql语句,需要根据connection对象去
// 创建statement对象才能够去执行sql语句
// statement对象其实就是用来去自动封装JDBC请求的,并且把sql语句传给我们的MySQL服务器
Statement statement = connection.createStatement();
Integer affectedRows = statement.executeUpdate("update clazz set name = '小鲁班'");
// 4. 获取执行sql语句的结果
System.out.println("affectedRows:" + affectedRows);
// 5. 解析结果
// 6. 释放资源
statement.close();
connection.close();
2.2.2 查询
// 加载驱动
DriverManager.registerDriver(new Driver());
// 获取连接
Connection connection = DriverManager.getConnection(url, username, password);
// 执行sql语句
Statement statement = connection.createStatement();
// ResultSet对象其实就是获取到的结果集
ResultSet resultSet = statement.executeQuery("select * from student");
// 获取执行结果
// 解析结果集
resultSet.next();
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("name:" + name + "\n age:" + age) ;
// 释放资源
resultSet.close();
statement.close();
connection.close();
2.2.3 解析结果集优化
// 加载驱动
DriverManager.registerDriver(new Driver());
// 获取连接
Connection connection = DriverManager.getConnection(url, username, password);
// 执行sql语句
Statement statement = connection.createStatement();
// ResultSet对象其实就是获取到的结果集
ResultSet resultSet = statement.executeQuery("select * from student");
// 获取执行结果
// 解析结果集
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
int clazzId = resultSet.getInt("clazz_id");
System.out.println("id:" + id );
System.out.println("name:" + name );
System.out.println("age:" + age );
System.out.println("clazzId:" + clazzId );
System.out.println("-----------------------------");
}
// 释放资源
resultSet.close();
statement.close();
connection.close();
3、使用工具类优化代码
3.1 第一个简单版本
public class JDBCUtils {
private static final String url = "jdbc:mysql://localhost:3306/31th_sql4?characterEncoding=utf8&useSSL=false";
private static final String username = "root";
private static final String password = "123456";
// 获取连接
public static Connection getConnection(){
Connection connection = null;
try {
// 加载驱动
DriverManager.registerDriver(new Driver());
// 获取连接对象
connection = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
// 关闭资源
public static void releaseSources(Connection connection, Statement statement, ResultSet resultSet){
// 关闭ResultSet
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 关闭statement
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 关闭connection
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.2 第二个版本
这个版本主要是把一些配置写到配置文件里面去,并且优化我们的驱动加载的代码。
public class JDBCUtils {
private static String url = null;
private static String username = null;
private static String password = null;
private static String driverClassName = null;
static {
try {
// 获取文件输入流
FileInputStream fileInputStream = new FileInputStream("jdbc.properties");
Properties properties = new Properties();
// 装载
properties.load(fileInputStream);
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
driverClassName = properties.getProperty("driverClassName");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取连接
public static Connection getConnection(){
Connection connection = null;
try {
// 加载驱动 com.mysql.cj.jdbc.Driver
Class.forName(driverClassName);
// 获取连接对象
connection = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return connection;
}
// 关闭资源
public static void releaseSources(Connection connection, Statement statement, ResultSet resultSet){
// 关闭ResultSet
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 关闭statement
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 关闭connection
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4、使用JDBC进行增删改查
public static void main(String[] args) throws SQLException {
JDBCDemo jdbcDemo = new JDBCDemo();
// insert
// jdbcDemo.insert();
// update
// jdbcDemo.update();
// delete
// jdbcDemo.delete();
// query
jdbcDemo.query();
}
private void query() throws SQLException {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from clazz");
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println("id:" + id + ", name :" + name);
}
JDBCUtils.releaseSources(connection,statement,resultSet);
}
private void delete() throws SQLException {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
int affectedRows = statement.executeUpdate("delete from clazz where id = 3");
System.out.println("affectedRows:" +affectedRows);
JDBCUtils.releaseSources(connection,statement,null);
}
private void update() throws SQLException {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
int affectedRows = statement.executeUpdate("update clazz set name = '王菲' where id = 3");
System.out.println("affectedRows:" +affectedRows);
JDBCUtils.releaseSources(connection,statement,null);
}
private void insert() throws SQLException {
// 获取连接
Connection connection = JDBCUtils.getConnection();
// 获取statement对象
Statement statement = connection.createStatement();
// 执行sql语句
int affectedRows = statement.executeUpdate("insert into clazz values (3,'张飞')");
// 输出
System.out.println("affectedRows:" +affectedRows);
// 关闭资源
JDBCUtils.releaseSources(connection,statement,null);
}
5、数据库注入问题
5.1 登录案例
public static void main(String[] args) throws SQLException {
LoginDemo loginDemo = new LoginDemo();
loginDemo.login("tom","bbb' or '1=1");
}
// 登录的方法
public Boolean login(String username, String password) throws SQLException {
// 获取连接
Connection connection = JDBCUtils.getConnection();
// 获取statement对象
Statement statement = connection.createStatement();
// 执行sql语句
String sql = "select * from user where username = '"+username+"' and password = '"+password+"'";
System.out.println("sql:" + sql);
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
System.out.println("登录成功!");
return true;
}
System.out.println("登录失败!");
return false;
}
我们通过以上的案例可以知道,用户在登录的时候,输入了一些sql里面的关键字 ,导致了sql的结构发生了变化,从而使得用户登录的时候不需要正确的密码也可以登录,只要用户输入的内容会符合 bbb' or '1=1
这种格式,不管前面的bbb是什么内容,都可以登录成功,为了避免这种问题,我们就有了一个预编译的statement,就是prepareStatement。
5.2 PrepareStatement
这个其实就是一个预编译的statement,是一个statement的子接口。
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtils.getConnection();
// 这一步其实就是预先对sql语句进行编译
// ? 表示占位
PreparedStatement preparedStatement = connection.prepareStatement("select * from user where username = ? and password = ?");
// 设置参数
preparedStatement.setString(1,"tom");
preparedStatement.setString(2,"tom1234");
// 执行sql
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
System.out.println("id:" + id + ", username:" + username);
}
JDBCUtils.releaseSources(connection,preparedStatement,resultSet);
}
5.3 登录案例改造
// 使用prepareStatement
public Boolean login4PrepareStatement(String username, String password) throws SQLException {
// 获取连接
Connection connection = JDBCUtils.getConnection();
// 获取statement对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from user where username = ? and password = ?");
// 设置参数
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
// 执行sql语句
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
System.out.println("登录成功!");
return true;
}
System.out.println("登录失败!");
return false;
}
6、批处理
开启JDBC的批处理需要在MySQL的链接后面添加一个参数:rewriteBatchedStatements=true
6.1 Statement批处理
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
// 添加sql语句到批处理小推车里面去
statement.addBatch("insert into user values (null,'lisi','lisi')");
statement.addBatch("insert into user values (null,'wangwu','wangwu')");
statement.addBatch("insert into user values (null,'shiye','shiye')");
statement.addBatch("insert into user values (null,'zhangmazi','zhangmazi')");
// 执行sql语句
int[] ints = statement.executeBatch();
for (int anInt : ints) {
System.out.println("affectedRows:" + anInt);
}
JDBCUtils.releaseSources(connection,statement,null);
}
主要的API:
// 添加sql语句到批处理
statement.addBatch(String sql);
// 执行批处理
statement.executeBatch();
6.2 PrepareStatement批处理
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("insert into user values (null,?,?)");
// 批处理
preparedStatement.setString(1,"sunwukong");
preparedStatement.setString(2,"sunwukong");
preparedStatement.addBatch();
preparedStatement.setString(1,"bajie");
preparedStatement.setString(2,"bajie");
preparedStatement.addBatch();
preparedStatement.setString(1,"jiabaoyu");
preparedStatement.setString(2,"jiabaoyu");
// 执行sql语句
preparedStatement.addBatch();
int[] ints = preparedStatement.executeBatch();
for (int anInt : ints) {
System.out.println("affectedRows:" + anInt);
}
JDBCUtils.releaseSources(connection,preparedStatement,null);
}
主要的API
// 添加参数到批处理
preparedStatement.addBatch();
// 执行批处理
preparedStatement.executeBatch();
6.3 对比
public static void main(String[] args) throws SQLException {
long startTime = System.currentTimeMillis();
// 不采用批处理
normalInsert();
long endTime1 = System.currentTimeMillis();
// 使用statement批处理
statementInsert();
long endTime2 = System.currentTimeMillis();
// 使用prepareStatement批处理
prepareStatementInsert();
long endTime3 = System.currentTimeMillis();
System.out.println("普通耗时:"+(endTime1-startTime) + "毫秒");
System.out.println("statement耗时:"+(endTime2-endTime1) + "毫秒");
System.out.println("预编译耗时:"+(endTime3-endTime2) + "毫秒");
}
// 采用预编译的方式进行批处理
private static void prepareStatementInsert() throws SQLException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("insert into user values (null,?,?)");
for (int i = 0; i < 10000; i++) {
String username = "王五"+i+"号";
String password = "wangwu"+i;
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
preparedStatement.addBatch();
}
preparedStatement.executeBatch();
preparedStatement.close();
connection.close();
}
// 采用statement.addBatch()进行批处理
private static void statementInsert() throws SQLException {
Connection connection = JDBCUtils.getConnection();
Statement statement = connection.createStatement();
for (int i = 0; i < 10000; i++) {
String username = "李四"+i+"号";
String password = "lisi"+i;
String sql = "insert into user values (null,'"+username+"','"+password+"')";
statement.addBatch(sql);
}
statement.executeBatch();
statement.close();
connection.close();
}
// 不采用批处理的方式进行插入
private static void normalInsert() throws SQLException {
Connection connection = JDBCUtils.getConnection();
for (int i = 0; i < 10000; i++) {
Statement statement = connection.createStatement();
String username = "张三"+i+"号";
String password = "zhangsan:"+i;
int affectedRows = statement.executeUpdate("insert into user values (null,'"+username+"','"+password+"')");
System.out.println("affectedRows:" + affectedRows);
statement.close();
}
connection.close();
}
结果:
没有添加配置:
添加配置以后:
总结一下:
我们的Statement进行批处理对比普通的循环执行效率上还是有一点提升的,但是提升不够大。prePareStatement的批处理方式对于效率的提升非常明显,是效率最高的一种批处理方式。为什么他的效率最高呢?
因为PrepareStatement这种批处理方式只对SQL语句进行编译了一次,其他的两种方式都编译了多次,所以效率更高。
但是也有弊端,也就是他支持的场景没有我们的statement那么灵活,它里面的sql语句的范式是不能更改的。