文章目录

  • JDBC概述
    • 1、什么是JDBC?
    • 2、简单的小Demo入门JDBC
      • 2.1、使用JDBC连接MySQL数据库
      • 2.2、优化第一个Demo
      • 2.3、 三种注册JDBC驱动的方式
      • 2.4、使用JDBC实现增删改查(CRUD)
      • 2.5、了解JDBC中的SQL注入问题

JDBC概述

1、什么是JDBC?

什么是JDBC?JDBC是Java DataBase Connectivity的缩写,它是Java程序访问数据库的标准接口。

使用Java程序访问数据库时,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接口来访问,而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。

例如,我们在Java代码中如果要访问MySQL,那么必须编写代码操作JDBC接口。注意到JDBC接口是Java标准库自带的,所以可以直接编译。而具体的JDBC驱动是由数据库厂商提供的,例如,MySQL的JDBC驱动由Oracle提供。因此,访问某个具体的数据库,我们只需要引入该厂商提供的JDBC驱动,就可以通过JDBC接口来访问,这样保证了Java程序编写的是一套数据库访问代码,却可以访问各种不同的数据库,因为他们都提供了标准的JDBC驱动。

JDBC架构

JDBC API支持用于数据库访问的两层和三层处理模型,但通常,JDBC体系结构由两层组成:

  • JDBC API:提供应用程序到JDBC管理器连接。
  • JDBC驱动程序API:支持JDBC管理器到驱动程序连接。

JDBC API使用驱动程序管理器并指定数据库的驱动程序来提供与异构数据库的透明连接。
JDBC驱动程序管理器确保使用正确的驱动程序来访问每个数据源。 驱动程序管理器能够支持连接到多个异构数据库的多个并发驱动程序。

2、简单的小Demo入门JDBC

2.1、使用JDBC连接MySQL数据库

连接JDBC需要的步骤为:

  • 1、注册驱动(url,driver)
  • 2、连接数据库(connection,statement)
  • 3、创建sql命令、执行sql语句
  • 4、将得到的数据进行相关操作
  • 5、关闭资源
package driver;/*** 学习如何用jdbc连接数据库完成操作。,第一版本*/import java.sql.*;public class Test_Driver {// public static String DB_Driver="com.mysql.jdbc.Driver";mysql5.0版本public static String URL = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";//6.0以上public  static String user="root";//用户public static String password="1234";//密码public static void main(String[]args) throws SQLException {Connection connection= null;Statement statement = null;ResultSet res = null;try{//1、注册驱动,方式有三种//DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());// Class.forName("com.mysql.cj.jdbc.Driver");//System.setProperty("JDBC:driver","com.mysql.cj.jdbc.Driver");//2、连接数据库,创建connection对象和statement对象System.out.println("connect to database....");connection = DriverManager.getConnection(URL,user,password);System.out.println("create statement...");statement = connection.createStatement();//3、选择sql命令操作数据库中的数据String sql;sql="select * from test_user";//4、执行sql语句,获取结果集res = statement.executeQuery(sql);//获取结果集//5、从结果集里面获取数据while(res.next()){int id = res.getInt("id");String name = res.getString("name");System.out.println(id+":"+name);}} catch (SQLException e ) {e.printStackTrace();}finally {if(connection!=null)connection.close();if(statement!=null)statement.close();if(res!=null)res.close();}}
}

运行结果:

注意

MySQL 5.5.45+, 5.6.26+ and 5.7.6+版本默认要求建立SSL连接
mysql8.0和之前版本的区别,首先驱动换了,不是com.mysql.jdbc.Driver而是’com.mysql.cj.jdbc.Driver’,
此外mysql8.0是不需要建立ssl连接的,你需要显示关闭。最后你需要设置时区,比如设置CST。
实例: "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC"

2.2、优化第一个Demo

第一个例子里面,很多代码是冗杂的,看起来很不友好。我们需要优化。
新建一个工具类如下:
全是静态方法的工具类

package driver;import java.sql.*;/*** 创建一个工具类,用于创建jdbc加载驱动,释放资源等,优化Test_Driver代码*/public final class JDBCUtils {private static String user = "root";private static String password = "1234";//URL格式,每个数据库都有自己的格式。一般为://JDBC:子协议:子名称://主机地址:端口/数据库?其他设置//mysql没有子名称,如果是主机可以写为"JDBC:mysql:///test"//注意,mysql8.0需要加上设置时区。private static String DB_URL =  "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";public static Connection getConnect() throws SQLException {//异常应该抛出return DriverManager.getConnection(DB_URL, user, password);}static{//使用class.forName()方法一般用于静态代码块,而且该方法注册驱动不依赖具体的类库try {//forName进行类的加载时优先加载静态代码块。Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}public static void free(Connection conn, Statement st, ResultSet res) {try {if (res != null) //原则1:晚点连接早点释放。原则2:先创建的后释放res.close();} catch (SQLException e) {e.printStackTrace();} finally {if (st != null)try {st.close();} catch (SQLException e) {e.printStackTrace();} finally {if (conn != null)try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
}

此时代码优化为:

package driver;import java.sql.*;/*** 学习如何用jdbc连接数据库完成操作。,第二版本使用工具类静态方法优化。*/public class Test_Driver2 {public static void main(String[]args) {Connection connection= null;Statement statement = null;ResultSet res = null;try{System.out.println("connect to database....");connection = JDBCUtils.getConnect();System.out.println("create statement...");statement = connection.createStatement();//3、选择sql命令操作数据库中的数据String sql;sql="select * from test_user";//4、执行sql语句,获取结果集res = statement.executeQuery(sql);//获取结果集//5、从结果集里面获取数据while(res.next()){int id = res.getInt("id");String name = res.getString("name");System.out.println(id+":"+name);}} catch (SQLException e) {e.printStackTrace();}finally {JDBCUtils.free(connection,statement,res);}}
}

基于单例设计模式的优化工具类

package driver;import java.sql.*;/*** 前面使用的是静态方法优化的jdbc驱动连接数据库。* 下面使用单例设计模式,同时考虑以下线程安全问题。*/public final class JDBCUtils1 {private  String user = "root";private  String password = "1234";//URL格式,每个数据库都有自己的格式。一般为://JDBC:子协议:子名称://主机地址:端口/数据库?其他设置//mysql没有子名称,如果是主机可以写为"JDBC:mysql:///test"//注意,mysql8.0需要加上设置时区。private  String DB_URL =  "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";private static JDBCUtils1 JB=null;//使用懒汉式private  JDBCUtils1(){}//构造器私有public static JDBCUtils1 getInstance(){//懒汉式if(JB==null){synchronized (JDBCUtils1.class){if(JB==null){//防止多线程导致的问题JB = new JDBCUtils1();//最先执行静态代码块}}}return JB;}static{//使用class.forName()方法一般用于静态代码块,而且该方法注册驱动不依赖具体的类库try {//forName进行类的加载时优先加载静态代码块。Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}public  Connection getConnect() throws SQLException {//异常应该抛出return DriverManager.getConnection(DB_URL, user, password);}public  void free(Connection conn, Statement st, ResultSet res) {try {if (res != null) //原则1:晚点连接早点释放。原则2:先创建的后释放res.close();} catch (SQLException e) {e.printStackTrace();} finally {if (st != null)try {st.close();} catch (SQLException e) {e.printStackTrace();} finally {if (conn != null)try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
}

2.3、 三种注册JDBC驱动的方式

  • Class.forName(“com.mysql.jdbc.Driver”);

  • DriverManager.registerDriver(new com.mysql.jdbc.Driver())

  • System.setProperty(“jdbc.drivers”,”com.mysql.jdbc.Driver”);

第一种
通过Class类方法加载对应数据库驱动类

Class.forName("com.mysql.jdbc.Driver");//加载数据库驱动
String url="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议
Connection conn=DriverManager.getConnection(url,"username","password");

利用了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!");}}
}

第一种方法好处:通过Class把驱动类Driver先装载到java的虚拟机中,加载过程中利用静态代码块注册驱动,好处在于能够在编译时不依赖于特定的JDBC Driver库,也就是减少了项目代码的依赖性,而且也很容易改造成从配置文件读取JDBC配置,从而可以在运行时动态更换数据库连接驱动。

第二种方法

System.setProperty("JDBC:driver","com.mysql.cj.jdbc.Driver");
//2、连接数据库,创建connection对象和statement对象
System.out.println("connect to database....");
connection = DriverManager.getConnection(URL,user,password);

通过系统的属性设置System.setProperty(“jdbc.driver”,”com.mysql.jdbc.Driver”);
虽然也能够在编译时不依赖于特定的JDBC Driver库,也就是减少了项目代码的依赖性,但是方法参数设置相对来说较为复杂,特点是它可以设置多个驱动,所以在加载单个驱动时,一般采用第一种方法(Class.forName(…))。

第三种方法

DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//2、连接数据库,创建connection对象和statement对象
System.out.println("connect to database....");
connection = DriverManager.getConnection(URL,user,password);

看起来比较直观的一种方式,注册相应的db的jdbc驱动,在编译时需要导入对应的驱动包。它内部有一个vector<driver>来存储这些注册的驱动。要用的时候一个一个取出。这里创建单个driver,则Driver类的静态代码块会最先执行,那么此时new Driver的时候就会注册一次,然后外层registerDriver()方法又会注册一次,所以注册了两次驱动。

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!");}}
}

它一定要有jdbc的驱动类才可以通过编译,这样做十分依赖Jar包,一旦jar包找不到,编译时期就会报错,并且为程序换数据库会带来麻烦,并且如上所说加载了两次驱动,虽然这并不影响我们程序,但是这样做实在是没有必要,还会影响程序的运行。

小结
推荐使用第一种方法。Class.forName(驱动);

实际上去掉驱动注册的这三种方法,一样可以运行程序。
如图,三种方式都注释掉

运行结果:

使用DEBUG模式,发现

connection = DriverManager.getConnection(URL,user,password);

一样会去注册Drive

2.4、使用JDBC实现增删改查(CRUD)

1、Create()
用到的方法statement.executeUpdate(sql)

public static void create(){try{System.out.println("connect to database....");connection = JDBCUtils1.getInstance().getConnect();System.out.println("create statement...");statement = connection.createStatement();String sql;sql="insert into test_user(id,name) values(6,'王二小')";//4、执行sql语句,获取结果集int i = statement.executeUpdate(sql);//获取影响的记录条数System.out.println("i="+i);} catch (SQLException e) {e.printStackTrace();}finally {JDBCUtils1.getInstance().free(connection,statement,res);}}

2、Read()

 public static void read(){try{System.out.println("connect to database....");connection = JDBCUtils1.getInstance().getConnect();System.out.println("create statement...");statement = connection.createStatement();String sql;sql="select id,name from test_user where id>4";//4、执行sql语句,获取结果集res = statement.executeQuery(sql);//获取结果集while(res.next()){int id = res.getInt("id");String name = res.getString("name");System.out.println(id+":"+name);}} catch (SQLException e) {e.printStackTrace();}finally {JDBCUtils1.getInstance().free(connection,statement,res);}}

3、Update()

public static void update(){try{System.out.println("connect to database....");connection = JDBCUtils1.getInstance().getConnect();System.out.println("create statement...");statement = connection.createStatement();String sql;sql="update test_user set name='王二蛋' where id>4 ";//4、执行sql语句,获取结果集int i = statement.executeUpdate(sql);//获取影响的记录条数System.out.println("i="+i);} catch (SQLException e) {e.printStackTrace();}finally {JDBCUtils1.getInstance().free(connection,statement,res);}}

4、Delete()

 public static void delete(){try{System.out.println("connect to database....");connection = JDBCUtils1.getInstance().getConnect();System.out.println("create statement...");statement = connection.createStatement();String sql;sql="delete from test_user where name='王二蛋'";//4、执行sql语句,获取结果集int i = statement.executeUpdate(sql);//获取影响的记录条数System.out.println("i="+i);} catch (SQLException e) {e.printStackTrace();}finally {JDBCUtils1.getInstance().free(connection,statement,res);}}

2.5、了解JDBC中的SQL注入问题

package CRUD_test;import driver.JDBCUtils1;import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;/*** 了解SQL注入问题*/public class SQL_injection {private static Connection connection= null;private static Statement statement = null;private static ResultSet res = null;public static void main(String[]args){String name="小肥仔";read(name);}private static void read(String s) {try{System.out.println("connect to database....");connection = JDBCUtils1.getInstance().getConnect();System.out.println("create statement...");statement = connection.createStatement();String sql;sql="select id,name from test_user where name="+"'"+s+"'";System.out.println(sql);//4、执行sql语句,获取结果集res = statement.executeQuery(sql);//获取结果集while(res.next()){int id = res.getInt("id");String name = res.getString("name");System.out.println(id+":"+name);}} catch (SQLException e) {e.printStackTrace();}finally {JDBCUtils1.getInstance().free(connection,statement,res);}}
}


修改:

 String name="' or 1 or '";read(name);

运行程序,结果:

并没有名字叫'or 1 or '的记录,却查出了4条。发生了SQL注入问题,原因就是输入的' or 1 or'干扰了命令的匹配。SQL语句里面or是一个关键字,1表示结果为真,也就是全都符合。与实际上我们想要实现的功能不符合。原因:我们没有明确statement的使用场景。

一般场景
Statement 用于对数据库进行通用访问,在运行时使用静态SQL语句时很有用。 Statement接口不能接受参数。
PreparedStatement 当计划要多次使用SQL语句时使用。PreparedStatement接口在运行时接受输入参数。
CallableStatement 当想要访问数据库存储过程时使用。CallableStatement接口也可以接受运行时输入参数。

所以我们可以使用PreparedStatement来改进程序:

public static void main(String[]args){String name="' or 1 or '";read(name);}private static void read(String s) {try{System.out.println("connect to database....");connection = JDBCUtils1.getInstance().getConnect();System.out.println("create statement...");String sql;sql="select id,name from test_user where name=?";//使用?号表示未知参数System.out.println(sql);//4、执行sql语句,获取结果集preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1,s);//将第一个问号参数设置为sres= preparedStatement.executeQuery();//执行,不用sql,如果填入sql,则之前的设置失效while(res.next()){int id = res.getInt("id");String name = res.getString("name");System.out.println(id+":"+name);}} catch (SQLException e) {e.printStackTrace();}finally {JDBCUtils1.getInstance().free(connection,preparedStatement,res);}}

一样的输入,却没有报错了。

注意:res= preparedStatement.executeQuery(sql);//执行,不用sql,如果填入sql,则之前的设置失效

对比Statement和PreparedStatement

SQL注入问题以往的防御方式
以前对付这种漏洞的方式主要有三种:

  • 字符串检测:限定内容只能由英文、数字等常规字符,如果检查到用户输入有特殊字符,直接拒绝。但缺点是,系统中不可避免地会有些内容包含特殊字符,这时候总不能拒绝入库。

  • 字符串替换:把危险字符替换成其他字符,缺点是危险字符可能有很多,一一枚举替换相当麻烦,也可能有漏网之鱼。

  • 存储过程:把参数传到存储过程进行处理,但并不是所有数据库都支持存储过程。如果存储过程中执行的命令也是通过拼接字符串出来的,还是会有漏洞。

现在的办法就是参数化查询

近年来,自从参数化查询出现后,SQL注入漏洞已成明日黄花。

参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值。

在使用参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的一部份来处理,而是在数据库完成SQL指令的编译后才套用参数运行,因此就算参数中含有指令,也不会被数据库运行。Access、SQL Server、MySQL、SQLite等常用数据库都支持参数化查询。

JDBC实战(一)JDBC概述相关推荐

  1. [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[JDBC](1)

    MIS信息管理系统实战开发之使用MySQL实现保存 开发背景 ID.姓名.年龄为公共信息,而学生有成绩,工人有工资 定义一个抽象类Person(ID.姓名.年龄),学生是其子类,有成绩,工人是其子类有 ...

  2. JDBC:软件架构、概述、数据库连接:普通项目添加jar包,URL、sql注入,类型转换,查询流程表、操作BLOB类型字段、批量插入,替换jar包、事务/数据库连接池

    0 常识说明 0.1 软件架构方式介绍 B/S架构:浏览器 与 服务器之间的交互. C/S架构:安装的本地软件(如:今日头条等) 与 服务器进行交互. 0.2 JavaWeb技术概览 JDBC核心技术 ...

  3. hibernate和jdbc的优缺点,概述

    1.分析hibernate和jdbc的优缺点 jdbc的优点和缺点 缺点 1.查询代码特别繁琐 2.重复性代码特别多,频繁的try,catch 3.没有做到数据的缓存 4.sql的移植性不好(mysq ...

  4. JDBC 第一章 JDBC概述

    文章目录 1.JDBC的本质 2.模拟JDBC 传送门 1.JDBC的本质 JDBC(Java DateBase Connectivity Java语言连接数据库)本质上是SUN公司制定的一套接口 面 ...

  5. jmeter jdbc mysql_jmeter获取JDBC响应做接口关联(三)

    概述: jmeter中,常常需要连接数据库去断言业务是否正确.因此jdbc数据库关联是必须掌握的核心知识. 基础操作 JDBC请求,最核心的是两个jar包: mysql驱动-mysql-connect ...

  6. Java与数据库连接——JDBC(JDBC概念理解+JDBC搭建使用六步骤,作者君倾情奉上,爆赞!)

    文章目录 JDBC概述 JDBC有什么用? JDBC是什么? JDBC API JDBC的搭建和使用 1 导入数据库驱动包 2 加载数据库驱动 3 建立数据库连接 4 向数据库发送sql语句 Stat ...

  7. mysql jdbc tomcat_Tomcat+MySql+jdbc

    Tomcat+MySql+jdbc Linux下安装配置 Jdk+Tomcat+MySql+jdbc 1.安装JDK 下载 jdk-7-linux-i586.rpm http://java.sun.c ...

  8. mysql jdbc dbcp_连接数据库 JDBC、DBCP、JNDI

    一.JDBC package com.direct.util; import java.sql.Connection; import java.sql.DriverManager; import ja ...

  9. could not load java7_xml导入properties文件报异常:Could not load JDBC driver class [${jdbc.driver}]...

    突然遇上一个很奇怪的问题 applicationContext-dao.xml在创建dataSource时无法使用表达式${jdbc.driver} tomcat在启动时会报一大段异常(异常信息放在问 ...

  10. [转]理解JNDI中 java:comp/env/jdbc/datasource 与 jdbc...

    为什么80%的码农都做不了架构师?>>>    在描述JNDI,例如获得数据源时,JNDI地址有两种写法,例如同是  jdbc/testDS 数据源: A:        java: ...

最新文章

  1. opencv拟合高维曲线
  2. Spring 3.1 Environment Profiles--转载
  3. 【Linux】一步一步学Linux——cksum命令(235)
  4. list 根据某个数字所在位置_【Python学习笔记】09、使用list和tuple
  5. Selenium不打开浏览器采爬取数据 Java
  6. Java-File-文件操作
  7. matlab输出高质量图片,Matlab 画平滑轮廓 print 高质量 figure | 学步园
  8. 20-spring学习-Spring MVC基本操作
  9. r k-means 分类结果_《机器学习》之 Kmeans聚类的原理及代码
  10. 阅读芯片手册与STC16F40K128芯片手册阅读
  11. 基于java的oa协同办公系统_基于java的OA系统设计 毕业论文.doc
  12. java gps 纠偏_【实测可用】GPS纠偏算法-Java版
  13. word怎么填满一页_Word文档如何让插入的表格自动充满整个页 – 手机爱问
  14. 抖音seo源码二次开发 抖音seo源码二次开发
  15. c语言中用梯形法求定积分
  16. 在线语音合成 Java SDK 文档(科大讯飞)
  17. Python3.8 新特性:f-strings调试
  18. MACD指标在外汇交易中的另类运用方法
  19. Ubuntu_ROS 学习
  20. NodeJS 5分钟 连接MySQL 增删改查

热门文章

  1. 禅智听书《精进:如何成为一个很厉害的人》
  2. 什么软件可以测试QQ特别关心,qq特别关心查询工具
  3. Web服务器压力测试工具?
  4. android拼音书写格式,Android自定义带拼音音调Textview
  5. 公司邮箱通讯录的更新
  6. 【amtlib.dll文件下载】amtlib.dll怎么替换
  7. 提供三份程序员简历模板
  8. 汇编中DOSBox的使用
  9. 如何使用速问速答求解一元二次方程
  10. linux修音软件下载,修音软件下载 Auto Tune 8(修音工具) v8.1.2 免费安装版 下载-脚本之家...