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