1 什么是JDBC

  Java程序都是通过JDBC(Java Data Base Connectivity)连接数据库的,通过SQL对数据库编程。JDBC是由SUN公司(SUN公司已被Oracle公司收购)提出的一系列规范,只定义了接口规范,具体的实现是由各个数据库厂商去完成的。因为每个数据库都有其特殊性,这些是Java规范没有办法确定的,所以JDBC就是一种典型的桥接模式。

  

  2 常用接口

  2.1 Driver接口

  要连接数据库,必须先加载特定厂商的数据库驱动程序,不同的数据库有不同的加载方法。共有2种方式。

  2.1.1 Class.forName("com.mysql.jdbc.Driver");

  推荐这种方式,不会对具体的驱动类产生依赖。

  例如:

 // 加载Oracle驱动
Class.forName("oracle.jdbc.driver.OracleDriver");

  2.1.2 DriverManager.registerDriver("com.mysql.jdbc.Driver");

  会造成DriverManager中产生2个一样的驱动,并会对具体的驱动类产生依赖。

  2.2 Connection接口

  与特定数据库连接后,Connection执行SQL语句并返回结果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定义的数据库Connection连接上。例如:

 // 连接MySQL数据库
Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password"); // 连接Oracle数据库
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password");

  

  常用方法:

  createStatement():创建向数据库发送sql的statement对象。

  prepareStatement(sql) :创建向数据库发送预编译sql的PreparedSatement对象。

  prepareCall(sql):创建执行存储过程的callableStatement对象。

  setAutoCommit(boolean autoCommit):设置事务是否自动提交。

  commit() :在此连接上提交事务。

  rollback() :在此连接上回滚事务。

  

  2.3 Statement接口

  用于执行静态SQL语句并返回它所生成结果的对象。

  3个Statement类:

  Statement:由createStatement方法创建,用于发送简单的SQL语句(不带参数)。

  PreparedStatement :继承Statement类,由prepareStatement方法创建,用于发送含有1个或多个参数的SQL语句。与父类Statement相比,有以下几个优点:

  —可以防止SQL注入。

  DBUtils辅助类

 package com.test.jdbc;

 import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author Administrator
* 模板类DBUtils
*/
public final class DBUtils {
// 参数定义
private static String url = "jdbc:mysql://localhost:3306/mytest"; // 数据库地址
private static String username = "root"; // 数据库用户名
private static String password = "root"; // 数据库密码 private DBUtils() { }
// 加载驱动
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.out.println("驱动加载出错!");
}
} // 获得连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
} // 释放连接
public static void free(ResultSet rs, Statement st, Connection conn) {
try {
if (rs != null) {
rs.close(); // 关闭结果集
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (st != null) {
st.close(); // 关闭Statement
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close(); // 关闭连接
}
} catch (SQLException e) {
e.printStackTrace();
} } } } }

  在写SQL注入演示类之前看下数据库结构(简单的users表):

  

  SQL注入演示类

 package com.test.jdbc;

 import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement; public class SqlInner {
public static void main(String[] args) {
//Read("zhangsan"); // 如果普通查询可以
Read("' or 1 or'");
}
public static void Read(String name) {
Statement st = null;
ResultSet rs = null;
Connection conn = null;
try {
conn = DBUtils.getConnection();
st = conn.createStatement();
String sql = "select * from users where lastname = '" + name + "'"; // 主要注入发生地
System.out.println("sql: " + sql); // 打印SQL语句
rs = st.executeQuery(sql);
System.out.println("age\tlastname\tfirstname\tid");
while (rs.next()) {
System.out.println(rs.getInt(1) + "\t" + rs.getString(2)
+ "\t\t" + rs.getString(3) + "\t\t" + rs.getString(4));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.free(rs, st, conn);
}
}
}

  如果用普通的键zhangsan来查询,结果如下:

  

  使用下面的' or 1 or',结果如下:

  

  把所有结果都打印出来了。打印生成后的SQL语句如下:

  

  这是一种SQL注入,用了2个单引号,分别把前后的两个''给封闭了,变成两个空,然后通过or 1即or true符合所有条件。

  使用PreparedStatement

 package com.test.jdbc;

 import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; public class SqlInner {
public static void main(String[] args) {
Read("' or 1 or'");
}
public static void Read(String name) {
PreparedStatement st = null;
ResultSet rs = null;
Connection conn = null;
try {
conn = DBUtils.getConnection();
String sql = "select * from users where lastname = ?"; // 这里用问号
st = conn.prepareStatement(sql);
st.setString(1,name); // 这里将问号赋值
rs = st.executeQuery();
System.out.println("age\tlastname\tfirstname\tid");
while (rs.next()) {
System.out.println(rs.getInt(1) + "\t" + rs.getString(2)
+ "\t\t" + rs.getString(3) + "\t\t" + rs.getString(4));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.free(rs, st, conn);
}
}
}

  PreparedStatement用?代替变量,然后通过setString赋值。由于PreparedStatement内置了字符过滤,就相当于where name = ' or 1 or ',没有对应记录,所以结果为空。这体现了PreparedStatement的防注入功能。

  —在特定的驱动数据库下相对效率要高(不绝对)。

  —因为已经预加载了,所以不需要频繁编译。

  CallableStatement:继承PreparedStatement类,由prepareCall方法创建,用于调用存储过程。

  Statement常用方法:

  execute(String sql):返回值为true时,执行的是查询语句,可以通过getResultSet方法获取结果;返回值为false时,执行的是更新语句或DDL语句。

  executeQuery(String sql):执行select语句,返回ResultSet结果集。

  executeUpdate(String sql):执行insert/update/delete语句,返回影响的行数。

  addBatch(String sql) :把多条SQL语句放到一个批处理中。

  executeBatch():向数据库发送一批SQL语句并执行,返回每条SQL语句执行后的影响行数的int数组。

  2.4 ResultSet接口

  结果集

  检索不同类型字段的常用方法:

  getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。

  getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。

  getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据对象。

  getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据对象。

  getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据对象。

  

  对结果集进行滚动的方法:

  next():移动到下一行。

  Previous():移动到前一行。

  absolute(int row):移动到指定行。

  beforeFirst():移动resultSet的最前面。

  afterLast() :移动到resultSet的最后面。

  3 使用JDBC的步骤

  加载JDBC驱动 → 获取数据库连接Connection对象 → 创建执行SQL语句的PreparedStatement对象 → 处理执行结果ResultSet对象 → 释放资源。

  注意,释放资源顺序:ResultSet → PreparedStatement → Connection。

  3.1 加载JDBC驱动(只加载1次)

  Class.forName("com.MySQL.jdbc.Driver");

  3.2 获取数据库连接Connection对象

 Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");

  URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:

  

  其他参数如:useUnicode=true&characterEncoding=utf8

  

  3.3 创建执行SQL语句的PreparedStatement对象

 //Statement
String id = "5";
String sql = "delete from table where id=" + id;
Statement st = conn.createStatement();
st.executeQuery(sql);
//存在sql注入的危险
//如果用户传入的id为“5 or 1=1”,那么将删除表中的所有记录

 

 //PreparedStatement 有效的防止sql注入(SQL语句在程序运行前已经进行了预编译,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1'也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令)
String sql = “insert into user (name,pwd) values(?,?)”;
PreparedStatement ps = conn.preparedStatement(sql);
ps.setString(1, “col_value”); //占位符顺序从1开始
ps.setString(2, “123456”); //也可以使用setObject
ps.executeQuery();

  

  3.4 处理执行结果ResultSet对象

 ResultSet rs = ps.executeQuery();
While(rs.next()){
rs.getString(“col_name”);
rs.getInt(1);
//…
}

  

  3.5 释放资源

 // 数据库连接(Connection)非常耗资源,尽量晚创建,尽量早的释放
// 都要加try catch finally以防前面关闭出错,后面的就不执行了
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (st != null) {
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

  

  4 什么是MyBatis

  官网 http://www.mybatis.org/mybatis-3/zh/index.html

  MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。

  5 JDBC演变到MyBatis过程

  5.1 连接获取和释放

  频繁地开启和关闭数据库连接造成资源浪费,影响系统性能。

  解决方案:

  通过数据库连接池反复利用已经建立的连接去访问数据库,减少连接的开启和关闭的时间。即使用数据库连接池动态管理数据库连接。

  但数据库连接池多种多样,有可能采用DBCP连接池,也有可能采用容器本身的JNDI连接池。

  从DataSource里面获取数据库连接,具体实现由用户来配置。

  5.2 SQL语句统一存取

  使用JDBC操作数据库时,SQL语句基本都散落在各个Java类中,这样有3个不足之处:

  —可读性很差,不利于维护以及做性能调优。

  —改动Java代码需要重新编译、打包部署。

  —不利于取出SQL语句在数据库客户端执行(因为之前编写好的SQL语句通过+号进行拼凑,所以取出后需要删掉中间的Java代码)。

  解决方案:

  SQL语句统一存放到XML中。

  

  5.3 重复SQL语句

  如何复用SQL语句?

  解决方案:

  将重复的SQL片段独立成一个SQL块,然后各个SQL语句引用重复的SQL块。

  5.4 传入参数映射和动态SQL

  根据前台传入参数的不同,如何动态生成对应的SQL语句呢?

  解决方案:

  使用if、choose、when、otherwise元素组装SQL语句,#{变量名}传递SQL所需要的参数,${变量名}传递SQL语句本身。

  5.5 结果映射和结果缓存

  如何封装结果处理?

  解决方案:

  为了将结果匹配到对应的数据结构,SQL处理器需要明确两点:第一,需要返回什么类型的对象?第二,结果如何映射到相应的数据结构?

  如何存储SQL执行结果即缓存来提升性能?

  缓存数据都是key-value的格式,SQL语句和传入参数两部分合起来可以作为数据缓存的key值。

  

  参考资料

  《深入浅出MyBatis技术原理与实战》

  JDBC教程

  JDBC详解

  JDBC Statement接口实现的execute方法

  JDBC PrepareStatement对象执行批量处理实例

  JDBC之PreparedStatement

  终结篇:MyBatis原理深入解析(一)

Mybatis JDBC->Mybatis的更多相关文章

  1. jdbc,mybatis,hibernate各自优缺点及区别

    先比较下jdbc编程和hibernate编程各自的优缺点.    JDBC:    我们平时使用jdbc进行编程,大致需要下面几个步骤:    1,使用jdbc编程需要连接数据库,注册驱动和数据库信息 ...

  2. Hello Mybatis 02 mybatis generator

    接着上一篇文章通过Mybatis完成了一个User的CRUD的功能之后,这篇开始还需要建立一个Blog类,这样就可以模拟一个简单的微博平台的数据库了. 数据库准备 首先我们,还是需要在数据库中新建一个 ...

  3. MyBatis笔记----MyBatis 入门经典的两个例子: XML 定义与注解定义

    ----致敬MyBatis官方开放文档让大家翻译,不用看书直接看文档就行了,mybatis的中文文档还需要完备的地方 简介 什么是 MyBatis ? MyBatis 是支持定制化 SQL.存储过程以 ...

  4. 【Mybatis】MyBatis对表执行CRUD操作(三)

    本例在[Mybatis]MyBatis配置文件的使用(二)基础上继续学习对表执行CRUD操作 使用MyBatis对表执行CRUD操作 1.定义sql映射xml文件(EmployeeMapper.xml ...

  5. 【Mybatis】MyBatis配置文件的使用(二)

    本例在[Mybatis]MyBatis快速入门(一)基础上继续学习XML映射配置文件 MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properti ...

  6. 【转】Mybatis学习---MyBatis知识、原始Dao开发和mapper代理开发

    [原文]https://www.toutiao.com/i6594610137560777223/ 一.什么是MyBatis MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及 ...

  7. 【MyBatis】MyBatis之如何配置

    1,MyBatis简介 MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索.MyBatis 使用简 ...

  8. springboot集成mybatis及mybatis generator工具使用

    原文链接 前言mybatis是一个半自动化的orm框架,所谓半自动化就是mybaitis只支持数据库查出的数据映射到pojo类上,而实体到数据库的映射需要自己编写sql语句实现,相较于hibernat ...

  9. MyBatis -01- 初识 MyBatis + MyBatis 环境搭建

    MyBatis -01- 初识 MyBatis + MyBatis 环境搭建 MyBatis 本是 apache 的一个开源项目 iBatis(iBATIS = "internet" ...

  10. MyBatis之MyBatis环境搭建

    MyBatis之MyBatis环境搭建 一.MyBatis开发环境搭建 1.引入Jar包 ①MyBatis mybatis-3.4.1.jar ant-1.9.6.jar ant-launcher-1 ...

随机推荐

  1. SQLSERVER 数据量太大,重启服务器后,数据库显示正在恢复

    问题:如题. 解决方法:右键数据库   属性——选项——恢复模式:简单

  2. IT设备服务监控的方法论

    有方法论提导,在技战术方面才不会偏离目录. 使用服务级别作为关键语,召示着承诺和责任. https://www.circonus.com/2018/06/comprehensive-container ...

  3. 一脸懵逼学习Hive的使用以及常用语法(Hive语法即Hql语法)

    Hive官网(HQL)语法手册(英文版):https://cwiki.apache.org/confluence/display/Hive/LanguageManual Hive的数据存储 1.Hiv ...

  4. SpringBank 开发日志 使用maven构建dubbo服务的可执行jar包

    写这篇日志的时候,我已经完成了这个目标,并且中间经历了一次面试.现在回过头看,已经觉得印象不那么深刻了,果然还是一边思考,一边记录这样最好.但我还是严格要求自己,从新做了梳理,对相关配置进行了整理和说 ...

  5. 移动端开发demo—移动端web相册(一)

    本文主要是介绍开发移动端web相册这样一案例用到的前置知识. 一.移动端样式 移动端更接近手机原生的方式. 如下是一个angular mobile的demo的例子: 移动端demo做成这样的好处: 在 ...

  6. MySQL的预处理技术

    所谓的预处理技术,最初也是由MySQL提出的一种减轻服务器压力的一种技术! 传统mysql处理流程 1,  在客户端准备sql语句 2,  发送sql语句到MySQL服务器 3,  在MySQL服务器 ...

  7. ubuntu ibus pinyin输入法异常

    http://blog.csdn.net/granvillegao/article/details/41115211 命令行运行 ibus-setup 在常规页面,取消了“在应用程序窗口中启用内嵌编辑 ...

  8. C# 之 提高WebService性能大数据量网络传输处理

    1.直接返回DataSet对象 特点:通常组件化的处理机制,不加任何修饰及处理: 优点:代码精减.易于处理,小数据量处理较快: 缺点:大数据量的传递处理慢,消耗网络资源: 建议:当应用系统在内网.专网 ...

  9. Codeforces Gym100543B 计算几何 凸包 线段树 二分/三分 卡常

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF-Gym100543B.html 题目传送门 - CF-Gym100543B 题意 给定一个折线图,对于每一条 ...

  10. Java 异常处理的重要认识

    异常类的继承结构 Exception : 一般标识的是程序中出现的问题,可以直接使用try---catch处理. Error : 一般值得是JVM错误,程序中无法处理. 检测异常类需要在throws后 ...