第7章WEB07- JDBC篇
? 使用JDBC完成對分類表的CRUD的操作(JDBC的回顧)
? 使用DBCP,C3P0連接池完成基本數據庫的操作(抽取工具類)
? 使用元數據抽取一個JDBC的框架.
? 能夠使用DBUtils完成CRUD的操作
教學導航
教學目標
掌握DBCP和C3P0連接池的使用並掌握連接池的原理.
掌握使用元數據抽取基本的JDBC通用方法的思想.
能夠使用DBUtils完成基本的CRUD的操作
教學方法
案例驅動法
1.1 上次課內容回顧:
MYSQL:
- 數據庫的概述:指的是一個文件系統,這個文件系統通過SQL訪問.
- 關系型數據庫:數據庫中存放的是實體之間的關系.
- 常見的關系型數據庫:MYSQL,Oracle,DB2,SQLServer,SQLite,SyBase
- 安裝和卸載:
- 存儲結構:
- 一臺電腦安裝了MYSQL的服務器軟件,那麽這臺電腦稱為是MYSQL數據庫服務器.一個軟件創建一個數據庫,有一個實體類創建一個表與之對應,實體的實例對象通常使用表中的記錄與之對應.
SQL:結構化查詢語言:
- 一臺電腦安裝了MYSQL的服務器軟件,那麽這臺電腦稱為是MYSQL數據庫服務器.一個軟件創建一個數據庫,有一個實體類創建一個表與之對應,實體的實例對象通常使用表中的記錄與之對應.
- SQL的分類:
- DDL:數據定義語言.create,alter,drop...
- DML:數據操縱語言.update,insert,delete
- DCL:數據控制語言.grant,if,...
- DQL:數據查詢語言.select
- SQL對數據庫CRUD:
- SQL對數據庫中表CRUD的操作
- SQL對數據庫中表的記錄的操作:
- update,insert,delete
- Select ... From ... Where ... Group by ... Having ... Order by ...;
- SQL的多表設計:
- 一對多:在多的一方創建外鍵執向一的一方的主鍵.
- 多對多:創建中間表,至少兩個字段作為外鍵執向多對多雙方的主鍵.
- 一對一:唯一外鍵對應,主鍵對應.
- SQL的多表的查詢:
- 連接查詢:
- 交叉連接:
- 內連接:
- 顯式內連接:inner join
- 隱式內連接:
- 外連接
- 左外連接:left outer join
- 右外連接:right outer join
- 子查詢:
1.2 案例一:使用JDBC完成CRUD的操作:1.2.1 需求:
對分類管理使用JDBC進行CRUD的操作.
1.2.2.1 技術分析:
【JDBC的概述】
? JDBC:Java DataBase Connectivity Java數據庫的連接.
- 連接查詢:
- 是SUN公司統一提供的一套接口規範(JDBC).各個數據庫生產商提供實現.
? 驅動:兩個硬件設備之間通信的橋梁.
【JDBC的開發步驟】
? 註冊驅動:
? 獲得連接:
? 獲得執行SQL語句對象:
? 釋放資源:
1.2.2.2 步驟分析:
【步驟一】:創建一個Java項目.
【步驟二】:引入mysql的驅動包.
【步驟三】:編寫代碼.
【步驟四】:完成CRUD的操作:
1.2.3 代碼實現:
工具類的抽取:
public class JDBCUtils {
/**
* 註冊驅動的方法
*/
public static void loadDriver(){
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 獲得連接的方法
*/
public static Connection getConnection(){
Connection conn = null;
try {
loadDriver();
conn = DriverManager.getConnection("jdbc:mysql:///web_07", "root", "123");
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 釋放資源的方法
*/
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;
}
}
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 class JDBCUtils {
private static final String driverClass;
private static final String url;
private static final String username;
private static final String password;
static {
Properties properties = null;
// 讀取屬性文件:使用Java中Properties的對象.
try{
InputStream is = new FileInputStream("src/jdbc.properties");
properties = new Properties();
properties.load(is);
}catch(Exception e){
e.printStackTrace();
}
driverClass = properties.getProperty("driverClass");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
}
/**
* 註冊驅動的方法
*/
public static void loadDriver(){
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 獲得連接的方法
*/
public static Connection getConnection(){
Connection conn = null;
try {
loadDriver();
conn = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 釋放資源的方法
*/
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;
}
}
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;
}
}
}
參見JDBCDemo2類:
1.2.4 總結:
1.2.4.1 JDBC的API:
【Connection】
? 創建執行SQL的對象:
? 進行事務管理:
【Statement】
? 執行SQL語句:
? 執行批處理:
【ResultSet】
? 獲得結果集中的數據:
- getXXX(int idx);
- select cname,cid from category;
- getXXX(String name);
? 默認情況下:next(); - 正常的情況下結果集只能向下的.
1.3 案例二:使用連接池改造JDBC的工具類:
1.3.1 需求:
傳統JDBC的操作,對連接的對象銷毀不是特別好.每次創建和銷毀連接都是需要花費時間.可以使用連接池優化的程序. - 在程序開始的時候,可以創建幾個連接,將連接放入到連接池中.用戶使用連接的時候,可以從連接池中進行獲取.用完之後,可以將連接歸還連接池.
1.3.2 分析:
1.3.2.1 技術分析:
【自定義連接池】(了解) - SUN公司提供了一個連接池的接口.(javax.sql.DataSource).
- 定義一個連接池:實現這個接口.
- 使用List集合存放多個連接的對象.
【自定義連接池的代碼】
public class MyDataSource implements DataSource{
// 創建一個List集合用於存放多個連接對象.
private List<Connection> list = new ArrayList<Connection>();
// 在程序開始的時候,初始化幾個連接,將連接存放到list中.
public MyDataSource() {
// 初始化3個連接:
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
@Override
// 獲得連接的方法:
public Connection getConnection() throws SQLException {
if(list.size() <= 0){
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
return conn;
}
// 歸還連接的方法:
public void addBack(Connection conn){
list.add(conn);
}
...
}
【自定義連接池中問題及如何解決】
? 問題?
1.如果使用自定義連接池,那麽需要額外記住自定義連接池中的API.
2.能不能使用面向接口的編程方式.
? 解決:
不額外提供API方法,就可以解決上述兩個問題!!!
能不能還調用Connection的close方法.能不能增強Connection的close方法,原有的銷毀變為歸還!!!
? 如何增強Connection的close方法:
- 增強一個Java類中的某個方法有幾種方式???
- 一種方式:繼承的方式.
- 能夠控制這個類的構造的時候,才可以使用繼承.
- 二種方式:裝飾者模式方式.
- 包裝對象和被包裝的對象都要實現相同的接口.
- 包裝的對象中需要獲得到被包裝對象的引用.
***** 缺點:如果接口的方法比較多,增強其中的某個方法.其他的功能的方法需要原有調用.
- 三種方式:動態代理的方式.
- 被增強的對象實現接口就可以.
【繼承和裝飾者的案例】
/**
- 被增強的對象實現接口就可以.
- 一種方式:繼承的方式.
- 繼承的方式增強一個類中某個方法:
*/
class Man{
public void run(){
System.out.println("跑....");
}
}
class SuperMan extends Man{
public void run(){
// super.run();
System.out.println("飛....");
}
}
/** - 使用裝飾者的方式完成類的方法的增強
*/
interface Waiter{
public void server();
}
class Waiteress implements Waiter{@Override
br/>@Override
System.out.println("服務...");
}
}
class WaiteressWrapper implements Waiter{
private Waiter waiter;
public WaiteressWrapper(Waiter waiter) {
this.waiter = waiter;
}
@Override
public void server() {
System.out.println("微笑...");
// this.waiter.server();
}
}
【使用裝飾者模式增強Connection的close方法】
public class MyConnection implements Connection{
private Connection conn;
private List<Connection> list;
public MyConnection(Connection conn,List<Connection> list) {
this.conn = conn;
this.list = list;}
@Override
br/>}
@Override
}
...
}
連接池的getConnection方法:
@Override
br/>list.add(conn);
}
...
}
連接池的getConnection方法:
@Override
public Connection getConnection() throws SQLException {
if(list.size() <= 0){
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
MyConnection myConn = new MyConnection(conn, list);
return myConn;
}
【常見的開源的數據庫連接池】:
? DBCP:
DBCP(DataBase connection pool),數據庫連接池。是 apache 上的一個 java 連接池項目,也是 tomcat 使用的連接池組件。單獨使用dbcp需要2個包:commons-dbcp.jar,commons-pool.jar由於建立數據庫連接是一個非常耗時耗資源的行為,所以通過連接池預先同數據庫建立一些連接,放在內存中,應用程序需要建立數據庫連接時直接到連接池中申請一個就行,用完後再放回去。
? C3P0:
C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規範和JDBC2的標準擴展。目前使用它的開源項目有Hibernate,Spring等。
? Tomcat內置連接池:
【DBCP連接池的使用】
第一步:引入DBCP連接池的jar包.
第二步:編寫DBCP代碼:
- 手動設置參數:
-
配置文件設置參數:【DBCP連接池的使用】
@Test
br/>【DBCP連接池的使用】
@Test
-
手動方式:
/
public void demo1(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///web_07");
dataSource.setUsername("root");
dataSource.setPassword("123");
try{
// 獲得連接:
conn = dataSource.getConnection();
// 編寫SQL:
String sql = "select from category";
// 預編譯SQL:
stmt = conn.prepareStatement(sql);
// 執行SQL:
rs = stmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("cid")+" "+rs.getString("cname"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs,stmt, conn);
}
}@Test
/** -
配置文件方式:
*/
public void demo2(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
Properties properties = new Properties();try{ properties.load(new FileInputStream("src/dbcpconfig.properties")); DataSource dataSource = BasicDataSourceFactory.createDataSource(properties); // 獲得連接: conn = dataSource.getConnection(); // 編寫SQL: String sql = "select * from category"; // 預編譯SQL: stmt = conn.prepareStatement(sql); // 執行SQL: rs = stmt.executeQuery(); while(rs.next()){ System.out.println(rs.getInt("cid")+" "+rs.getString("cname")); } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(rs,stmt, conn); }
}
【C3P0連接池的使用】
第一步:引入C3P0連接池的jar包.
第二步:編寫代碼:- 手動設置參數:
- 配置文件設置參數:
【C3P0改造工具類】
public class JDBCUtils2 {
private static final ComboPooledDataSource DATA_SOURCE =new ComboPooledDataSource();
/**
- 獲得連接的方法
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = DATA_SOURCE.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
...
}
1.4 案例三:手動抽取一個DBUtils的工具類:
1.4.1 需求:
每次進行JDBC的CURD的操作的時候,有很多的代碼都是相似的.可以不可以抽取工具類.完成一些通用性的代碼?
1.4.2 分析:
1.4.2.1 技術分析:
【JDBC的元數據MataData】(了解)-- 編寫通用性較高的代碼.
? DatabaseMetaData:獲得數據庫連接的信息,獲得數據庫的表的信息.- 獲得數據庫元數據:Connection中getMetaData();
? ParameterMetaData:獲得SQL中的參數的個數及類型. - 獲得參數元數據:PreparedStatement中getParameterMetaData()
? ResultSetMetaData:獲得結果集中的列名及列的類型. - 獲得結果集元數據:ResultSet中getMeta()【元數據的使用】
@Test
br/>【元數據的使用】
@Test
- 獲得數據庫元數據:Connection中getMetaData();
-
數據庫元數據
*/
public void demo1(){
Connection conn = null;
conn = JDBCUtils2.getConnection();
// 獲得數據庫元數據:
try {
DatabaseMetaData metaData = conn.getMetaData();
System.out.println("獲得驅動名稱:"+metaData.getDriverName());
System.out.println("獲得驅動URL:"+metaData.getURL());
System.out.println("獲得用戶名:"+metaData.getUserName());// 獲得表中的主鍵: ResultSet rs = metaData.getPrimaryKeys(null, null, "category"); if(rs.next()){ String name = rs.getString("COLUMN_NAME"); System.out.println(name); } } catch (SQLException e) { e.printStackTrace(); }
}
@Test
/** -
參數元數據:
*/
public void demo2(){
Connection conn = null;
PreparedStatement stmt = null;
try{
conn = JDBCUtils2.getConnection();
String sql = "update category set cname = ? where cid = ?";
stmt = conn.prepareStatement(sql);
ParameterMetaData metaData = stmt.getParameterMetaData();
int count = metaData.getParameterCount();
System.out.println(count);
}catch(Exception e){}
}
@Test
/** -
結果集元數據:
/
public void demo3(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try{
conn = JDBCUtils2.getConnection();
String sql = "select from category";
stmt = conn.prepareStatement(sql);
rs = stmt.executeQuery();ResultSetMetaData metaData = rs.getMetaData(); int count = metaData.getColumnCount(); for(int i = 1;i<=count ;i++){ String name = metaData.getColumnName(i); String type = metaData.getColumnTypeName(i); System.out.println(name+type); } }catch(Exception e){ }
}
-
第7章WEB07- JDBC篇