JDBC API 万字详解(通俗易懂)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: JDBC 第二节 详解API 通俗易懂!

目录

一、前言

二、JDBC API概述

三、获取连接的三种方式

       0.朝花夕拾 :

       1.方式一 —— 通过new关键字 :

       2.方式二 —— 通过反射机制 :

       3.方式三 —— 通过DriverManager

               Δ方式三简化版

               Δ方式三优化版

四、 ResultSet

       1.简介 :

       2.代码演示 :

       3.底层实现 :

五、SQL注入

       1.什么是SQL注入?

       2.SQL注入演示 :

       3.PreparedStatement :

               ①简介

               ②牛逼之处

               ③使用演示

六、总结 :


一、前言

    • 第二节内容,up主要和大家分享一下JDBC——API方面的内容。
    • 注意事项——代码中的注释也很重要;不要眼高手低;点击文章的侧边栏目录或者文章开头的目录可以进行跳转。
    • 良工不示人以朴,所有文章都会适时补充完善。大家如果有问题都可以在评论区进行交流或者私信up。感谢阅读!

    二、JDBC API概述

          JDBC API是一系列的接口,它统一和规范了应用程序与数据库的连接,执行SQL语句并得到返回结果等各类操作,相关类和接口在java.sqljavax.sql包下

                   相关体系图如下(建议阅读完毕后返回来细品☕) :

    image.gif编辑


    三、获取连接的三种方式

          0.朝花夕拾 :

                   上一小节内容中,我们提到了编写JDBC程序的核心四部曲,这里再来回顾一下——

      •        1° 注册驱动
      •        2° 获取连接
      •        3° 执行SQL
      •        4° 释放资源

                     这里我们要重点再说一下第二个步骤——即获取数据库的连接

             1.方式一 —— 通过new关键字 :

                     这也是我们在第一小节中,演示第一个JDBC程序时用到的方法。即先通过com.mysql.cj.jdbc.Driver()来获取到Driver类对象,然后再通过Driver类中的connect方法来获取连接。connect方法的详细信息如下:

      Connection connect(String url, Properties info) :需要传入一个包含数据库信息的url字符串对象,以及一个包含登录用户信息的Properties对象。

                     这种方法有什么弊端?

                     通过new的方法获取到Driver对象,Driver对象属于第三方,并且是静态加载,导致灵活性低,依赖性强

                     up以JdbcConn类为演示类,来给大家演示一下第一种方式获取连接,其实就是把第一小节的程序演示再来一遍罢了(当然这里我们不会像第一小节讲那么细了)。

                     代码如下 :

      package api.connection;
      import com.mysql.cj.jdbc.Driver;
      import org.testng.annotations.Test;
      import java.sql.Connection;
      import java.sql.SQLException;
      import java.util.Properties;
      /**
       * @author : Cyan_RA9
       * @version : 21.0
       */
      public class JdbcConn {
      //演示JDBC连接数据库的三种方式
          //1.方式一 —— new关键字静态加载
          @Test
          public  void connection_1() throws SQLException {
              Driver driver = new Driver();
              String url = "jdbc:mysql://localhost:3306/jdbc_ex";
              Properties info = new Properties();
              info.setProperty("user","root");
              info.setProperty("password","RA9_Cyan");
              Connection connect = driver.connect(url, info);
              System.out.println("方式一获取到的连接 = " + connect);
              connect.close();
              System.out.println("--------------------------------------------------");
          }
      }

      image.gif

                    运行结果 :

      image.gif编辑

             2.方式二 —— 通过反射机制 :

                     提到了灵活性和依赖性,我们就不由得想到了反射机制。反射机制可以动态的加载和构建对象,属于动态加载,相比new关键字的方式具有更高的灵活性,同时也减低了依赖性。我们可以使用 Class.forName("com.mysql.cj.jdbc.Driver"); 来获取Driver类实例。

                     up仍然以JdbcConn类为演示类代码如下 :

      package api.connection;
      import com.mysql.cj.jdbc.Driver;
      import org.testng.annotations.Test;
      import java.sql.Connection;
      import java.sql.SQLException;
      import java.util.Properties;
      public class JdbcConn {
      //演示JDBC连接数据库的三种方式
          //2.方式二 —— 反射机制
          @Test
          public void connection_2() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
              Class<?> clazz = Class.forName("com.mysql.cj.jdbc.Driver");
              Driver driver = (Driver) clazz.newInstance();
              String url = "jdbc:mysql://localhost:3306/jdbc_ex";
              Properties info = new Properties();
              info.setProperty("user", "root");
              info.setProperty("password", "RA9_Cyan");
              Connection connect = driver.connect(url, info);
              System.out.println("方式二获取到的连接 = " + connect);
              System.out.println("--------------------------------------------------");
          }
      }

      image.gif

                     运行结果 :

      image.gif编辑

             3.方式三 —— 通过DriverManager

                    在反射机制的基础上,使用DriverManager替代Driver,进行统一管理,具有更好的拓展性。并且,单独定义url, user, password也具有更高的灵活性。

                     需要用到DriverManager类的两个方法,如下——

        1. static void registerDriver(Driver driver) : 根据传入的Driver类对象,注册Driver驱动。
        2. static Connection getConnection(String url, String user, String password) : 根据传入的数据库URL,获取数据库连接。

                       up仍然以JdbcConn类为演示类代码如下 :

        package api.connection;
        import com.mysql.cj.jdbc.Driver;
        import org.testng.annotations.Test;
        import java.lang.reflect.Constructor;
        import java.lang.reflect.InvocationTargetException;
        import java.sql.Connection;
        import java.sql.DriverManager;
        import java.sql.SQLException;
        import java.util.Properties;
        public class JdbcConn {
        //演示JDBC连接数据库的三种方式
            //方式三 —— 通过DriverManager
            @Test
            public void connection_3() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, SQLException {
                //使用反射机制加载Driver类
                Class<?> clazz = Class.forName("com.mysql.cj.jdbc.Driver");
                Constructor<?> constructor = clazz.getConstructor();
                Driver driver = (Driver) constructor.newInstance();
                //创建url,user,password
                String url = "jdbc:mysql://localhost:3306/jdbc_ex";
                String user = "root";
                String password = "RA9_Cyan";
                //注册Driver驱动
                DriverManager.registerDriver(driver);
                //获取连接
                Connection connection = DriverManager.getConnection(url, user, password);
                System.out.println("方式三获取到的连接= " + connection);
            }
        }

        image.gif

                       运行结果 :

        image.gif编辑

                      Δ方式三简化版

                       PS_1 :

                       其实,在方式三的基础上,可以进行简化——

                       通过Class.forName()方法动态加载Driver类后,不需要接收Class对象,也不需要获取构造器对象再得到Driver类对象。
                       不需要通过DriverManager类的registerDriver方法来注册Driver驱动,即不需要注册驱动,而是直接通过getConnection方法来获取连接。

                       仍然以JdbcConn类为演示类,代码如下 :

        package api.connection;
        import com.mysql.cj.jdbc.Driver;
        import org.testng.annotations.Test;
        import java.lang.reflect.Constructor;
        import java.lang.reflect.InvocationTargetException;
        import java.sql.Connection;
        import java.sql.DriverManager;
        import java.sql.SQLException;
        import java.util.Properties;
        public class JdbcConn {
        //演示JDBC连接数据库的三种方式
            //方式三 —— DriverManager(简化版)
            @Test
            public void connection_3() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, SQLException {
                Class.forName("com.mysql.cj.jdbc.Driver");
                String url = "jdbc:mysql://localhost:3306/jdbc_ex";
                String user = "root";
                String password = "RA9_Cyan";
                Connection connection = DriverManager.getConnection(url, user, password);
                System.out.println("方式三简化后得到的连接 = " + connection);
            }
        }

        image.gif

                       运行结果 :

        image.gif编辑

                       可以看到, 简化后,整个代码简洁了许多。

                       但是,这时候可能就要有p小将(Personable小将,指风度翩翩的人)出来bb问了:👴把编写JDBC程序的核心四部曲背的比家谱都熟,第一步就是注册驱动,好家伙,隔你这儿直接给省略了?给👴爬!

                       p哥先息怒,其实这里之所以能顺利获取连接,是因为jvm底层做了优化,当Driver类被动态加载时,会自动帮我们注册Driver驱动,我们查看com.mysql.cj.jdbc.Driver类的源码,可以找到一个静态代码块如下 :

        static {
                try {
                    DriverManager.registerDriver(new Driver());
                } catch (SQLException var1) {
                    throw new RuntimeException("Can't register driver!");
                }
            }

        image.gif

                       答案很明显了——当Driver类被动态加载时,静态代码块被执行。而静态代码块里的try语句中,调用了DriverManager类的registerDriver方法,完成了“注册驱动”的操作

                       还要说明一点,这种“简化版”的第三种方式,是实际开发中用到最多的。

                      PS_2 :

                       其实,在上述“简化版”的第三种方式中,就连调用forName的语句都可以省略。MySQL 5.1.6及以上版本无需使用forName语句;从JDK1.5以后使用了JDBC4,不再需要显示调用Class.forName(...)注册驱动,而是自动调用驱动,根据jar包下META-INF\services\java.sql.Driver文本中的类名称去注册,如下图所示 :

        image.gif编辑

                       但是,就像我们上面说的那样,“简化版”的方式三是实际开发中用到最多的方式,因此还是建议大家写上,以更明确。

                       Δ方式三优化版

                       在简化版的基础上,我们可以将url, user,以及password中的各种信息,诸如端口,数据库,用户名和用户密码等保存到properties配置文件中,使得我们的操作更加快捷和灵活。

                       up先在JdbcConn类本包下,创建一个mysql.properties文件,如下图所示 :

        image.gif编辑

                       JdbcConn类代码如下 :

        package api.connection;
        import com.mysql.cj.jdbc.Driver;
        import org.testng.annotations.Test;
        import java.io.FileInputStream;
        import java.io.FileNotFoundException;
        import java.io.IOException;
        import java.lang.reflect.Constructor;
        import java.lang.reflect.InvocationTargetException;
        import java.sql.Connection;
        import java.sql.DriverManager;
        import java.sql.SQLException;
        import java.util.Properties;
        public class JdbcConn {
        //演示JDBC连接数据库的三种方式
            //方式三 —— DriverManager
            @Test
            public void connection_3() throws ClassNotFoundException, SQLException, IOException {
                //通过Properties对象获取配置文件信息
                Properties properties = new Properties();
                properties.load(new FileInputStream("src/api/connection/mysql.properties"));
                //通过获取到的配置文件信息,得到对应的值
                String driver = properties.getProperty("driver");
                String url = properties.getProperty("url");
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
                //注册驱动
                Class.forName(driver);
                //获取连接
                Connection connection = DriverManager.getConnection(url, user, password);
                System.out.println("方式三优化后得到的连接 = " + connection);
            }
        }

        image.gif


        四、 ResultSet

               1.简介 :

               ResultSet表示数据结果集的数据表,通常通过DQL(Data Query Language)来生成。ResultSet对象保持一个光标,该光标指向其当前的数据行最初,光标位于第一行之前,next方法会使光标移动到下一行,并且当ResultSet对象中没有更多行时返回false,因此可以使用While循环来遍历结果集。

               默认的ResultSet对象不可更新,并且只有一个向前移动的光标。因此,默认只能从第一行到最后一行迭代一次。但是,可以手动生成可滚动/可更新的ResultSet对象。

               PS_1 : 若有需求让光标向上移动一行,可以使用previous()方法;如果再往上没有行可以返回时,返回false。

               PS_2 : 使用getXxx()方法返回获得的记录(一行数据)中指定的字段,需要传入要获取的字段的索引(从1开始);或者也可以直接传入字段名。

               PS_3 : 若有需求以对象的形式来接收返回的字段,可以使用getObject(...)方法,传入的实参与getXxx方法一致。

               2.代码演示 :

                       根据对ResultSet的描述,我们不难会联想到迭代器的执行原理。只不过相比迭代器来说,ResultSet的next方法是把两件事都干了——判断和移动指针。

                       现有一张学生表如下 :

        image.gif编辑

                       根据ResultSet结果集的简介,当我们通过while循环遍历结果集时,一开始ResultSet保持的光标位置会指在学生表第一条记录的上面,如下图所示 :

        image.gif编辑

                       现在我们通过JDBC的方式查询这张表,up以ResultSet_Demo类为演示类,代码如下 :

        package api.resultSet;
        import java.io.FileInputStream;
        import java.io.FileNotFoundException;
        import java.io.IOException;
        import java.sql.*;
        import java.util.Properties;
        public class ResultSet_Demo {
            public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
            //编写JDBC程序核心四部曲:
                Properties properties = new Properties();
                properties.load(new FileInputStream("src/api/connection/mysql.properties"));
                String driver = properties.getProperty("driver");
                String url = properties.getProperty("url");
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
                //1.注册驱动
                Class.forName(driver);
                //2.获取连接
                Connection connection = DriverManager.getConnection(url, user, password);
                //3.执行SQL
                Statement statement = connection.createStatement();
                String sql = "SELECT * FROM stus;";
                ResultSet resultSet = statement.executeQuery(sql);
                /**
                    注意 : 执行DQL(数据查询语句)要使用Statement类中的executeQuery方法。
                 */
                while (resultSet.next()) {  //使用while循环来遍历结果集
                    //获取当前光标指向的记录的第一个字段
                    int id = resultSet.getInt(1);
                    //获取第二个字段
                    String name = resultSet.getString(2);
                    //获取第三个字段
                    String sex = resultSet.getString(3);
                    //获取第四个字段
                    double score = resultSet.getDouble(4);
                    /*打印获取的字段*/
                    System.out.println(String.format("%d\t%5s\t%s\t%.2f", id,name,sex,score));
                }
                //4.释放资源
                resultSet.close();      //结果集也需要关闭!
                statement.close();
                connection.close();
            }
        }

        image.gif

                       运行结果 :

        image.gif编辑

               3.底层实现 :

                       接下来,我们通过Debug的方式看一下ResultSet类的源码,看看它底层到底是如何实现的。

                       在返回结果集的代码行设置断点,进入Debug,如下图所示 :

        image.gif编辑

                       可以发现ResultSet对象其实是一个ResultSet接口的实现类(JDBC规定要实现的接口),如下图所示 :

        image.gif编辑

                       该实现类又继承了NativeResultset类,如下图所示 :

        image.gif编辑

                       至于为什么要说这个事儿呢?接着往下看你就明白了。

                       在该实现类的众多成员中,存放数据的成员是rowData,我们可以在ResultSetImpl类中找到这个rowData,如下图所示 :

        image.gif编辑

                       但是,当我们使用Ctrl + b/B快捷键访问rowData源码时,会发现rowData其实不是ResultSetImpl类的成员,而是它的父类NativeResultset中的成员,如下图所示 :

        image.gif编辑

                       可以看到,rowData本身是ResultsetRows类型(是个接口),此处使用protected访问权限修饰符,表示其可以被子类访问。

        image.gif编辑

                       但在实际使用中,rowData的类型其实是一个实现了ResultsetRows接口的ResultsetRowsStatic类的对象。而ResultsetRowsStatic类的成员rows才是真正存放表中数据的地方,rows本身是List接口类型,如下图所示 :

        image.gif编辑

                       但实际使用中,它是一个实现了List接口的ArrayList类对象,其中存放了表中所有行的数据(所有记录)。

        image.gif编辑

                       可以看到,仍然是我们熟悉的elementData数组(up之前出过ArrayList类的源码分析,大家有兴趣可以去看看)。现在elementData数组中有四个元素,对应我们要查询的学生表中共四条记录。

        image.gif编辑

                       继续,elementData数组中元素的类型实际是ByteArrayRow类型,而ByteArrayRow类中有包含一个成员internalRowData,是一个byte类型的数组,如下图所示 :

        image.gif编辑

                       这个byte数组中又有四个元素,是对应了我们学生表中的四个字段(id,name,sex,score),此处存放的是字段的值对应的ASCII码值


        五、SQL注入

               1.什么是SQL注入?

               Statement也是JDBC规范的接口之一。用于执行静态SQL语句并返回其生成的结果的对象。

               在建立连接后,需要对数据库进行访问,执行SQL语句,可以通过Statement, PreparedStatement(预处理), 或者CallableStatement(存储过程)三种途径。

               但是,使用Statement会存在SQL注入的风险。所谓SQL注入,指的是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令,恶意攻击数据库

               防范SQL注入可以使用PreparedStatement来取代Statement

               2.SQL注入演示 :

                       举一个简单的SQL注入的栗子,输入用户的用户名为:1' OR,输入用户的密码为:OR '1'  = '1。因为我们在WHERE子句中确定name和password时,会使用单引号。那么当我们以上述的用户名和密码来登录时,就会造成如下效果 :

               ...WHERE name = '1' OR' AND password = 'OR '1' = '1';

               ...WHERE name = '1'OR' AND password = 'OR '1' = '1';

                       可以看到,由于输入的用户名和密码中恶意使用了单引号,使得原来的条件验证被改成了条件1 OR 条件2 OR 条件3的格式,并且这里的条件3 —— '1' = '1'是永真式。

                       up以用户表users来演示(表示可登录的用户),创建表的代码如下 :

        CREATE TABLE IF NOT EXISTS `users`(
            `name` VARCHAR(32) NOT NULL,
            `password` VARCHAR(32) NOT NULL
        ) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin ENGINE INNODB;
        INSERT INTO users
            VALUES
            ('Ice', '12345'),
            ('Bob', 'bbbbb');
        SELECT * FROM users;

        image.gif

                       users表效果如下 :

        image.gif编辑

                       测试SQL注入,如下:

        SELECT * FROM users
            WHERE `name` = '1' OR'
            AND password = 'OR '1' = '1';

        image.gif

                       查询结果如下 :  

        image.gif编辑

                      如果登录程序以“能否查询到表中的内容”为判定管理员是否存在,那么SQL注入的方式就可以顺利侵入数据库

                       接下来我们使用Java程序来演示一下SQL注入

                       up以Sumulation类为演示类,代码如下:

        package api.sql_injection;
        import java.io.FileInputStream;
        import java.io.FileNotFoundException;
        import java.io.IOException;
        import java.sql.*;
        import java.util.Properties;
        import java.util.Scanner;
        public class Simulation {
            public static void main(String[] args) throws ClassNotFoundException, SQLException, IOException {
            //核心四部曲
                Scanner scanner = new Scanner(System.in);
                System.out.println("请输入要登录用户的用户名:");
                String name = scanner.nextLine();
                System.out.println("请输入要登录用户的密  码:");
                String password_ex = scanner.nextLine();
                Properties properties = new Properties();
                properties.load(new FileInputStream("src/api/connection/mysql.properties"));
                String driver = properties.getProperty("driver");
                String url = properties.getProperty("url");
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
                //1.注册驱动
                Class.forName(driver);
                //2.获取连接
                Connection connection = DriverManager.getConnection(url, user, password);
                //3.执行SQL
                String sql = "SELECT * FROM users " +
                                "WHERE `name` = '" + name + "'" +
                                "AND password = '" + password_ex + "';";
                Statement statement = connection.createStatement();
                ResultSet resultSet = statement.executeQuery(sql);
                /**
                 * 认为 ———— 只要查询到表中的内容,就说明当前管理员是存在的,判定登录成功。
                 */
                if (resultSet.next()) {
                    System.out.println("Log on successfully!");
                } else {
                    System.out.println("Failed to log on!");
                }
                //4.释放资源
                resultSet.close();
                statement.close();
                connection.close();
                scanner.close();
            }
        }

        image.gif

                       运行结果 :

        image.gif编辑

               3.PreparedStatement :

                       ①简介

               PreparedStatement也是一个接口,并且是Statement接口的子接口,因此也可以使用Statement接口中的一些方法。

               PreparedStatement执行的SQL语句中的参数用?来表示(?表示占位符),通过调用该类的setXxx方法来设置这些参数。如下图所示 :

        image.gif 编辑

               可以看到,这些setXxx方法均有两个形参。其中,第一个形参均为int类型,代表了要设置的参数在对应SQL语句中存在的位置(从1开始)第二个形参便是具体要设置的值

               PS :

               1>同Statement类似,调用executeQuery()方法来执行DQL(查),返回ResultSet对象;而调用executeUpdate()来执行DML(增,删,改),返回int类型的受影响的行数

               2>获取PreparedStatement时,直接传入要执行的SQL字符串,使两者关联;之后调用executeQuery和executeUpdate方法时,不再需要传入形参。

                       ②牛逼之处

          • ----->不再需要使用+拼接SQL语句,减少了编程时的语法错误;
          • ----->有效解决了SQL注入的问题;
          • ----->大大减少了编译次数,执行效率较高。

                         ③使用演示

                         up以Prepared_Demo类作为演示类,代码如下 :

          package api.sql_injection;
          import java.io.FileInputStream;
          import java.io.FileNotFoundException;
          import java.io.IOException;
          import java.sql.*;
          import java.util.Properties;
          import java.util.Scanner;
          /**
           * @author : Cyan_RA9
           * @version : 21.0
           */
          public class PreparedStatement_Demo {
              public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
                  Scanner scanner = new Scanner(System.in);
                  System.out.println("请输入用户名:");
                  String name = scanner.nextLine();
                  System.out.println("请输入密  码:");
                  String password_ex = scanner.nextLine();
                  Properties properties = new Properties();
                  properties.load(new FileInputStream("src/api/connection/mysql.properties"));
                  String driver = properties.getProperty("driver");
                  String url = properties.getProperty("url");
                  String user = properties.getProperty("user");
                  String password = properties.getProperty("password");
              //JDBC核心四部曲
                  //1.注册驱动
                  Class.forName(driver);
                  //2.获取连接
                  Connection connection = DriverManager.getConnection(url, user, password);
                  String sql = "SELECT * FROM users " +
                                  "WHERE `name` = ? " +
                                  "AND password = ? ;";
                  PreparedStatement ps = connection.prepareStatement(sql);
                  ps.setString(1, name);
                  ps.setString(2, password_ex);
                  //3.执行SQL
                  ResultSet resultSet = ps.executeQuery();
                  if (resultSet.next()) {
                      System.out.println("Log on successfully!");
                  } else {
                      System.out.println("Failed to log on!");
                  }
                  //4.释放资源
                  resultSet.close();
                  ps.close();
                  connection.close();
                  scanner.close();
              }
          }

          image.gif

                         运行效果 :

                         我们先来测试一下输入正确的用户 :

          image.gif编辑

                         再来测试一下SQL注入,如下图所示 :

          image.gif编辑

                         可以看到,使用PreparedStatement代替Statement后,SQL注入被成功拦截

                         对于PreparedStatement执行DML的情况,很简单,大家可以自己去试试,改用executeUpdate方法,把ResultSet去掉,用int类型的变量做接收。非常容易,这里不做演示。


          六、总结 :

            • 🆗,以上就是JDBC 第二节的全部内容了。
            • 总结一下,我们在日常开发中最终要使用的JDBC连接方式,就是方式三(DriverManager)的简化版的优化版,以核心四部曲为框架,即——直接使用Class.forName(...)的反射形式动态加载Driver类,底层自动完成注册驱动的操作;使用DriverManager类的getConnection方法来获取连接(传入的参数从properties配置文件获得);使用PreparedStatement来执行SQL;释放资源。
            • 下一节内容——JDBC Utils,我们不见不散。感谢阅读!

                   System.out.println("END------------------------------------------------------------------------------");

            相关实践学习
            如何在云端创建MySQL数据库
            开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
            全面了解阿里云能为你做什么
            阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
            目录
            相关文章
            |
            SQL Java 关系型数据库
            JDBC知识【JDBC API详解】第三章下篇
            JDBC知识【JDBC API详解】第三章下篇
            |
            6月前
            |
            SQL Java 数据库连接
            JDBC开发之四大核心API:DriverManager Connection Statement ResultSet
            JDBC开发之四大核心API:DriverManager Connection Statement ResultSet
            40 1
            |
            7月前
            |
            Java 关系型数据库 数据库连接
            实时计算 Flink版操作报错之遇到错误org.apache.flink.table.api.ValidationException: Could not find any factory for identifier 'jdbc',该如何解决
            在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
            |
            7月前
            |
            SQL Java 数据库连接
            JDBC Java标准库提供的一些api(类+方法) 统一各种数据库提供的api
            JDBC Java标准库提供的一些api(类+方法) 统一各种数据库提供的api
            53 0
            |
            7月前
            |
            SQL Java 关系型数据库
            JDBC概念及API详解
            JDBC概念及API详解
            114 2
            |
            7月前
            |
            SQL Java 关系型数据库
            JDBC编程步骤、JDBC API详解和数据库连接池
            JDBC编程步骤、JDBC API详解和数据库连接池
            |
            SQL Java 关系型数据库
            JDBC知识【JDBC API详解】第三章上篇
            JDBC知识【JDBC API详解】第三章上篇
            |
            SQL druid Java
            JDBC BasicDAO详解(通俗易懂)
            JDBC 第七节 详解BasicDAO 通俗易懂!
            155 0
            |
            SQL 安全 Java
            JDBC Apache—DBUtils 详解(通俗易懂)
            JDBC 第六节 ApacheDBUtils详解!
            381 0
            |
            监控 druid Java
            JDBC 连接池 详解(通俗易懂)
            JDBC 第五节 详解连接池 通俗易懂!
            549 0