0
点赞
收藏
分享

微信扫一扫

JDBC的使用记录

春意暖洋洋 2024-01-31 阅读 12


现在绝大部分情况下都是使用持久层框架来操作数据,抽时间整理了下JDBC的原生使用方法,供以后需要时查阅。


一、单条执行模式

public void standardJdbc() throws Exception {
    //待执行的SQL语句
    String sql1 = "update t_user set age = 22 where id = 91" ;
    String sql2 = "update t_user set age = 23 where id = 92" ;
    String sql3 = "update t_user set age = 24 where id = 93" ;
    String sql4 = "update t_user set age = 25 where id = 94" ;
    
    String url = "jdbc:mysql://localhost:3306/test" ;
    String username = "admin" ;
    String password = "admin" ;
    // 加载并注册JDBC驱动[非必须]
    Class.forName("com.mysql.jdbc.Driver");
    // 通过驱动来获取数据库连接
    Connection connection = DriverManager.getConnection(url, username, password);
    try{
        Statement stmt = connection.createStatement();
        
        //在设置手动事务前执行的语句会自动提交
        //即便后面开启了事务后报错回滚了也不影响这里
        stmt.executeUpdate(sql1);
        
        //在手动事务开启前创建了保存点,执行语句不会报错,
        //但不能在后面回滚到这里,会提示保存点不存在(SQLSyntaxErrorException: SAVEPOINT 保存点0 does not exist)
        //此语句依旧自动提交
        Savepoint savepoint0 = connection.setSavepoint("保存点0") ;
        stmt.executeUpdate(sql2);
        
        
        //开启手动事务
        connection.setAutoCommit(false);
        //在活动事务范围内创建保存点
        Savepoint savepoint1 = connection.setSavepoint("保存点1") ;
        int result3 = stmt.executeUpdate(sql3);
        
        Savepoint savepoint2 = connection.setSavepoint("保存点2") ;
        /*
         *执行给定的SQL语句,该语句可以是INSERT、UPDATE或DELETE语句,
         *也可以是不返回任何结果的SQL语句,例如SQL DDL语句。
         * 返回结果:
         * (1) SQL数据操作语言(DML)语句的行数;
         * (2) 不返回任何结果的SQL语句的行数为0
         */
        int result4 = stmt.executeUpdate(sql4);
        
        
        //注意,如果在这里设置为true,等同于执行了commit
        //但由于改成了自动提交,后面的commit等操作将抛出异常
        //connection.setAutoCommit(true);    
    
        
        //如果这里回退到保存点0则会抛出异常[SQLSyntaxErrorException: SAVEPOINT 保存点0 does not exist]
        //如果没开启事务情况下执行,也会抛出异常[]
        connection.rollback(savepoint2);
        //提交事务[如果没开启手动事务的情况下执行该方法,则会报错-SQLException: Can't call commit when autocommit=true]
        connection.commit();
        
    }catch (SQLException e ){
        
        //如果没开启手动事务的情况下执行该方法,则会报错
        //SQLNonTransientConnectionException: Can't call rollback when autocommit=true
        connection.rollback();
    }finally {
        connection.close();
    }
}

Statement中的以下方法说明:

  • executeUpdate(String):执行给定的SQL语句,该语句可以是INSERT、UPDATE或DELETE语句,也可以是不返回任何结果的SQL语句,例如SQL DDL语句。
  • executeQuery(String):执行给定的SQL语句,该语句返回单个ResultSet对象。注意:这个方法不能在PreparedStatement或CallableStatement上调用
  • execute(String):执行给定的SQL语句,该语句可能返回多个结果。在某些(不常见的)情况下,单个SQL语句可能返回多个结果集和/或更新计数。通常情况下,您可以忽略这一点,除非您正在执行一个您知道可能返回多个结果的存储过程,或者动态执行一个未知的SQL字符串。execute方法执行一条SQL语句,并指出第一个结果的形式。然后必须使用getResultSet或getUpdateCount方法来检索结果,并使用getMoreResults 来移动到任何后续结果。如果第一个结果是ResultSet对象,则为true;如果是更新计数或没有结果,则为False。注意:这个方法不能在PreparedStatement或CallableStatement上调用
  • addBatch(String):将给定的SQL命令添加到此Statement对象的当前命令列表中。可以通过调用executeBatch方法批量执行此列表中的命令。执行的SQL通常是一个INSERT或UPDATE语句。注意:这个方法不能在PreparedStatement或CallableStatement上调用。
  • int[] executeBatch():向数据库提交一批命令以供执行,如果所有命令都成功执行,则返回一个更新计数数组。返回的数组的int元素被排序为与批处理中的命令相对应,而批处理中的命令是根据它们添加到批处理中的顺序排序的。executeBatch方法返回的数组元素可能是以下其中一种:
  • 大于或等于0的数字——表示命令已成功处理,并且是一个更新计数,表示数据库中受命令执行影响的行数
  • SUCCESS_NO_INFO——的值表示该命令已成功处理,但受影响的行数未知如果批处理更新中的一个命令未能正确执行,则此方法会抛出BatchUpdateException, JDBC驱动程序可能会或可能不会继续处理批处理中的其余命令。然而,驱动程序的行为必须与特定的DBMS保持一致,要么总是继续处理命令,要么永远不继续处理命令。如果驱动程序在失败后继续处理,则BatchUpdateException方法返回的数组。getUpdateCounts将包含与批处理中的命令一样多的元素,并且至少有一个元素将如下所示:
  • EXECUTE_FAILED——表示命令执行失败,只有在命令失败后驱动程序继续处理命令时才会发生


PreparedStatement中的以下方法说明:

  • ResultSet executeQuery() :执行这个PreparedStatement对象中的SQL查询,并返回查询生成的ResultSet对象。返回包含查询产生的数据的ResultSet对象(永远不会为null)
  • int executeUpdate() : 执行PreparedStatement对象中的SQL语句,该语句必须是SQL数据操作语言(SQL Data Manipulation Language, DML)语句,如INSERT、UPDATE或DELETE;或者是不返回任何结果的SQL语句,比如DDL语句。可能返回两种情况:SQL数据操作语言(DML)语句的行数;不返回任何结果的SQL语句的行数为0
  • boolean execute():执行PreparedStatement对象中的SQL语句,该对象可以是任何类型的SQL语句。一些预处理语句返回多个结果;execute方法处理这些复杂的语句以及由executeQuery和executeUpdate方法处理的简单形式的语句。execute方法返回一个布尔值来指示第一个结果的形式。你必须调用getResultSet或getUpdateCount方法来检索结果;你必须调用getMoreResults 来移动到任何后续的结果。
  • void addBatch():向此PreparedStatement对象的命令批添加一组参数。

 

setSavepoint方法说明:

在当前事务中创建具有给定名称的保存点,并返回表示它的新保存点对象。如果在活动事务之外调用setSavepoint,则事务将在这个新创建的保存点启动。以下的情况会导致SQLException异常的发生

  • 如果发生数据库访问错误
  • 此方法在参与分布式事务时调用
  • 在关闭的连接上调用此方法
  • 此连接对象当前处于自动提交模式

保存点的小结

  1. 要手动执行commit(),rollback(),rollback(SavePoint)方法不报错,则必须先手动开启事务setAutoCommit(false)
  2. 自动提交模式下依然可以执行创建保存点的方法,由于自动提交下不会执行commit(),rollback(),rollback(SavePoint)方法,所以没影响
  3. 有效的保存点创建,必须在开启事务之后


二、批量操作模式

批量插入【Statement】:

public static void statementBatchInsert() throws Exception{
    List<String> sqlList = new ArrayList<>() ;
    String url = "jdbc:mysql://localhost:3306/test" ;
    String username = "admin" ;
    String password = "admin" ;
    // 加载并注册JDBC驱动[非必须]
    Class.forName("com.mysql.cj.jdbc.Driver");
    // 通过驱动来获取数据库连接
    Connection connection = DriverManager.getConnection(url, username, password);
    try{
        connection.setAutoCommit(false); //开启事务
        //创建待执行的语句
        Statement stmt = connection.createStatement();
        for(String sql : sqlList) {
            stmt.addBatch(sql);
        }
        //批量执行
        stmt.executeBatch() ;
        //提交
        connection.commit();
    }catch (SQLException e){
        connection.rollback();
    }finally {
        connection.close();
    }
}

批量插入【PreparedStatement】:

public static void preparedStatementBatchInsert() throws Exception{
    String sql = "insert into t_user(name) values (?)";
    String url = "jdbc:mysql://localhost:3306/test" ;
    String username = "admin" ;
    String password = "admin" ;
    // 加载并注册JDBC驱动[非必须]
    Class.forName("com.mysql.cj.jdbc.Driver");
    // 通过驱动来获取数据库连接
    Connection connection = DriverManager.getConnection(url, username, password);
    try{
        connection.setAutoCommit(false); //开启事务
        //创建待执行的语句
        PreparedStatement stmt = connection.prepareStatement(sql);
        for(int i=0;i<500;i++) {
            stmt.setString(1,"名字-"+i);
            stmt.addBatch();
            if (i % 100 == 0) {
                //批量执行
                stmt.executeBatch();
                stmt.clearBatch();
            }
        }
        //注意,如果在这里设置为true,等同于执行了commit
        //但由于改成了自动提交,后面的commit等操作将抛出异常
        //connection.setAutoCommit(true);      

        //批量执行
        stmt.executeBatch() ;
        //提交
        connection.commit();
    }catch (SQLException e){
        connection.rollback();
    }finally {
        //connection.setAutoCommit(true); //恢复自动提交
        connection.close();
    }
}


三、存储过程调用

public static void callableStatementExec() throws Exception{
    //调用存储过程的sql语句
    String sql="{call pro_findAllDept(?)}";
    String url = "jdbc:mysql://localhost:3306/test" ;
    String username = "admin" ;
    String password = "admin" ;
    // 加载并注册JDBC驱动[非必须]
    Class.forName("com.mysql.jdbc.Driver");
    // 通过驱动来获取数据库连接
    Connection connection = DriverManager.getConnection(url, username, password);
    CallableStatement call=null;
    try{
        call = connection.prepareCall(sql) ;
        //申明输出参数类型:下标从1开始
        call.registerOutParameter(1, OracleTypes.CURSOR); //返回多行:游标类型
        //执行调用
        call.execute();
        //接收返回的结果
        ResultSet resultSet=((OracleCallableStatement)call).getCursor(1);
        while (resultSet.next()){
            //TODO
        }
    }finally {
        connection.close();
    }
}


四、获取元数据

private static void getTableMeta(String tableNamePattern) throws SQLException {
    Connection connection = DriverManager.getConnection(p6spyUrl, username, password);
    //数据库元数据
    DatabaseMetaData databaseMetaData = connection.getMetaData() ;
    
    //获取指定表名的元数据
    ResultSet tableResultSet = databaseMetaData.getTables(connection.getCatalog(),connection.getSchema(),
            tableNamePattern,new String[]{"TABLE"}) ;
    while (tableResultSet.next()){
        //表名
        String tableName = tableResultSet.getString("TABLE_NAME");
        //表模式(可能为空)
        String tableSchem = tableResultSet.getString("TABLE_SCHEM");
        //表目录(可能为空)
        String tableCat = tableResultSet.getString("TABLE_CAT");
        //表注释
        String remarks = tableResultSet.getString("REMARKS");
        //获取指定表下的主键【检索给定表的主键列的描述。它们按COLUMN_NAME排序】
        ResultSet pkResultSet = databaseMetaData.getPrimaryKeys(connection.getCatalog(),connection.getSchema(),tableName) ;
        while (pkResultSet.next()) {
            //可能为空
            pkResultSet.getString("TABLE_CAT") ;
            //可能为空
            pkResultSet.getString("TABLE_SCHEM");
            //表名
            pkResultSet.getString("TABLE_NAME");
            //列名
            pkResultSet.getString("COLUMN_NAME");
            //可能为空
            pkResultSet.getString("PK_NAME");
            //主键内的序列号(值1表示主键的第一列,值2表示主键内的第二列)
            pkResultSet.getString("KEY_SEQ");
        }
        //获取表的列信息
        ResultSet columnResultSet = databaseMetaData.getColumns(connection.getCatalog(),connection.getSchema(),tableName, "%") ;
        while (columnResultSet.next()) {
            //表目录(可能为空)
            columnResultSet.getString("TABLE_CAT");
            //表模式(可能为空)
            columnResultSet.getString("TABLE_SCHEM");
            //表名
            columnResultSet.getString("TABLE_NAME");
            //表类型。 典型的类型有“TABLE”、“VIEW”、“SYSTEM TABLE”、“GLOBAL TEMPORARY”、“LOCAL TEMPORARY”、“ALIAS”、“SYNONYM”。
            columnResultSet.getString("TABLE_TYPE");
            //表名
            columnResultSet.getString("COLUMN_NAME");
            //数据类型. java.sql.Types
            columnResultSet.getInt("DATA_TYPE");
            //类型名
            columnResultSet.getString("TYPE_NAME");
            //列大小
            columnResultSet.getInt("COLUMN_SIZE");
            //小数位数的数目。对于不适用DECIMAL_DIGITS的数据类型返回Null。
            columnResultSet.getInt("DECIMAL_DIGITS");
            //基数(通常为10或2)
            columnResultSet.getInt("NUM_PREC_RADIX");
            //允许为空.columnNoNulls -可能不允许NULL值;columnNullable -绝对允许NULL值;columnNullableUnknown -空性未知;
            columnResultSet.getInt("NULLABLE");
            columnResultSet.getString("REMARKS");
            //列的默认值,当值被括在单引号中时(可能为空),它应该被解释为字符串。
            columnResultSet.getString("COLUMN_DEF");
            //对于char类型,列中的最大字节数
            columnResultSet.getInt("CHAR_OCTET_LENGTH");
            //表中列的索引(从1开始)
            columnResultSet.getInt("ORDINAL_POSITION");
            //ISO规则用于确定列的可空性.YES、NO、empty
            columnResultSet.getString("IS_NULLABLE");
            //指示该列是否自动递增.YES、NO、empty
            columnResultSet.getString("IS_AUTOINCREMENT");
            //指示这是否是生成的列.YES、NO
            columnResultSet.getString("IS_GENERATEDCOLUMN");
        }
        //获取索引信息【检索给定表的索引和统计信息的描述。它们按NON_UNIQUE、TYPE、INDEX_NAME和ORDINAL_POSITION排序】
        ResultSet indexResultSet = databaseMetaData.getIndexInfo(connection.getCatalog(),connection.getSchema(), tableName, false, true);
        while (indexResultSet.next()) {
            //索引名称;当TYPE为tableIndexStatistic时为null
            String indexName = indexResultSet.getString("INDEX_NAME");
            //列名称;当TYPE为tableIndexStatistic时为null
            String colName = indexResultSet.getString("COLUMN_NAME");
            //索引值可以非唯一吗?当TYPE为tableIndexStatistic时为false
            indexResultSet.getBoolean("NON_UNIQUE") ;
            //索引目录(可能为空);当TYPE为tableIndexStatistic时为null
            indexResultSet.getString("INDEX_QUALIFIER") ;
            /*
                索引类型.
                tableIndexStatistic——标识与表的索引描述一起返回的表统计信息
                tableIndexClustered -这是一个聚集索引
                tableIndexHashed -这是一个散列索引
                tableIndexOther——这是另一种类型的索引
             */
            indexResultSet.getShort("TYPE") ;
            //索引内的列序号;当TYPE为tableIndexStatistic时为0
            indexResultSet.getShort("ORDINAL_POSITION") ;
            //列排序序列,"A" =>升序,"D" =>降序,如果不支持排序序列,可能为空;当TYPE为tableIndexStatistic时为null
            indexResultSet.getString("ASC_OR_DESC") ;
            //当TYPE为tableIndexStatistic时,这是表中的行数;否则,它是索引中唯一值的个数。
            indexResultSet.getInt("CARDINALITY") ;
            if ("PRIMARY".equalsIgnoreCase(indexName)) {  //主键
            }
        }
        /*
        检索在更新行中的任何值时自动更新的表列的描述【伴随更新时的自动更新列】
        1.SCOPE short =>没有被使用
        2.COLUMN_NAME String =>列名
        3.DATA_TYPE int => SQL数据类型从java.sql.Types
        4.TYPE_NAME String =>依赖于数据源的类型名称
        5.COLUMN_SIZE int => precision
        6.BUFFER_LENGTH int =>列值的长度,以字节为单位
        7.DECIMAL_DIGITS short => scale—对于不适用DECIMAL_DIGITS的数据类型返回Null。
        8.PSEUDO_COLUMN short =>是否是像Oracle ROWID那样的伪列
          - versionColumnUnknown -可能是也可能不是伪列
          - versionColumnNotPseudo -不是伪列
          - versionColumnPseudo -伪列
         COLUMN_SIZE列表示给定列的指定列大小。
         1.对于数值数据,这是最大精度。
         2.对于字符数据,这是以字符为单位的长度。
         3.对于日期时间数据类型,这是字符串表示的字符长度(假设小数秒组件允许的最大精度)。
         4.对于二进制数据,这是以字节为单位的长度。
         5.对于ROWID数据类型,这是以字节为单位的长度。
         6.对于列大小不适用的数据类型返回Null。
        */
        ResultSet versionColumnsResultSet = databaseMetaData.getVersionColumns(catalogName, schemaName, tableName) ;
        while (versionColumnsResultSet.next()) {
            versionColumnsResultSet.getString("COLUMN_NAME") ;
            versionColumnsResultSet.getString("DATA_TYPE") ;
            versionColumnsResultSet.getString("TYPE_NAME") ;
            versionColumnsResultSet.getString("COLUMN_SIZE") ;
        }
    }
}


五、事务提交时机

JDBC的使用记录_SQL



举报

相关推荐

0 条评论