1 JDBC的介绍
Java Database Connectivity
1、是Java和数据库之间的桥梁,是一个规范,但不是一个实现。
2、能够执行sql语句,它是由一组用Java语言编写的类和接口组成。
3、各种不同的数据库都有相应的实现。
2 JDBC的步骤
2.1 创建Java工程
2.2 新建lib目录,导入专用jar包
访问MySQL数据库需要用到第三方的类,
这些第三方的类,都被压缩在一个.Jar的文件里。
mysql-connector-java-8.0.27-bin.jar包可以在https://mvnrepository.com下载,
通常下载到该jar包之后将其放到在项目的lib目录下
-
jar包下载网址:https://mvnrepository.com
-
jar包下载位置:
2.3 将下载好的jar包放在项目的lib目录下
2.4 设置lib目录 Add as Library
2.5 连接步骤
- 5.1 初始化驱动
通过初始化驱动类com.mysql.jdbc.Driver或者com.mysql.cj.jdbc.Driver
Class.forName("com.mysql.cj.jdbc.Driver");
- 5.2 建立JDBC和数据库之间的Connection连接
String url = "jdbc:mysql://localhost:3306/user_db";
Connection conn = DriverManager.getConnection(url, "root", "root");
-
数据库服务端的IP地址:127.0.0.1 (这是本机,如果连接其他电脑上的数据库,需填写相应的IP地址)
-
数据库的端口号: 3306 (mysql专用端口号)
-
数据库名称 exam(根据你自己数据库中的名称填写)
-
编码方式 UTF-8
-
账号 root
-
密码 root(如果你在创建数据库的时候没有使用默认的账号和密码,请填写自己设置的账号和密码)
-
5.3 创建Statement或者PreparedStatement接口,执行sql
-
使用Statement接口
- Statement接口创建之后,可以执行SQL语句,完成对数据库的增删改查。其中 ,增删改只需要改变SQL语句的内容就能完成,然而查询略显复杂。在Statement中使用字符串拼接的方式,该方式存在句法复杂,容易犯错等缺点 .
//创建传输器
Statement s = conn.createStatement();
//书写sql,执行sql
String sql = "select * from user where name = 'Tom' and pwd = '123'";
ResultSet rs = s.executeQuery(sql);
- 使用PreparedStatement接口
- 与 Statement一样,PreparedStatement也是用来执行sql语句的与创建Statement不同的是,需要根据sql语句创建PreparedStatement。除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接
String sql = "select * from user where name = ? and pwd = ?";
PreparedStatement ps = conn.prepareStatement(sql);
//给?号设置值
ps.setObject(1, name);
ps.setObject(2, pwd);
//直接执行预编译的sql
ResultSet rs = ps.executeQuery();
- 5.4 处理和显示结果
- 执行查询语句,并把结果集返回给集合ResultSet
ResultSet rs = s.executeQuery(sql);
- 利用While(ResultSet.next()){…}循环将集合ResultSet中的结果遍历出来
- ResultSet.getXX(); 这里的get方法的括号里面可以填属性值。还可以填该属性在数据表中的列号,从1开始编码。
while (rs.next()){
int a = rs.getInt(1);
System.out.println(a);
int id = rs.getInt("id");
System.out.println("字段id对应的值是"+id);
- 5.5 关闭资源
- 在JDBC编码的过程中我们创建了Connection、ResultSet等资源,这些资源在使用完毕之后是一定要进行关闭的。
- 关闭的过程中遵循从里到外的原则。
- 因为在增删改查的操作中都要用到这样的关闭操作,为了使代码简单,增加其复用性,这里我将这些关闭的操作写成一个方法和建立连接的方法一起放到一份工具类中
package cn.edu;
import java.sql.*;
/**
* //封装了一些常用方法,提高代码的复用性--高内聚
*/
public class JDBCUtils2 {
/**
* 调用者即将得到一个数据库的连接对象Connection
* @return 表示了和数据库的连接
* @throws Exception
*/
static public Connection get() throws Exception{
//1,注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2,获取连接
String url = "jdbc:mysql://localhost:3306/cgb211001?characterEncoding=utf8";
Connection c = DriverManager.getConnection(url, "root", "root");
return c; //交给调用者
}
/**
* 关闭资源,调用者告诉close()即将关闭啥资源
*/
static public void close(ResultSet r, PreparedStatement s, Connection c){
if(r != null){//防止发生空指针异常
try {
r.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
//防止发生异常导致r没被关闭,手动置空,等着GC垃圾回收了.
r = null;
}
}
if(s!=null) {
try {
s.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
//防止发生异常导致s没被关闭,手动置空,等着GC垃圾回收了.
s = null;
}
}
if(c!=null) {
try {
c.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
c = null;
}
}
}
}
3 基础案例
3.1 工具类1和工具类2
package cn.edu;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 封装成工具类调用
*/
public class JDBCUtils1 {
public static Connection getConnection() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/user_db";
Connection conn = DriverManager.getConnection(url, "root", "root");
return conn;
}
}
package cn.edu;
import java.sql.*;
/**
* //封装了一些常用方法,提高代码的复用性--高内聚
*/
public class JDBCUtils2 {
/**
* 调用者即将得到一个数据库的连接对象Connection
* @return 表示了和数据库的连接
* @throws Exception
*/
static public Connection get() throws Exception{
//1,注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2,获取连接
String url = "jdbc:mysql://localhost:3306/cgb211001?characterEncoding=utf8";
Connection c = DriverManager.getConnection(url, "root", "root");
return c; //交给调用者
}
/**
* 关闭资源,调用者告诉close()即将关闭啥资源
*/
static public void close(ResultSet r, PreparedStatement s, Connection c){
if(r != null){//防止发生空指针异常
try {
r.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
//防止发生异常导致r没被关闭,手动置空,等着GC垃圾回收了.
r = null;
}
}
if(s!=null) {
try {
s.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
//防止发生异常导致s没被关闭,手动置空,等着GC垃圾回收了.
s = null;
}
}
if(c!=null) {
try {
c.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
c = null;
}
}
}
}
3.2 入门案例
package cn.edu;
import java.sql.*;
/**
* JDBC入门案例
*/
public class TestJdbc001 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.注册驱动,参数是类的全路径
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
String url = "jdbc:mysql://localhost:3306/user_db";
Connection conn = DriverManager.getConnection(url, "root", "root");
//3、获取传输器
Statement s = conn.createStatement();
//4、书写sql,执行sql
String sql = "select * from user";
ResultSet rs = s.executeQuery(sql);
//5、遍历结果集
while (rs.next()){
int a = rs.getInt(1);
System.out.println(a);
int id = rs.getInt("id");
System.out.println("字段id对应的值是"+id);
String s1 = rs.getString(2);
System.out.println(s1);
String s2 = rs.getString(3);
System.out.println(s2);
Object object = rs.getObject(2);
System.out.println(object);
}
//6、关闭资源
rs.close();
s.close();
conn.close();
}
}
3.3 入门案例,使用Junit4 注解测试
package cn.edu;
import org.junit.Test;
import java.sql.*;
/**
* Junit4 注解测试
*/
public class TestJdbc002 {
@Test
public void testGetDept() throws ClassNotFoundException, SQLException {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
String url = "jdbc:mysql://localhost:3306/user_db";
Connection conn = DriverManager.getConnection(url, "root", "root");
//3.创建传输器
Statement s = conn.createStatement();
//4.书写sql,执行sql
String sql = "select * from dept";
ResultSet rs = s.executeQuery(sql);
//5.获取结果集遍历
while (rs.next()){
System.out.println(rs.getInt(1));
System.out.println(rs.getString(2));
System.out.println(rs.getString(3));
}
//6.关闭资源
rs.close();
s.close();
conn.close();
}
}
3.4 使用封装的工具类来执行数据库操作
package cn.edu;
import org.junit.Test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 使用封装的工具类来执行数据库操作
*/
public class TestJdbc003 {
@Test
public void TestJdbcEmp() throws Exception {
Connection conn = JDBCUtils1.getConnection();
Statement s = conn.createStatement();
String sql = "select * from emp";
ResultSet rs = s.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getInt(1));
System.out.println(rs.getString(2));
System.out.println(rs.getString(3));
}
rs.close();
s.close();
conn.close();
}
}
3.5 模拟用户登录过程
package cn.edu;
import org.junit.Test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 模拟用户登录过程
* 如果查到了数据,就可以登录
* 如果没查到,就登录失败
*/
public class TestJdbc004 {
@Test
public void TestLogin() throws Exception {
//1.获取数据库连接
Connection conn = JDBCUtils1.getConnection();
//2.获取传输器
Statement s = conn.createStatement();
//3.书写sql,执行sql
String sql = "select * from user where name = 'Tom' and pwd = '123'";
//String sql = "select * from user where name = 'Tom'# and pwd = '123'";
/*
SQL注入攻击:
出现了特殊的符号# ,改变了SQL的语义,#之后的条件会被注释掉.#之后的sql被当成注释来解析
改变原意之后的sql语句变为:
String sql = "select * from user where name = 'Tom'
*/
ResultSet rs = s.executeQuery(sql);
//4.解析结果集
if(rs.next()){
System.out.println("登陆成功,欢迎回来");
}else {
System.out.println("登录失败");
}
//5.关闭资源
rs.close();
s.close();
conn.close();
}
}
3.6 优化004代码
package cn.edu;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
/**
* 优化004代码
* 模拟用户登录 键盘输入
*/
public class TestJdbc005 {
public static void main(String[] args) throws Exception {
//1.获取数据库连接
Connection conn = JDBCUtils1.getConnection();
//2.获取传输器
Statement s = conn.createStatement();
//3.执行sql
System.out.println("请输入您的用户名");
String name = new Scanner(System.in).nextLine();
System.out.println("请输入您的密码");
String pwd = new Scanner(System.in).nextLine();
String sql = "select * from user where name='"+name+"' and pwd = '"+pwd+"'";
ResultSet rs = s.executeQuery(sql);
//4.遍历结果集
if (rs.next()){
System.out.println("欢迎您回来");
}else {
System.out.println("登录失败");
}
//5.关闭资源
rs.close();
s.close();
conn.close();
}
}
3.7 解决004 SQL注入攻击的问题
package cn.edu;
import java.sql.ResultSet;
import java.util.Scanner;
import java.sql.Connection;
import java.sql.PreparedStatement;
/**
* 解决004 SQL注入攻击的问题
* 使用新的传输器PreparedStatement 代替现在的传输器Statement
* 解决了SQL攻击的问题,把特殊符号#当做一个普通的字符在用,不会当做注释来解析.
*/
public class TestJdbc006 {
public static void main(String[] args) throws Exception {
//1.获取数据库连接
Connection conn = JDBCUtils1.getConnection();
//2.获取传输器,执行sql
System.out.println("请您输入账号:");
String name = new Scanner(System.in).nextLine();
System.out.println("请您输入密码:");
String pwd = new Scanner(System.in).nextLine();
//SQL的骨架,?叫占位符,有预编译的功能
String sql = "select * from user where name = ? and pwd = ?";
PreparedStatement ps = conn.prepareStatement(sql);
//给?号设置值
ps.setObject(1, name);
ps.setObject(2, pwd);
//直接执行预编译的sql
ResultSet rs = ps.executeQuery();
//3.解析结果
if(rs.next()){
System.out.println("登陆成功");
}else {
System.out.println("登录失败");
}
//4.关闭资源
rs.close();
ps.close();
conn.close();
}
}
3.8 使用PreparedStatement传输器,进行数据新增操作
package cn.edu;
import java.sql.Connection;
import java.sql.PreparedStatement;
/**
* 使用PreparedStatement传输器,进行数据新增操作
*/
public class TestJdbc007 {
public static void main(String[] args) throws Exception {
//1.利用工具类获取数据库连接
Connection conn = JDBCUtils1.getConnection();
//2.准备sql骨架,获取传输器对象
String sql = "insert into dept values(null,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
//设置参数
ps.setObject(1, "后勤部");
ps.setObject(2, "第九办公室");
//执行新增操作
ps.executeUpdate();
//3.关闭资源
ps.close();
conn.close();
}
}
3.9 优化关闭资源操作,使用PreparedStatement的传输器 向dept表里新增一条数据
package cn.edu;
import java.sql.Connection;
import java.sql.PreparedStatement;
/**
* 使用PreparedStatement的传输器 向dept表里新增一条数据
*/
public class TestJdbc008 {
public static void main(String[] args) {
Connection c = null; //为了让finally里能够使用变量,所以扩大作用范围
PreparedStatement s = null;
try{
//利用工具类
c = JDBCUtils1.getConnection();
//准备SQL骨架
String sql = "insert into dept values(null,?,?)";
//获取新的传输器--高效,安全,SQL简洁
s = c.prepareStatement(sql);
//设置参数
s.setObject(1,"开发部");
s.setObject(2,"第五办公室");
//executeUpdate()执行增删改的SQL
s.executeUpdate();
}catch(Exception e){
System.out.println("插入失败~~");
}finally {//第一会被执行--关闭资源
JDBCUtils2.close(null,s,c);//使用工具类的close()
}
}
}
4 SQL注入
4.1 什么是SQL注入
- SQL 注入其实就是恶意用户通过在表单中填写包含 SQL 关键字的数据来使数据库执行非常规代码的过程。
- 简单来说,就是数据越俎代庖,做了代码才能干的事情。
- 这个问题的来源是,SQL 数据库的操作是通过 SQL 语句来执行的,而无论是执行代 码还是数据项都必须写在 SQL 语句之中。
- 这就导致如果我们在数据项中加入了某些 SQL 语 句关键字,这些关键字就很可能在数据库写入或读取数据时得到执行。
4.2 如何避免SQL注入
- 用PreparedStatement对象替换前面的Statement对象。 - PreparedStatement对象可以预先处理给定的SQL语句,在sql语句里面使用?占位符来替代后续要传递进来的变量。
- 后面进来的变量值,将会被看成是字符串,不会产生任何的关键字
4.3 Statement和PreparedStatement区别
- 关系:PreparedStatement继承自Statement,都是接口
- 区别:PreparedStatement可以使用占位符,是预编译的,批处理比Statement效率高
- PreparedStatement有预编译的过程,而且绑定了sql语句,无论执行多少遍,都不会再次进行编译。而Statement则不同,sql执行多少遍,就需要编译多少遍,所以PreparedStatement效率要比Statement高
- PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次: