JavaWeb系列--JDBC
总览图
什么是JDBC
JDBC的全称是Java数据库连接(Java Database Connectivity),它是一套用于执行SQL语句的Java API。应用程序可通过这套API连接到关系型数据库,并使用SQL语句来完成对数据库中数据的CRUD等操作。说白了就是用Java语言来操作数据库。
JDBC原理
早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动。
所以jdbc是不变的,但是驱动却有很多种。
Demo
准备数据
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
`age` int(11) NULL DEFAULT NULL COMMENT '年龄',
`email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'Jone', 18, 'test1@qq.com', '1230984');
INSERT INTO `user` VALUES (2, 'Jack', 20, 'test2@qq.com', '5364363');
INSERT INTO `user` VALUES (3, '吴芳吉', 28, 'test3@qq.com', '4564574');
INSERT INTO `user` VALUES (4, 'Sandy', 21, 'test4@qq.com', '4573452');
INSERT INTO `user` VALUES (5, '李华', 24, 'test5@qq.com', '6546373');
SET FOREIGN_KEY_CHECKS = 1;
搭建Maven项目
JDBCDemo.java
import org.junit.Test;
import java.sql.*;
/**
* @Author: Luzy
* @Date: 2020-07-22 11:04
*/
public class JDBCDemo {
private Connection conn = null;
private Statement stat = null;
private ResultSet rs = null;
@Test
public void query() {
try {
//注册数据库驱动获取连接
conn = JDBCUtil.getConnection();
//获取传输器
stat = conn.createStatement();
//利用传输器发送SQL到数据库执行,并返回执行结果
rs = stat.executeQuery("select * from user");
//处理结果:将表中所有的记录输出在控制台
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String email = rs.getString("email");
System.out.println(id + " : " + name + " : " + age + " : " + email);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
JDBCUtil.close(conn, stat, rs);
}
}
}
JDBCUtil.java
import java.sql.*;
/**
* @Author: Luzy
* @Date: 2020-07-23 9:10
* @Description: jdbc工具类
*/
public class JDBCUtil {
private static final String url = "jdbc:mysql://ip:port/user";
private static final String name = "com.mysql.jdbc.Driver";
private static final String username = "root";
private static final String password = "123456";
/**
* 注册数据库驱动,获取连接
**/
public static Connection getConnection() throws ClassNotFoundException, SQLException {
//1.注册数据库驱动
Class.forName(name);
//2.获取数据库连接
return DriverManager.getConnection(url, username, password);
}
/**
* 释放资源
**/
public static void close(Connection conn, Statement stat, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs = null;
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
stat = null;
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
运行结果
JDBC对象介绍
DriverManager
该类管理数据库驱动程序
在JDBCUtil.java代码中可能出现的两种异常:
//1.注册数据库驱动
Class.forName(name);
//2.获取数据库连接
return DriverManager.getConnection(url, username, password);
- ClassNotFoundException
- 没有给出mysql的jar包
- 类名称打错了,查看类名是不是com.mysql.jdbc.Driver
- SQLException
- 出现这个异常就是三个参数的问题,往往username和password一般不是出错,所以需要认真查看url是否打错
Connection
管理数据库建立的连接
Connection最为重要的方法就是获取Statement:Statement stmt = con.createStatement();
Statement
负责将要执行的sql体局提交到数据库
Statement最为重要的方法是:
-
int executeUpdate(String sql):
执行更新操作,即执行insert、update、delete语句,其实这个方法也可以执行create table、alter table,以及drop table等语句,但我们很少会使用JDBC来执行这些语句
-
ResultSet executeQuery(String sql)
执行查询操作,执行查询操作会返回ResultSet,即结果集
-
boolean execute()
Statement还有一个boolean execute()方法,这个方法可以用来执行增、删、改、查所有SQL语句。该方法返回的是boolean类型,表示SQL语句是否有结果集!
如果使用execute()
方法执行的是更新语句,那么还要调用int getUpdateCount()
来获取insert、update、delete语句所影响的行数。
如果使用execute()
方法执行的是查询语句,那么还要调用ResultSet getResultSet()
来获取select语句的查询结果。
ResultSet
执行sql查询语句返回的结果集
ResultSet主要用于存储结果集,可以通过next()方法由前向后逐个获取结果集中的数据,如果想获取结果集中任意位置的数据,则需要在创建Statement对象时,设置两个ResultSet定义的常量,具体设置方式如下:
Statement st = conn.createStatement( ResultSet.TYPE_SCROLL_INSENITIVE, ResultSet.CONCUR_READ_ONLY ); ResultSet rs = st.excuteQuery(sql);
第一个参数:
- ResultSet.TYPE_FORWARD_ONLY:不滚动结果集
- ResultSet.TYPE_SCROLL_INSENSITIVE:滚动结果集,但结果集数据不会再跟随数据库而变化
- ResultSet.TYPE_SCROLL_SENSITIVE:滚动结果集,但结果集数据会再跟随数据库而变化
第二个参数:
- CONCUR_READ_ONLY:结果集是只读的,不能通过修改结果集而反向影响数据库
- CONCUR_UPDATABLE:结果集是可更新的,对结果集的更新可以反向影响数据库
如果结果集是不可滚动的,那么只能使用next()方法来移动游标,而beforeFirst()、afterLast()、first()、last()、previous()、relative()方法都不能使用,如果想使用滚动的结果集,我们应该选择TYPE_SCROLL_INSENSITIVE!其实很少有数据库驱动会支持TYPE_SCROLL_SENSITIVE的特性!通常我们也不需要查询到的结果集再受到数据库变化的影响。通常可更新结果集这一“高级特性”我们也是不需要的!
可以通过next()方法使ResultSet的游标向下移动,当游标移动到你需要的行时,就需要来获取该行的数据了,ResultSet提供了一系列的获取列数据的方法:
上面方法中,参数columnIndex
表示列的索引,列索引从1开始,而不是0,这第一点与数组不同。如果你清楚当前列的数据类型,那么可以使用getInt()
之类的方法来获取,如果你不清楚列的类型,那么你应该使用getObject()
方法来获取
ResultSet还提供了一套通过列名称来获取列数据的方法:
PreparedStatement
PreparedStatement对象可以对SQL语句进行预编译,预编译的信息会存储在该对象中。当相同的SQL语句再次执行时,程序会使用PreparedStatement对象中的数据,而不需要对SQL语句再次编译去查询数据库,这样就大大的提高了数据的访问效率。
防止sql注入 、提高代码可读性可维护性、提高程序执行的效率
SQL注入攻击
由于后台的SQL语句是拼接而来的。其中的参数是由用户提交的,如果用户在提交参数时,在其中掺杂了一些SQL关键字或者特殊符号,就可能会导致SQL语句的语意发生变化。从而执行一些意外的操作。
模拟登陆示例
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
/**
* @Author: Luzy
* @Date: 2020-07-23 16:40
*/
public class LoginTest1 {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
Scanner scanner = new Scanner(System.in);
System.out.println("please login first");
System.out.println("please enter username:");
String name = scanner.nextLine();
System.out.println("please enter password:");
String password = scanner.nextLine();
try {
conn = JDBCUtil.getConnection();
stat = conn.createStatement();
String sql = "SELECT * FROM user WHERE name= '"+name+"' and password = '"+password+"' ";
System.out.println(sql);
rs = stat.executeQuery(sql);
if (rs.next()) {
System.out.println("login success");
}else{
System.out.println("login fail");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
JDBCUtil.close(conn, stat, rs);
}
}
}
运行之后控制台输入
如图所示,便是SQL注入的结果
使用PreparedStatement防止SQL注入
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Scanner;
/**
* @Author: Luzy
* @Date: 2020-07-23 16:40
*/
public class LoginTest2 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement preparedStatement = null;
ResultSet rs = null;
Scanner scanner = new Scanner(System.in);
System.out.println("please login first");
System.out.println("please enter username:");
String name = scanner.nextLine();
System.out.println("please enter password:");
String password = scanner.nextLine();
try {
conn = JDBCUtil.getConnection();
String sql = " SELECT * FROM user WHERE name = ? and password = ? ";
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString(1, name);
preparedStatement.setString(2, password);
rs = preparedStatement.executeQuery();
if (rs.next()) {
System.out.println("login success");
} else {
System.out.println("login fail");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
JDBCUtil.close(conn, preparedStatement, rs);
}
}
}
如图防止SQL注入成功