文章目录
- 1. 前言
- 2. JDBC 概述
- 2.1 概念
- 2.2 优点
- 3. JDBC 快速入门
- 4. JDBC API详解
- 4.1 DriverManager
- 4.1.1 注册驱动
- 4.1.2 获取连接
- 4.2 Connection
- 4.2.1 获取执行sql的对象
- 4.2.2 事务管理
- 4.3 Statement
- 4.4 ResultSet
- 4.5 PreparedStatement
- 4.5.1 sql注入问题
- 4.5.2 preparedStatement 原理
- 5. 数据库连接池
- 5.1 概念
- 5.2 实现
- 6. Druid 连接池的使用
- 7. 准备工作
- 8. 实战案例
- 9. 增删改查操作练习
- 9.1 查询所有
- 9.2 添加数据
- 9.3 修改数据
- 9.4 删除数据
- 10. 总结
- 橙子精品文章学习推荐
1. 前言
大家好,我是橙子。最近又肝了几个大夜,总结了 JDBC 完整版的基础教程和实战案例训练。快来看看这些 Java 基础性的代码你有没有忘记?
在 Java 开发中,使用 Java 语言操作数据库是非常重要的一部分,那么 Java 语言是如何操作数据库的呢?
我们需要使用不同厂商的数据库时,例如 MySQL,Oracle 等,显然一套 Java 代码是不能同时操作不同的数据库的,那么怎样实现一套 Java 代码对不同的数据库的操作呢?
JDBC 应运而生,JDBC 是使用 Java 语言操作关系型数据库的一套 API,其中定义了对数据库操作的规范,
这里的规范在 Java 中就是指接口,不同的数据库厂商提供了不同的 JDBC 实现类,称为驱动,使用时,只需要导入需要的数据库驱动 jar 包,便可以操作不同的关系型数据库,其实际是使用了 jar 包中的实现类。
本系列文章的学习目标:
- 什么是 JDBC,如何使用?
- 使用 JDBC 完成数据的增删改查操作
- JDBC API 熟练使用
学会使用Java代码操作数据库,本文是2 万字学习教程,阅读本文需要20 分钟,建议收藏长期学习使用。
2. JDBC 概述
2.1 概念
JDBC , Java DataBase Connectivity
, 是使用 Java 语言操作关系型数据库的一套API。本质上来说,JDBC 中定义了一套操作关系型数据库的规范,但是我们不能直接使用这套接口来操作数据库,于是各大数据库厂商提供了 JDBC 不同的实现类,称为驱动,例如 MySQL 驱动,此时,我们只需要操作 JDBC 规范好的代码便可以完成对数据库的操作。在实现对数据库的操作时,其底层是使用了 jar 包中定义的实现类。
当我们使用不同的数据库时,例如测试时使用 MySQL 数据库,部署时使用 Oracle 数据库,只需要编写一套 Java 代码便可以实现对不同关系型数据库的操作。
2.2 优点
使用 JDBC 操作关系型数据库时,各大数据库厂商提供了不同的实现类,我们不需要针对不同的数据库进行单独开发,因此,我们也可以随时替换数据库,而不用大量修改 Java 代码。
我们只需要在使用时导入需要使用的数据库对应的驱动 jar包到项目中,便可以实现对指定数据库的操作,使 Java 操作数据库变得轻松便捷。
下图就是MySQL的驱动jar包:
不同版本的 jar 包可以在官网下载。
3. JDBC 快速入门
使用 Java 语言操作数据库,实际上就是 Java 代码将 sql 语句发送到 MySQL 数据库服务端,MySQL 服务端接收并执行 sql 语句,同时返回一个执行结果,最后该结果会发送到 Java 代码进行处理。
对数据库的操作大致分为以下几个步骤:
- 注册驱动
- 获取连接
- 编写sql
- 获取执行sql对象
- 执行sql
- 处理返回结果
- 释放资源
在进行编码之前,需要先创建工程,导入数据库的驱动 jar 包。这里以 JDBC 操作 MySQL 数据库为例,步骤如下:
创建空项目,定义项目名称及路径:
在File / Project Structure 中进行项目设置,JDK版本,编译版本等:
创建新模块,指定名称及位置:
在 jdbc-demo 模块中新建 Lib 文件夹:
将下载好的 MySQL驱动 jar 包导入 Lib 目录下作为库文件:
右键 MySQL 驱动 jar 包,点击 Add as Library ,并选择模块下有效:
创建好工程以后,就可以编写代码操作数据库啦!
在 idea 中右键 src / new / Java Class ,输入类名 JDBCDemo 创建一个新的类。
第一步:注册驱动
//1.注册驱动Class.forName("com.mysql.jdbc.Driver");
目的是把 Driver 类加载到内存中。其实,Java 中用于注册驱动的是 registerDriver() 方法,而在 Driver 类中使用了该方法,所以只要把 Driver 类加载到内存中 ,包含 registerDriver 方法的静态代码块就会执行,驱动就会被注册。我们可以查看JDK源码中的Driver 类:
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!"); } }}
第二步:获取连接
String url="jdbc:mysql://localhost:3306" /> 执行 DQL 语句时需要使用下面的方法:
ResultSet excuteQuery(String sql)//执行sql语句完成查询操作,返回单个ResultSet对象
该方法在下面 ResultSet类中讲解。
4.4 ResultSet
ResultSet
,结果集对象类,其作用是封装 sql 查询语句的结果。执行了 DQL 查询语句后就会返回该类的对象,执行 DQL 语句的方法如下:
ResultSet excuteQuery(String sql)//执行sql语句完成查询操作,返回单个ResultSet对象
ResultSet 类提供了操作查询结果数据的方法,如下:
/*作用:将指针从当前位置移动到下一行,然后这一行是否为有效行返回值:true 当前行为有效行,false 当前行没有数据*/boolean next()/*作用:获取数据 xxx表示数据类型 例如 int getInt() 参数:int类型的参数表示列的编号,这个编号从0开始;String类型的参数,表示列的名称*/xxx getXxx(参数)
操作查询结果数据的方法如下图:
一开始指针位于第一行前,如图所示红色箭头指向于表头行。当我们调用了 next()
方法后,指针就下移到第一行数据,并且方法返回 true,此时就可以通过 getInt("id")
获取当前行 id 字段的值,也可以通过 getString("name")
获取当前行 name 字段的值。如果想获取下一行的数据,继续调用 next()
方法,以此类推。获取某个字段的值时,既可以传入 int 类型,即列的编号,也可以传入列对应的字段名。
示例,查询 account 表中数据,并且打印所有结果:
public class JDBCDemo { public static void main(String[] args) throws Exception { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写 String url = "jdbc:mysql://localhost:3306/db1" /> 为了解决这个问题,出现了 preparedStatement ,该类用于预编译 sql 语句并执行,其优点是可以防止sql 注入,并且预编译sql提高了性能。其实底层是将特殊字符进行了转义,转义的 sql 如下:
select * from tb_user where username = 'lisi' and ppassword = '\'or \'1\' = \'1'
示例:
public class JDBCDemo { public static void main(String[] args) throws Exception { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2. 获取连接 String url="jdbc:mysql://localhost:3306/db1?useSSL=false"; String username="root"; String ppassword="abc123"; Connection conn = DriverManager.getConnection(url,username,password); //Java代码接收到客户端发送的用户名和密码 String name="lisi"; String ppwd="' or '1' ='1"; //3. 定义sql String sql="select * from tb_user where username=? and password=?"; //4. 获取执行sql的对象psmt PreparedStatement psmt = conn.prepareStatement(sql); //设置问号的值 psmt.setString(1,name); psmt.setString(2,pwd); //5. 执行sql ResultSet rs = psmt.executeQuery();//此时不再需要传入sql语句 //6. 处理返回结果 if(rs.next()){ System.out.println("yes"); }else{ System.out.println("no"); } //7. 释放资源 rs.close(); psmt.close(); conn.close(); }}
4.5.2 preparedStatement 原理
前面使用 preparedStatement
解决了 sql 注入的问题,其实我们还没有开启预编译的功能,JDBC 中是如何通过预编译来提高性能的呢?
要学习 prepareStatement 实现预编译的原理,首先要明白 Java 操作数据库的步骤:
首先 Java 代码将 sql 发送到 MySQL 服务端,MySQL 服务端接收到 sql 语句以后,会对 sql 语句进行检查(检查 sql 语句的语法),编译(编译 sql 语句,将 sql 语句编译成可执行的函数),执行的操作。而检查和编译 sql 语句花费的时间往往较长,如果想要提高 sql 的性能,就可以从这方面入手。在使用预编译的方法时,检查和编译 sql 语句的操作将会在获取执行 sql 的对象时完成,并且不会重复执行,从而提高了性能。
要想打开预编译的功能,就需要在 url 中设置如下的参数:
useServerPrepStmts=true //参数开启预编译功能
示例:
public class JDBCDemo { public static void main(String[] args) throws Exception { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2. 获取连接 String url="jdbc:mysql://localhost:3306/db1" /> 我们使用数据库连接池来重复利用数据库的连接对象,即 Connection 类对象。
数据库连接池是一个负责分配,管理数据库连接对象的容器,它允许应用程序重复使用同一个数据库连接对象。数据库连接池可以释放空闲时间超过最大空闲时间的数据库连接对象来避免因为没有释放数据库连接对象而引起的数据库连接遗漏。
连接池是在一开始就创建好了一些连接对象存储起来,用户需要连接数据库时,不需要自己创建连接对象,而只需要从连接池中获取一个连接对象进行使用,使用完毕后再将连接对象归还给连接池。这样就可以起到资源重用的作用,也节省了频繁创建连接销毁连接所花费的时间,从而提升了系统响应的速度。
总结来说使用数据库连接池有以下几点好处:
- 实现资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
5.2 实现
sun 公司提供了数据库连接池的标准接口 DataSource
,我们一般使用第三方实现该接口的实现类,所有实现类都继承了其获取连接的方法:
Connection getConnection()
常见的数据库连接池有:
- Druid(德鲁伊)
- C3P0
- DBCP
使用了数据库连接池以后,在获取数据库连接对象时不需要通过 DriverManager类的 getConnection() 方法,而是直接从数据库连接池中获取。我们今天要使用的是 Druid 连接池,它是阿里巴巴开源的数据库连接池项目,其功能强大,性能优秀,使用占比高,是一款优秀的数据库连接池。
6. Druid 连接池的使用
下面以 Druid 连接池为例,讲解通过数据库连接池获取数据库连接对象,主要有以下几个步骤:
- 导入Druid 连接池的 jar 包
- 定义配置文件
- 加载配置文件
- 获取数据库连接池对象
- 获取连接
第一步:将 Druid 的 jar 包放入项目中的 Lib 目录下作为库文件,jar 包自行下载。
选择 Add as Library,选择模块下有效:
第二步:编写配置文件,在 src 目录下创建文件 druid.properties
。
driverClassName=com.mysql.jdbc.Driverurl=jdbc:mysql:///db1" /> 其中,DruidDataSourceFactory 类中的 createDataSource()
方法既可以传入一个 Map 集合,也可以传入 prop 对象,其中都存放配置信息,用来获取连接池对象。
小tips:如果代码中文件的相对路径报错,可以使用 System.getProperty("user.dir")
获取项目的当前路径。
7. 准备工作
前面我们说过,在 Java 代码中,执行 sql 查询语句以后返回一个 ResultSet 类的对象,并且 ResultSet 类提供了方法让我们可以操作查询结果数据,例如可以直接打印所有查询的数据。
示例:
public class JDBCDemo { public static void main(String[] args) throws Exception { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2. 获取连接 String url="jdbc:mysql://localhost:3306/blog" /> 显然这样并不是我们想要的效果,数据零散不好处理。所以我们可以把每条数据封装为一个实体类的对象,每个对象对应数据库表的一条记录,并且把每个对象放到集合中存储。练习使用的数据库表:
drop table if exists student;create table student(id int primary key auto_increment,name varchar(10),gender char(1));insert into student(name,gender) values('张三','男'),('李四','女'),('王五','男');
在 Navicat 中查看数据表:
8. 实战案例
查询学生信息表数据,封装为 Student 类对象,并存放在 ArrayList 集合中。
思路:要解决这个问题,大概分为以下几个步骤:
- 创建一个 Student 实体类
- 查询数据库中的数据,并且赋值给对象的属性
- 将对象存储到 ArrayList 集合中
第一步:创建一个Student 实体类,并定义 set() 方法,重写 Object 类中的 toString 方法,方便查看打印效果。
public class Student { private int id; private String name; private String gender; public int getId() { return id; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", gender='" + gender + '\'' + '}'; }}
小tips :在 idea 中使用 Alt + Inset 快捷键可以快速给类添加或重写一些方法,例如 get() ,set() ,toString() 方法等。使用 Ctrl + 鼠标左键可以快速选择多项方法。
第二步:使用 Java 代码操作数据库,查询数据库表中所有学生信息并通过 set() 方法赋值给 Student 类的对象,将对象存储到集合中。
public class JDBCDemo { public static void main(String[] args) throws Exception { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2. 获取连接 String url = "jdbc:mysql://localhost:3306/blog" /> 这样,我们的程序就达到了预期的效果,将来需要使用的数据全部封装为对象并存放在了集合中。
9. 增删改查操作练习
在数据库连接池入门篇中,我们学习了 Druid 连接池的使用,数据库连接池允许重复使用一个现有的数据库连接对象,提升了系统的响应速度和时间。下面我们使用数据库连接池来练习解决上面的问题,并且在获取 sql 执行对象时,我们使用 PreparedStatement 类,解决sql 注入的问题。
9.1 查询所有
查询所有数据,并把查询结果数据封装为对象存储在集合中,这里的数据表,Student 实体类和上面例子中相同。
public class DruidDemo { public static void main(String[] args) throws Exception { //加载配置文件 Properties prop = new Properties(); prop.load(new FileInputStream("jdbc-demo/src/druid.properties")); //获取数据库连接池对象 DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); //获取数据库连接对象 Connection conn = dataSource.getConnection(); //定义sql String sql = "select * from student"; //获取 sql 执行对象 PreparedStatement pstmt = conn.prepareStatement(sql); //执行 sql ResultSet rs = pstmt.executeQuery(); //处理数据,将查询结果数据封装为对象存储在集合中 List<Student> students = new ArrayList<>(); while (rs.next()) { Student s = new Student(); int id = rs.getInt(1); String name = rs.getString(2); String gender = rs.getString(3); s.setId(id); s.setName(name); s.setGender(gender); students.add(s); } System.out.println(students); //释放资源 rs.close(); pstmt.close(); conn.close(); }}
9.2 添加数据
现在演示往数据库中添加一条记录,应用场景为用户在客户端输入一条数据时,我们需要将数据添加到数据库。示例:
public class DruidDemo { public static void main(String[] args) throws Exception { //接收到用户的数据 int id=4; String name="小美"; String gender="女"; //加载配置文件 Properties prop = new Properties(); prop.load(new FileInputStream("jdbc-demo/src/druid.properties")); //获取数据库连接池对象 DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); //获取数据库连接对象 Connection conn = dataSource.getConnection(); //定义sql String sql = "insert into student values(?,?,?)"; //获取 sql 执行对象 PreparedStatement pstmt = conn.prepareStatement(sql); //设置参数 pstmt.setInt(1,id); pstmt.setString(2,name); pstmt.setString(3,gender); //执行 sql int count = pstmt.executeUpdate();//返回受影响的行数 //处理数据 if(count>0){ System.out.println("添加成功"); }else{ System.out.println("添加失败"); } //释放资源 pstmt.close(); conn.close(); }}
此时的数据表:
9.3 修改数据
现在数据表中的数据,应用场景为用户在客户端修改数据,对应数据库中的数据也要完成修改。示例:
public class DruidDemo { public static void main(String[] args) throws Exception { //接收到用户的数据 int id=1; String name="小王"; String gender="女"; //加载配置文件 Properties prop = new Properties(); prop.load(new FileInputStream("jdbc-demo/src/druid.properties")); //获取数据库连接池对象 DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); //获取数据库连接对象 Connection conn = dataSource.getConnection(); //定义sql String sql = "update student set name=" />此时的数据表:
9.4 删除数据
下面演示删除数据,用户在客户端选择删除某条数据记录时,数据库中的数据也要完成删除操作。示例:
public class DruidDemo { public static void main(String[] args) throws Exception { //接收到用户的数据 int id=4; //加载配置文件 Properties prop = new Properties(); prop.load(new FileInputStream("jdbc-demo/src/druid.properties")); //获取数据库连接池对象 DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); //获取数据库连接对象 Connection conn = dataSource.getConnection(); //定义sql String sql = "delete from student wherealigncenter" src="https://img.maxssl.com/uploads/?url=https://img.maxssl.com/uploads/?url=https://img-blog.csdnimg.cn/img_convert/d988db44ba3d81dc726625c3b552d4c4.png" />10. 总结
学习完这篇文章,你将学会使用 Java 代码操作数据库,包括数据库的连接,创建,数据的增删改查等操作。
JDBC 作为基础性的代码,在开发中其实存在着很多的缺点,所以在开发中并不会直接使用,而一般会使用框架开发,使用框架开发不仅能够提高开发的效率,而且大大提高了代码的规范性。MyBatis 就是一款优秀的持久层框架,专门用来简化 JDBC 开发,后面我们马上会接触到,但是在这之前,一定要先熟练 JDBC 的使用。
橙子精品文章学习推荐
❓很多朋友问我:怎样系统的学习一门编程语言?怎样学好 Java?强烈推荐大家学习:
Java编程基础教程系列 快速订阅