一、JDBC是什么?
JDBC是一个Java API,用中文可以通俗的解释为,使用Java语言访问访问数据库的一套接口集合。这是调用者(程序员)和实行者(数据库厂商)之间的协议,可以访问任何类型表列数据,特别是存储在关系数据库中的数据。JDBC(java database connection)
既然JDBC也是由Java类和接口组成,那么首先要学习的是,它属于哪个包下面。
在JDBC中包含了两个包:java.sql和javax.sql
- java.sql:基本功能,这个包中的类和接口主要针对基本的数据库编程服务,如生成连接、执行语句以及准备语句和运行批处理查。
询等。同时也有一些儿高级的处理,比如批处理更新、事务隔离和可滚动结果集等。 - javax.sql:扩展功能。它主要为数据库方面的高级操作提供了接口和类。如为连接管理、分布式事务和旧有的连接提供了更好的抽象,它引入了容器管理的连接池、分布式事务和行集等。
二、JDBC结构概述
![91d55988ec035c6c90e48174b14ea82c.png](https://i-blog.csdnimg.cn/blog_migrate/f8c56b4a3e840bb4d4280410f2726172.png)
可以看出JDBC在图中的位置,前文说过是程序员与数据库厂商之间的协议。首先Java提拱了JDBC的接口,服务器厂商提供了JDBC的驱动,这样我们通过JDBC Driver Manager来进行管理数据库。
主要接口和类
DriverManager: 这个类管理数据库驱动程序的列表。
确定内容是否符合从Java应用程序使用的通信子协议正确的数据库驱动程序的连接请求。
识别JDBC在一定子协议的第一个驱动器将被用来建立数据库连接。
Driver: 此接口处理与数据库服务器通信。
很少直接直接使用驱动程序(Driver)对象,一般使用DriverManager中的对象,
它用于管理此类型的对象。它也抽象与驱动程序对象工作相关的详细信息
Connection : 此接口与接触数据库的所有方法。
连接对象表示通信上下文,即,与数据库中的所有的通信是通过此唯一的连接对象。
Statement : 可以使用这个接口创建的对象的SQL语句提交到数据库。
一些派生的接口接受除执行存储过程的参数。
ResultSet: 这些对象保存从数据库后,执行使用Statement对象的SQL查询中检索数据。
它作为一个迭代器,可以通过移动它来检索下一个数据。
SQLException: 这个类用于处理发生在数据库应用程序中的任何错误。
三、JDBC编程(以mysql为例)
1. 注册驱动(有三种方式)
①Class.forName("com.mysql.jdbc.Driver"); Java规范中明确规定,所有的驱动程序必须在静态初始代码块中将驱动注册到驱动程序的管理器中
static{
try{
Class.forName(driver);
}
catch(Exception ex){
ex.printStackTrace();
}
}
这样做的好处是,再类被加载到工程时就被执行了,而且之执行一次,数据库驱动只要加载一次就可以了。
【知识点】 Class类
在这里面我们看到了,这个里面有一个Class类,它调用了一个forName()方法,那么这个Class类是个什么东西呢?
Class类是在java.lang包下的,所以不用手动的导入。
看一下Class的构造方法[1]
private Class(ClassLoader loader){
classLoader = loader;
}
不用去管什么ClassLoader,看了一眼private关键字,应该就知道了,当private修饰构造方法的时候,说明该类是不能在类的外面进行实例化的。所以Class类不能像普通的类一样,以new Xxx()的形式进行创建对象,它的对象只能由JVM创建。[2]
Class类到底是什么呢?不妨我们通俗的说一下,Java程序在运行的时候,我们会让它创建一些对象,系统通过RTTI对所有的对象进行运行时类型表示。
【知识点】RTTI
RTTI(runtime type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
我们把它叫成运行时类型信息,或许更加的好理解。这个是Java语言中很强大的机制。
首先要知道的运行时都会存在哪些类型信息呢?如果一个类继承自另一个类,在创建子类的对象时,需要RTTI存储这些关系,他会有typeid操作符,返回指针和引用所指的实际类型,typeid函数等等,我们简要的理解为Java的RTTI里面就是记录着各种类型的信息。
以下是一些常用的方式。
- A = (A)b;向下转型
我们可以针对于基类来编程,从而降低程序的耦合度,以Java来说常常通过继承的方式,来达到这种效果。
public class Demo {
public static void main(String[] args) {
Animal [] animals = new Animal[2];
animals[0] = new Tiger();
animals[1] = new fish();
for (Animal animal : animals) {
animal.breath();
}
}
}
abstract class Animal {
abstract void breath();
}
class Tiger extends Animal{
@Override
void breath() {
// TODO Auto-generated method stub
}
}
class fish extends Animal{
@Override
void breath() {
// TODO Auto-generated method stub
}
}
像这种继承关系,之所以能够编译通过,是因为Tiger和FIsh都会向上转为基类,他们自身的类型信息会丢失,但是程序运行的时候,当我们调用animal.breath();
它们却能准确的找到所属类型的方法进行调用,这是为什么呢?这就是运行时绑定(动态绑定)机制。
进一步分析,RTTI里面就是存储着程序运行时类型的方法列表,继承结构等等信息。
- load class(类的装载过程)
类从被加载到卸载,他的生命周期包括:加载 验证 准备 解析 初始化 使用 和卸载七个阶段
而类的加载阶段有:加载 验证 准备 解析 初始化。
重点说一下加载阶段:类只有在要运行的时候才会被加载进JVM,编译后的class文件现在还没有加载到jvm,当我们命令行输入java xxx.class
,这个时候类才开始加载到虚拟机,一般来说一个class只会被加载一次,下一次就会从Jvm的class缓存中获取,不会再去文件系统中获取了。具体的过程是:java这个命令是java.exe进行的,java.exe找到jre,再找到JRE中的jvm.dll,这个就是Java 虚拟机,这个时候虚拟机启动,它首先就加载了第一个类加载器--Bootstrap Loader,这个BootstrapLoader又加载了第二个类加载器ExtClassLoader,设定parent为null(本质上是bootstraploader,但是它是由C++编写的,无法找到这个实例)这个BootstrapLoader又加载了最后一个类加载器APPClassLoader,设定它的parent为ExtClassLoader。
![1ead1ec710ccef08879727dfafb67337.png](https://i-blog.csdnimg.cn/blog_migrate/282f305f0285f1027d808a1ad9bf713f.jpeg)
- 类的类class of classes
Class类原理:当各类被实例化后,JVM会自动创建该类的Class对象,或者通过类加载器defineClass()方法生成。
所有的类都继承自Object
类,Object类有一个getClass()
方法,这个方法可以获取某个对象的Class引用,这个引用就是指向的是Class类的对象。
![0d878371471c4641a1b36b2d7c2b4320.png](https://i-blog.csdnimg.cn/blog_migrate/30ad17431b7420142649fb82416fc99e.jpeg)
因为是针对类的关系而言,所以一个Class对象对应多个类的实例化。
①获取Class对象:
- 通过类名来获取:
public class Hello{} Class hello = Class.forName("Hello"); - 通过对象来获取。
public class Hello{} Hello h = new Hello(); Class hello = h.getClass(); - 通过类字面量来获取
public class Hello{} Class hello = Hello.class;
【注】使用第三种方法来获取Class对象,Jvm不会加载类,也就不会初始化,而其他两种方法,会自动加载并初始化类。
回到正题。第二种注册数据库驱动的方法是。
②
Driver drv = new oracle.jdbc.driver.OracleDriver();
DriverManager.registerDriver(drv);
③ 编译时在虚拟机中加载驱动
java -D jdbc.drivers = 驱动全名 类名
例如: javac -D jdbc.drivers = oracle.jdbc.driver.OracleDrvier xxx.java
2. 创建连接
创建一个Connection
对象,建立物理连接
static final String USER = "XXX";
static final String PASSWORD = "xxx";
System.out.println("database is connecting");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.23:1521:tarena",USER,PASSWORD);
回想之前的结构图,我们可以知道,通过DriverManager
可以操作数据库的驱动,从而进行数据库的相关操作。
这里Connection
连接就是通过DriverManager
的静态方法getCOnnection()
来得到的,这个方法的实质是把参数传到实际的Driver
中的connect()
方法来忽的数据库的连接的。
下面是一些格式的补充:
Oracle URL的格式:jdbc:oracle:thin:(协议)@XXX.XXX.X.XXX:XXXX(IP地址及端口号):XXXXXX(所使用的库名)
MySql URL:jdbc:mysql://192.168.8.21:3306/test
SQLServer URL 的写法 jdbc:microsoft:sqlserver://192.168.8.21:1433
3. 获取Statement对象
通过Connection
对象createStatement()
来创建Statement对象
Statement stm = conn.createStatement();
4. 执行SQL语句
通过Statement来执行
stm.excuteQuery(Sring sql); //返回一个查询结果集
stm.excuteUpdate(String sql); //返回值为 int 型,表示影响记录的条数。
stm.excute(String sql);//返回 true,表示查询;返回 false,表示其它操作。
5. 处理结果集ResultSet
通过excuteQuery(String sql)
可以使用select语句进行查询,并且返回一个结果集ResultSet。ResultSet的next()方法会操作一个游标从第一条记录的前面开始读取,直到最后一条记录。
【注】只有执行select语句才有结果集返回。
while(rs.next()){
System.out.println(rs.getInt("id));
System.out.println(rs.getString("name"));
}
6. 关闭数据库连接(释放资源)
rs.close();
stm.close();
conn.close()
应该顺序关闭,如果先关闭Connetction后面可能还需要其他的Statement连接。
![f427adf2d6e2add83d7d3f2d42f4c387.png](https://i-blog.csdnimg.cn/blog_migrate/59e6d141a9122f28eb13b75f158d768f.jpeg)
四、写到这里
关于JDBC学习之了解Class就介绍完了,小伙伴们是否有疑问呢?欢迎留言与我讨论,关注程序员曾曾,每天分享你不知道的Java知识~