一、概述

官方解释:

  JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的 Java API,可以为多种关系型数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,同时,JDBC也是个商标名。

简单地说,JDBC 可做三件事:与数据库建立连接、发送 操作数据库的语句并处理结果。

理解:

  JDBC就是一套访问数据库的规范(或者说是一组接口),提供数据库的协议标准,各个数据库厂商根据这套规范提供一套访问自己数据库的驱动。

二、JDBC工作原理

从图中可以看到 JDBC 的几个重要组成要素。最顶层是我们自己编写的Java应用程序(Java Application),Java 应用程序可以使用集成在JDK中的 java.sql 和 javax.sql 包中的JDBC API来连接和操作数据库。下面我们依次介绍JDBC的组成要素:

1、JDBC API

  JDBC API 由Sun公司提供,提供了Java 应用程序与各种不同数据库交互的标准接口,如:Connection(连接)接口,Statement 接口,ResultSet(结果集)接口,PreparedStatement 接口等。开发者使用这些JDBC接口进行各种数据库操作。

  • DriverManager:此类管理数据库驱动程序列表。使用通信子协议将来自 Java 应用程序的连接请求与适当的数据库驱动程序匹配;
  • Driver:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用DriverManager对象来管理这种类型的对象;
  • Connection:该接口具有用于联系数据库的所有方法。连接对象通信上下文,即,与数据库的所有通信仅通过连接对象;
  • Statement:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派生接口还接受参数;
  • ResultSet: 在使用 Statement 对象执行 SQL查询后,这些对象保存从数据库检索的数据。它作为一个迭代器,允许我们移动其数据;
  • PreparedStatement:继承自 Statement 接口,主要用于执行预编译 SQL,除了具有 Statement 接口的所有功能,还另外它还添加了一整套方法,用于设置发送给数据库以取代 IN 参数占位符的值;

2、JDBC Driver Manager

  JDBC Driver Manager(驱动程序管理器)由Sun公司提供,它是 JDBC 体系结构的支柱,负责管理各种不同的 JDBC 驱动,把 Java 应用程序连接到相应的 JDBC 驱动程序上,位于JDK的 java.sql 包中。

3、JDBC Driver

  JDBC Driver 由各个数据库厂商或第三方中间件厂商提供,负责连接各种不同的数据库。例如,上图中,访问 MySQL 和 Oracle 时需要不同的JDBC驱动,这些 JDBC 驱动都实现了JDBC API 中定义的各种接口。在开发Java应用程序时,我们只需正确加载JDBC驱动,正确调用 JDBC API,就可以进行数据库访问了。

三、JDBC 访问数据库的步骤

我们先来看一下下面这段代码:

public class App {
private static final String URL = "jdbc:mysql://192.168.182.131:3306/cakes";
private static final String USER_NAME = "root";
private static final String PASSWD = "123456"; public static void main(String[] args) throws Exception { // 1.加载驱动
Class.forName("com.mysql.jdbc.Driver"); // 2.建立连接
Connection connection = DriverManager.getConnection(URL, USER_NAME, PASSWD); // 3.建立Statement
Statement statement = connection.createStatement(); // 4.执行sql
boolean execResult = statement.execute("insert into user(`name`,`passwd`) values('木木','123456')");
// statement.executeUpdate返回的是影响的行数
int effectRows = statement.executeUpdate("insert into user(`name`,`passwd`) values('大叔','888888')"); System.out.println("execResult = " + execResult);
System.out.println("effectRows:" + effectRows); // 5.资源关闭
statement.close();
connection.close();
}
}

JDBC 示例

执行结果如下:

以上红色字体为警告信息,意思是:com.mysql.jdbc.Driver 这个驱动已经被弃用了,建议使用 com.mysql.cj.jdbc.Driver 这个驱动,更改后再次运行则没有这个警告信息了。

所以标准代码如下:

public class App {
private static final String URL = "jdbc:mysql://192.168.182.131:3306/cakes";
private static final String USER_NAME = "root";
private static final String PASSWD = "123456"; public static void main(String[] args) throws Exception { // 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver"); // 2.建立连接
Connection connection = DriverManager.getConnection(URL, USER_NAME, PASSWD); // 3.建立Statement
Statement statement = connection.createStatement(); // 4.执行sql
boolean execResult = statement.execute("insert into user(`name`,`passwd`) values('木木','123456')");
// statement.executeUpdate返回的是影响的行数
int effectRows = statement.executeUpdate("insert into user(`name`,`passwd`) values('大叔','888888')"); System.out.println("execResult = " + execResult);
System.out.println("effectRows:" + effectRows); // 5.资源关闭
statement.close();
connection.close();
}
}

同时,因为我本地的 Gradle 工程引入的 mysql-connector-java 包的版本是 8.0.16,所以加载驱动那一步可以省略不写(5.x.x版本之后)。

JDBC访问数据库基本步骤:

1、加载驱动程序

2、建立连接

3、创建 Statement 对象

4、执行 SQL 语句

5、处理返回结果

6、关闭资源,包括:ResultSet,Statement 和 Connection

四、Statement 和 PreparedStatement

我们先来看两段代码:

public static void test01() {

  Connection connection = null;
Statement statement = null; try {
connection = DriverManager.getConnection(URL, USER_NAME, PASSWD); statement = connection.createStatement(); String sql = "insert into user(`name`,`passwd`) values('木木','123456')"; int executeNum = statement.executeUpdate(sql);
System.out.println(String.format("成功插入%d条数据",executeNum)); } catch (SQLException se) {
se.printStackTrace();
}finally {
try {
if (null != statement) {
statement.close();
}
if (null != connection) {
connection.close();
}
} catch (SQLException se) {
se.printStackTrace();
}
}
}

Statement

public static void test02() {

  Connection connection = null;
PreparedStatement preparedStatement = null; try {
connection = DriverManager.getConnection(URL, USER_NAME, PASSWD); String sql = "insert into user(`name`,`passwd`) values(?, ?)"; preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"jack");
preparedStatement.setString(2,"112233"); int executeNum = preparedStatement.executeUpdate();
System.out.println(String.format("成功插入%d条数据",executeNum)); } catch (SQLException se) {
se.printStackTrace();
}finally {
try {
if (null != preparedStatement) {
preparedStatement.close();
}
if (null != connection) {
connection.close();
}
} catch (SQLException se) {
se.printStackTrace();
}
}
}

PreparedStatement

PreparedStatment 接口继承自 Statement 接口,PreparedStatment 执行的是预编译 SQL,使用占位符,创建 PreparedStatment 对象时就会把 SQL语句作为参数参入。

通常情况下,我们更建议使用 PreparedStatment,原因如下:

1、提高了代码的可读性和可维护性

虽然使用 PreparedStatement 来代替 Statement 会多几行代码,但避免了烦琐麻烦又容易出错的SQL语句拼接,提高了代码的可读性和可维护性。

2、提高了SQL语句执行的性能

创建 Statement 对象时不使用SQL语句做参数,不会解析和编译SQL语句,每次调用方法执行SQL语句时都要进行SQL语句解析和编译操作,即操作相同仅仅是数据不同。

创建 PreparedStatement 对象时使用SQL语句做参数,会解析和编译该SQL语句,也可以使用带占位符的SQL语句做参数,在通过 setXxx ()方法给占位符赋值后执行SQL语句时无须再解析和编译SQL语句,直接执行即可。多次执行相同操作可以大大提高性能。

3、提高了安全性

PreparedStatement 使用预编译语句,传入的任何数据都不会和已经预编译的SQL语句进拼接,避免了SQL注入攻击。

五、ResultSet

使用 Statement 或 PreparedStatement 对象执行 SQL 查询后,会返回 ResultSet 结果集,作为一个迭代器,我们一般使用 next() 方法来判断把光标挪向下一行。

public class UserDemo3 {

  private static final String URL = "jdbc:mysql://192.168.182.131:3306/cakes";
private static final String USER_NAME = "root";
private static final String PASSWD = "123456"; public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null; try {
connection = DriverManager.getConnection(URL, USER_NAME, PASSWD);
String sql = "select * from user where name=?"; preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "大叔"); resultSet = preparedStatement.executeQuery(); ArrayList<User> users = new ArrayList<>(); while (resultSet.next()) {
User user = User.of();
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
user.setPasswd(resultSet.getString("passwd")); users.add(user);
}
for (User user : users) {
System.out.println(user);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (null != resultSet) {
resultSet.close();
}
if (null != preparedStatement) {
preparedStatement.close();
}
if (null != connection) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

ResultSet

六、事物操作

七、SQL 执行工具设计

下面我们使用 JDBC 简单设计一款 SQL 执行工具

public interface JdbcConsts {

  String URL = "jdbc:mysql://192.168.182.131:3306/cakes";

  String USER_NAME = "root";

  String PASSWD = "123456";

}

JdbcConsts 接口

JdbcConsts 接口中存放的是数据库连接配置。

public final class ConnectionHandler {

  private ConnectionHandler() {
} public static ConnectionHandler of() {
return new ConnectionHandler();
} public Connection getConnection() {
try {
return DriverManager.getConnection(URL, USER_NAME, PASSWD);
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
}

ConnectionHandler 类

ConnectionHandler 类封装了数据库连接这个操作。

public abstract class AbstractDBHandler<T> {

  /**
* @param sql SQL语句, update user set name=?,age=? where id=?
* @param params [zhangsan,23,12]
*/
public int modify(String sql, List<Object> params) {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = ConnectionHandler.of().getConnection(); // statement = connection.prepareStatement(sql);
//
// for (int i = 0; i < params.size(); i++) {
// statement.setObject(i + 1, params.get(i));
// }
statement = preparedStatement(sql,params,connection); int effectRows = statement.executeUpdate(); System.out.println("effectRows = " + effectRows); return effectRows;
} catch (SQLException e) {
throw new IllegalStateException(e);
} finally {
release(null, statement, connection);
}
} public List<T> select(String sql, List<Object> params) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = ConnectionHandler.of().getConnection(); // statement = connection.prepareStatement(sql);
//
// for (int i = 0; i < params.size(); i++) {
// statement.setObject(i + 1, params.get(i));
// }
statement = preparedStatement(sql,params,connection); resultSet = statement.executeQuery(); List<T> result = new ArrayList<>();
while (resultSet.next()) {
// 抽象点,通过抽象方法来实现
T t = mapping(resultSet);
result.add(t);
}
return result;
} catch (SQLException e) {
throw new IllegalStateException(e);
} finally {
release(resultSet, statement, connection);
}
} private PreparedStatement preparedStatement(String sql,List<Object> params,
Connection connection) throws SQLException {
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; i < params.size(); i++) {
statement.setObject(i + 1, params.get(i));
}
return statement;
} protected abstract T mapping(ResultSet resultSet) throws SQLException; private void release(ResultSet resultSet, Statement statement, Connection connection) {
try {
if (null != resultSet) {
resultSet.close();
}
if (null != statement) {
statement.close();
}
if (null != connection) {
connection.close();
}
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
}

AbstractDBHandler 抽象类

AbstractDBHandler 类中定了了 select() 和 modify() 两个方法,modify()用于执行增、删、改,select()用于执行查询。modify() 方法传入两个参数 sql 和 List<Object> params,具体执行的什么 SQL 和 该 SQL 应该传入什么值,全部在 Dao 中定义。select() 方法查询出的结果对于不同的表返回的是不一样的,所以,我们使用了一个抽象方法 mapping() 来抽象返回结果,具体我们查询的是哪张表,返回的是什么样的结果,全部在 Dao中定义,如下:

public class UserDao extends AbstractDBHandler<User> {

  @Override
protected User mapping(ResultSet resultSet) throws SQLException { User user = User.of();
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
user.setPasswd(resultSet.getString("passwd")); return user;
} public int addUser(User user) {
String sql = "insert into user(`name`,`passwd`) values(?,?)";
List<Object> params = new ArrayList<>();
params.add(user.getName());
params.add(user.getPasswd());
return modify(sql, params);
} public int delUser(User user) {
String sql = "delete from user where name=? and passwd=?";
List<Object> params = new ArrayList<>();
params.add(user.getName());
params.add(user.getPasswd());
return modify(sql, params);
} public List<User> queryByName(User user) {
String sql = "select * from user where name=? and passwd=?";
List<Object> params = new ArrayList<>();
params.add(user.getName());
params.add(user.getPasswd());
return select(sql, params);
}
}

UserDao

UserDao 继承自 AbstractDBHandler 类,当中定义了关于 User 表的 增、删、改、查等操作。其中,增、删、改调用的是 modify() 方法而查询调用的是 select() 方法。同时也定义了 User 表查询结果由三个字段组成:id,name,passwd。

public class App {

  @Test
public void testInsert() {
UserDao userDao = new UserDao(); User user = User.of("小芹", "s123456"); userDao.addUser(user);
} @Test
public void testDelete() {
UserDao userDao = new UserDao(); User user = User.of("jack", "123456"); userDao.delUser(user);
} @Test
public void testSelect() {
UserDao userDao = new UserDao(); User user = User.of("大叔", "111222"); List<User> users = userDao.queryByName(user); System.out.println(users);
}
}

测试类中只需要传入执行 增、删、改、查具体的数据就可以。

以上,只是对针对 JDBC 的操作进行了简单的设计和封装,还有很多地方可以优化,这里就不再深入讲解。

Java学习之==>JDBC的更多相关文章

  1. Java学习之JDBC 2019/3/10

    Java学习之JDBC 大部分的程序都是用来通过处理数据来达到人们预期的效果,数据是粮食,没有数据操作的程序就像helloworld程序一样没有用处.因此数据库操作是重中之重,是程序发挥功能的基石,j ...

  2. java学习之JDBC

    之前学习了数据库原理,上学期也学了oracle数据库,我的学习视频上是讲的mysql数据库,其实都差不多,复习了下sql知识,数据库的学习就没有写下来了,就从Java怎么操作数据库开始吧. 因为这年过 ...

  3. Java学习笔记——JDBC读取properties属性文件

    Java 中的 properties 文件是一种配置文件,主要用于表达配置信息,文件类型为*.properties,格式为文本文件. 文件的内容是格式是"键=值"(key-valu ...

  4. Java学习笔记——JDBC之与数据库MySQL的连接以及增删改查等操作

    必须的准备工作 一.MySQL的安装.可以参考博文: http://blog.csdn.net/jueblog/article/details/9499245 二.下载 jdbc 驱动.可以从在官网上 ...

  5. Java学习笔记--JDBC数据库的使用

    参考  hu_shengyang的专栏 : http://blog.csdn.net/hu_shengyang/article/details/6290029 一. JDBC API中提供的常用数据库 ...

  6. java学习之jdbc的封装

    jdbc是连接数据库必不可少的工具,但每次连接都要重新写一遍太麻烦了,也不利于代码的可读性,这里做一个工具类进行封装. package com.gh; import java.sql.Connecti ...

  7. JAVA学习笔记 -- JDBC及其应用

    一个.准备工作 1.开放SQL Server服务与支持TCP/IP  进一步确认TCPport watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjk ...

  8. Java学习(JDBC java连接数据库)

    一.概述 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写 ...

  9. Java学习:JDBC快速入门

    本节类容 JDBC基本概念 快速入门 JDBC基本概念 概念: Java DataBase Connectivity Java 数据库连接,Java语言操作数据库 JDBC本质: 其实是官方(sun公 ...

随机推荐

  1. loj2318 「NOIP2017」宝藏[状压DP]

    附带其他做法参考:随机化(模拟退火.爬山等等等)配合搜索剪枝食用. 首先题意相当于在图上找一颗生成树并确定根,使得每个点与父亲的连边的权乘以各自深度的总和最小.即$\sum\limits_{i}dep ...

  2. golang之运算符

    目录 一.golang之运算符 1. 算术运算符 2. 关系运算符 3. 逻辑运算符 4. 位运算符 5. 赋值运算符 一.golang之运算符 Go 语言内置的运算符有:(比python少了一个成员 ...

  3. thinkPHP5.0.22初体验---路由,url访问

    “豪情卷起万重浪,吼吼哈哈-”一学thinkPHP才知道这是个国内研究的php web开发框架,瞬间自豪感如电流一般传遍全身 这就不多不说说 一.控制器 所谓MVC编程,无外乎函数(sometimes ...

  4. LightOJ-1104-birthday Paradox(概率)

    链接: https://vjudge.net/problem/LightOJ-1104 题意: Sometimes some mathematical results are hard to beli ...

  5. qt5---滑动条QSlider

    需要    #include <QSlider> #include "win.h" #include <QDebug> #include <QPush ...

  6. 【SQL-自动生成编号】按规则自动生成单据编号 以及并发问题_使用触发器、函数 等

    描述:每种单据新建时,自动生成它的单据编号. 规则如:固定码+日期+流水号 (ABC1603180001) 方法一:触发器 触发器的缺点是,执行了sql之后才看到编码. 测试:流水号不能超过最大数,否 ...

  7. 对abel 转译 class 过程的研究----------------------引用

    作为当下最流行的 JavaScript 编译器,Babel 替我们转译 ECMAScript 语法,而我们不用再担心如何进行向后兼容. 零.前言 虽然在 JavaScript 中对象无处不在,但这门语 ...

  8. Invalid [xxx] in servlet mapping 、 <url-pattern>的匹配规则 、 DefaultServlet介绍

    真的是很容易被忽视的错误,servlet 配置url的时候遇到问题,这个之前确实没有详细了解过. 出现这个错误的时候往往伴随着一系列高大上的错误,比如会出现类似[StandardEngine[Cata ...

  9. three months timestamp

    1.有效期三个月 package com.hengqin.life.idps; import java.text.SimpleDateFormat; import java.util.Calendar ...

  10. yum 源的搭建

    repos和epel的关系 https://blog.csdn.net/fantaxy025025/article/details/84918199 配置阿里云的yum源 https://www.cn ...