史上最详细的JDBC入门教程(初学者必看)

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,本文中的代码都是针对MySQL数据库实现的。

内容很充实,如果你能够仔细阅读完这篇文章,JDBC的相关知识我想你一定会有所掌握。在阅读的过程中,有任何不理解的地方都欢迎留言讨论。


JDBC介绍

JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,本文中的代码都是针对MySQL数据库实现的。

图片.png

JDBC编程步骤

1.装载相应数据库的JDBC驱动并进行初始化


  • 导入专用的jar包(不同的数据库需要的jar包不同)

访问MySQL数据库需要用到第三方的类,这些第三方的类,都被压缩在一个.Jar的文件里。mysql-connector-java-5.0.8-bin.jar包可以在网上下载,或者在MySQL的安装目录下找到。通常下载到该jar包之后将其放到在项目的lib目录下,在本例就会放在E:\project\j2se\lib 这个位置,然后在eclipse中导入这个jar包。


导包步骤: 右键project->property->java build path->libaries->add external jars

图片.png

如果没有完成上述步骤的导包操作,后面会抛出ClassNotFoundException

  • 初始化驱动

通过初始化驱动类com.mysql.jdbc.Driver,该类就在 mysql-connector-java-5.0.8-bin.jar中。如果你使用的是oracle数据库那么该驱动类将不同。

注意:Class.forName需要捕获ClassNotFoundException.


try {
        Class.forName("com.mysql.jdbc.Driver");        
        } catch (ClassNotFoundException e) {                 
            e.printStackTrace();
        }

Class.forName是把这个类加载到JVM中,加载的时候,就会执行其中的静态初始化块,完成驱动的初始化的相关工作。


若有疑问可观看视频教程:https://www.bilibili.com/video/BV1Bt41137iB


2.建立JDBC和数据库之间的Connection连接

这里需要提供:数据库服务端的IP地址:127.0.0.1 (这是本机,如果连接其他电脑上的数据库,需填写相应的IP地址)

                        数据库的端口号: 3306 (mysql专用端口号)

                        数据库名称 exam(根据你自己数据库中的名称填写)

                        编码方式 UTF-8

                        账号 root

                        密码 admin(如果你在创建数据库的时候没有使用默认的账号和密码,请填写自己设置的账号和密码)

Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8", "root", "admin");

Connection是与特定数据库连接回话的接口,使用的时候需要导包,而且必须在程序结束的时候将其关闭。getConnection方法也需要捕获SQLException异常。


因为在进行数据库的增删改查的时候都需要与数据库建立连接,所以可以在项目中将建立连接写成一个工具方法,用的时候直接调用即可:

/**
     * 取得数据库的连接
     * @return 一个数据库的连接
     */
public static Connection getConnection(){
        Connection conn = null;
         try {
                 //初始化驱动类com.mysql.jdbc.Driver
                Class.forName("com.mysql.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8","root", "admin");
                //该类就在 mysql-connector-java-5.0.8-bin.jar中,如果忘记了第一个步骤的导包,就会抛出ClassNotFoundException
            } catch (ClassNotFoundException e) {                 
                e.printStackTrace();
            }catch (SQLException e) {                            
                e.printStackTrace();
            }
         return conn;
    }

3.创建Statement或者PreparedStatement接口,执行SQL语句

  • 使用Statement接口

Statement接口创建之后,可以执行SQL语句,完成对数据库的增删改查。其中 ,增删改只需要改变SQL语句的内容就能完成,然而查询略显复杂。在Statement中使用字符串拼接的方式,该方式存在句法复杂,容易犯错等缺点,具体在下文中的对比中介绍。所以Statement在实际过程中使用的非常的少,所以具体的放到PreparedStatement那里给出详细代码。

字符串拼接方式的SQL语句是非常繁琐的,中间有很多的单引号和双引号的混用,极易出错。

Statement s = conn.createStatement();
// 准备sql语句
// 注意: 字符串要用单引号'
String sql = "insert into t_courses values(null,"+"'数学')";
//在statement中使用字符串拼接的方式,这种方式存在诸多问题
s.execute(sql);
System.out.println("执行插入语句成功");
  • 使用PreparedStatement接口

与 Statement一样,PreparedStatement也是用来执行sql语句的与创建Statement不同的是,需要根据sql语句创建PreparedStatement。除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。

给数据库中添加课程: (以下代码中最后关闭资源的两个方法 DbUtil.close(pstmt); DbUtil.close(conn); 和上面的建立连接的方法是一样的,是在工具类中定义了的关闭方法,下文会给出其代码)

/**
     * 添加课程
     * @param courseName 课程名称
     */
    public void addCourse(String courseName){
        String sql = "insert into t_course(course_name) values(?)";  
 //该语句为每个 IN 参数保留一个问号(“?”)作为占位符
        Connection conn = null;                //和数据库取得连接
        PreparedStatement pstmt = null;        //创建statement
        try{
            conn = DbUtil.getConnection();
            pstmt = (PreparedStatement) conn.prepareStatement(sql);
            pstmt.setString(1, courseName); //给占位符赋值
            pstmt.executeUpdate();            //执行
        }catch(SQLException e){
            e.printStackTrace();
        }
        finally{
            DbUtil.close(pstmt);
            DbUtil.close(conn);        //必须关闭
        }
    }

对数据库中的课程进行删除:

/**
     * 删除课程
     * @param courseId
     */
    public void delCourse(int courseId){
        String sql = "delete from t_course where course_id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = DbUtil.getConnection();
            pstmt = (PreparedStatement) conn.prepareStatement(sql);
            pstmt.setInt(1, courseId);
            pstmt.executeUpdate();
        } catch (SQLException e) {
            // TODO: handle exception
            e.printStackTrace();
        }finally{
            DbUtil.close(pstmt);
            DbUtil.close(conn);        //必须关闭
        }
    }

对数据库中的课程进行修改:

/**
     * 修改课程
     * @param courseId
     * @param courseName
     */
    public void modifyCourse(int courseId,String courseName){
        String sql = "update t_course set course_name =? where course_id=?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = DbUtil.getConnection();
            pstmt = (PreparedStatement) conn.prepareStatement(sql);
            pstmt.setString(1, courseName);  //利用Preparedstatement的set方法给占位符赋值
            pstmt.setInt(2, courseId);
            pstmt.executeUpdate();
        } catch (SQLException e) {
            // TODO: handle exception
            e.printStackTrace();
        }finally{
            DbUtil.close(pstmt);
            DbUtil.close(conn);        //必须关闭
        }
    }

由上面的增删改程序可以看出,他们的代码都是大同小异的,主要是SQL语句存在差异,其他的地方几乎是完全相同的。其中有几个地方需要注意:


使用PreparedStatement时,他的SQL语句不再采用字符串拼接的方式,而是采用占位符的方式。“?”在这里就起到占位符的作用。这种方式除了避免了statement拼接字符串的繁琐之外,还能够提高性能。每次SQL语句都是一样的,java类就不会再次编译,这样能够显著提高性能。

String sql = "update t_course set course_name =? where course_id=?";

后面需要用到PreparedStatement接口创建的pstmt的set方法给占位符进行赋值。注意一点,这里的参数索引是从1开始的。

pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setString(1, courseName);  //利用Preparedstatement的set方法给占位符赋值
pstmt.setInt(2, courseId);
pstmt.executeUpdate();
  1. 增删改都使用pstmt.executeUpdate();语句进行SQL语句的提交 ,下文的查询会有所不同,请注意。
  2. 在添加的过程的,如果添加的数据量比较大的话,可以用批量添加。  PreparedStatement接口提供了相应的批量操作的方法。
for(int i=1;i<100;i++){
     pstmt.setInt(1,8000+i);
     pstmt.setString(2,"赵_"+i);
     pstmt.addBatch();
//批量更新
     if(i%10==0){
     pstmt.executeBatch();
    }
}

下面我们来看稍显麻烦一点的查询操作:

/**
     * 查询课程
     * @return
     */
    public List<Course> findCourseList(){
        String sql = "select * from t_course order by course_id";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        //创建一个集合对象用来存放查询到的数据
        List<Course> courseList = new ArrayList<>();
        try {
            conn = DbUtil.getConnection();
            pstmt = (PreparedStatement) conn.prepareStatement(sql);
            rs = (ResultSet) pstmt.executeQuery();
            while (rs.next()){
                int courseId = rs.getInt("course_id");
                String courseName = rs.getString("course_name");
                //每个记录对应一个对象
                Course course = new Course();
                course.setCourseId(courseId);
                course.setCourseName(courseName);
                //将对象放到集合中
                courseList.add(course);
            }
        } catch (SQLException e) {
            // TODO: handle exception
            e.printStackTrace();
        }finally{
            DbUtil.close(pstmt);
            DbUtil.close(conn);        //必须关闭
        }
        return courseList;
    }

查询操作使用executeQuery()进行更新。其他相关的问题放在第四步(处理和显示结果)中解释。

4.处理和显示结果

执行查询语句,并把结果集返回给集合ResultSet

ResultSet rs = s.executeQuery(sql);

利用While(ResultSet.next()){…}循环将集合ResultSet中的结果遍历出来。


ResultSet.getXX(); 这里的get方法的括号里面可以填属性值,如下图代码中的course_id,还可以填该属性在数据表中的列号,从1开始编码,例如:course_id在我的t-courses数

据表中位于第一列,所以执行get方法的时候,我除了代码段中写法外,还可以这样写int courseId = rs.getInt(1);但是不推荐使用列号的这种方式,因为一段数据表中个属性值得顺序发生变化,就会导致这里出错,而使用属性名则不会出现这样的问题。

while (rs.next()){
        int courseId = rs.getInt("course_id");
        String courseName = rs.getString("course_name");
        //每个记录对应一个对象
        Course course = new Course();
//在我的项目中创建了course类,其中定义了set方法,所以这里将查询到的值传给了course,也可以直接打印到控制台
        course.setCourseId(courseId);
        course.setCourseName(courseName);
        //将对象放到集合中
        courseList.add(course);
        }

还有一点需要说明的是:

       因为在我的项目中创建了course类,其中定义了set方法,所以这里将查询到的值传给了course,你也可以直接用打印语句将CourseId和CourseName打印到控制台。

course.setCourseId(courseId);
 course.setCourseName(courseName); 

5.释放资源

在JDBC编码的过程中我们创建了Connection、ResultSet等资源,这些资源在使用完毕之后是一定要进行关闭的。关闭的过程中遵循从里到外的原则。因为在增删改查的操作中都要用到这样的关闭操作,为了使代码简单,增加其复用性,这里我将这些关闭的操作写成一个方法和建立连接的方法一起放到一份工具类中。

/**
     * 封装三个关闭方法
     * @param pstmt
     */
    public static void close(PreparedStatement pstmt){
        if(pstmt != null){                        //避免出现空指针异常
            try{
                pstmt.close();
            }catch(SQLException e){
                e.printStackTrace();
            }
        }
    }
    public static void close(Connection conn){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
    }
    public static void close(ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
    }

JDBC编程的内容就这些了,如果你已经全部掌握,还有事务、获取自增、获取元数据、ORM、DAO、数据连接池等内容可以自行了解一下。

另外,Statement和PreparedStatement的异同,execute和executeUpdate的区别等内容,这里做一些介绍。


Statement和PreparedStatement的异同及优缺点

:两者都是用来执SQL语句的

:PreparedStatement需要根据SQL语句来创建,它能够通过设置参数,指定相应的值,不是像Statement那样使用字符串拼接的方式。

PreparedStatement的优点

  • 其使用参数设置,可读性好,不易记错。在statement中使用字符串拼接,可读性和维护性比较差。
  • 其具有预编译机制,性能比statement更快。
  • 其能够有效防止SQL注入攻击。


execute和executeUpdate的区别

相同点:二者都能够执行增加、删除、修改等操作。

不同点

  • execute可以执行查询语句,然后通过getResult把结果取出来。executeUpdate不能执行查询语句。
  • execute返回Boolean类型,true表示执行的是查询语句,false表示执行的insert、delete、update等。executeUpdate的返回值是int,表示有多少条数据受到了影响。

关于JDBC的介绍就这么多了!!!

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
5月前
|
设计模式 Java 数据库
持续霸榜GitHub的面试神器:字节跳动Java面试参考手册,限时开源
最近又赶上跳槽的高峰期(招聘旺季),好多读者都问我有没有面试字节的神器,我苦苦寻到了一份内部资料《2023字节跳动Java面试参考手册(第二期)》。
|
6月前
|
设计模式 前端开发 JavaScript
前端Web开发学习,入门到进阶,推荐几本很不错的书籍
前端Web开发学习,入门到进阶,推荐几本很不错的书籍
95 0
|
8月前
|
存储 分布式计算 Serverless
阿里P8大牛手写《分布式系统手册》Github一天星标就超60K
前言 自20世纪40年代计算机诞生以来,及互联网呈现出高速发展的趋势,(互联网理财,移动支付,短视频应用,直播)计算机以及互联网已经深刻影响了人们的生活和工作的方方面面。 而这一切都离不开背后那个神秘的“巨人”——分布式系统。
|
9月前
|
SQL Oracle Java
JDBC 拾枝杂谈—入门篇(通俗易懂)
JDBC 第一节 拾枝杂谈 带你快速入门!
115 0
|
存储 Java 数据安全/隐私保护
Java开发必学知识合集(一)
Java开发过程中,常用且重要的知识点是开发中最基本的,尤其是刚接触Java开发不久的开发者来说,基本功很重要,是直接做Java工作的基本,那么本文就来分享一下在Java开发过程中的常用的知识点,由于篇幅问题,这里只分享一部分内容,没有分享到的会在后面的博文中分享。
75 0
Java开发必学知识合集(一)
|
前端开发 JavaScript 算法
java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
目录前言1. 学习路线2. 学习方法 前言 这篇文章主要总结我之前所学过的框架以及学习路线 从实打实的零基础到框架再到项目 之后实习工作 也在这篇博客中记录我的学习笔记 以及在笔记中遇到的配置安装、代码bug等错误 都从零开始摸索并且记录了下来 后期也会一直实时更新 一键三连防丢失 直奔主题 1. 学习路线 作为刚开始的程序员小白,你可能想成为一名大佬,但你连基本的程序、命令等都不懂,不用慌,学就完事了,大家都是这么过来的 以下文章链接都是实打实的记录学术博客 你可以通过我的文章进行巩固学习 在每篇博客中还
225 0
|
SQL 自然语言处理 Oracle
专为初学者打造的JDBC基础教程【详细步骤!!!】
JDBC是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,本文中的代码都是针对MySQL数据库实现的。
168 0
专为初学者打造的JDBC基础教程【详细步骤!!!】
|
SQL Java 数据库连接
老杜JDBC基础入门视频教程,轻松掌握jdbc基础+核心技术(来自动力节点)
JDBC是Sun提供的一套数据库编程接口API函数,由Java语言编写的类、界面组成。 JDBC API 的设计初衷就是为了让简单的事情更简单,这意味着JDBC使得执行所有数据库任务都更容易. 用JDBC写的程序能够自动地将SQL语句传送给相应的数据库管理系统。不但如此,使用Java编写的应用程序可以在任何支持Java的平台上运行,不必在不同的平台上编写不同的应用。 Java和JDBC的结合可以让开发人员在开发数据库应用程序时真正实现“WriteOnce,RunEverywhere!”
201 0
|
设计模式 算法 安全
强烈推荐10本程序员必读的书
强烈推荐10本程序员必读的书
101 0
|
自然语言处理 Java 程序员
JavaWeb学习之路(2)--了解编程语言
本文目录 1. 什么是语言 2. 自然语言的特点 3. 编程语言的特点 4. 编程语言的江湖 5. 规则不可避免 6. 小结