市面上很多人建议都是简单的学习jdbc,因为后面都会使用mybatis相关框架进行封装。虽然确实在实际开发中很少小关于jdbc的代码。但是对于一个代码热爱者来说,我们还是有必要进一步学习jdbc,首先在学习mybatis、spring等框架源码时,我们必不可少地会接触到jdbc的源码;再者,我们可以在jdbc中学习到很多知识和思维:工厂模式、装饰者模式、反射、集合框架等。
首先我们来看看本文章的思维导图:
JDBC
JDBC概述
JDBC 环境依赖相关以及快速案例
依赖引入
数据库搭建
JDBC里面各种API详解
Driver
DriverManger
Connection
PreparedStatment and Statement
ResultSet
ResultSetMetaData
JDBC 自定义封装工具类及方法
getConnction
close
update
select
JDBC事务相关
JDBC连接池
DBUtils工具类介绍及源码分析
一.JDBC概述
- JDBC是java中的一个接口,各种数据库如mysql、sqlserver等数据都需要实现这个接口,才能够使得java能够使用相关产品的数据库,如果哪一个数据库不提供相关的JDBC实现类,就会被“踢出”java生态。
- JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
- 目前我们使用的框架如mybatis其底层就是使用的JDBC写的,所以我们现在学习JDBC也是为了以后学习mybatis源码打好基础。(当然像反射中的动态代理、设计模式等同样很重要)
能看到本文章的可能都会对JDBC有一定的了解,接下来我们直接进入正题吧!
二.JDBC 环境依赖相关以及快速案例
1.依赖引入:
这里我们使用maven来管理项目:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
2.数据库环境介绍:
这里我们使用mysql数据库,并建立一张表供我们后续操作持续使用: 我们建立一个学生表
下面是建立表和插入的测试数据,
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(225) NOT NULL,
`age` int DEFAULT NULL,
`sex` varchar(225) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
insert into `student`(`id`,`username`,`age`,`sex`) values (1,'以某',22,'男'),(2,'小二',19,'男'),(3,'张三',12,'男'),(4,'李四',12,'男'),(5,'王五',22,'男');
3.快速使用JDBC
@Test
public void demo1() throws SQLException {
Driver driver = new Driver();
String url="jdbc:mysql://localhost:3306/zjdata";
String user="root";
String password="12345678";
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
String sql="delete from student where id="+5;
statement.execute("delete from student where id=5");
connection.close();
}
以上通过JDBC实现了数据库中id为5的数据的删除,当然这只是初级的使用,后面还有更高级的用法。
三.JDBC相关API详解
1.Driver类
一个是java.sql包下的Driver抽象类,里面给出了Driver的各种规定方法,各种数据库厂商需要根据此抽象类编写相关的数据库驱动,我们程序员不需要面向该接口进行编程。 另一个则是相关的数据库厂商编写出来的Driver实现类,就拿mysql的驱动包举例:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException {
}
}
这个类就是mysql方法给我们提供的驱动类,平时我们在写jdbc时,就必须把这个类加载到内存中并注册。在我的快速案例中写的代码是:
Driver driver = new Driver();
DriverManager.registerDriver(driver);
首先这个 DriverManager.registerDriver(driver); 我们可以省略不写,同样可以成功,因为在Driver中有static代码块:
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
该静态代码块在加载时就会为我们执行Driver的注册操作。
另外对于new Driver() 其实写其的主要目的是将Driver这个驱动类加载到内存当中,我们在学习反射时,知道常用的三种方式将类加载到内存中:
Driver driver = new Driver();
Class.forName("com.mysql.jdbc.Driver");
ClassLoader.getSystemClassLoader().loadClass("com.mysql.jdbc.Driver");
上面这三种加载方式中,我们最常用的就是 Class.forName(“com.mysql.jdbc.Driver”); 以后我们加载驱动时就用该代码。
2.DriverManager类
该类主要用于管理驱动的类,我们通过该类调用getConnection()方法来获得相关的数据库连接。当然在我们注册驱动(加载Driver时自动注册)之后、获取连接之前需要获取连接mysql的相关信息,这里的getConnection参数就提示了我们要填写的信息:
@CallerSensitive
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()));
}
这里需要我们填写的分别是:
- url:
填写要连接的数据库地址 jdbc:mysql://localhost:3306/zjdata 其中jdbc:mysql代表协议字段,localhost:3306代表自己数据库连接地址,这里指的是本机数据库,zjdata是要连接的数据库名称 - user:数据库登录名
- password:数据库登录密码
我们将这些参数设置过后,就能正常获得我们的连接对象connection了!!
另外提一下:我们现在写的Driver路径、url、user、password都高度耦合在代码当中,这样其实是非常不好的,我们如果要修改就会很麻烦,所以建议通过properties文件来写这些需要手动输入的东西。 在resource目录下建立jdbc.xml:
url=jdbc:mysql://localhost:3306/zjdata
driverClassName=com.mysql.jdbc.Driver
username=root
password=12345678
在java代码中,我们通过properties类来读取properties文件来加载相关信息:
InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
String driverClassName = properties.getProperty("driverClassName");
String url = properties.getProperty("url");
Class.forName(driverClassName);
Connection connection = DriverManager.getConnection(url, username, password);
以上通过文件的方式减少了代码耦合性。
3.Connection
该连接对象其实表示的是mysql客户端与mysql服务端的一个连接,通常只有我们只有我们获取了对象后才能真正的使用sql相关语句和事务相关操作。获取该对象主要通过DriverManager获取或者通过数据库连接池对象(后面介绍)来获取
4.statement与PreparedStatement的区别
当我们获取了连接对象后,我们就可以通过connection调用方法来获取statement对象:
- statement:不支持预编译,只能通过拼串的方式来解决占位问题,例如:
public void insert delete(String id,Statement statement){
String sql="delete from studen where id="+id;
statement.execute(sql);
}
其中进行了拼串操作,也会有一定的开销。 另外statement也会出现sql注入问题:
public void insert delete(String id,Statement statement){
String sql="select id,name,age,sex from studen where id="+id;
statement.execute(sql);
}
所以在我们平时的开发中,不会使用statement二而是使用preparedStatement,其采用了预编译的机制,可以有效解决预编译的问题:
PreparedStatement ps=connection.prepareStatement("delete from student where id=?");
ps.setObject(1,3);
boolean flag= ps.excute();
上面我们看到了PreparedStatement的两个方法:
- setObject(int parameterIndex, Object x) 用于设置占位符的值
- excute() 执行预编译的语句,返回true则表示使用select返回了结果集,返回false则表示返回了影响行数。
接下来我们详细介绍更改数据库和查询数据库的具体操作以及相关Api:
3.1 更改操作
更改操作包括增删改: 我们调用预编译对象的excuteupdate()方法来返回影响行数。
int i=ps.excuteupde()
3.2 查询语句
其实更改操作都是比较简单的,下面介绍一下相对较为复杂的查询语句: 调用预编译对象的excuteQuery() 方法,返回的结果集 ResultSet:
ResultSet resultSet = preparedStatement.executeQuery();
这个结果集对象可能包含了多行数据,我们怎么在java中通过ResultSet获取所有的查询信息呢? 首先我们要创建一个实体类对象来接受我们要查询的数据:
List<Student> list=new ArrayList<Student>();
ResultSetMetaData metaData = resultSet.getMetaData();
int len=metaData.getColumnCount();
while(resultSet.next()){
Student student = new Student();
for (int i = 0; i < len; i++) {
Object object = resultSet.getObject(i + 1);
String columnLabel = metaData.getColumnLabel(i + 1);
Field declaredField = student.getClass().getDeclaredField(columnLabel);
declaredField.setAccessible(true);
declaredField.set(student,object);
list.add(student);
}
}
上面代码的思路就是首先由一个实体类Student来接受数据(注意该实体类必须对象表的各个字段类型和名称要相等),之后通过结果集获得元数据对象metadata,该对象主要是封装了返回数据的各个字段信息,包括列名、列数量等,我们获取了列名后,就可以结果集获取相应的列的数据,然后通过反射机制,强制修改student对象中的属性(即赋值),这样循环下去,我们就可以获取多个对象对应的多个数据!
5.ResultSet
6.ResultSetMetaData
四.JDBC 自定义封装工具类及方法
----------------------------分割线------------------------------------------------------------- 2021/12/16 剩下文章后续继续更新,后面我们将自己建立一个工具类来封装之前的代码,增加可用性
|