文章目录
一、 JDBC概述
1、JDBC定义
1、JDBC 是一种可用于,执行 SQL 语句的, JavaAPI。它由 Java 语言编写的一些类和界面组成。
2、JDBC为数据库,应用开发人员,提供了一种标准的应用程序设计接口,使开发人员可以用纯 Java 语言,编写完整的数据库应用程序。
3、 通过使用 JDBC,开发人员可以很方便地将 SQL 语句,传送给几乎任何一种数据库。
4、JDBC 是一种底层 API,这意味着它将直接调用 SQL 命令。
5、JDBC 还扩展了 Java 的功能。例如,用 Java 和 JDBC API 可以发布含有 applet 的网页。
2、数据的持久化储存
持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用
3、JDBC体系结构
JDBC接口(API)包括两个层次:
- 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
- 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。
4、JDBC程序编写步骤
二、数据库连接
1、数据库连接的三要素
1、Driver接口实现类
java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口
mySql的驱动:com.mysql.jdbc.Driver
2、URL
DBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
JDBC URL的标准由三部分组成,各部分间用冒号分隔。
- jdbc:子协议:子名称
- 协议:JDBC URL中的协议总是jdbc
- 子协议:子协议用于标识一个数据库驱动程序
- 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名
例子:
jdbc:mysql://localhost:3306/test_1?useUnicode=true&characterEncoding=utf8&usessl=true"
3、用户名和密码
user=root
password=123456
2、连接方式
1、不使用配置文件
public class TestConnection {
@Test
public void getConnection() {
try {
//1、连接数据库的4个基本要素
String url = "jdbc:mysql://localhost:3306/test_1?useUnicode=true&characterEncoding=utf8&usessl=true";
String user = "root";
String password = "123456";
String driverName = "com.mysql.jdbc.Driver";
//2、实例化Driver
Class clazz = Class.forName(driverName);
Driver driver = (Driver) clazz.newInstance();
//3.注册驱动
DriverManager.registerDriver(driver);
//连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
} catch (Exception e) {
e.printStackTrace();
}
}
}
实际上,我们可以省略这一步,或者说成简化,因为,在Driver类的源码中有这样一段代码
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
静态代码块,随着类的加载而执行,在Class.forName(driverName)中,我们就已经将Driver加载进了内存。
所以,我们还可以将代码,进行简化。
@Test
public void getConnection2(){
try {
//连接数据库的4个要素
String url="jdbc:mysql://localhost:3306/test_1?useUnicode=true&characterEncoding=utf8&usessl=true";
String user="root";
String password="123456";
String driverName="com.mysql.jdbc.Driver";
//加载驱动
Class.forName(driverName);
//数据库连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
} catch (Exception e) {
e.printStackTrace();
}
}
2、使用配置文件
user=root
password=123456
url=jdbc:mysql://localhost:3306/test_1?useUnicode=true&characterEncoding=utf8&usessl=true
driverClass=com.mysql.jdbc.Driver
@Test
public void getConnection3() {
try {
//加载配置文件
InputStream is = TestConnection.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties ps = new Properties();
ps.load(is);
//读取配置信息
String url=ps.getProperty("url");
String user=ps.getProperty("user");
String password=ps.getProperty("password");
String driverName=ps.getProperty("driverClass");
//加载驱动
Class.forName(driverName);
//连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
} catch (Exception e) {
e.printStackTrace();
}
}
三、JDBC的使用
1、Statement的不安全性与sql注入问题
JDBC Statement 形式的,数据库操作,是将一个组装好的,带有数据的SQL,直接提交给,MySQL服务。是极其不安全的。
举个例子:
数据库与表设计
-- 创建数据库
CREATE DATABASE `calss`;
-- 切换到 bank 库
USE `calss`;
-- 创建表
CREATE TABLE `user_balance` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NOT NULL ,
`balance` BIGINT NOT NULL ,
PRIMARY KEY (`id`)
)
ENGINE = INNODB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci;
插入数据
INSERT INTO `user_balance`(`name`,`balance`)VALUES('张三',100);
INSERT INTO `user_balance`(`name`,`balance`)VALUES('李四',101);
package Connection4;
import java.sql.*;
/**
* @author shkstart
* @create 2021-08-22 15:50
*/
public class UnsafeStatement {
private static String user = "root";
private static String password = "123456";
private static String driverClass = "com.mysql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/calss?useUnicode=true&characterEncoding=utf8";
public static void select(String name) throws ClassNotFoundException, SQLException {
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url,user, password);
Statement stmt = null;
try {
stmt = conn.createStatement();
String sql = String.format("SELECT * FROM user WHERE name='%s'", name);
System.out.println(sql);
ResultSet resultSet = stmt.executeQuery(sql);
while (resultSet.next()) {
System.out.printf("id: %s, name: %s, balance: %s\n",
resultSet.getLong("id"),
resultSet.getString("name"),
resultSet.getLong("balance"));
}
resultSet.close();
} finally {
if (stmt != null) {
stmt.close();
}
conn.close();
}
}
public static void main(String[] args) throws SQLException, ClassNotFoundException {
select("zhangsan' OR '1'='1");
}
}
查询结果
在这里,我们只是想通过name查询单条记录,如果黑客通过sql注入暴力破解密码。那那么数据库将毫无保留的,展现在黑客的面前
2、使用PreparedStatement实现增删改查
PreparedStatement,继承自Statement,但比Statement功能强大的多。
优点:
1、PreparedStatement是预编译的,比Statement速度快。
当同时要执行多条相同结构sql语句时使用,这时可以用setObject(),addBatch()和executeBatch()这几个函数。2、可以防止sql注入。
对JDBC而言,SQL注入攻击只对Statement有效,对PreparedStatement是无效的,这是因为PreparedStatement不允许在插入时改变查询的逻辑结构
1、增
@Test
public void insertTest(){
Connection conn=null;
PreparedStatement ps=null;
try {
// 1.读取配置文件中的4个基本信息
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
// 2.加载驱动
Class.forName(driverClass);
// 3.获取连接
conn = DriverManager.getConnection(url, user, password);
//4、预编译sql,返回preparedStatement实例
String sql="insert into customers(name,email,birth) values(?,?,?)";
ps = conn.prepareStatement(sql);
//5、填充占位符
ps.setObject(1,"张国荣");
ps.setObject(2,"[email protected]");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("1000-01-01");
ps.setDate(3,new java.sql.Date(date.getTime()));
//6、执行操作
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//7、资源的关闭
if(ps!=null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
运行前
运行后
2、删
@Test
public void testCommonUpdate() {
String sql="delete from customers where id>?";
update(sql,8);
}
public void update(String sql, Object... args) {
//sql中占位符的个数与可变形参的长度相同!
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);//小心参数声明错误!!
}
//4.执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.资源的关闭
JDBCUtils.closeResouce(conn, ps);
}
}
删除前
删除后
3、改
@Test
public void updatetest() throws SQLException, IOException, ClassNotFoundException {
Connection conn=null;
PreparedStatement ps=null;
try {
//1、获取数据库连接
conn = JDBCUtils.getConnection();
//2、预编译sql,返回prearedStatement实例
String sql="update customers set name=? where id =?";
ps = conn.prepareStatement(sql);
//3、填充占位符
ps.setObject(1,"欢子");
ps.setObject(2,8);
//4、执行
ps.execute();
} catch (IOException e) {
e.printStackTrace();
} finally {
//5、关闭资源
JDBCUtils.closeResouce(conn,ps);
}
}
运行前
运行后
4、查
public class PreparedStatementList {
@Test
public void getList(){
String sql="select id,name,email,birth from customers where id<?";
List<Customer> allList = getAllList(Customer.class, sql, 8);
allList.forEach(System.out::println);
}
public <T> List<T> getAllList(Class<T> clazz, String sql, Object...args){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
//1、读取配置文件
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i <args.length ; i++) {
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
//获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取结果集中的列数
int columnCount = rsmd.getColumnCount();
//创建集合,存放数据
ArrayList<T> list = new ArrayList<T>();
if(rs.next()){
T t = clazz.newInstance();
//处理结果集的每一列
for (int i = 0; i <columnCount ; i++) {
//获取列值
Object columValue = rs.getObject(i + 1);
//获取列名
String columnname = rsmd.getColumnLabel(i+1);
//给t对象指定columnname属性
Field field = clazz.getDeclaredField(columnname);
field.setAccessible(true);
field.set(t,columValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
}
3、操作数据库的方法总结
方法名 | 功能描述 |
---|---|
java.sql.Driver | JDBC 驱动程序需要实现的接口 |
Properties | 配置文件所使用的类 |
forname | 返回一个类 |
DriverManager | 管理驱动 |
Connection | 与特定数据库的连接(会话)。 执行SQL语句并在连接的上下文中返回结果 |
Statement | 用于执行静态SQL语句并返回其生成的结果的对象 |
PreparedStatement | 表示预编译的SQL语句的对象 |
ResultSet | 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。 ResultSet对象保持一个光标指向其当前的数据行。 最初,光标 |
getMetaData | 检索一个ResultSetMetaData对象,其中包含有关执行此PreparedStatement对象时将返回的ResultSet对象的列的信息 |