一、概述

官方解释:

  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. xss过滤方法

    用的白名单过滤,是我们的论坛自用的方法,也许考虑不周,欢迎来黑我们的论坛!https://www.ebcms.com/forum.html // 安全过滤 function safe_html($ht ...

  2. 优化oracle读写任务

    查读盘次数最多的前十个sql操作: SELECT * FROM (select PARSING_USER_ID, EXECUTIONS, SORTS, COMMAND_TYPE, DISK_READS ...

  3. linux的逻辑运算符

    1:expression :用于计算括号中的组合表达式,如果整个表达式的计算按结果为真,则测试结果也为真. 2:!exp:客队表达式进行逻辑非运算,即对测试结果求反 3:符合 -a 或者 && ...

  4. qt5--QLabel标签控件

    需要 #include <QLabel> label->setFrameShape(QFrame::Box);     //设置边框 边框样式参看:https://www.cnblo ...

  5. qt5-信号和槽

    信号函数: connect(btn,&QPushButton::clicked,this,&QWidget::close); //参数1 信号发送者://参数2 信号:---& ...

  6. 43 java中的异常处理机制的简单原理和应用

  7. Struts2基本原理【转】

    阐述struts2的执行流程. Struts 2框架本身大致可以分为3个部分:核心控制器FilterDispatcher.业务控制器Action和用户实现的企业业务逻辑组件. 核心控制器FilterD ...

  8. Junit 4测试框架使用

    断言是编写测试用例的核心实现方式,即期望值是多少,测试的结果是多少,以此来判断测试是否通过. 断言核心方法 assertArrayEquals(expecteds, actuals) 查看两个数组是否 ...

  9. Python 面向对象Ⅴ

    基础重载方法 下表列出了一些通用的功能,你可以在自己的类重写: 运算符重载 Python同样支持运算符重载,实例如下: 以上代码执行结果如下所示: 类属性与方法 类的私有属性 __private_at ...

  10. linux运维、架构之路-K8s健康检查Health Check

    一.Health Check介绍         强大的自愈能力是k8s容器编排引擎一个重要特性,自愈能力的默认实现方式为自动重启发生故障的容器,另外还可以利用Liveness和Readiness探测 ...