JDBC和数据库连接池的使用
1.JBDC和驱动程序
JDBC提供了一种基准(JAVA API),据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
我们安装好数据库之后,应用程序是没法连接使用数据库的,需要通过驱动程序来使用,驱动程序就是实现了JDBC的jar,不同的数据库有不同的驱动程序。
2.JDBC中的接口
1. Driver接口
Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:
装载MySql驱动:Class.forName(“com.mysql.jdbc.Driver”);
2.Connection接口
Connection与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定义的数据库Connection连接上。
连接MySql数据库:Connection conn = DriverManager.getConnection(“jdbc:mysql://host:port/database”, “user”, “password”);
常用方法:
- createStatement():创建向数据库发送sql的statement对象。
- prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
- setAutoCommit(boolean autoCommit):设置事务是否自动提交。
- commit() :在链接上提交事务。
- rollback() :在此链接上回滚事务。
3.Statement接口
用于执行静态SQL语句并返回它所生成结果的对象。
二种Statement类:
- Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。
- PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。
常用Statement方法:
-
executeQuery(String sql):运行select语句,返回ResultSet结果集。
-
executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
4.ResultSet接口
ResultSet提供检索不同类型字段的方法,常用的有:
- getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。
ResultSet还提供了对结果集进行滚动的方法:
- next():移动到下一行
使用后依次关闭对象及连接:ResultSet → Statement → Connection
3.使用JDBC
先导入包:mysql-connector-java-5.1.37-bin.jar
通常使用jdbc的步骤为:加载JDBC驱动程序 → 建立数据库连接Connection → 创建执行SQL的语句Statement → 处理执行结果ResultSet → 释放资源
public class AttackDemo2 {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql://localhost:3306/student";
String user="root";
String password="123456";
Connection con=DriverManager.getConnection(url, user, password);
Scanner sc=new Scanner(System.in);
String username=sc.nextLine();
String pass=sc.nextLine();
//preparestatement方法中的sql语句里面的参数值必须是问号占位符,表示预编译。
String sql="select * from user where name=? and psword=?";
/*通过Connection的PreparedStatement prepareStatement(String sql)来创建SQL预编译对象
表示预编译的 SQL 语句的对象。
SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。
*/
PreparedStatement ps =con.prepareStatement(sql);
//通过PreparedStatement中的setObject(int index,Object x)来设置参数值,这样就不存在SQL注入攻击,而且查询效率高
ps.setObject(1, username);
ps.setObject(2, pass);
ResultSet rls=ps.executeQuery(sql);
while(rls.next()) {
System.out.println(rls.getInt("id")+" "+rls.getString("name")+" "+
rls.getString("psword"));
}
rls.close();
ps.close();
con.close();
}
}
实际开发中,获取连接和释放链接非常消耗系统资源所以一般都是通过数据库连接池来获取连接。
4.数据库连接池
连接池的作用就是为了提高性能。
连接池的作用:连接池是将已经创建好的连接保存在池中,当有请求来时,直接使用已经创建好的连接对数据库进行访问。这样省略了创建连接和销毁连接的过程。这样性能上得到了提高。
基本原理是这样的:
(1)建立数据库连接池对象(服务器启动)
(2)按照事先指定的参数创建初始数量的数据库连接(即:空闲连接数)。
(3)对于一个数据库访问请求,直接从连接池中得到一个连接。如果数据库连接池对象中没有空闲的连接,且连接数没有达到最大(即:最大活跃连接数),创建一个新的数据库连接。
(4)存取数据库。
(5)关闭数据库,释放所有数据库连接(此时的关闭数据库连接,并非真正关闭,而是将其放入空闲队列中。如实际空闲连接数大于初始空闲连接数则释放连接)。
(6)释放数据库连接池对象(服务器停止、维护期间,释放数据库连接池对象,并释放所有连接)
连接池放了N个Connection对象,本质上放在内存当中,在内存中划出一块缓存对象,应用程序每次从池里获得Connection对象,而不是直接从数据里获得,这样不占用服务器的内存资源。
1.c3p0连接池
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring
准备步骤
1.首先导入四个包:c3p0-0.9.5.2.jar、mchange-commons-java-0.2.12.jar、mysql-connector-java-5.1.37-bin.jar、commons-dbutils-1.7.jar。
2.添加一个配置文件c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!--默认配置-->
<default-config>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
<!--配置连接池mysql-->
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/student</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</named-config>
<!--配置连接池2-->
......
<!--配置连接池3-->
......
<!--配置连接池4-->
......
</c3p0-config>
3.创建一个c3p0工具类
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class c3p0Utils {
private static DataSource dataSource = new ComboPooledDataSource("mysql");
//声明了一个 ThreadLocal 变量t1,t1可以为每一个引用该类的线程保存Connection类型的对象
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
// 直接可以获取一个连接池
public static DataSource getDataSource() {
return dataSource;
}
// 获取连接对象
public static Connection getConnection() throws SQLException {
Connection con = tl.get();
if (con == null) {
con = dataSource.getConnection();
tl.set(con);
}
return con;
}
// 开启事务
public static void startTransaction() throws SQLException {
Connection con = getConnection();
if (con != null) {
con.setAutoCommit(false);
}
}
// 事务回滚
public static void rollback() throws SQLException {
Connection con = getConnection();
if (con != null) {
con.rollback();
}
}
// 提交并且 关闭资源及从ThreadLocall中释放
public static void commitAndRelease() throws SQLException {
Connection con = getConnection();
if (con != null) {
con.commit(); // 事务提交
con.close();// 关闭资源
tl.remove();// 从线程绑定中移除
}
}
// 关闭资源方法
public static void closeConnection() throws SQLException {
Connection con = getConnection();
if (con != null) {
con.close();
}
}
public static void closeStatement(Statement st) throws SQLException {
if (st != null) {
st.close();
}
}
public static void closeResultSet(ResultSet rs) throws SQLException {
if (rs != null) {
rs.close();
}
}
}
4.创建一个测试类
import java.sql.SQLException;
import java.util.Arrays;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import 工具类.c3p0Utils;
public class c3p0Test {
private static QueryRunner qr=new QueryRunner(c3p0Utils.getDataSource());
public static void main(String[] args) throws SQLException {
select();
}
public static void select() {
String sql="select * from product where id=?";
try {
Object [] obj=qr.query(sql, new ArrayHandler(),"1");
System.out.println(Arrays.toString(obj));
c3p0Utils.closeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
分析:
1.数据源(DataSource)制SUN制定的用于获取数据库连接的规范接口。它存在于 javax.sql包中,用来代替 DriverManager 的方式来获取连接。实现类是由第三方提供
2. new ComboPooledDataSource(“mysql”)就是实现了DataSource接口,通过构造方法的参数,读取c3p0-config.xml配置文件的参数。
3.DbUtils(org.apache.commons.dbutils.DbUtils)是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。DbUtils类主要负责装载驱动、关闭连接的常规工作。
4. QreryRunner类(org.apache.commons.dbutils.QueryRunner) 是Dbutils的核心类之一,它显著的简化了SQL操作。和ResultSetHandler协同工作将使编码量大为减少。
5. qr.query(String sql, ResultSetHandler rsh ,Object… params),其中的ArrayHandler实现了ResultSetHandler接口。读取结果集并返回一个Object数组。
除了ArrayHandler还有以下结果集类:
- ArrayListHandler:把结果集中的每一行数据都转成一个对象数组,再存放到List中。
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。//重点
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。//重点
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List