MySQL第04篇:JDBC

核心概述:在实际开发中,我们需要通过Java程序来操作数据库,此时Java给我们提供了Java访问数据库的标准规范就是JDBC,我们可以通过调用JDBC接口中的方法实现操作数据库。
MySQL第04篇:JDBC_第1张图片

第一章:JDBC快速入门

1.1-客户端操作MySQL的方式

01-使用第三方客户端来访问 MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL Manager for MySQL 。

02-使用 MySQL 自带的命令行方式 。

03-通过 Java 来访问 MySQL 数据库,本篇要讲解的内容

1.2-JDBC概述

什么是JDBC

JDBC (Java Database Connectivity,简称JDBC )是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个 数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供。

JDBC的好处

01-程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。

02-使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库 。

MySQL第04篇:JDBC_第2张图片

使用 JDBC 开发使用到的包

会使用到的包 说明
java.sql 所有与 JDBC 访问数据库相关的接口和类
javax.sql 数据库扩展包,提供数据库额外的功能。如:连接池
数据库的驱动 由各大数据库厂商提供,需要额外去下载,是对 JDBC 接口实现的类

1.3-JDBC核心API

接口或类 作用
DriverManager 类 1) 管理和注册数据库驱动
2) 得到数据库连接对象
Connection 接口 一个连接对象,可用于创建 Statement 和 PreparedStatement 对象
Statement 接口 一个 SQL 语句对象,用于将 SQL 语句发送给数据库服务器。
PreparedStatement 接口 一个 SQL 语句对象,是 Statement 的子接口
ResultSet 接口 用于封装数据库查询的结果集,返回给客户端 Java 程序

1.4-导入驱动jar包

下载

链接:https://pan.baidu.com/s/1igjB1mTP7GBLBAZGP_bsGA
提取码:v72g

导入步骤

MySQL第04篇:JDBC_第3张图片

1.5-加载和注册驱动

加载方法

加载和注册驱动的方法 描述
Class.forName(数据库驱动实现类) 加载和注册数据库驱动,数据库驱动由 mysql 厂商"com.mysql.jdbc.Driver"

具体实现

public class Demo1 {
 public static void main(String[] args) throws ClassNotFoundException {
     //抛出类找不到的异常,注册数据库驱动
 	Class.forName("com.mysql.jdbc.Driver");
 }
}

疑问:为什么这样可以注册驱动?

public class Driver implements java.sql.Driver {
     public Driver() throws SQLException {
     }
     static {
         try {
             DriverManager.registerDriver(new Driver()); //注册数据库驱动
             } catch (SQLException var1) {
             throw new RuntimeException("Can't register driver!");
         }
     }
}

com.mysql.jdbc.Driver 源代码:

Driver 接口,所有数据库厂商必须实现的接口,表示这是一个驱动类。

注意事项

从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。Class.forName 这句话可以省略。

为了向下兼容,建议不要省略。

第二章:DriverManager类

2.1-DriverManager作用

  1. 管理和注册驱动
  2. 创建数据库的连接

2.2-类中的方法

DriverManager 类中的静态方法 描述
Connection getConnection (String url, String user, String password) 通过连接字符串,用户名,密码来得到数据库的连接对象
Connection getConnection (String url, Properties info) 通过连接字符串,属性对象来得到连接对象

2.3-连接数据库的四个参数

参数 说明
用户名 登录的用户名
密码 登录的密码
连接字符串 URL 不同的数据库 URL 是不同的
mysql 的写法jdbc:mysql://localhost:3306/数据库[?参数名=参数值]
驱动类的字符串名 com.mysql.jdbc.Driver

2.4-连接数据库URL

格式

协议名:子协议://服务器名或 IP 地址:端口号/数据库名?参数=参数值

MySQL格式

MySQL第04篇:JDBC_第4张图片

MySQL简写格式

前提:必须是本地服务器,端口号是 3306 。

简写格式:jdbc:mysql:///数据库名

乱码的处理

如果数据库出现乱码,可以指定参数: ?characterEncoding=utf8,表示让数据库以 UTF-8 编码来处理数据。

jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8

2.5-代码演示

方式1:使用用户名、密码、URL 得到连接对象

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 得到连接对象
*/
public class Demo2 {
 public static void main(String[] args) throws SQLException {
     String url = "jdbc:mysql://localhost:3306/db3";
     //1) 使用用户名、密码、URL 得到连接对象
     Connection connection = DriverManager.getConnection(url, "root", "root");
     //com.mysql.jdbc.JDBC4Connection@68de145
     System.out.println(connection);
 }
}

方式2:使用属性文件和 url 得到连接对象

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class Demo3 {
 public static void main(String[] args) throws SQLException {
     //url 连接字符串
     String url = "jdbc:mysql://localhost:3306/db3";
     //属性对象
     Properties info = new Properties();
     //把用户名和密码放在 info 对象中
     info.setProperty("user","root");
     info.setProperty("password","root");
     Connection connection = DriverManager.getConnection(url, info);
     //com.mysql.jdbc.JDBC4Connection@68de145
     System.out.println(connection);
 }
}

第三章:Connection接口

作用

Connection 接口,具体的实现类由数据库的厂商实现,代表一个连接对象。

方法

方法 描述
Statement createStatement() 创建一条 SQL 语句对象

第四章:Statement 接口

4.1-JDBC访问数据库的步骤

MySQL第04篇:JDBC_第5张图片

  1. 注册和加载驱动(可以省略)
  2. 获取连接
  3. Connection Statement 对象
  4. 使用 Statement 对象执行 SQL 语句
  5. 返回结果集
  6. 释放资源

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-释放资源

  1. 需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接
  2. 释放原则:先开的后关,后开的先关。ResultSet →Statement →Connection
  3. 放在哪个代码块中:finally 块

4.5-执行DDL

需求:使用 JDBC 在 MySQL 的数据库中创建一张学生表

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 创建一张学生表
*/
public class Demo4DDL {
 public static void main(String[] args) {
     //1. 创建连接
     Connection conn = null;
     Statement statement = null;
     try {
         conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
         //2. 通过连接对象得到语句对象
         statement = conn.createStatement();
         //3. 通过语句对象发送 SQL 语句给服务器
         //4. 执行 SQL
         statement.executeUpdate("create table student (id int PRIMARY key auto_increment, " +
         "name varchar(20) not null, gender boolean, birthday date)");
         //5. 返回影响行数(DDL 没有返回值)
         System.out.println("创建表成功");
     } catch (SQLException e) {
    	 e.printStackTrace();
     }
     //6. 释放资源
     finally {
         //关闭之前要先判断
         if (statement != null) {
         try {
        	 statement.close();
         } catch (SQLException e) {
         	e.printStackTrace();
         }
          }
         if (conn != null) {
             try {
             	conn.close();
             } catch (SQLException e) {
            	 e.printStackTrace();
             }
         }
     }
 }
}

4.6-执行DML

需求

向学生表中添加 4 条记录,主键是自动增长

MySQL第04篇:JDBC_第6张图片

步骤

  1. 创建连接对象
  2. 创建 Statement 语句对象
  3. 执行 SQL 语句:executeUpdate(sql)
  4. 返回影响的行数
  5. 释放资源

代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 向学生表中添加 4 条记录,主键是自动增长
*/
public class Demo5DML {
 public static void main(String[] args) throws SQLException {
    // 1) 创建连接对象
     Connection connection = DriverManager.getConnection("jdbc:mysql:///day24", "root",
    "root");
    // 2) 创建 Statement 语句对象
     Statement statement = connection.createStatement();
    // 3) 执行 SQL 语句:executeUpdate(sql)
     int count = 0;
    // 4) 返回影响的行数
     count += statement.executeUpdate("insert into student values(null, '孙悟空', 1, '1993-03-
    24')");
     count += statement.executeUpdate("insert into student values(null, '白骨精', 0, '1995-03-
    24')");
     count += statement.executeUpdate("insert into student values(null, '猪八戒', 1, '1903-03-
    24')");
     count += statement.executeUpdate("insert into student values(null, '嫦娥', 0, '1993-03-
    11')");
     System.out.println("插入了" + count + "条记录");
    // 5) 释放资源
     statement.close();
     connection.close();
 }
}

4.7-执行DQL

ResultSet 接口

作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。

MySQL第04篇:JDBC_第7张图片

接口中的方法

MySQL第04篇:JDBC_第8张图片

其他方法

MySQL第04篇:JDBC_第9张图片

常用的数据类型转换表

MySQL第04篇:JDBC_第10张图片

java.sql.Date、Time、Timestamp(时间戳),三个共同父类是:java.util.Date

需求

确保数据库中有 3 条以上的记录,查询所有的学员信息

步骤

  1. 得到连接对象
  2. 得到语句对象
  3. 执行 SQL 语句得到结果集 ResultSet 对象
  4. 循环遍历取出每一条记录
  5. 输出的控制台上
  6. 释放资源

代码

import java.sql.*;
/**
* 查询所有的学生信息
*/
public class Demo6DQL {
 public static void main(String[] args) throws SQLException {
     //1) 得到连接对象
     Connection connection =
    DriverManager.getConnection("jdbc:mysql://localhost:3306/day24","root","root");
     //2) 得到语句对象
     Statement statement = connection.createStatement();
     //3) 执行 SQL 语句得到结果集 ResultSet 对象
     ResultSet rs = statement.executeQuery("select * from student");
     //4) 循环遍历取出每一条记录
     while(rs.next()) {
         int id = rs.getInt("id");
         String name = rs.getString("name");
         boolean gender = rs.getBoolean("gender");
         Date birthday = rs.getDate("birthday");
         //5) 输出的控制台上
         System.out.println("编号:" + id + ", 姓名:" + name + ", 性别:" + gender + ", 生日:" +
        birthday);
     }
     //6) 释放资源
     rs.close();
     statement.close();
     connection.close();
 }
}

结果

MySQL第04篇:JDBC_第11张图片

ResultSet使用注意事项

  1. 如果光标在第一行之前,使用 rs.getXX()获取列值,报错:Before start of result set
  2. 如果光标在最后一行之后,使用 rs.getXX()获取列值,报错:After end of result set
  3. 使用完毕以后要关闭结果集 ResultSet,再关闭 Statement,再关闭 Connection

第五章:数据库工具类 JdbcUtils

如果一个功能经常要用到,我们建议把这个功能做成一个工具类,可以在不同的地方重用。

需求

上面写的代码中出现了很多重复的代码,可以把这些公共代码抽取出来

代码实现

  1. 可以把几个字符串定义成常量:用户名,密码,URL,驱动类
  2. 得到数据库的连接:getConnection()
  3. 关闭所有打开的资源: close(Connection conn, Statement stmt)close(Connection conn, Statement stmt, ResultSet rs)
import java.sql.*;
/**
* 访问数据库的工具类
*/
public class JdbcUtils {
 //可以把几个字符串定义成常量:用户名,密码,URL,驱动类
 private static final String USER = "root";
 private static final String PWD = "root";
 private static final String URL = "jdbc:mysql://localhost:3306/day24";
 private static final String DRIVER= "com.mysql.jdbc.Driver";
 /**
 * 注册驱动
 */
 static {
     try {
        Class.forName(DRIVER);
     } catch (ClassNotFoundException e) {
         e.printStackTrace();
     }
 }
 /**
 * 得到数据库的连接
 */
 public static Connection getConnection() throws SQLException {
 	return DriverManager.getConnection(URL,USER,PWD);
 }
 /**
 * 关闭所有打开的资源
 */
 public static void close(Connection conn, Statement stmt) {
     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(Connection conn, Statement stmt, ResultSet rs) {
     if (rs!=null) {
     try {
     	rs.close();
     } catch (SQLException e) {
     	e.printStackTrace();
     }
     }
     close(conn, stmt);
 }
}

第六章:用户登录案例

6.1-需求

  1. 有一张用户表
  2. 添加几条用户记录
  3. 使用 Statement 字符串拼接的方式实现用户的登录, 用户在控制台上输入用户名和密码。
create table user (
 id int primary key auto_increment,
 name varchar(20),
 password varchar(20)
)
insert into user values (null,'jack','123'),(null,'rose','456');
-- 登录, SQL 中大小写不敏感
select * from user where name='JACK' and password='123';
-- 登录失败
select * from user where name='JACK' and password='333';

6.2-步骤

  1. 得到用户从控制台上输入的用户名和密码来查询数据库
  2. 写一个登录的方法
    • 通过工具类得到连接
    • 创建语句对象,使用拼接字符串的方式生成 SQL 语句
    • 查询数据库,如果有记录则表示登录成功,否则登录失败
    • 释放资源
import com.it.utils.JdbcUtils;
import javax.xml.transform.Result;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class Demo7Login {
 //从控制台上输入的用户名和密码
 public static void main(String[] args) {
     Scanner sc = new Scanner(System.in);
     System.out.println("请输入用户名:");
     String name = sc.nextLine();
     System.out.println("请输入密码:");
     String password = sc.nextLine();
     login(name, password);
 }
 /**
 * 登录的方法
 */
 public static void login(String name, String password) {
     //a) 通过工具类得到连接
     Connection connection = null;
     Statement statement = null;
     ResultSet rs = null;
     try {
         connection = JdbcUtils.getConnection();
         //b) 创建语句对象,使用拼接字符串的方式生成 SQL 语句
         statement = connection.createStatement();
         //c) 查询数据库,如果有记录则表示登录成功,否则登录失败
         String sql = "select * from user where name='" + name + "' and password='" + password
        + "'";
         System.out.println(sql);
         rs = statement.executeQuery(sql);
         if (rs.next()) {
            System.out.println("登录成功,欢迎您:" + name);
         } else {
            System.out.println("登录失败");
         }
     } catch (SQLException e) {
         e.printStackTrace();
     } finally {
        //d) 释放资源
        JdbcUtils.close(connection, statement, rs);
     }
 }
}

6.3-SQL 注入问题

现象

当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了

请输入用户名:
newboy
请输入密码:
a' or '1'='1
select * from user where name='newboy' and password='a' or '1'='1'
登录成功,欢迎您:newboy

问题分析

select * from user where name='newboy' and password='a' or '1'='1'
-- name='newboy' and password='a' 为假
-- '1'='1' 真
-- 相当于
select * from user where true; -- 查询了所有记录

我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了 原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进 行简单的字符串拼接。

第七章:PreparedStatement 接口

7.1-继承结构与作用

MySQL第04篇:JDBC_第12张图片

PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句

7.2-PreparedSatement 的执行原理

MySQL第04篇:JDBC_第13张图片

  1. 因为有预先编译的功能,提高 SQL 的执行效率。
  2. 可以有效的防止 SQL 注入的问题,安全性更高。

7.3-PreparedStatement 对象

创建

常用方法

作用

  1. prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。 可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
  2. 安全性更高,没有 SQL 注入的隐患。
  3. 提高了程序的可读性

7.4-使用 PreparedStatement 的步骤

  1. 编写 SQL 语句,未知内容使用?占位:"SELECT * FROM user WHERE name=? AND password=?";
  2. 获得 PreparedStatement 对象
  3. 设置实际参数:setXxx(占位符的位置, 真实的值)
  4. 执行参数化 SQL 语句
  5. 关闭资源

MySQL第04篇:JDBC_第14张图片

使用 PreparedStatement 改写上面的登录程序,看有没有 SQL 注入的情况

import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
/**
* 使用 PreparedStatement
*/
public class Demo8Login {
 //从控制台上输入的用户名和密码
 public static void main(String[] args) throws SQLException {
     Scanner sc = new Scanner(System.in);
     System.out.println("请输入用户名:");
     String name = sc.nextLine();
     System.out.println("请输入密码:");
     String password = sc.nextLine();
     login(name, password);
 }
 /**
 * 登录的方法
 * @param name
 * @param password
 */
 private static void login(String name, String password) throws SQLException {
     Connection connection = JdbcUtils.getConnection();
     //写成登录 SQL 语句,没有单引号
     String sql = "select * from user where name=? and password=?";
     //得到语句对象
     PreparedStatement ps = connection.prepareStatement(sql);
     //设置参数
     ps.setString(1, name);
     ps.setString(2,password);
     ResultSet resultSet = ps.executeQuery();
     if (resultSet.next()) {
     	System.out.println("登录成功:" + name);
     }
     else {
    	 System.out.println("登录失败");
     }
     //释放资源,子接口直接给父接口
     JdbcUtils.close(connection,ps,resultSet);
 }
}

7.5-表与类的关系

MySQL第04篇:JDBC_第15张图片

案例:使用 PreparedStatement 查询一条数据,封装成一个学生 Student 对象

import com.it.entity.Student;
import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Demo9Student {
     public static void main(String[] args) throws SQLException {
         //创建学生对象
         Student student = new Student();
         Connection connection = JdbcUtils.getConnection();
         PreparedStatement ps = connection.prepareStatement("select * from student where id=?");
         //设置参数
         ps.setInt(1,2);
         ResultSet resultSet = ps.executeQuery();
         if (resultSet.next()) {
         //封装成一个学生对象
         student.setId(resultSet.getInt("id"));
         student.setName(resultSet.getString("name"));
         student.setGender(resultSet.getBoolean("gender"));
         student.setBirthday(resultSet.getDate("birthday"));
         }
         //释放资源
         JdbcUtils.close(connection,ps,resultSet);
         //可以数据
         System.out.println(student);
     }
}

案例:将多条记录封装成集合 List,集合中每个元素是一个 JavaBean 实体类

需求: 查询所有的学生类,封装成 List返回

import com.it.entity.Student;
import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class Demo10List {
 public static void main(String[] args) throws SQLException {
     //创建一个集合
     List<Student> students = new ArrayList<>();
     Connection connection = JdbcUtils.getConnection();
     PreparedStatement ps = connection.prepareStatement("select * from student");
     //没有参数替换
     ResultSet resultSet = ps.executeQuery();
     while(resultSet.next()) {
         //每次循环是一个学生对象
         Student student = new Student();
         //封装成一个学生对象
         student.setId(resultSet.getInt("id"));
         student.setName(resultSet.getString("name"));
         student.setGender(resultSet.getBoolean("gender"));
         student.setBirthday(resultSet.getDate("birthday"));
         //把数据放到集合中
         students.add(student);
     }
     //关闭连接
     JdbcUtils.close(connection,ps,resultSet);
     //使用数据
     for (Student stu: students) {
        System.out.println(stu);
     }
 }
}

7.6-PreparedStatement 执行 DML 操作

import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo11DML {
 public static void main(String[] args) throws SQLException {
     //insert();
     //update();
     delete();
 }
 //插入记录
 private static void insert() throws SQLException {
     Connection connection = JdbcUtils.getConnection();
     PreparedStatement ps = connection.prepareStatement("insert into student
    values(null,?,?,?)");
     ps.setString(1,"小白龙");
     ps.setBoolean(2, true);
     ps.setDate(3,java.sql.Date.valueOf("1999-11-11"));
     int row = ps.executeUpdate();
     System.out.println("插入了" + row + "条记录");
     JdbcUtils.close(connection,ps);
 }
 //更新记录: 换名字和生日
 private static void update() throws SQLException {
     Connection connection = JdbcUtils.getConnection();
     PreparedStatement ps = connection.prepareStatement("update student set name=?, birthday=?
    where id=?");
     ps.setString(1,"黑熊怪");
     ps.setDate(2,java.sql.Date.valueOf("1999-03-23"));
     ps.setInt(3,5);
     int row = ps.executeUpdate();
     System.out.println("更新" + row + "条记录");
     JdbcUtils.close(connection,ps);
 }
 //删除记录: 删除第 5 条记录
 private static void delete() throws SQLException {
     Connection connection = JdbcUtils.getConnection();
     PreparedStatement ps = connection.prepareStatement("delete from student where id=?");
     ps.setInt(1,5);
     int row = ps.executeUpdate();
     System.out.println("删除了" + row + "条记录");
     JdbcUtils.close(connection,ps);
 }
}

第八章:JDBC处理事务

之前我们是使用 MySQL 的命令来操作事务。接下来我们使用 JDBC 来操作银行转账的事务

8.1-准备数据

CREATE TABLE account (
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(10),
    balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME, balance) VALUES ('Jack', 1000), ('Rose', 1000);

8.2-API介绍

MySQL第04篇:JDBC_第16张图片

8.3-开发步骤

  1. 获取连接
  2. 开启事务
  3. 获取到 PreparedStatement
  4. 使用 PreparedStatement 执行两次更新操作
  5. 正常情况下提交事务
  6. 出现异常回滚事务
  7. 最后关闭资源
import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo12Transaction {
 //没有异常,提交事务,出现异常回滚事务
 public static void main(String[] args) {
     //1) 注册驱动
     Connection connection = null;
     PreparedStatement ps = null;
     try {
     	//2) 获取连接
    	 connection = JdbcUtils.getConnection();
         //3) 开启事务
         connection.setAutoCommit(false);
         //4) 获取到 PreparedStatement
         //从 jack 扣钱
         ps = connection.prepareStatement("update account set balance = balance - ? where
        name=?");
         ps.setInt(1, 500);
         ps.setString(2,"Jack");
         ps.executeUpdate();
         //出现异常
         System.out.println(100 / 0);
         //给 rose 加钱
         ps = connection.prepareStatement("update account set balance = balance + ? where
        name=?");
         ps.setInt(1, 500);
         ps.setString(2,"Rose");
         ps.executeUpdate();
         //提交事务
         connection.commit();
         System.out.println("转账成功");
     } catch (Exception e) {
    	 e.printStackTrace();
         try {
         //事务的回滚
         connection.rollback();
         } catch (SQLException e1) {
         e1.printStackTrace();
         }
         System.out.println("转账失败");
     }
     finally {
         //7) 关闭资源
         JdbcUtils.close(connection,ps);
     }
 }
}

第九章:数据库连接池

9.1-为什么要学习数据库连接池

数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。
一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下
数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池(简单说:在一个“池”里放了好多数据库连接对象),由应用程序动态地对池中的连接进行申请、使用和释放。
对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。
连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

9.2-什么是数据库连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

MySQL第04篇:JDBC_第17张图片

9.3-如何使用数据库连接池

Java提供了数据库连接池接口:DataSource javax.sql包下的。

数据库厂商实现接口,常用的有以下两种:

  1. C3P0:数据库连接池技术
  2. Druid:数据库连接池实现技术,由阿里巴巴提供的

9.4-C3P0

准备资料

MySQL第04篇:JDBC_第18张图片

下载

链接:https://pan.baidu.com/s/1XAstwgWObgRM2G1J0iLTLA
提取码:450q

使用步骤

01-导入jar包 (两个) c3p0-0.9.5.2.jarmchange-commons-java-0.2.12.jar 。不要忘记导入数据库驱动jar包

02-定义配置文件。

  • 名称:c3p0.properties 或者 c3p0-config.xml
  • 路径:直接将文件放在src目录下即可。

03-创建核心对象 数据库连接池对象 ComboPooledDataSource。

04-获取数据库连接对象。

c3p0-config.xml配置文件

<c3p0-config>
  
  <default-config>
  	
    
    <property name="driverClass">com.mysql.jdbc.Driverproperty>
    
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db2property>
    
    <property name="user">rootproperty>
    
    <property name="password">rootproperty>
    
    
    
    <property name="initialPoolSize">5property>
    
    <property name="maxPoolSize">10property>
    
    <property name="checkoutTimeout">3000property>
  default-config>
  
  <named-config name="otherc3p0"> 
    
    <property name="driverClass">com.mysql.jdbc.Driverproperty>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25property>
    <property name="user">rootproperty>
    <property name="password">rootproperty>
    
    
    <property name="initialPoolSize">5property>
    <property name="maxPoolSize">8property>
    <property name="checkoutTimeout">1000property>
  named-config>
c3p0-config>

代码演示

public class Test01 {
  public static void main(String[] args) throws SQLException {
    // 创建数据库连接池对象
    DataSource ds = new ComboPooledDataSource();
    // 获取数据库连接对象
    Connection con = ds.getConnection();
    System.out.println(con);
  }
}

9.5-Druid

准备资料

MySQL第04篇:JDBC_第19张图片

下载

链接:https://pan.baidu.com/s/1nWIFTscCI-UODwAEaAy8NQ
提取码:f43e

使用步骤

	1. 导入jar包 druid-1.0.9.jar
	2. 定义配置文件:
		* 是properties形式的
		* 可以叫任意名称,可以放在任意目录下
	3. 加载配置文件。Properties
	4. 获取数据库连接池对象:通过工厂来来获取  DruidDataSourceFactory
	5. 获取连接:getConnection

druid.properties配置文件

# 数据库驱动
driverClassName=com.mysql.jdbc.Driver
# 数据库连接地址
url=jdbc:mysql://127.0.0.1:3306/db2
# 用户名
username=root
# 密码
password=root
# 初始化连接对象个数
initialSize=5
# 最大个数
maxActive=10
# 超时等待时长
maxWait=3000

代码

public class Test01 {
  public static void main(String[] args) throws Exception {
    Properties pro = new Properties();
    InputStream is  = Test01.class.getClassLoader().getResourceAsStream("druid.properties");
    pro.load(is);
    DataSource ds = DruidDataSourceFactory.createDataSource(pro);
    Connection con = ds.getConnection();
    System.out.println(con);
  }
}

9.6-DruidUtils

DruidUtils

package it.leilei.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DruidUtils {
  private static DataSource ds;
  private static InputStream is;
  static {
    Properties pro = new Properties();
    try {
      // 读取配置文件
      is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
      pro.load(is);
      // 创建数据库连接池对象
      ds = DruidDataSourceFactory.createDataSource(pro);
    } catch (IOException e) {
      e.printStackTrace();
    } catch (Exception e) {
      e.printStackTrace();
    }finally {
      if(is!=null){
        try {
          is.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

  }
  // 获取Connection对象
  public static Connection getConnection() throws SQLException {
   return ds.getConnection();
  }
  // 释放资源
  public static void close(Statement sta, Connection con, ResultSet rs) {
    if(sta!=null){
      try {
        sta.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }

    }
    if(con!=null){
      try {
        con.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(rs!=null){
      try {
        rs.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
  public static void close(Statement sta, Connection con){
    close(sta,con,null);
  }
  // 获取连接池对象
  public static DataSource getDataSource(){
    return ds;
  }
}

测试:向数据库db2中的表account中添加一条新的数据

package it.leilei.DruidDemo;

import it.leilei.util.DruidUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Test02 {
  public static void main(String[] args) {
    Connection con = null;
    PreparedStatement ps = null;
    try {
      // 获取数据库连接对象
      con = DruidUtils.getConnection();
      // 定义SQL
      String sql = "INSERT INTO account  VALUES(NULL,?,?)";
      // 创建执行SQL语句对象
      ps = con.prepareStatement(sql);
      // 给参数设置值
      ps.setString(1,"小龙女");
      ps.setDouble(2,8000);
      // 执行sql
      int count = ps.executeUpdate();
      if(count>0){
        System.out.println("添加成功");
      }else {
        System.out.println("添加失败");
      }
    } catch (SQLException e) {
      e.printStackTrace();
    }finally {
      DruidUtils.close(ps,con);
    }
  }
}

第十章:Spring JDBC

10.1-概述

以上数据库连接池确实优化并简化了Java对SQL的操作,但是在针对增删改查操作时相对还是有些繁琐,在实际开发中,我们通常会使用框架中封装好的JDBC来操作数据库,可以更加简化Java对SQL的操作,提高开发效率。

Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发。

10.2-使用Spring JDBC

准备资料

MySQL第04篇:JDBC_第20张图片

下载

链接:https://pan.baidu.com/s/1JLru45Sb3Xog35U_xv7nNA
提取码:73vt

使用步骤

  1. 导入jar包(lib目录下所有jar包)

    • commons-logging-1.2.jar
    • spring-beans-5.0.0.RELEASE.jar
    • spring-core-5.0.0.RELEASE.jar
    • spring-jdbc-5.0.0.RELEASE.jar
    • spring-tx-5.0.0.RELEASE.jar
  2. 创建JdbcTemplate对象。依赖于数据源DataSource。

  3. 调用JdbcTemplate的方法来完成CRUD的操作

    • update():执行DML语句。增、删、改语句

    • queryForMap()
      

      :查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合

      • 注意:这个方法查询的结果集长度只能是1
    • queryForList()
      

      :查询结果将结果集封装为list集合

      • 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
    • query()
      

      :查询结果,将结果封装为JavaBean对象

      • query的参数:RowMapper
        1. 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装
        2. new BeanPropertyRowMapper<类型>(类型.class)
    • queryForObject:查询结果,将结果封装为对象

      • 一般用于聚合函数的查询

代码演示

package it.leilei.JDBCTemplateDemo;
import it.leilei.domain.Emp;
import it.leilei.util.DruidUtils;
import org.junit.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class Test01 {
  // 创建JdbcTemplate对象
  private  JdbcTemplate jt = new JdbcTemplate(DruidUtils.getDataSource());
  /*
    添加一条新的数据
  */
  @Test
  public void test01(){
    // 定义SQL
    String sql = "INSERT INTO emp(NAME,gender,salary)VALUES(?,?,?)";
    // 执行
    int i = jt.update(sql,"张三丰","男",90000);
    System.out.println(i);
  }
  /*
    更新一条数据
  */
  @Test
  public void test02(){
    String sql = "update emp set salary=? where id=1";
    int i = jt.update(sql,10000);
    System.out.println(i);
  }
  /*
    删除一条数据
  */
  @Test
  public void test03(){
    String sql = "delete from emp where id=?";
    int i = jt.update(sql,8);
    System.out.println(i);
  }
  /*
  * 查询一条数据返回map集合
  * */
  @Test
  public void test04(){
    String sql = "select * from emp where id=?";
    Map<String,Object>  map = jt.queryForMap(sql,1);
    System.out.println(map);
  }
  /*
   * 查询多条数据返回List集合
   * */
  @Test
  public void test05(){
    String sql = "select * from emp where id=? or id=?";
    List<Map<String, Object>> list = jt.queryForList(sql,1,2);
    System.out.println(list);
  }
  /*
  * 查询多条数据返回List集合
  * */
  @Test
  public void test06(){
    String sql = "select * from emp where id=? or id=?";
    List<Emp> list = jt.query(sql,new BeanPropertyRowMapper<Emp>(Emp.class),1,2);
    System.out.println(list);
  }

  /*
  * 聚合函数
  * */
  @Test
  public void test07(){
    String sql = "select count(id) from emp";
    long l = jt.queryForObject(sql,Long.class);
    System.out.println(l);
  }

}

你可能感兴趣的:(数据库,mysql,sql)