一、JDBC概述
1. 什么是JDBC
-
JDBC(Java Data Base Connectivity),是 Java连接数据库的技术。
-
是一种执行SQL的API, 可以为多种关系型数据库提供统一的访问。
-
它是由一组用java语言编写的类和接口组成, 是Java访问数据库的标准规范。
2. JDBC的原理
什么是数据库驱动程序
-
数据库驱动就是直接操作数据库的一个程序
-
不同数据产品的数据库驱动名字有差异
-
在程序中需要依赖数据库驱动来完成对数据库的操作
二、JDBC实现查询
1. 引入数据库驱动包
将MySQL的驱动jar包添加到项目中的lib文件夹, 并将jar添加到libraries。
2. API使用
2.1 加载注册驱动
MySQL驱动包提供的实现类: com.mysql.cj.jdbc.Driver。
加载注册驱动的方式:Class.forName(数据库启动实现类)。
注册驱动的目的:
//MySQL的Driver类是MySQL提供的数据库驱动类,实现了JDBC的Driver接口
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
//无参构造
public Driver() throws SQLException {
}
//静态代码块,在加载类的时候就会执行,且只执行这一次
static {
try {
//DriverManager是驱动管理类,可以通过驱动管理类获取到数据库的连接
//registerDriver(new Driver());注册驱动
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
2.2 获取连接
JDBC提供了Connection接口, 代表一个数据库连接接口。MySQL通过DriverManager类中的静态方法getConnection可以获取连接。
获取连接的静态方法:Connection getConnection(String url, String user, String password)
url主要组成部分的详细说明 :
2.3 获取SQL执行对象
通过Connection 的 createStatement方法获取sql语句执行对象。
Connection接口中的方法 | 说明 |
---|---|
Statement createStatement() | 创建SQL语句执行对象 |
2.4 执行SQL获取返回结果
Statement对象用于发送SQL语句给MySQL服务器,执行SQL 语句并返回它所生成结果。
常用方法 | 说明 |
---|---|
int executeUpdate(String sql) | 执行insert、delete、update语句,返回int类型,代表受影响的函数 |
ResultSet executeQuery(String sql) | 执行select语句,返回ResultSet结果集对象 |
2.5 处理结果集
查询结果会放到结果集中, 通过结果集获取查询的结果。
ResultSet接口方法 | 说明 |
---|---|
boolean next() | 1.游标向下一行 2.返回boolean类型,如果有下一条记录,返回true,否则返回false |
xxx getXxx(String or int) | 1.通过列名获取数据 2.通过列索引获取数据,索引从1开始 |
ResultSet使用原理:
2.6 资源的释放
-
需要释放的对象:ResultSet结果集,Statement语句,Connection连接
-
ResultSet结果集: 当它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被自动关闭
-
释放原则: 先开的后关, 后开的先关 Statement ==> Connection
2.7 总结
public class JDBCDemo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1. 注册驱动(使用反射)
Class.forName("com.mysql.cj.jdbc.Driver");
//2. 获取数据库连接
String user = "root";
String password = "root3306";
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
Connection connection = DriverManager.getConnection(url, user, password);
//3. 获取SQL语句执行对象
Statement statement = connection.createStatement();
//4. 执行SQL语句得到结果
String sql = "select * from jdbc_user";
ResultSet resultSet = statement.executeQuery(sql);
//5. 遍历ResultSet结果集对象
while(resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password1 = resultSet.getString("password");
int age = resultSet.getInt("age");
System.out.println("id:" + id + ", username:" + username + ", password:" + password1 + ", age:" + age);
}
//6. 释放资源
statement.close();
connection.close();
}
}
三、ORM编程思想
1. ORM简介
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/R mapping)是一种为了解决面相对象语言与关系型数据库存在互不匹配的现象。
实体类就是一个定义了属性,拥有getter、setter、无参构造方法(基本必备)的一个类。实体类可以在数据传输过程中对数据进行封装,相当于一个“工具”、“容器”、“载体”,能存储、传输数据,能管理数据。
四、PreparedStatement预处理对象
1. PreparedStatement接口介绍
-
PreparedStatement 是Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句对象。
-
预编译:是指SQL 语句被预编译, 并存储在PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。
-
特点:因为有预先编译的功能,提高SQL的执行效。可以有效的防止SQL注入问题,安全性更高。
SQL注入: 输入的内容作为sql执行一部分, 改变了原有sql的真正含义。
2.使用
通过Connection创建PreparedStatement对象。
PreparedStatement prepareStatement(String sql) | 指定预编译的SQL语句,SQL语句中使用占位符?创建一个语句对象 |
1.编写SQL语句, 未知内容使用 ? 占位
String sql = "select * from jdbc_user where username=? and password=?";
2.获取PreparedStatement对象。
3.设置实际参数: setXxx(占位符的位置, 真实的值)。
4.执行SQL。
etXxx重载方法 | 说明 |
---|---|
void setDouble(int parameterIndex, double x) | 给指定位置设置double类型的值 |
void setInt(int parameterIndex, int x) | 给指定位置设置int类型的值 |
void setString(int parameterIndex, String x) | 给指定位置设置String 类型的值 |
void setObject(int parameterIndex, Object x) | 给指定位置设置Object 类型的值 |
代码:
//使用PreparedStatement完成登录功能
public class Test3 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.next();
System.out.println("请输入密码:");
String password = scanner.next();
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库连接
String String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";url = "jdbc:mysql://test?serverTimezone=Asia/Shanghai";
connection = DriverManager.getConnection(url, "root", "root3306");
//3.创建执行语句对象
statement = connection.createStatement();
String sql = "select * from jdbc_user where username='"+username + "' and password='"+password+"'";
//4.执行SQL得到结果集
ResultSet resultSet = statement.executeQuery(sql);
if(resultSet.next()){
System.out.println("登录成功!");
}else{
System.out.println("登录失败!");
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
```
###
public class Test3 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.next();
System.out.println("请输入密码:");
String password = scanner.next();
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库连接
String String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";url = "jdbc:mysql://test?serverTimezone=Asia/Shanghai";
connection = DriverManager.getConnection(url, "root", "root3306");
//3.创建执行语句对象
statement = connection.createStatement();
String sql = "select * from jdbc_user where username='"+username + "' and password='"+password+"'";
//4.执行SQL得到结果集
ResultSet resultSet = statement.executeQuery(sql);
if(resultSet.next()){
System.out.println("登录成功!");
}else{
System.out.println("登录失败!");
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
3.执行原理
Statement:
PreparedStatement:
4. 批量添加
JDBC提供的批处理功能(Batch)。关键点如下:
-
批处理是指将关联的SQL语句组合成一个批处理,并将他们当成一次调用提交给数据库, 一次发送多个SQL语句到数据库,可以减少通信的资源消耗,从而提高了性能。
-
executeBatch() 方法用于启动执行所有组合在一起的语句。
-
executeBatch() 方法返回一个整数数组,数组中的每个元素代表了各自的影响行数。
public class Test6 {
public static void main(String[] args) throws Exception {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取数据库连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" + "&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
Connection connection = DriverManager.getConnection(url, "root", "root3306");
String sql = "insert into jdbc_user values(null,?,?,?)";
//创建预编译语句对象
PreparedStatement ps = connection.prepareStatement(sql);
//设置参数
ps.setString(1, "张无忌");
ps.setString(2, "111");
ps.setInt(3, 20);
ps.addBatch(); //添加到批量操作
ps.setString(1, "赵敏");
ps.setString(2, "222");
ps.setInt(3, 25);
ps.addBatch(); //添加到批量操作
//执行批量操作
int[] ints = ps.executeBatch();
System.out.println(Arrays.toString(ints));
ps.close();
connection.close();
}
}
5. 获取自增主键值
public class Test7 {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" + "&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
Connection connection = DriverManager.getConnection(url, "root", "root3306");
String sql = "insert into jdbc_user values(default,?,?,?)";
//3.创建预编译对象 本次参数要比之前多个返回值的指定
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
//4.设置参数
ps.setString(1, "貂蝉");
ps.setString(2, "貂蝉");
ps.setInt(3, 20);
//5.执行sql得到结果
int i = ps.executeUpdate();
//6.获取自增主键
ResultSet resultSet = ps.getGeneratedKeys();
if(resultSet.next()){
System.out.println("主键id是:" + resultSet.getInt(1));
}
ps.close();
connection.close();
}
}
五、JDBC事务控制
1. 事务相关API
方法 | 说明 |
---|---|
setAutoCommit(true | false) | 参数设置为true,表示自动提交; 设置为false,表示手动提交 |
void commit() | 提交事务 |
void rollback() | 回滚事务 |
2.代码实现
public class Test8 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement ps = null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" + "&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
connection = DriverManager.getConnection(url, "root", "root3306");
//3.设置手动提交事务
connection.setAutoCommit(false);
String sql = "update account set money=money+? where name=?";
//4.创建预编译语句对象
ps = connection.prepareStatement(sql);
//5.设置占位符参数
ps.setInt(1, -500);
ps.setString(2, "张三");
//6.执行sql
ps.executeUpdate();
int i = 1 / 0;
//7.设置占位符参数
ps.setInt(1, 500);
ps.setString(2, "李四");
//8.执行sql
ps.executeUpdate();
//提交事务
connection.commit();
} catch (Exception e) {
e.printStackTrace();
try {
//事务回滚
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
if(ps != null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
六、连接池
1. 连接池原理
数据库连接池的基本原理是连接池对象中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。
2. 连接池的工具类
2.1 分类
-
C3P0数据库连接池,速度相对较慢,稳定性不错。(hibernate,spring)
-
DBCP数据库连接池,速度相比C3P0快,但不稳定。
-
Druid(德鲁伊)数据库连接池,是由阿里提供的连接池,及DBCP,C3P0,...优点于一身的数据库连接池。
2.2 使用
C3P0使用:
//创建一个数据源对象
ComboPooledDataSource cpds = new ComboPooledDataSource();
String url = "jdbc:mysql://localhost:3306/zqwl?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
String driver = "com.mysql.cj.jdbc.Driver";
String username = "root";
String password = "root";
cpds.setDriverClass(driver);
cpds.setJdbcUrl(url);
cpds.setUser(username);
cpds.setPassword(password);
//设置初始化连接数
cpds.setInitialPoolSize(10);
//最大连接数
cpds.setMaxPoolSize(50);
//测试连接池的效率, 测试对 mysql 5000 次操作
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = cpds.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 5000 连接 mysql 耗时=" + (end - start));
Druid使用
//封装Druid工具类
public class DruidUtils {
private static DruidDataSource dbs;
static {
String url = "jdbc:mysql://localhost:3306/zqwl?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
String driver = "com.mysql.cj.jdbc.Driver";
String username = "root";
String password = "root";
try {
dbs = new DruidDataSource();
dbs.setDriverClassName(driver);
dbs.setUrl(url);
dbs.setUsername(username);
dbs.setPassword(password);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return dbs.getConnection();
}
public static void close(Statement statement, Connection connection) {
try {
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}