准备工作:JDK1.9,idea 2017,jdbc架包mysql-connector-java-5.1.18-bin.jar
写作目的:之前写JDBC连接,一直都是按照老师给的模板,一步一步的写,没有想过为什么,而通过工作后对编程理解的加深和对源码的挖掘,理解了每一步为什么这样写。在此将自己的理解做以梳理和总结,以供日后学习。
目录
JDBC连接介绍
JDBC(Java DataBase Conectivity ) java数据库连接,善良的Sun公司为了解决不同的数据库连接而制定的一种规范(或者说是一些接口和类)。不同的数据库实现java的连接就要实现这些接口(或继承这些类)。这里的mysql-connector-java-5.1.18-bin.jar就是mysql实现jdbc的类jar包。Java提供访问数据库规范称为JDBC,而生产厂商(如mysql、Oracle)提供规范的实现类称为驱动。
JDBC的四个核心类
DriverManager:用于加载驱动
Connection:用户获取连接
Statement:用于操作数据库sql语句
ResultSet:结果集
数据库表:
建表
CREATE TABLE `book` (
`bookid` int(11) NOT NULL AUTO_INCREMENT,
`bookname` varchar(50) DEFAULT NULL,
`rmb` float(7,2) DEFAULT NULL,
`FRO` varchar(50) DEFAULT NULL,
PRIMARY KEY (`bookid`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
插入数据
六步jdbc连接分析
1、加载驱动
市面是加载驱动的方法是通过加载jar包下com.mysql.jdbc.Driver的字节码文件实现的,我们来探究下为什么要这样写!
实际上加载驱动用到的是DriverManager下的静态方法registerDriver(java.sql.Driver driver)
源码如下(1-1):
public static void registerDriver(java.sql.Driver driver)
throws SQLException {
registerDriver(driver, null);
}
这是sun公司提供的规范,传的参数driver是相应的sql驱动
而mysql的jdbc连接jar包内相应的注册驱动的源码是(1-2)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
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!");
}
}
}
将1-2以对象形式作为参数传入1-1中
即(1-3)
DriverManager.registerDriver(new com.mysql.jdbc.Driver())
如此即完成了数据库的加载,但是观察1-1和1-2的代码可知1-1本身即是加载数据库驱动,但是1-2作为被传入的参数对象,在被编译后,会加载内部的静态代码块!静态代码块中的DriverManager.registerDriver(new Driver())也加载了数据库驱动!所以,这里等于加载了两次驱动!为了使驱动只被加载一次!此处可以通过仅new mysql jar包中的Driver对象,加载里面的静态方法来实现数据库的加载!
即(1-4)
new com.mysql.jdbc.Driver()
其实用这样的方式加载驱动对于一个初入java坑的新手来说是最好理解,也足够实现功能的一个方式!但是java开发追求的是高可用性!通过new驱动的方式加载驱动,显得不够灵活!而通过加载字节码文件的方式加载com.mysql.jdbc.Driver类来编译运行内部的静态代码块执行DriverManager.registerDriver(new Driver()) 将驱动变成参数,这样的话,当使用不同的数据库时只需要改变(1-5)
Class.forName("com.mysql.jdbc.Driver")
内部的参数即可,大大提高的了代码的灵活性。
其实jdbc驱动设计的初期已经将代码的灵活性考虑在内!所以将注册驱动的方法设计在了静态代码块中!
2、获取连接
DriverManager下的静态方法getConnection() 源码如图(1-6)
DriverManager.getConnection(url,user,password)
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
这里可以很清楚的看到user信息和password信息被传进了Properties集合,这个集合也是java预留的唯一的一个可以加载流文件的集合
3、获取执行sql语句对象
Connection 的createStatement()方法或者 prepareStatement(String sql)
createStatement()处理的sql不能有参数也就是?号,返回Statement
prepareStatement可以用来处理有参数的sql语句,返回PrepareStatement,效率要比createStatment高,所以一般都用prepareStatement。
prepareStatement给参数赋值的方法时setObject(int index,Objectparament) 使用Object的好处是可以忽略数据库和java之间数据类型的转化!
4、执行sql语句
调用executeQuery()方法和executeUpdate方法 createStatement在此传入sql语句
executeQuery()只执行查询语句(DQL),executeUpdate()只执行增删改语句(DML)
5、获取结果
执行完sql语句后返回的是一个ResultSet集合,里面的next用来指向下一行
获取每行元素需要调用getInt(int index)、getString(int index)\getDouble(int index)等方法依次获取对应的值,这里不建议用getObject(int index)的方法获取值,因为往往需要获取每个值后,需要对其进行操作,如果获取的是Object类型的数据,则在使用的时候需要向下转型。
6、关闭资源
所有和外部资源相关的资源都需要关!
连接(Connection),sql对象(Statement/PrepareStatement),结果集(ResultSet)
JDBC连接代码实现
以上是对JDBC六步的分析。简单的JDBC从加载驱动、获取连接,到获取执行sql的对象、执行sql,再到处理结果集、关闭资源实际上就是一个系统实现被用户使用,获取数据的一个完整的过程。用户使用系统,就是频繁的进行数据的增删改查,而频繁的增删改查过程中,数据库似乎只需要被加载一次!这样加载驱动这一步就可以单独写在一个类的静态代码块中,在类被加载时,执行静态代码块进而加载驱动,类只会被加载一次!而类只要被加载,就会执行静态代码块!数据库驱动就会被加载!
这个放加载驱动代码块的类,可以是一个工具类!工具类里面处理定义加载驱动的静态代码块,还可以定义获取连接的静态方法,在其他类调用此工具类的获取连接方法时,如果类没被加载,则加载类,如果已经被加载,那就直接获取连接。此外这个类还可以定义关闭资源的方法,在使用完连接后关闭相应资源。分析至此一个高复用性的工具类就诞生了,里面包含的是加载驱动的静态代码块,获取连接的静态方法,以及关闭资源的静态方法。代码如下(2-1)
package test01;
import java.sql.*;
/**
* @Author: ${user}
*/
public class JdbcPlus {
/*私有化构造函数,这是定义工具类的固有格式,想java源码中的Colletions、Arrays等都是工具类,都私有化了构造函数*/
private JdbcPlus(){}
//1、私有化的静态成员变量,在静态代码块中实现赋值
private static String url;
private static String user;
private static String password;
static {
try {
//2、加载驱动,同时赋值
url = "jdbc:mysql://localhost:3306/school";
user = "root";
password = "1234";
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接,参数已经在静态代码块中被初始化,返回类型为Connection
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
//关闭资源,此处为了提高效率,加了非空判断,如果是空,代表没获取到,则不需要关
public static void close(Connection conn, Statement stat, ResultSet rs) {
try {
if (conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (stat != null)
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (rs != null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
一个高可用的工具类就完成了。在实际开发中只需要调用JdbcPlus.getConnection()即可实现加载驱动获取连接的功能,而数据库驱动也只加载一次,我们再定义一个Test_jsp类(2-2)来完成接下来的步骤:
package test01;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @Author: ${user}
*/
public class Test_jsp {
public static void main(String[] args) throws SQLException {
//加载获取连接
Connection conn = JdbcPlus.getConnection();
String str = "select * from book";
//获取执行sql的对象
PreparedStatement ps = conn.prepareStatement(str);
//执行查询功能
ResultSet rs = ps.executeQuery();
//处理结果集
while(rs.next()){
System.out.print(rs.getInt(1));
System.out.print(rs.getString(2));
System.out.print(rs.getDouble(3));
System.out.println(rs.getString(4));
}
//关闭资源
JdbcPlus.close(conn,ps,rs);
}
}
总结:
脑子里面觉得代码实现其实也就那回事,但是真正把它具体的描述出来发现总是词穷,羞于表达!学习的路程还漫长!诸位看客觉得有哪些地方在下没表达清楚的欢迎指出,也欢迎诸位沟通赐教!
能力尚浅,有待进步,如有不足,不吝赐教!
后面是配置文件方式进行JDBC连接的详细步骤及源码,特意来附上连接,方便跳转