1.JDBC
1.JDBC入门
概念
Java Data Base Connectivity(JDBC):Java数据库连接
作用:通过JDBC可以让Java程序操作数据库
本质:官方定义一套用来操作关系型数据库的规则(接口),而各数据库的厂商为了实现这个接口,就会提供各自的驱动jar包,我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
(简单的理解就是添加对应得jar才能连接对应得数据库)
JDBC得由来
1.直接写代码操作数据库
直接写代码操作数据库存在的问题:
-
不知道MySQL数据库的操作⽅式,解析⽅式
-
代码繁琐,写起来麻烦
-
MySQL和Oracle等其他数据库的操作⽅式和解析⽅式不同,每个数据库都要写⼀套代码
-
MySQL和Oracle等其他数据库相互切换麻烦
-
JDBC规范定义接⼝,具体的实现由各⼤数据库⼚商来实现 JDBC是Java访问数据库的标准规范。真 正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库⼚商根据⾃家数据库的通 信格式编写好⾃⼰数据库的驱动。所以我们只需要会调⽤JDBC接⼝中的⽅法即可。数据库驱动由 数据库⼚商提供。
JDBC得好处
- 我们只需要会调用JDBC接口方法即可,使用简单
- 使用同一套java代码,进行少量得修改就可以访问其他得JDBC支持得数据库
JDBC得四个核心对象
DriverManager | 用于注册驱动 PS:需要注册驱动才能让JDBC跑起来,因为会自动注册 |
---|---|
Connection | 表示数据库的连接 PS:我的理解就是类似于socket 开启管道使得Java程序连接数据库 |
Statement | 执行SQL语句的对象 PS:理解成一个小敞篷小货车,把要执行的sql语句运送到数据库 |
ResultSet | 结果集或一张虚拟表 PS:接受和保存数据库传回来的数据 |
JDBC使用步骤
- 注册驱动
- 获取连接
- 获取SQL语句对象
- 执行SQL语句并返回结果
- 处理结果
- 释放资源
代码如下
package com.xjggb.demo;
import com.mysql.jdbc.Driver;
import java.sql.*;
public class Demo01 {
public static void main(String[] args) throws SQLException {
//注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//获取连接
Connection connection = DriverManager.getConnection(
"jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8",
"root",
"root");
//获取敞篷小火车
Statement statement = connection.createStatement();
//Sql语句
String sql ="SELECT * FROM `user`";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
int user_id = resultSet.getInt("user_id");
String age = resultSet.getString("age");
String name = resultSet.getString("name");
String pwd = resultSet.getString("pwd");
System.out.println("age = " + age);
System.out.println("user_id = " + user_id);
System.out.println("name = " + name);
System.out.println("pwd = " + pwd);
}
//释放资源
resultSet.close();
statement.close();
connection.close();
}
}
注意事项
如果数据出现乱码需要加上参数: ?characterEncoding=utf8,表示让数据库以UTF-8编码来处理数
据。 如: jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8
注册驱动
我们Java程序需要通过数据库驱动才能连接到数据库,因此需要注册驱动。 MySQL的驱动的⼊⼝类
是: com.mysql.jdbc.Driver
java.sql.DriverManager 类⽤于注册驱动。提供如下⽅法注册驱动
public class Demo01 {
public static void main(String[] args) throws SQLException {
//注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
}
}
通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将⾃⼰进⾏注册
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
//自己注册
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
注意:
使⽤ DriverManager.registerDriver(new com.mysql.jdbc.Driver()); ,存在两⽅⾯不⾜
- 硬编码,后期不易维护扩展程序和维护
- 驱动被注册两次
使⽤ Class.forName(“com.mysql.jdbc.Driver”); 加载驱动,这样驱动只会注册⼀次
package com.xjggb.demo;
import java.sql.*;
public class Demo02 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
Connection connection = DriverManager.getConnection(
"jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8",
"root",
"root");
//获取连接敞篷小火车
Statement statement = connection.createStatement();
//Sql语句
String sql ="SELECT * FROM `user`";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
int user_id = resultSet.getInt("user_id");
String age = resultSet.getString("age");
String name = resultSet.getString("name");
String pwd = resultSet.getString("pwd");
System.out.println("age = " + age);
}
//释放资源
resultSet.close();
statement.close();
connection.close();
}
}
2.JDBC实现对单表数据得CRUD
准备数据
-- 创建分类表
CREATE TABLE category (
cid INT PRIMARY KEY AUTO_INCREMENT,
cname VARCHAR(100)
);
-- 初始化数据
INSERT INTO category (cname) VALUES('家电');
INSERT INTO category (cname) VALUES('服饰');
INSERT INTO category (cname) VALUES('化妆品');
获取Statement对象API介绍
boolean execute(String sql)
此⽅法可以执⾏任意sql语句。返回boolean值,表示是否返回ResultSet结果集。仅当执⾏
select语句,且有返回结果时返回true, 其它语句都返回false;
int executeUpdate(String sql)
根据执⾏的DML(INSERT、UPDATE、DELETE)语句,返回受影响的⾏数
ResultSet executeQuery(String sql)
根据查询语句返回结果集,只能执⾏SELECT语句
小结:
- executeUpdate :用于删除 ,更新,插入
- executeQuery :用于查询
代码如下
package com.xjggb.demo;
import com.mysql.jdbc.Driver;
import java.sql.*;
public class Demo01 {
public static void main(String[] args) throws SQLException {
//注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//获取连接
Connection connection = DriverManager.getConnection(
"jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8",
"root",
"root");
//获取敞篷小火车
Statement statement = connection.createStatement();
//定义sql
String sql="";
//插入记录
sql="INSERT INTO category (cname) VALUES ('⼿机');";
int i = statement.executeUpdate(sql);
System.out.println("插入影响得行数" + i);
//修改记录
sql="UPDATE category SET cname='汽⻋' WHERE cid=4;";
int i1 = statement.executeUpdate(sql);
System.out.println("返回更新得行数 " + i1);
sql="delete from category where cid=1";
int i2 = statement.executeUpdate(sql);
System.out.println("返回删除得行数 " + i1);
//释放资源
statement.close();
connection.close();
}
}
使⽤JDBC查询数据库中的数据的步骤
- 注册驱动
- 获取连接
- 获取到Statement
- 使⽤Statement执⾏SQL
- ResultSet处理结果
- 关闭资源
package com.xjggb.demo;
import com.mysql.jdbc.Driver;
import java.sql.*;
public class Demo01 {
public static void main(String[] args) throws SQLException {
//注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//获取连接
Connection connection = DriverManager.getConnection(
"jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8",
"root",
"root");
//获取敞篷小火车
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM category;");
while (resultSet.next()){
int cid = resultSet.getInt("cid");
String cname = resultSet.getString("cname");
System.out.println("cname+\"===\"+cid = " + cname+"==="+cid);
}
//释放资源
resultSet.close();
statement.close();
connection.close();
}
}
数据类型得转换
小结,
使用jdbc操作数据库得步骤:
- 注册驱动
- 获取连接
- 获取小火车
- 执行增删改查
- 释放资源
3.jdbc工具类
工具类是为了解放双手,封装繁琐得代码
定义一个JDBCUtils
类。把注册驱动,获取连接,得到Statement,以及释放资源的代码放到这个类的方法中。以后直接调用方法即可
package com.xjggb.utils;
import java.sql.*;
public class JDBCUtils {
private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
private static final String URL="jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8";
private static final String USERNAME="root";
private static final String PASSWORD="root";
// 2.在静态代码块中注册驱动(只注册一次)
// 当这个类加载到内存的时候就走这个静态代码块,再去触发Driver类中的静态代码块,主动注册
//尝试过了可以不写自动注册
static {
try {
Class.forName(DRIVER_CLASS);
} catch (ClassNotFoundException e) {}
}
// 2.提供一个获取连接的方法`static Connection getConneciton();`
// 谁使用谁处理异常, 这个方法把连接返回给别人使用,有问题也抛给他处理
public static Connection getConnection() throws SQLException {
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
return connection;
}
// 3.定义关闭资源的方法`close(Connection conn, Statement stmt, ResultSet rs)`
// 谁使用谁处理异常, 其他地方调用close会传入 conn, stmt, rs给我们使用.就处理异常
public static void close(Connection conn, Statement statement, ResultSet resultSet) {
//关流从后往前关
if (resultSet!=null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
// 4.重载关闭方法`close(Connection conn, Statement stmt)`
public static void close(Connection conn, Statement statement) {
close(conn,statement);
}
}
小结
编写jdbc得目的是什么?
简化JDBC得代码
4.JDBC登录案例
案例分析
- 使用数据库保存用户得账号和密码
- 让用户输入账号和密码
- 使用SQL根据用户的账号和密码去数据库查询数据
- 如果查询到数据,说明登录成功
- 如果查询不到数据,说明登录失败
添加数据
CREATE TABLE USER (
id INT AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(50),
PASSWORD VARCHAR(50)
);
INSERT INTO USER (NAME, PASSWORD) VALUES('admin', '123'), ('test', '123'), ('gm', '123');
代码如下
package com.xjggb.demo;
import com.xjggb.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class Demo04 {
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();
//获取连接
Connection connection = JDBCUtils.getConnection();
//获取敞篷小火车
Statement statement = connection.createStatement();
//定义sql
String sql = "SELECT * FROM USER1 WHERE name='" + name + "' AND password='" + password + "';";
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
//能进来查询到了数据.
String name2 = resultSet.getString("name");
System.out.println("欢迎您," + name2);
} else {
//查询不到数据,说明登录失败
System.out.println("账号或密码错误...");
}
//关闭资源
JDBCUtils.close(connection,statement,resultSet);
}
}
5.sql注入介绍
SQL注入问题
在我们前面JDBC实现登录案例中,当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了
请输入用户名:
hehe
请输入密码:
a'or'1'='1
问题分析:
// 代码中的SQL语句
"SELECT * FROM USER1 WHERE name='" + name + "' AND password='" + password + "';";
// 将用户输入的账号密码拼接后
"SELECT * FROM USER1 WHERE name='hehe' AND password='a'or'1'='1';"
我们让用户输入的密码和SQL语句进行字符串拼接。用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入。
要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。需要使用PreparedSatement类解决SQL注入。
小结:
什么是sql注入?
用户输入得内容和SQL语句拼接改变了SQL语句原本得含义
任何人都可以直接登录,主要是a’or’1’=’1的原因 ,or导致了数据库对传输进来的sql语句判断为true(or语句只要有一边为ture就都是true),则会判断账户以及密码比对正确,就会成功登录
6.PreparedStatement预编译对象
解决SQL注入
为了避免这种情况的发生,Statement就被我们淘汰了,我们使用子接口PreparedStatement
PreparedStatement预编译执行者对象
预编译:SQL语句在执行前就已经编译好了,执行速度更快。
安全性更高:没有字符串拼接的SQL语句,所以避免SQL注入的问题
代码的可读性更好,因为没有字符串拼接
PreparedStatement使用
SQL语句中的参数使用?作为占位符
给?占位符赋值
设置参数
setXxx(参数1,参数2); Xxx代表:数据类型
参数1:第几个? (编号从1开始)
参数2:?的实际参数
执行SQL语句
int executeUpdate(); 执行insert、update、delete语句
ResultSet executeQuery(); 执行select语句
1.入门案例解决SQL注入问题
package com.xjggb.demo;
import com.xjggb.utils.JDBCUtils;
import java.sql.*;
import java.util.Scanner;
public class Demo04 {
public static void main(String[] args) throws SQLException {
//让用户输入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入账号");
String na = sc.nextLine();
System.out.println("请输入密码");
String password = sc.nextLine();
//获取连接
Connection connection = JDBCUtils.getConnection();
//定义sql
String sql = "SELECT * FROM USER1 WHERE name= ? AND password= ?;";
// prepareStatement()会先将SQL语句发送给数据库预编译。
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1,na);
ps.setString(2,password);
ResultSet resultSet = ps.executeQuery();
if (resultSet.next()) {
String name = resultSet.getString("name");
System.out.println("name:" + name);
} else {
System.out.println("没有找到数据...");
}
//关闭资源
JDBCUtils.close(connection,ps,resultSet);
}
}
2.PreparedStatement的API介绍
继承结构
Statement
↑ 继承
PreparedStatement
获取PreparedStatement
在java.sql.Connection
有获取PreparedSatement
对象的方法
在java.sql.PreparedStatement
中有设置SQL语句参数,和执行参数化的SQL语句的方法
-
void setDouble(int parameterIndex, double x) 将指定参数设置为给定 Java double 值。
-
void setFloat(int parameterIndex, float x) 将指定参数设置为给定 Java REAL 值。
-
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 值。
-
ResultSet executeQuery() 在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的ResultSet对象。
-
int executeUpdate() 在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 SQL 数据操作语言DML语句,比如 INSERT、UPDATE 或 DELETE 语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。
小结:
PreparedSatement如何设置?的参数
setXxx(第几个问号, 问号的值);
3.PreparedStatement实现增删改
1.创建表
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT,
address VARCHAR(50)
);
2.添加数据
package com.xjggb.demo01;
import com.xjggb.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo01 {
public static void main(String[] args) throws SQLException {
//获取连接
Connection connection = JDBCUtils.getConnection();
//设置SQL
String sql=" insert into employee values (null ,?,?,?) ";
PreparedStatement pstmt = connection.prepareStatement(sql);
// 设置参数
pstmt.setString(1, "刘德华");
pstmt.setInt(2, 57);
pstmt.setString(3, "香港");
int i = pstmt.executeUpdate();
System.out.println("影响的行数:" + i);
// 再次设置参数
pstmt.setString(1, "张学友");
pstmt.setInt(2, 55);
pstmt.setString(3, "澳门");
i = pstmt.executeUpdate();
System.out.println("影响的行数:" + i);
// 再次设置参数
pstmt.setString(1, "黎明");
pstmt.setInt(2, 52);
pstmt.setString(3, "香港");
i = pstmt.executeUpdate();
System.out.println("影响的行数:" + i);
//关闭资源
JDBCUtils.close(connection,pstmt);
}
}
3.修改数据
package com.xjggb.demo01;
import com.xjggb.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo02 {
public static void main(String[] args) throws SQLException {
//获取连接
Connection connection = JDBCUtils.getConnection();
//设置sql
String sql = "UPDATE employee SET address=? WHERE id=?;";
//获取连接小火车
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"台湾");
preparedStatement.setInt(2, 4);
int i = preparedStatement.executeUpdate();
System.out.println("更新影响得行数 " + i);
JDBCUtils.close(connection,preparedStatement);
}
}
4.删除数据
package com.xjggb.demo01;
import com.xjggb.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.JDBCType;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo03 {
//删除数据
public static void main(String[] args) throws SQLException {
//获取连接
Connection connection = JDBCUtils.getConnection();
//添加SQL
String sql= "delete from employee where id=?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,3);
int i = statement.executeUpdate();
System.out.println("删除影响得行数" + i);
//关闭资源
JDBCUtils.close(connection,statement);
}
}
5.查询
package com.xjggb.demo;
import com.xjggb.utils.JDBCUtils;
import java.sql.*;
import java.util.Scanner;
public class Demo04 {
public static void main(String[] args) throws SQLException {
//让用户输入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入账号");
String na = sc.nextLine();
System.out.println("请输入密码");
String password = sc.nextLine();
//获取连接
Connection connection = JDBCUtils.getConnection();
//定义sql
String sql = "SELECT * FROM USER1 WHERE name= ? AND password= ?;";
// prepareStatement()会先将SQL语句发送给数据库预编译。
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1,na);
ps.setString(2,password);
ResultSet resultSet = ps.executeQuery();
if (resultSet.next()) {
String name = resultSet.getString("name");
System.out.println("name:" + name);
} else {
System.out.println("没有找到数据...");
}
//关闭资源
JDBCUtils.close(connection,ps,resultSet);
}
}
小结:
PreparedSatement使用步骤
- 编写SQL语句,未知内容使用?占位
- 获得PreparedStatement对象
- 设置实际参数
- 执行参数化SQL语句
- 关闭资源
将SQL语句不确定的值使用?先占位,然后使用PreparedStatement处理,没有SQL注入
7.JDBC事务
1.准备数据
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME, balance) VALUES ('张三', 1000), ('李四', 1000);
2.API介绍
Connection
接口中与事务有关的方法
-
void setAutoCommit(boolean autoCommit) throws SQLException; false:开启事务, ture:关闭事务
-
void commit() throws SQLException; 提交事务
-
void rollback() throws SQLException; 回滚事务使用步骤
3.使用步骤
- 注册驱动
- 获取连接
- 开启事务
- 获取 到Statement
- Statement 执行sql
- 提交或者回滚事务
- 关闭资源
package com.xjggb.demo01;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Demo04 {
private static final String URL="jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8";
private static final String USERNAME="root";
private static final String PASSWORD="root";
public static void main(String[] args) {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
//拿到连接
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
//开启事务 false:开启事务, ture:关闭事务
conn.setAutoCommit(false);
Statement statement = conn.createStatement();
// 张三减500
String sql = "UPDATE account SET balance = balance - 500 WHERE id=1;";
statement.executeUpdate(sql);
// 李四加500
sql = "UPDATE account SET balance = balance + 500 WHERE id=2;";
statement.executeUpdate(sql);
// 模拟异常
int i = 10 / 0;
//关闭资源
statement.close();
//提交事务
conn.commit();
} catch (Exception e) {
e.printStackTrace();
try {
//出现异常 回滚事务
conn.rollback();
System.out.println(" 出现异常事务回滚 ");
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
try {
//关闭资源
if (conn!=null){
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
小结:
JDBC与事务相关得API
在Connection接口中.
setAutoCommit(false); 表示开启事务
commit(); 提交事务
rollback(); 回滚事务
8.ParameterMetaData元数据
元数据的基本概述
什么是元数据:数据库、表、列的定义信息。
ParameterMetaData作用
ParameterMetaData
可用于获取有关PreparedStatement
对象中每个参数标记的类型和属性。
ParameterMetaDataAPI介绍
-
int getParameterCount() 获取PreparedStatement的SQL语句参数?的个数
-
int getParameterType(int param) 获取指定参数的SQL类型。
使用步骤
- 获取
ParameterMetaData
对象 - 使用对象调用方法
注意事项
不是所有的数据库驱动都能获取到参数类型(MySQL会出异常)
package com.xjggb.demo01;
import com.xjggb.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo {
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtils.getConnection();
String sql="INSERT INTO account (NAME, balance) VALUES (?, ?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
int parameterCount = parameterMetaData.getParameterCount();
System.out.println("参数个数" + parameterCount);
// Parameter metadata not available for the given statement
// MySQL不支持获取参数类型
// System.out.println("参数类型: " + md.getParameterType(1));
}
}
小结
ParameterMetaData的作用
得到元数据得参数个数
9.ResultSetMetaData元数据
1.ResultSetMetaData作用
2.ResultSetMetaDataAPI介绍
-
int getColumnCount() 返回此 ResultSet对象中的列数
-
String getColumnName(int column) 获取指定列的名称
-
String getColumnTypeName(int column) 获取指定列的数据库特定类型名称
代码如下
package com.xjggb.demo01;
import com.xjggb.utils.JDBCUtils;
import java.sql.*;
public class DEmo05 {
public static void main(String[] args) throws SQLException {
String sql="SELECT * FROM account WHERE id=1";
Connection connection = JDBCUtils.getConnection();
PreparedStatement stmt = connection.prepareStatement(sql);
ResultSet resultSet = stmt.executeQuery();
//获取结果元数据
ResultSetMetaData md = resultSet.getMetaData();
int columnCount = md.getColumnCount();
System.out.println("列数:" + columnCount);
for (int i = 0; i < columnCount; i++) {
System.out.println("列名:" + md.getColumnName(i + 1)); // 获取列名
System.out.println("列类型:" + md.getColumnTypeName(i + 1)); // 获取类的类型
System.out.println("-----------");
}
}
}
10.把增删改都写成一个方法
package com.xjggb.utils;
import java.sql.*;
public class JDBCUtils {
private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
private static final String URL="jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8";
private static final String USERNAME="root";
private static final String PASSWORD="root";
// 2.在静态代码块中注册驱动(只注册一次)
// 当这个类加载到内存的时候就走这个静态代码块,再去触发Driver类中的静态代码块,主动注册
//尝试过了可以不写自动注册
static {
try {
Class.forName(DRIVER_CLASS);
} catch (ClassNotFoundException e) {}
}
// 2.提供一个获取连接的方法`static Connection getConneciton();`
// 谁使用谁处理异常, 这个方法把连接返回给别人使用,有问题也抛给他处理
public static Connection getConnection() throws SQLException {
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
return connection;
}
// 3.定义关闭资源的方法`close(Connection conn, Statement stmt, ResultSet rs)`
// 谁使用谁处理异常, 其他地方调用close会传入 conn, stmt, rs给我们使用.就处理异常
public static void close(Connection conn, Statement statement, ResultSet resultSet) {
//关流从后往前关
if (resultSet!=null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
// 4.重载关闭方法`close(Connection conn, Statement stmt)`
public static void close(Connection conn, Statement statement) {
close(conn,statement,null);
}
public static int executeCUD(String sql, Object...args) throws SQLException {
//获取连接
Connection connection = getConnection();
PreparedStatement pstmt = connection.prepareStatement(sql);
// 通过PreparedStatement得到参数的元数据
ParameterMetaData parameterMetaData = pstmt.getParameterMetaData();
// 获取?号的个数
int parameterCount = parameterMetaData.getParameterCount();
System.out.println("parameterCount = " + parameterCount);
for (int i = 1; i <= parameterCount; i++) {
pstmt.setObject(i, args[i - 1]);
}
//执行sql
int i = pstmt.executeUpdate();
//关闭资源
close(connection,pstmt);
return i;
}
}
测试
package com.xjggb.demo01;
import com.xjggb.utils.JDBCUtils;
import java.sql.SQLException;
public class Demo06 {
public static void main(String[] args) throws SQLException {
//show2();
show1();
}
public static void show2() throws SQLException {
String sql = "UPDATE employee SET address=? WHERE id=?;";
int i = JDBCUtils.executeCUD(sql, "来了老弟",8);
System.out.println("i = " + i);
}
public static void show1() throws SQLException {
String sql = "INSERT INTO employee VALUES (null, ?, ?, ?);";
int i = JDBCUtils.executeCUD(sql, "迪丽热巴", 39, "新疆");
System.out.println("i = " + i);
}
}
2.连接池介绍
没有连接池得现状
之前JDBC访问数据库的步骤:
创建数据库连接→运行SQL语句→关闭连接 每次数据库访问执行这样重复的动作
获取数据库连接需要消耗比较多的资源,而每次操作都要重新获取新的连接对象,执行一次操作就把连接关闭,而数据库创建连接通常需要消耗相对较多的资源。这样数据库连接对象的使用率低。
我们现实生活中每日三餐。我们并不会吃一餐饭就将碗丢掉,而是吃完饭后将碗放到碗柜中,下一餐接着使用。目的是重复利用碗,我们的数据库连接也可以重复使用,可以减少数据库连接的创建次数。提高数据库连接对象的使用率。
连接池得概念
连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的。
连接池得原理
- 启动连接池,连接池就会初始化一些连接
- 当用户需要使用数据库连接,直接从连接池中取出
- 当用户使用完连接,会将连接重新放回连接池中
连接池得好处
连接池中会保存一些连接,这些连接可以重复使用,降低数据资源的消耗
常用的连接池实现组件有以下这些
- 阿里巴巴-德鲁伊Druid连接池:Druid是阿里巴巴开源平台上的一个项目
- C3P0是一个开源的连接池,目前使用它的开源项目有Hibernate,Spring等。
- DBCP(DataBase Connection Pool)数据库连接池,是Tomcat使用的连接池组件。
小结:
连接池原理
1.启动连接池时,这个连接池会保存一些链接
2.用户使用连接时,去连接池中取连接使用
3.使用完连接,将连接放回连接池
连接池得好处?
连接池中的连接可以重复使用,提交数据库资源的使用
1.C3P0连接池
C3P0地址:https://sourceforge.net/projects/c3p0/?source=navbar
注意 :配置文件名字不能改,放在src目录下
C3P0常用的配置参数解释
参数 | 说明 |
---|---|
initialPoolSize | 连接池刚启动时,连接池内包含的连接数量 |
maxPoolSize | 连接池中最多可以放多少个连接 |
checkoutTimeout | 连接池中没有连接时最长等待时间 |
maxIdleTime | 连接池中的空闲连接多久没有使用就会回收。默认是0,0表示不回收 |
C3P0配置文件
配置文件的要求
- 文件名:c3p0-config.xml
- 放在源代码即src目录下
配置文件c3p0-config.xml
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 数据库连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">2000</property>
<property name="maxIdleTime">1000</property>
</default-config>
<named-config name="xiaoguo">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">2000</property>
<property name="maxIdleTime">1000</property>
</named-config>
<named-config name="abc">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">2000</property>
<property name="maxIdleTime">1000</property>
</named-config>
</c3p0-config>
API介绍:
ComboPooledDataSource类API
-
public ComboPooledDataSource() 无参构造使用默认配置(使用xml中default-config标签中对应的参数)
-
public ComboPooledDataSource(String configName) 有参构造使用命名配置(configName:xml中配置的名称,使用xml中named-config标签中对应的参数)
-
public Connection getConnection() throws SQLException 从连接池中取出一个连接
使用步骤
- 导入jar包
c3p0-0.9.5.2.jar
和mchange-commons-java-0.2.12.jar
- 复制配置文件
c3p0-config.xml
放在src目录下,配置对应参数 - 创建连接池对象
ComboPooledDataSource
- 从连接池中获取连接对象
- 使用连接对象操作数据库
- 关闭资源,将连接还回连接池中
代码如下
package com.xjggb.demo02;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo01 {
public static void main(String[] args) throws SQLException {
//
// // 方式一: 使用默认配置(default-config)
// ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 方式二: 使用命名配置(named-config:配置名)
// new ComboPooledDataSource("配置名");
ComboPooledDataSource dataSource = new ComboPooledDataSource("xiaoguo");
//获取连接
Connection connection = dataSource.getConnection();
// System.out.println("connection = " + connection);
// connection.close();
//
// for (int i = 0; i <9 ; i++) {
// System.out.println("dataSource.getConnection() = " + dataSource.getConnection());
//
// }
//
// Connection connection1 = dataSource.getConnection();
// System.out.println("connection1 = " + connection1);
//添加SQL
String sql= "delete from employee where id=?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,5);
int i = statement.executeUpdate();
System.out.println("返回影响得行数" + i);
statement.close();
//放回线程
dataSource.close();
}
}
小结:
配置文件 得好处
- 可以连接不同得数据库 xiaoguo , abc
- 可是使用不同得连接池参数:maxPoolSize
- 可以连接不同厂商的数据库:Oracle或MySQL
2.Druid连接池
介绍
Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控数据库连接池和SQL的执行情况。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。
Druid地址:https://github.com/alibaba/druid
Druid常用的配置参数
参数 | 说明 |
---|---|
initialSize | 刚启动连接池时,连接池中包含连接的数量 |
maxActive | 连接池中最多可以放多少个连接 |
maxWait | 获取连接时最大等待时间,单位毫秒 |
Druid连接池基本使用
API介绍
com.alibaba.druid.pool.DruidDataSourceFactory
类有创建连接池的方法
public static DataSource createDataSource(Properties properties)
创建一个连接池,连接池的参数使用properties中的数据
我们可以看到Druid连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用properties文件来保存对应的参数。
Druid连接池的配置文件名称随便,放到src目录下面方便加载
druid.properties
文件内容:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.93.222:3306/day19?characterEncoding=utf8
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
使用步骤
- 导入druid的jar包
- 赋值druid.properties文件到src下,并设置对应参数
- 加载properties文件的内容到Properties对象中
- 创建Druid连接池,使用配置文件中的参数
- 从Druid连接池中取出连接
- 执行SQL语句
- 关闭资源
package com.xjggb.demo03;
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.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
public class Demo01 {
public static void main(String[] args) throws Exception {
//加载配置文件
InputStream resourceAsStream = Demo01.class.getResourceAsStream("/druid.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
//创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// for (int i = 0; i < 9; i++) {
// Connection conn = dataSource.getConnection();
// System.out.println(conn);
// }
//
// Connection connection = dataSource.getConnection();
//
// System.out.println("connection = " + connection);
// connection.close();
//
// //放回去拿出来是一致得
// Connection connection1 = dataSource.getConnection();
// System.out.println("connection = " + connection1);
//获取连接
Connection connection = dataSource.getConnection();
String sql="SELECT * FROM `category`";
PreparedStatement statement = connection.prepareStatement(sql);
//查询到数据
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()){
System.out.println("resultSet.getInt(\"cid\") = " + resultSet.getInt("cid"));
System.out.println("resultSet.getString(\"cname\") = " + resultSet.getString("cname"));
}
}
}
小结:
Druid常用的配置参数
参数 | 说明 |
---|---|
initialSize | 启动连接池时,默认的连接数量 |
maxActive | 连接池中最大的连接数量 |
maxWait | 没有连接时最大的等待时间 |
Druid连接池基本使用不管是C3P0连接池,配置大致都可以分为2种:1.连接数据库的参数
,2.连接池的参数
,这2种配置大致参数作用都相同,只是参数名称可能不一样。
3.工具类
package com.xjggb.utils;
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 DataSourceUtils {
// 1.声明静态数据源成员变量
private static DataSource dataSource;
static {
InputStream resourceAsStream = DataSourceUtils.class.getResourceAsStream("/druid.properties");
Properties properties = new Properties();
try {
properties.load(resourceAsStream);
//创建连接池
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取数据源 方法
public static DataSource getDataSource(){
return dataSource;
}
//获取连接对象
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 5.定义关闭资源的方法
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {}
}
}
// 6.重载关闭方法
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
}
测试
package com.xjggb.demo03;
import com.xjggb.utils.DataSourceUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Demo02 {
public static void main(String[] args) throws SQLException {
Connection connection = DataSourceUtils.getConnection();
String sql="SELECT * FROM `category`";
PreparedStatement statement = connection.prepareStatement(sql);
//查询到数据
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()){
System.out.println("resultSet.getInt(\"cid\") = " + resultSet.getInt("cid"));
System.out.println("resultSet.getString(\"cname\") = " + resultSet.getString("cname"));
}
//关闭资源
DataSourceUtils.close(connection,statement,resultSet);
}
}
小结:
使用工具类使得代码更加简单,关注sql就行
3.总结
1.理解JDBC得概念
jdbc是操作数据库规范
通过jdbc,可以让java操作数据库
-
能够使用Connection接⼝
Connection conn = DriverManager.getConnection("jdbc:mysql:///day16", "root", "root"); Statement stmt = conn.createStatement();
-
能够使用Statement接⼝
执行SQL语句的对象,相当于小货车 Statement stmt = conn.createStatement(); int i = stmt.executeUpdate(sql); ResultSet rs = stmt.executeQuery(sql);
-
能够使用ResultSet接⼝
对结果集得处理
5.能够使用JDBC操作CRUD
6.能够使用JDBC操作事务
操作事务的方法在Connection中
void setAutoCommit(false): 开启事务
void commit(); 提交事务
void rollback(); 回滚事务