从JDBC基础到C3P0的使用看这一篇就行了——基于MySQL8.0(完整版)

JDBC在mysql8.x上的使用

jdbc下载地址

  • mysql8.0和之前版本的区别,首先驱动换了,不是com.mysql.jdbc.Driver而是com.mysql.cj.jdbc.Driver

    5.5版本的注册

    Class.forName(com.mysql.jdbc.Driver());
    

    8.x版本的注册

    Calss.forName(com.mysql.cj.jdbc.Driver());
    
  • mysql8.x是不需要建立ssl连接的,需要显示关闭。需要配置serverTimezone属性设置时区

    Url="jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8&autoReconnect=true";
    
    //连接地址+ssl连接关闭+时区+字符集为utf-8+数据库自动连接
    

    UTC代表的是全球标准时间 ,但是我们使用的时间是北京时区也就是东八区,领先UTC八个小时。时区使用中国标准时间,也是就serverTimezone=Asia/Shanghai。

用jdbc连接数据库

package com.lxc.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.mysql.cj.jdbc.Driver;

public class JDBCDemo1 {
	
	public static void main(String[] args) {
		
		Connection conn=null;
		Statement stmt=null;
		ResultSet resultSet=null;
		try {
			
			//1、加载驱动
			//DriverManager.registerDriver(new Driver());//会进行两次注册驱动(加载Driver类一次,执行这条语句又一次)
			//加载Driver类,Driver类中的静态代码块中含有DriverManager.registerDriver(new Driver());代码,只要加载Driver类就会注册驱动
			Class.forName("com.mysql.cj.jdbc.Driver");
			
			//2、获得连接
			//三个参数1、localhost是主机名,3306是端口号,jdbctest是数据库 2、用户名 3、密码
			conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest?useSSL=false&serverTimezone=Asia/Shanghai","root","123456");
			
			//3、创建执行SQL语句的对象,并执行SQL
			//3.1创建执行sql语句的对象
			stmt=conn.createStatement();             
			String sql="select * from user";
			//3.2、执行sql语句获得结果集
			resultSet=stmt.executeQuery(sql);
		 	//3.3、读取结果集中的数据
			while(resultSet.next()) {
				int id=resultSet.getInt("uid");
				String username=resultSet.getString("username");
				String password=resultSet.getString("password");
				String name=resultSet.getString("name");
				System.out.println(id+"  "+username+"  "+password+"  "+name);
			}
			 
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			//4、释放资源
			
			if(resultSet!=null) {
				try {
					resultSet.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				resultSet=null;
			}
			
			if(stmt!=null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				stmt=null;
			}
			
			if(conn!=null) {
				try {
					//这里虽然关闭了资源,但不会马上被垃圾回收机制回收
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				//手动置为空,让这个连接资源马上被回收
				conn=null;
			}
		}
	}
}
  • 释放资源必须放在finally中,因为放在try块中,如果在前面发生了异常,那么这些资源将不会被正常回收。但是放在finally块中,无论是否会有异常,都会执行这个块中的代码,进行资源的释放。
  • 特别是Connection对象,它是非常稀有的资源(数据库中规定了最大的连接数),用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。

DriverManager :驱动管理类

主要作用:

  • 注册驱动

    • 实际开发中注册驱动会使用如下的方式:
    Class.forName("com.mysql.cj.jdbc.Driver");
    
  • 获得连接

    Connection conn=DriverManager.getConnection(String url,String username,String password)
    
    url写法: jdbc:mysql://localhost:3306/jdbc?.....
    
    • jdbc :协议
    • mysql:子协议
    • localhost :主机名
    • 3306:端口号

Connection:获得连接对象(Connection是接口)

主要作用:

  • 创建执行SQL语句的对象(调用下面的方法获得不同的执行SQL语句的对象)
    • Statement createStatement():获得执行SQL语句的对象,有SQL注入的漏洞存在。
    • PreparedStatement prepareStatement(String sql):这个接口继承了Statement,预编译SQL语句,解决SQL注入漏洞的问题。
    • CallableStatement prepareCall(String sql):执行SQL中的存储过程。
  • 进行事务的管理
    • setAutoCommit(boolean autoCommit):设置事务是否自动提交,true为自动,false为禁止自动
    • commit():事务提交
    • rollback():事务回滚

Statement(接口):执行SQL语句

主要作用:

  • 执行SQL语句

    • boolean execute(String sql):执行SQL,执行select语句返回true,其它返回false。
    • ResultSet executeQuery(String sql):执行SQL中的select语句
    • int executeUpdate(String sql):执行SQL中的insert/update/delete语句,返回影响的行数
  • 执行批处理操作

    • addBatch(String sql):添加到批处理
    • executeBatch():执行批处理,返回一个数组,保存着每执行一次sql语句影响的行数
    • clearBatch():清空批处理

PreparedStatement的这些函数在传参等方面略有不同

//用PreparedStatement的批处理执行多条delete操作
package com.lxc.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBCDemo3 {
	public static void main(String[] args) {
		
		String driver="com.mysql.cj.jdbc.Driver";
		String url="jdbc:mysql://localhost:3306/jdbctest?useSSL=false&serverTimezone=Asia/Shanghai";
		String username="root";
		String password="123456";
		Connection conn=null;
		PreparedStatement pstmt=null;
		try {
			Class.forName(driver);
			conn=DriverManager.getConnection(url,username,password);
			String sql="delete from user where username=?";
			pstmt=conn.prepareStatement(sql);
			//设置为不自动提交事务,自动提交不能实现真正的批处理
			conn.setAutoCommit(false);
			for(int i=1;i<10;i++) {
				pstmt.setString(1, "lewis"+String.valueOf(i));
				pstmt.addBatch();
			}
			int[] cnt=pstmt.executeBatch();//返回每次操作影响了几行的数组
			conn.commit();//手动提交事务
			System.out.println("总共执行了"+cnt.length+"次删除操作");
			for(int n:cnt) {
				System.out.println(n);
			}
			conn.setAutoCommit(true);//恢复自动提交事务
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if(pstmt!=null) {
				try {
					pstmt.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				pstmt=null;
			}
			if(conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				conn=null;
			}
		}
	}
}

ResultSet:结果集

结果集:其实就是查询语句(select)查询结果的封装。

主要作用:

  • 通过结果集获取到查询结果
  • next():针对不同类型的数据可以使用getXXX()获取数据,通用的获取方法:getObject()

提取工具类

properties文件:

properties文件的应用

  • 以键值对的形式保存信息,存储格式键=值
  • 我这里的文件是放在ClassPath根目录下的
driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbctest?useSSL=false&serverTimezone=Asia/Shanghai
username=root
password=123

工具类:

  • 一些可能变化的信息从属性文件中读取,如果改变,只需改变属性文件就行
package com.lxc.jdbc.utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class JDBCUtils {

	private static String driverClass;
	private static String url;
	private static String username;
	private static String password;
	
    //静态代码块,在类加载的时候执行
	static {
		//加载属性文件并解析
		Properties properties=new Properties();
		//使用类加载器的方式获取属性文件的输入流
		/*不能使用FileInputStream()的方式获得文件的输入流,这种方法只能用在java程序中,这种方式如果在web项目中程序会到tomcat/bin目录下加载该属性文件*/
		//jdbc.properties放在classpath根目录下,下面是到classpath根目录下读取jdbc.properties文件
		//同样也可以使用JDBCUtils.class.getResourceAsStream("/jdbc.properties")获得该文件输入流
        InputStream is=JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
		try {
            
            //解析.properties文件中的键值对,存进HashTable中,供后面的getProperty(Stirng key)使用
			properties.load(is);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//从属性文件中获取键值对中的值
        //本质是通过键获取HashTable中的值
		driverClass=properties.getProperty("driverClass");
		url=properties.getProperty("url");
		username=properties.getProperty("username");
		password=properties.getProperty("password");
	}
	
	/**
	 * 注册驱动的方法
	 */
	public static void loadDriver() throws ClassNotFoundException {
		Class.forName(driverClass);
	}
	
	/**
	 * 获得连接的方法
	 * @throws ClassNotFoundException 
	 * @throws SQLException 
	 */
	public static Connection getConnection() throws ClassNotFoundException, SQLException {
		JDBCUtils.loadDriver();
		return DriverManager.getConnection(url,username,password);
	}
	
	
	/**
	 * 释放资源
	 */
	
	public static void release(Connection conn,PreparedStatement pstmt) {
		if(pstmt!=null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			pstmt=null;
		}
		if(conn!=null) {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			conn=null;
		}
	}
	
	public static void release(Connection conn,PreparedStatement pstmt,ResultSet rs) {
		if(rs!=null) {
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			rs=null;
		}
		if(pstmt!=null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			pstmt=null;
		}
		if(conn!=null) {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			conn=null;
		}
	}
}
  • 类名.class.getClassLoader.getResourceAsStream(path)中路径问题
    • path不能以“/”开头。
    • path是从ClassPath(项目中的src,在Eclipse项目文件中的bin目录)根下获取。

[关于Class.getResource和ClassLoader.getResource的路径问题]

Class.getResource和ClassLoader.getResource的区别分析

使用工具类:

package com.lxc.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;

import com.lxc.jdbc.utils.JDBCUtils;

public class JDBCDemo3 {
	public static void main(String[] args) {
		
		Connection conn=null;
		PreparedStatement pstmt=null;
		try {
            //使用工具类中的方法获得连接对象
			conn=JDBCUtils.getConnection();
			String sql="delete from user where username=?";
			pstmt=conn.prepareStatement(sql);
			conn.setAutoCommit(false);
			for(int i=1;i<10;i++) {
				pstmt.setString(1, "lewis"+String.valueOf(i));
				pstmt.addBatch();
			}
			int[] cnt=pstmt.executeBatch();
			conn.commit();
			System.out.println("总共执行了"+cnt.length+"次删除操作");
			for(int n:cnt) {
				System.out.println(n);
			}
			conn.setAutoCommit(true);
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
            //使用工具类中的方法释放资源
			 JDBCUtils.release(conn,pstmt);
		}
	}
}

SQL注入漏洞的解决

产生的原因

public static boolean login(String username,String password){
		Connection conn = null;
		Statement stmt  = null;
		ResultSet rs = null;
		boolean flag = false;
		try{
			conn = JDBCUtils.getConnection();
			stmt = conn.createStatement();
			String sql = "select * from user where username = '"+username+"' and password = '"+password+"'";
			rs = stmt.executeQuery(sql);
			if(rs.next()){
				flag = true;
			}else{
				flag = false;
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			JDBCUtils.release(rs, stmt, conn);
		}
		return flag;
	}

上面是验证登录的代码,如果我们在传入参数的时候,在参数中包含一些SQL的关键字(and\or\注释)。都会被拼接到SQL语句中,成为SQL语句的一部分。比如login("aaa or '1==1'",password),无论密码是什么都会登录成功,因为传入参数后SQL语句变成了select * from user where username='aaa' or '1==1' and password='asdfj';,只要用户名对了就能登录成功。

SQL注入漏洞的解决

  • PreparedStatement能很好的解决这个问题
  • PreparedStatementStatement的子接口
  • PreparedStatement相对于Statement而言
    • PreparedStatement可以避免SQL注入的问题,因为在PreparedStatement中的SQL语句中会使用占位符,传入参数的时候,尽管传入一些含有SQL关键字的字符串,但是SQL语句并不会识别这些关键字,而仅仅是当作普通的字符串来处理。(SQL语句的格式已固定)
    • Statement会使数据库频繁编译SQL,可能会造成数据库缓冲区的溢出。而PreparedStatement可对SQL语句进行预编译(只要是同一种格式的SQL语句,只编译一次,可使用多次),从而提高数据库的执行效率。
    • 并且PreparedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化SQL语句的编写。
public static boolean login2(String username,String password){
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		boolean flag = false;
		try{
			conn = JDBCUtils.getConnection();
			String sql = "select * from user where username = ? and password = ?";
			pstmt = conn.prepareStatement(sql);
            //第一个参数代表第几个占位符,从1开始
			pstmt.setString(1, username);
			pstmt.setString(2, password);
			rs = pstmt.executeQuery();
			if(rs.next()){
				flag = true;
			}else{
				flag = false;
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			JDBCUtils.release(rs, pstmt, conn);
		}
		return flag;
	}

JDBC连接池—C3P0

  • C3P0是开源的JDBC连接池

  • 连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要他们的线程使用。

  • 原来没有连接池的缺点
    在这里插入图片描述
    没有连接池的时候,这些连接都是需要的时候创建,用完后马上销毁,所以会导致数据库一直在创建和销毁连接。

  • 有连接池的优点
    在这里插入图片描述
    在数据库连接池中存在着一定数量的连接,当需要使用的时候就取出来使用,等到用完后不会销毁,而是归还到连接池中,这样就避免了大量的创建连接耗费资源等问题。

    [外链图片转存失败(img-FGRZICiR-1565245636240)(assets/1565192749899.png)]

C3P0的使用

Jar包下载

  • 下载后需要把``c3p0-0.9.5.4.jar和依赖包mchange-commons-java-0.2.15.jar两个包导入到工程的ClassPath`中

配置c3p0-config.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <named-config name="mysql">
        <!-- 配置数据库用户名 -->
        <property name="user">root</property>
        <!-- 配置数据库密码 -->
        <property name="password"></property>
        <!-- 配置数据库链接地址 -->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/cdcol?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false&amp;serverTimezone=Asia/Shanghai</property>
        <!-- 配置数据库驱动 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <!-- 数据库连接池一次性向数据库要多少个连接对象 -->
        <property name="acquireIncrement">20</property>
        <!-- 初始化连接数 -->
        <property name="initialPoolSize">10</property>
        <!-- 最小连接数 -->
        <property name="minPoolSize">5</property>
        <!--连接池中保留的最大连接数。Default: 15 -->
        <property name="maxPoolSize">30</property>
        <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属於单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0 -->
        <property name="maxStatements">0</property>
        <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
        <property name="maxStatementsPerConnection">0</property>
        <!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能 通过多线程实现多个操作同时被执行。Default:3 -->
        <property name="numHelperThreads">3</property>
        <!--用户修改系统配置参数执行前最多等待300秒。Default: 300 -->
        <property name="propertyCycle">3</property>
        <!-- 获取连接超时设置 默认是一直等待单位毫秒 -->
        <property name="checkoutTimeout">1000</property>
        <!--每多少秒检查所有连接池中的空闲连接。Default: 0 -->
        <property name="idleConnectionTestPeriod">3</property>
        <!--最大空闲时间,多少秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
        <property name="maxIdleTime">10</property>
        <!--配置连接的生存时间,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待它close再断开。配置为0的时候则不会对连接的生存时间进行限制。 -->
        <property name="maxIdleTimeExcessConnections">5</property>
        <!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
        <property name="acquireRetryDelay">1000</property>
        <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。Default: null -->
        <property name="automaticTestTable">Test</property>
        <!-- 获取connnection时测试是否有效 -->
        <property name="testConnectionOnCheckin">true</property>
    </named-config>
</c3p0-config>
  • 在xml文件中可以配置多个数据库源连接信息,比如可以是mysql、oracle

    可以使用c3p0-config.xml文件配置连接信息,使用xml作为配置信息的话,comboPoolDataSource还可以接受一个String参数,这个参数的名称是在c3p0-config.xml文件中配置的,也可以使用无参的默认配置。这样就能连接不同的数据库、连接不同厂商的数据库

    public ComboPooledDataSource()
    //无参构造使用默认配置(使用xml中default‐config标签中对应的参数)
    
    public ComboPooledDataSource(String configName)
    //有参构造使用命名配置(configName:xml中配置的名称,使用xml中named‐config标签中对应的参数)
    //例如在xml中:<named-config name="oracle">各种配置信息</named-config>
    

    我的c3p0-config.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
    
      <!--默认配置,无参时使用这个-->
      <default-config>
      	<!--连接参数 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <!--使用&amp;对&进行转义-->
    	<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbctest?useSSL=false&amp;serverTimezone=Asia/Shanghai</property>
    	<property name="user">root</property>
    	<property name="password">123456</property>
    	<!-- 连接池参数 -->
    	<property name="initialPoolSize">5</property>
    	<property name="maxPoolSize">20</property>
      </default-config>
      
      <!--命名配置,配置mysql数据库,传入"mysql"参数使用这个  -->
      <named-config name="mysql">
            <!-- 配置数据库用户名 -->
            <property name="user">root</property>
            <!-- 配置数据库密码 -->
            <property name="password">123456</property>
            <!-- 配置数据库链接地址 -->
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false&amp;serverTimezone=Asia/Shanghai</property>
            <!-- 配置数据库驱动 -->
            <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
            <!-- 数据库连接池一次性向数据库要多少个连接对象 -->
            <property name="acquireIncrement">20</property>
            <!-- 初始化连接数 -->
            <property name="initialPoolSize">10</property>
            <!-- 最小连接数 -->
            <property name="minPoolSize">5</property>
            <!--连接池中保留的最大连接数。Default: 15 -->
            <property name="maxPoolSize">30</property>
       </named-config>
       
       <!-- 配置ORACLE数据库   -->
       <named-config name="oracle">各种配置信息</named-config>
    </c3p0-config>
    
  • 配置文件的连接方式

    • C3P0默认会在classpath(src)根目录读取配置文件c3p0-config.xml,不需要任何的设置。所以我们最好把配置文件放在classpath根目录下,而且文件名为c3p0-config.xml(不要更改)。

    • 也可以把c3p0-config.xml放到自己想放的位置,但必须在加载程序的时候进行设置

      System.setProperties(“ com.mchange.v2.c3p0.cfg.xml”,”config/c3p0-config.xml”); 
      

工具类:

package com.lxc.jdbc.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Utils {
	private static ComboPooledDataSource dataSource;
	
	static {
		dataSource=new ComboPooledDataSource();
        //也可以使用命名配置的数据库
        //dataSource=new ComboPooledDataSource("mysql");
	}
	
	/**
	 * 获得连接
	 * @throws SQLException 
	 */
	public static Connection getConnection() throws SQLException {
		return dataSource.getConnection();
	}
	
	/**
	 * 释放ResultSet和PreparedStatement资源,并把Connection归还到连接池中
	 * @param stmt
	 * @param conn
	 */
	public static void release(Statement stmt,Connection conn){
		if(stmt != null){
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			stmt = null;
		}
		if(conn != null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}
	
	public static void release(ResultSet rs,Statement stmt,Connection conn){
		if(rs!= null){
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
		if(stmt != null){
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			stmt = null;
		}
		if(conn != null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}
}

操作

package com.lxc.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.lxc.jdbc.utils.C3P0Utils;


public class C3P0Test {
	public static void main(String[] args) {
		
		Connection conn=null;
		PreparedStatement pstmt=null;
		ResultSet rs=null;
		
		try {
			//利用工具类获得连接
			conn=C3P0Utils.getConnection();
			String sql="select * from user";
			pstmt=conn.prepareStatement(sql);
			rs=pstmt.executeQuery();
			while(rs.next()) {
				System.out.println(rs.getInt("uid")+"  "+rs.getString("username")+"  "+rs.getString("password")+"  "+rs.getString("name"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			//以前调用conn.close()是释放资源,但是C3P0中对方法进行了升级,把销毁连接改成了把连接归还到连接池中
			C3P0Utils.release(rs, pstmt,conn);
		}	
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章