1. JDBC
1.1 什么是JDBC
今天我来介绍下JDBC. JDBC英文全程Java Database Connectivity.是一个独立于特定DBMS(数据库管理系统), 通用的SQL数据库存储和操作的公共接口集合。该接口定义在java.sql和javax.sql下。
说白了JDBC是一套接口, 规定了访问数据库的规范和标准
1.2 JDBC的好处
统一数据库访问途径, 方便开发和维护.JDBC为访问不同数据库提供了统一的途径.屏蔽了不同数据库系统之间的差异.解耦数据库访问, 方便扩展支持不同数据库。使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统, 具有更好的扩展性, 兼容性。这些驱动由数据库厂商根据JDBC接口约定的规范进行开发.这就像java中的子类实现接口, 这其实是一种面向接口编程的思想.我们使用的时候, 不去关心子类的实现, 屏蔽掉了子类实现细节, 直接new一个子类赋值个接口引用即可引用.
JDBC
1.3 JDBC体系结构
JDBC接口包括两个层次
面向应用的API
Java API, 抽象接口,供应用程序开发人员使用。面向数据库的API
Java Driver API, 供开发商开发数据库驱动用。
1.4 JDBC驱动程序的分类
第一类: JDBC-ODBC, Windows实现的为方便
第二类: 部分本地API部分Java的驱动程序
第二类
第三类: JDBC网络纯Java驱动程序
第三类
这种驱动采用中间件的
应用服务器来访问数据库。应用服务器作为一个到多个数据库的网关,客户端通过它间接联接数据库。
应用服务器通常有自己的网络协议.Java程序通过JDBC驱动程序将JDBC调用发送给应用服务器, 应用服务器使用本地程序驱动访问数据, 从而完成请求。
例子:举个例子, 比如阿里云提供了访问数据库服务器的接口, 这就是阿里云厂商提供的本地API, 而应用服务器者是买了阿里云的第三方自己去订制开发, 根据需求可以自己定义一套通信协议,并实现JDBC驱动来间接的调用阿里云
第四类: 本地协议的存Java驱动协议
数据库厂商已经提供了
网络协议, 用来约定客户端程序通过网络直接与数据库通信这类驱动完全使用Java编写, 通过与数据库建立的Socket连接, 采用具体的
网络协议, 把JDBC调用转换为直接连接的网络调用
第四类
第三和第四种都是走的是网络.
不同的是前者是通过数据库网关间接和数据库通信, 而不同的数据库网关可能有不同的协议, 后者是直接和数据库建立网络连接, 进行远程接口调用。
第二种走的是数据库本地API, 相当于提供头文件和库直接给你调用.扩展性不好,对于不同的语言就需要提供另外一套的接口,没有走网络协议的兼容性好。
第一种的Windows提供的已经淘汰忽略.
1.5 JDBC的API构架
JDBC API是一套接口集合.约定了应用程序能够进行数据库联接、执行SQL语句、并且得到返回结果。
JDBC的API构架
1.5(0x00) Driver 接口
java.sql.Driver接口是所有JDBC驱动程序要实现的接口。这个接口提供给数据库厂商使用,让数据库厂商根据自己的技术实现这些接口的规范。在程序中不需要直接访问实现了Driver接口的类。而是由驱动管理器去调用这些Driver实现
常用的数据库驱动
Oracle驱动:
oracle.jdbc.driver.OracleDriverMysql驱动:
com.mysql.jdbc.Driver
1.6 JDBC操作
1.6(0x00) JDBC操作步骤
加载驱动并注册获取
连接对象获取
预编译对象(代码模板更易读懂、更高效(mysql不支持)、防止SQL注入)执行SQL语句释放资源(连接对象、预编译对象、结果集)(有顺序要求,注意异常处理的正确顺序)
1.6(0x01) 具体步骤
引入驱动包,这里我使用Maven引入
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency>
加载和注册驱动
一、通过Class.forName
通过反射直接使用Class.forName方式进行加载和注册.
Class.forName("com.mysql.jdbc.Driver");原理
通过Class.forName直所以能够加载注册驱动, 是因为Mysql驱动的实现类,使用了静态代码块, 并在静态代码块中使用
驱动管理器(DriverMangaer)进行驱动的注册。如下是Mysql驱动实现的代码
package com.mysql.jdbc;import java.sql.DriverManager;import java.sql.SQLException;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!");
}
}
}二、通过DriverManager自动进行加载和注册.
在JDK6.0, JDBC 4.0之后实际上我们不需要再调用Class.forName来加载驱动程序了, 我们只需要把驱动的jar包放到工程的类加载路径里,那么驱动就会被自动加载.
只要一调用这句代码, 就会加载DriverManager,其会去classpath下面扫描数据库驱动, 并自动进行加载和注册.
public static Connection getConn() throws ClassNotFoundException, SQLException {
Connection conn = null;
String url = config.getProperty(JDBCUtil.URL_KEY);
conn = DriverManager.getConnection(
url,
config); return conn;
}原理
注意
虽然可以这样做, 但是在Web开发中这种方式隐式加载驱动方式不被认可, 我们还是要手动的使用Class.forName来加载驱动
主要原因在于Web开发过程中, 我们需要将数据库做成配置文件, 方便部署和维护, 而这时候需要我们在
配置文件中显示的指定需要加载的数据库驱动的全限定类名.所以不用这种自动加载方式。
Web开发中我们还需要加入Class.forName.
public static Connection getConn() throws ClassNotFoundException, SQLException {
Connection conn = null;
String url = config.getProperty(JDBCUtil.URL_KEY);
Class.forName(config.getProperty(JDBCUtil.DRIVE_KEY));
conn = DriverManager.getConnection(
url,
config); return conn;
}建立连接
一旦加载完驱动, 我们就可以通过驱动和数据库服务器建立远程通信联接, 并获取到该联接对象, 使用如下代码.也可以使用Class.forName的方式,通过反射, 创建Driver对象,并进行注册
public static Connection getConn() throws ClassNotFoundException, SQLException {
Connection conn = null;
String url = config.getProperty(JDBCUtil.URL_KEY);
Class.forName(config.getProperty(JDBCUtil.DRIVE_KEY));
conn = DriverManager.getConnection(
url,
config); return conn;
}获取执行体对象或其子对象
我们可以通过联接对象,来获取执行体对象.通过执行体对象来执行sql语句.一般我们都用预加载执行体对象PrepareStatement, 而不不用其父类Statement对象.主要原因有以下两个。
安全问题。Statement对象对于存在sql注入的,没有对sql语句进行严格的过滤.效率问题。PreparedStatement是预编译,对于支持预编译的数据库来说可以大大提高执行效率.
PreparedStatement创建步骤
创建执行提对象,绑定好预编译sql。
设置预编译sql中占位符所对应数据。
@Test
public void exer1() { // 创建Customer表, 包含id, name, age, gender,birthday。插入数据
Connection conn = null;
PreparedStatement ps = null;
StringBuilder sb = new StringBuilder(); try {
conn = JDBCUtil.getConn();
sb.append("create table if not exists Customer (id int auto_increment, name varchar(20),").
append("age int, gender varchar(3) not null,").
append("birthdate date,").
append("primary key(id)").append(") engine innodb charset utf8");
System.out.println(sb);
PreparedStatement preparedStatement = conn.prepareStatement(sb.toString());
preparedStatement.execute();
sb = new StringBuilder();
sb.append(" insert into Customer (name, age, gender, birthdate) ");
sb.append(" values (?,?,?,?) ");
preparedStatement = conn.prepareStatement(sb.toString());
preparedStatement.setObject(1, "SweetCS");
preparedStatement.setObject(2, 26);
preparedStatement.setObject(3, "男");
preparedStatement.setObject(4, LocalDate.of(1992,1,1).toString());
System.out.println(preparedStatement);
preparedStatement.execute();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}执行查询
调用执行体对象的executeQuery()方法或者executeUpdate()执行sql。
释放资源
由于我们是通过网络联接和数据库进行通信,这是属于系统调用, 系统调用JVM并没有权限回收资源, 所以我们需要自己关闭网络资源.
既然要关闭资源,在finally语句块中进行关闭是最合适
Connection conn = null;
String url = "jdbc:mysql://127.0.0.1:3306/jdbc"; try {
conn = DriverManager.getConnection(url, "root", "admin");
System.out.println(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally { if (null != conn) { try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}JDBCUtil封装(版本一)
只要思路在于,方便后续进行数据库联接。如果我们使用JDBC除了每次肯定要进行的操作主要有四步.
数据库驱动的加载
数据库连接的建立
数据库查询语句的执行
资源的释放
除了第三点之外, 其他三个可以说是完全一样的操作,对于重复代码我们要进行封装.
import java.io.IOException;import java.sql.*;import java.util.Properties;public class JDBCUtil { static Properties config = null; private static final String URL_KEY = "url"; private static final String DRIVER_CLASS_NAME_KEY = "driverClassName"; private static String configFileName = "config.properties"; public static String getConfigFileName() { return configFileName;
} public static void setConfigFileName(String configFileName) {
JDBCUtil.configFileName = configFileName;
} static {
config = new Properties(); try {
config.load(JDBCTest.class.getResourceAsStream(configFileName));
} catch (IOException e) {
e.printStackTrace();
}
} public static Connection getConn() throws ClassNotFoundException, SQLException {
Class.forName(config.getProperty(DRIVER_CLASS_NAME_KEY));
String url = config.getProperty(JDBCUtil.URL_KEY);
Connection conn = DriverManager.getConnection(
url,
config); return conn;
} public static void close(Connection conn) { if (null != conn) { try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} public static void close(Connection conn, Statement stmt) { try { if (null != stmt) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(conn);
}
} public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (null != rs) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(conn, stmt);
}
}
}释放顺序问题。需要注意的是执行体对象需要在连接对象之前先释放。字符串复用问题。字符串最好提取出来方便复用。配置文件字段名规范问题。该工具类对应config.properties的数据库配置文件
config.properties
作者:sixleaves
链接:https://www.jianshu.com/p/a509cb1ffafc
x
共同学习,写下你的评论
评论加载中...
作者其他优质文章





