现在绝大部分情况下都是使用持久层框架来操作数据,抽时间整理了下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异常的发生
- 如果发生数据库访问错误
- 此方法在参与分布式事务时调用
- 在关闭的连接上调用此方法
- 此连接对象当前处于自动提交模式
保存点的小结
- 要手动执行commit(),rollback(),rollback(SavePoint)方法不报错,则必须先手动开启事务setAutoCommit(false)
- 自动提交模式下依然可以执行创建保存点的方法,由于自动提交下不会执行commit(),rollback(),rollback(SavePoint)方法,所以没影响
- 有效的保存点创建,必须在开启事务之后
二、批量操作模式
批量插入【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") ;
}
}
}