一篇文章让你学会写JDBC!
一、JDBC基本概念
全称:Java DataBase Connectivity (Java 数据库连接)
概念:JDBC是一个独立于特定数据库管理系统、通用 的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准 Java类库,(java.sql,javax.sql)使用这个类库可以以一种标准的方法、方便地 访问数据库资源。
本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
目标:使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
二、JDBC体系结构
JDBC接口(API)包括两个层次:
- 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
- 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。
JDBC是SUN公司提供一套用于数据库操作的接口,java程序员只需要面向这 套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同实现。 不同的实现的集合,即为不同数据库的驱动。——面向接口编程
三、JDBC快速入门
步骤:
-
- 导入驱动jar包(以MySQL数据库为例)
- 复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下(在src目录下创建libs目录)
- 右键-->Add As Library
- 导入驱动jar包(以MySQL数据库为例)
- 2.注册驱动java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是 提供给数据库厂商使用的,不同数据库厂商提供不同的实现。
- Oracle的驱动:oracle.jdbc.driver.OracleDriver
- mysql的驱动: com.mysql.jdbc.Driver
在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理 器类(java.sql.DriverManager)去调用这些Driver实现
加载与注册 JDBC 驱动
方式一:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递 要加载的 JDBC 驱动的类名.
Class.forName(“com.mysql.jdbc.Driver”);
方式二:DriverManager 类是驱动程序管理器类,负责管理驱动程序.
DriverManager.registerDriver(com.mysql.jdbc.Driver);
通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱 动程序类的实例,因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
- 3.获取数据库连接对象 Connection1.可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接,User,password可以用“属性名=属性值”方式告诉数据库。2.JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序, 从而建立到数据库的连接。JDBC URL的标准由三部分组成,各部分间用冒号分隔。
- jdbc:子协议:子名称
- 协议:JDBC URL中的协议总是jdbc
- 子协议:子协议用于标识一个数据库驱动程序
- 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名
几种常用数据库的JDBC URL
//对于 Oracle 数据库连接,采用如下形式: jdbc:oracle:thin:@localhost:1521:db //对于 SQLServer 数据库连接,采用如下形式: jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=sid //对于 MYSQL 数据库连接,采用如下形式: jdbc:mysql://localhost:3306/ db
- 4.定义sql
//定义结构化SQL语句 String sql2 = "select * from user where username='" + uname + "' and upassword = '" + upwd + "'"; //对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从 Statement扩展而来) 就可以了取代 Statement。按照以下方式定义SQL语句即可。 String sql = "insert into sc(id,sname,score) values(?,?,?)";//? : 占位符
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户 输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1') ,从而利用系统的 SQL 引擎完成恶意行为的做法
常用数据类型转换表
- 5.获取执行sql语句的对象 PreparedStatement
- 可以通过调用 Connection 对象的 preparedStatement() 方法获取 PreparedStatement 对象。
- PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过 的 SQL 语句。
- PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示, 调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数.setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开 始),第二个是设置的 SQL 语句中的参数的值。
PreparedStatement 与 Statement比较
1.PreparedStatement代码的可读性和可维护性更好。
2.PreparedStatement 能最大可能提高性能:DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被 DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要 编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句 的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次
3.PreparedStatement 可以防止 SQL 注入。
- 6.执行sql,接受返回结果
//1.可以执行任意的sql boolean execute(String sql) //2.执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句 int executeUpdate(String sql) //返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。 //3.执行DQL(select)语句 ResultSet executeQuery(String sql) /* ResultSet通过调用 PreparedStatement 对象的 excuteQuery() 方法创建该对象 ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集, ResultSet 接口由数据库厂商实现 ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在 第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行 ResultSet 接口的常用方法: boolean next() getString() */
- 7.处理结果ResultSet:结果集对象,封装查询结果查询需要调用 Statement 的 executeQuery(sql) 方法,查询结果是一个 ResultSet 对象。boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true。
getXxx(参数):获取数据
Xxx:代表数据类型 如: int getInt() , String getString()
参数:- int:代表列的编号,从1开始 如: getString(1)
- String:代表列名称。 如: getDouble("balance")
//注意: /*使用步骤: 1. 游标向下移动一行 2. 判断是否有数据 3. 获取数据*/ //循环判断游标是否是最后一行末尾。 while(rs.next()){ //获取数据 int id = rs.getInt(1); String name = rs.getString("name"); double balance = rs.getDouble(3); System.out.println(id + "---" + name + "---" + balance); }
ResultSetMetaData
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
ResultSetMetaData meta = rs.getMetaData(); getColumnName(int column)//获取指定列的名称 getColumnLabel(int column)//获取指定列的别名 getColumnCount()//返回当前 ResultSet 对象中的列数。 getColumnTypeName(int column)//检索指定列的数据库特定的类型名称。 getColumnDisplaySize(int column)//指示指定列的最大标准宽度,以字符为单位。 isNullable(int column)//指示指定列中的值是否可以为 null。 isAutoIncrement(int column)//指示是否自动为指定列进行编号,这样这些列仍然是只读的。
- 8.释放资源释放ResultSet, PreparedStatement ,Connection。数据库连接(Connection)是非常稀有的资源,用完后必须马上释放, 如果Connection不能及时正确的关闭将导致系统宕机。Connection 的使用原则是尽量晚创建,尽量早的释放。
四、JDBC API 小结
- java.sql.DriverManager用来装载驱动程序,获取数据库连接。
- java.sql.Connection完成对某一指定数据库的连接
- java.sql.Statement在一个给定的连接中作为SQL执行声明的容器,他包含了两个重要的子类型。
- Java.sql.PreparedSatement 用于执行预编译的sql声明
- Java.sql.CallableStatement用于执行数据库中存储过程的调用
- java.sql.ResultSet对于给定声明取得结果的途径
- 两种思想
- 面向接口编程的思想
- ORM思想
ORM(Object Relational Mapping) : 对象关系映射 数据库中的一张表 ---- Java 中的一个类 数据表中一个字段 ---- Java 中的一个属性 数据表中一条数据 ---- Java 中一个对象
五、完整实现代码
5-1JDBC快速入门
//1.导入驱动jar包
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "password");
//4.定义sql语句
String sql = "update Student set score = 88 where id = 1";
//5.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//6.执行sql
int count = stmt.executeUpdate(sql);
//7.处理结果
System.out.println(count);
//8.释放资源
stmt.close();
conn.close();
5-2使用properties配置文件获取连接
/*
4.将四个连接数据库必须的字符串,提取到 properties 属性文件中,方便后续的维护与管理
4-1在src下新建文件jdbc.properties
4-2写入配置文件信息
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db1?rewriteBatchedStatements=true
user=root
password=password
*/
@Test
public void test4() throws Exception {
//创建Properties集合类
Properties pro = new Properties();
//加载文件
pro.load(this.getClass().getClassLoader().getResourceAsStream("jdbc.properties"));
//获取配置信息
String driverClassName = pro.getProperty("driverClassName");
String url = pro.getProperty("url");
String user = pro.getProperty("user");
String pwd = pro.getProperty("password");
//1.加载驱动
Class.forName(driverClassName);
//2.获取连接
Connection conn = DriverManager.getConnection(url,user,pwd);
System.out.println(conn);
}
5-3使用PreparedStatement实现DML语句
@Test
public void test3(){
Connection conn = null;
PreparedStatement psm = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection_Druid();
//2.通过当前连接,获取 PreparedStatement ,用于发送 SQL
String sql = "update sc set sname = '小白' where id = ?";//? : 占位符
psm = conn.prepareStatement(sql);
//3.填充占位符
psm.setInt(1,1);
//4.执行SQL
int row = psm.executeUpdate();
System.out.println("已影响" + row + "行");
}catch (Exception e){
e.printStackTrace();
}finally {
//5.关闭连接
JDBCUtils.close(conn,psm);
}
}
5-4使用 PreparedStatement 完成查询
@Test
public void test4(){
List<SC> list = getList();
Iterator<SC> iterator = list.iterator();
while(iterator.hasNext()){
SC SCs = iterator.next();
System.out.println(SCs);
}
}
public List<SC> getList(){
List<SC> list = new ArrayList<>();
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection_Druid();
String sql = "select * from sc where id <= ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 3);
rs = ps.executeQuery();
while(rs.next()){
int id = rs.getInt("id");
String sname = rs.getString("sname");
int score = rs.getInt("score");
SC sc = new SC(id,sname,score);
list.add(sc);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.close(conn, ps, rs);
}
return list;
}
六、数据库事务
事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存 下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚 (rollback)到最初状态。
为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元: 当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
事务的ACID(acid)属性
- 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
- 隔离性(Isolation)事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的 数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他 操作和数据库故障不应该对其有任何影响。
MySQL数据库中事务默认自动提交。
数据库事务:
以第一个 DML 语句的执行作为开始
以下面的其中之一作为结束:
- COMMIT 或 ROLLBACK 语句
- DDL 或 DCL 语句(自动提交)
- 用户会话正常结束
- 系统异常终了
DDL: Data Definition Language 数据定义语言(用来定义数据库结构): create table; alter table; drop table; create index; drop index
DCL: Data Control Language 数据控制语言(用来控制数据库的访问):grant; revoke; commit; rollback; lock;
DML: Data Manipulation Language 数据操纵语言(用来查询与更新记录): insert; update; delete
数据库的隔离级别
对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离 机制, 就会导致各种并发问题:
- 脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读 取的内容就是临时且无效的.
- 不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一 个字段, 值就不同了.
- 幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之 后, 如果 T1 再次读取同一个表, 就会多出几行.
数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题.
一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级 别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。
数据库提供的 4 种事务隔离级别:
Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE。
Oracle 默认的 事务隔离级别为: READ COMMITED
Mysql 支持 4 种事务隔离级别. Mysql 默认的事务隔离级别为: REPEATABLE READ
在 MySql 中设置隔离级别:
每启动一个 mysql 程序, 就会获得一个单独的数据库连接. 每个数据库连接都有一个 全局变量 @@tx_isolation, 表示当前的事务隔离级别.
查看当前的隔离级别: SELECT @@tx_isolation;
设置当前 mySQL 连接的隔离级别:
set transaction isolation level read committed;
设置数据库系统的全局的隔离级别:
set global transaction isolation level read committed;
SET autocommit = 0; //禁止操作自动提交
JDBC 事务处理
当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
为了让多个 SQL 语句作为一个事务执行:
- 调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
- 在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
- 在出现异常时,调用 rollback(); 方法回滚事务
- 若此时 Connection 没有被关闭, 则需要恢复其自动提交状态
使用Connection对象来管理事务:
开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
在执行sql之前开启事务
提交事务:commit()
当所有sql都执行完提交事务
回滚事务:rollback()
在catch中回滚事务
使用COMMIT 和 ROLLBACK语句,我们可以:
- 确保数据完整性。
- 数据改变被提交之前预览。
- 将逻辑上相关的操作分组。
数据完整性: 存储在数据库中的所有数据值均处于正确的状态。如果数据库中存储有不正确的数据值,则该数据库称为已丧失数据完整性。
数据库采用多种方法来保证数据完整性,包括外键、束约、规则和触发器。
实现代码
public class JDBCTransaction() {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
//2.定义sql
//2.1 张三 - 500
String sql1 = "update account set balance = balance - ? where id = ?";
//2.2 李四 + 500
String sql2 = "update account set balance = balance + ? where id = ?";
//3.获取执行sql对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
//4. 设置参数
pstmt1.setDouble(1,500);
pstmt1.setInt(2,1);
pstmt2.setDouble(1,500);
pstmt2.setInt(2,2);
//5.执行sql
pstmt1.executeUpdate();
// 手动制造异常
int i = 3/0;
pstmt2.executeUpdate();
//提交事务
conn.commit();
} catch (Exception e) {
//事务回滚
try {
if(conn != null) {
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt1,conn);
JDBCUtils.close(pstmt2,null);
}
}
七、数据库连接池
在开发基于数据库的web程序时,传统的模式基本是按以下步骤:
- 在主程序(如servlet、beans)中建立数据库连接
- 进行sql操作
- 断开数据库连接
存在的问题:
1.普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接 的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时 间。数据库的连接资源并没有得到很好的重复利用.若同时有几百人甚至几千人在线,频繁的 进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
2.对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致 数据库系统中的内存泄漏,最终将导致重启数据库。
3.这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也 可能导致内存泄漏,服务器崩溃。
为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。
概念:其实就是一个容器(集合),存放数据库连接的容器。当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接 的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将 一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池 能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请 求将被加入到等待队列中。
数据库连接池技术的优点
- 1.资源重用由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上, 另一方面也增加了系统运行环境的平稳性。
- 2.更快的系统反应速度数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工 作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间 开销,从而减少了系统的响应时间。
- 3.新的资源分配手段对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数 据库连接数的限制,避免某一应用独占所有的数据库资源。
- 4.统一的连接管理,避免数据库连接泄露在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规 数据库连接操作中可能出现的资源泄露。
JDBC 的数据库连接池
JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个 接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些 开源组织提供实现:
- DBCP 数据库连接池
- C3P0 数据库连接池
- Druid 数据库连接池
DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也 经常把 DataSource 称为连接池。
DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以 大幅度提高数据库访问速度。
C3P0 数据库连接池的使用
-
- 导入jar包 c3p0-0.9.1.2.jar
-
- 定义配置文件:名称: c3p0.properties 或者 c3p0-config.xml路径:直接将文件放在src目录下即可
- 3.创建核心对象 数据库连接池对象 ComboPooledDataSource
- 4.获取连接: getConnection
//c3p0 连接方式
//在src目录下新建c3p0-config.xml 配置文件
@Test
public void test2() throws SQLException {
DataSource ds = new ComboPooledDataSource("helloc3p0");
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
Druid 数据库连接池的使用
-
- 导入jar包 druid-1.0.9.jar
- 2.定义配置文件是properties形式的可以叫任意名称,可以放在任意目录下
-
- 加载配置文件。Properties
-
- 获取数据库连接池对象:通过工厂来来获取 DruidDataSourceFactory
-
- 获取连接:getConnection
//Druid 连接池
//在src目录下新建druid.properties 配置文件
@Test
public void test2() throws Exception {
Properties pro = new Properties();
pro.load(this.getClass().getClassLoader().getResourceAsStream("druid.properties"));
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
Connection conn = ds.getConnection();
System.out.println(conn);
}
八、JDBCUtils实现
public class JDBCUtils {
//定义成员变量 DataSource
private static DataSource ds;
/**
* 使用Druid数据库连接池获取数据库连接
* @return Connection
* @throws Exception
*/
public static Connection getConnection_Druid() throws Exception {
Properties pro = new Properties();
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("Druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
return ds.getConnection();
}
/**
* 使用C3P0数据库连接池获取数据库连接
* @return Connection
* @throws Exception
*/
public static Connection getConnection_C3P0() throws Exception {
ds = new ComboPooledDataSource("helloc3p0");
return ds.getConnection();
}
/**
* 关闭连接
* @param conn
* @param ps
*/
public static void close(Connection conn, PreparedStatement ps){
if (ps != null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public static void close(Connection conn, PreparedStatement ps, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (ps != null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
微信关注
本站所发布的一切软件资源、文章内容、页面内容可能整理来自于互联网,在此郑重声明本站仅限用于学习和研究目的;并告知用户不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
如果本站相关内容有侵犯到您的合法权益,请仔细阅读本站公布的投诉指引页相关内容联系我,依法依规进行处理!
作者:理想
链接:https://www.imyjs.cn/archives/358




站长
站长
站长
理想