22.JDBC開發(fā)(5)編寫JDBC框架(JavaEE筆記)

一、元數(shù)據(jù)(DataBaseMetaData)

  • 元數(shù)據(jù):數(shù)據(jù)庫、表、列的定義信息

  • Connection.getDatabaseMetaData()獲得元數(shù)據(jù)

  • DataBaseMetaData對(duì)象方法
    getURL():返回一個(gè)String類對(duì)象,代表數(shù)據(jù)庫的URL。
    getUserName():返回連接當(dāng)前數(shù)據(jù)庫管理系統(tǒng)的用戶名。
    getDatabaseProductName():返回?cái)?shù)據(jù)庫的產(chǎn)品名稱。
    getDatabaseProductVersion():返回?cái)?shù)據(jù)庫的版本號(hào)。
    getDriverName():返回驅(qū)動(dòng)驅(qū)動(dòng)程序的名稱。
    getDriverVersion():返回驅(qū)動(dòng)程序的版本號(hào)。
    isReadOnly():返回一個(gè)boolean值,指示數(shù)據(jù)庫是否只允許讀操作。

工程(day16
例1.獲取數(shù)據(jù)庫的元信息(Demo4.java

package cn.itcast.demo;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import cn.itcast.utils.JdbcUtils_C3P0;

public class Demo4 {

    public static void main(String[] args) throws SQLException {
        
        Connection connection = JdbcUtils_C3P0.getConnection();
        DatabaseMetaData metaData = connection.getMetaData();
        
        System.out.println(metaData.getDatabaseProductName());
        System.out.println(metaData.getDriverName());   
    }
}

二、元數(shù)據(jù)(ParameterMetaData)

  • PreparedStatement.getParameterMetaData()獲得代表PreparedStatement元數(shù)據(jù)的ParameterMetaData對(duì)象

  • ParameterMetaData對(duì)象方法
    getParameterCount()獲得指定參數(shù)的個(gè)數(shù)
    getParameterType(int param) 獲得指定參數(shù)的sql類型

例2.獲取數(shù)據(jù)庫的元信息(Demo5.java

package cn.itcast.demo;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import cn.itcast.utils.JdbcUtils_C3P0;

public class Demo5 {

    public static void main(String[] args) throws SQLException {
        Connection connection = JdbcUtils_C3P0.getConnection();
        String sql = "select * from user where name = ? and password = ?";
        PreparedStatement ps = connection.prepareStatement(sql);
        ParameterMetaData pm = ps.getParameterMetaData();
        //得到參數(shù)的個(gè)數(shù)
        System.out.println(pm.getParameterCount());
        System.out.println(pm.getParameterType(1));
    }
}

三、元數(shù)據(jù)(ResultSetMetaData)

  • ResultSet.getMetaData()獲得代表ResultSet對(duì)象元數(shù)據(jù)的ResultSetMetaData對(duì)象。

  • ResultSetMetaData對(duì)象
    getColumnCount()返回resultset對(duì)象的列數(shù)
    getColumnName(int column)獲得指定列的名稱
    getColumnTypeName(int column)獲得指定列的類型

例3.獲取數(shù)據(jù)庫元信息(Demo6.java

package cn.itcast.demo;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import cn.itcast.utils.JdbcUtils_C3P0;

public class Demo6 {

    public static void main(String[] args) throws SQLException {
        Connection connection = JdbcUtils_C3P0.getConnection();
        String sql = "select * from account";
        PreparedStatement pst = connection.prepareStatement(sql);
        ResultSet result = pst.executeQuery();
        ResultSetMetaData metadata = result.getMetaData();
        System.out.println(metadata.getColumnCount());//有3列數(shù)據(jù)
        System.out.println(metadata.getColumnName(1));

    }
}

四、使用元數(shù)據(jù)簡化JDBC代碼

  • 業(yè)務(wù)背景:系統(tǒng)中所有實(shí)體對(duì)象都涉及到基本的CRUD操作。所有實(shí)體的CUD操作代碼基本相同,僅僅是發(fā)送給數(shù)據(jù)庫的sql語句不同而已,因此可以把CUD操作的所有相同代碼抽取到工具類的一個(gè)update方法中,并定義參數(shù)接收變化的sql語句。

  • 實(shí)體的R操作,除sql語句不同之外,根據(jù)操作的實(shí)體不同,對(duì)ResultSet的映射也各不相同,因此可定義一個(gè)query方法,除以參數(shù)形式接收變化的sql語句外,可以使用策略模式由query方法的調(diào)用者決定如何把ResultSet中的數(shù)據(jù)映射到實(shí)體對(duì)象中。

這里我們通過編寫一個(gè)自己的框架來進(jìn)行說明(工程day16):
我們先定義一個(gè)bean(使用的是day16這個(gè)數(shù)據(jù)庫):
Account.java

package cn.itcast.domain;
public class Account {
    private int id ;
    private String name ;
    private float money ;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getMoney() {
        return money;
    }
    public void setMoney(float money) {
        this.money = money;
    }
}

對(duì)數(shù)據(jù)庫工具類進(jìn)行改進(jìn):
JdbcUtils.java

package cn.itcast.utils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtils {
    
    private static ComboPooledDataSource ds = null;
    
    static {
        try {
            ds = new ComboPooledDataSource();
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    
    public static Connection getConnection() throws SQLException{
        return ds.getConnection();
        
    }
    
    public static void release(Connection conn, Statement ps , ResultSet result){
        if(result != null){
            try {
                result.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            result = null;
        }
        if(ps != null){
            try {
                ps.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            ps = null;
        }
        if(conn != null){
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            conn = null;
        }
    }
    
    //除了sql語句和相關(guān)的參數(shù)不同外其他的代碼都一樣,這里可以替換我們之前在dao層中寫的增刪改方法
    public static void update(String sql, Object[] params) throws SQLException{
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet result = null ;
        try{
            connection = getConnection();
            ps = connection.prepareStatement(sql);
            for(int i = 0; i < params.length; i++){
                ps.setObject(i + 1, params[i]);
            }
            ps.executeUpdate();
        }finally{
            release(connection, ps, result);
        }
    }
    
    public static Object query(String sql, Object params[], ResultSetHandler rh) throws SQLException{
        
        Connection conn = null ;
        PreparedStatement ps = null ;
        ResultSet result = null;
        try{
            conn = getConnection();
            ps = conn.prepareStatement(sql);
            for(int i = 0; i < params.length; i++){
                ps.setObject(i + 1, params[i]);
            }
            result = ps.executeQuery();
            return rh.handler(result);
        }finally{
            release(conn, ps, result);
        }
    }
}

說明:

  • 1.對(duì)于增刪改方法來說,可以將其相同部分抽取出來,其不同的地方只有sql語句和相關(guān)的參數(shù)值,這里我們讓用戶將這兩個(gè)參數(shù)值傳遞進(jìn)來即可。
  • 2.對(duì)于查方法來說就有點(diǎn)不一樣了,首先是最后執(zhí)行的方法是executeQuery方法,其次是有返回值,當(dāng)然對(duì)于前一個(gè)不同來說,我們只需要將executeUpdate換成executeQuery方法即可,但是對(duì)于結(jié)果集的處理來說就有點(diǎn)麻煩了。首先我們不知道返回的結(jié)果集是什么,當(dāng)然這可以使用Object代替,但是我們不知道針對(duì)結(jié)果集如何處理,于是這里我們將結(jié)果集的處理方式交給用戶。我們定義了一個(gè)接口讓用戶來實(shí)現(xiàn):
    ResultSetHandler.java
package cn.itcast.utils;
import java.sql.ResultSet;
public interface ResultSetHandler {
    public Object handler(ResultSet result);
}
  • 3.可以發(fā)現(xiàn),讓用戶去寫處理代碼同樣不太好,這樣我們的框架就沒有達(dá)到簡化的效果,同時(shí)我們也看到一般用戶查到相關(guān)的數(shù)據(jù)之后是會(huì)封裝到一個(gè)javabean中去的,于是這里我們幫用戶實(shí)現(xiàn)一個(gè)基本的處理方法:
    BeanHandler.java
package cn.itcast.dao;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import cn.itcast.utils.ResultSetHandler;
public class BeanHandler implements ResultSetHandler {
    
    private Class clazz;
    public BeanHandler() {
    }
    public BeanHandler(Class clazz) {
        this.clazz = clazz;
    }
    
    @Override
    public Object handler(ResultSet result) {
        //這里有個(gè)條件是表中字段名和bean中的屬性名必須一致
        try{
            if(!result.next()){
                return null;
            }
            //實(shí)例化對(duì)象
            Object bean = clazz.newInstance();
            ResultSetMetaData metaData = result.getMetaData();
            int columnCount = metaData.getColumnCount();//得到結(jié)果集中數(shù)據(jù)列數(shù)
            for(int i = 0; i < columnCount; i++){
                //獲得每一列的列名
                String columnName = metaData.getColumnName(i + 1);
                Object columnData = result.getObject(i + 1);
                Field f = clazz.getDeclaredField(columnName);//反射出類上列名對(duì)應(yīng)的屬性
                f.setAccessible(true);//暴力反射
                f.set(bean, columnData);//在賦值的時(shí)候可以使用beanUtils框架工具
            }
            return bean;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }
}

說明:對(duì)于類的類型我們是不知道的,這里我們讓用戶在new的時(shí)候傳遞進(jìn)來。然后我們就可以通過反射的方式將查詢到的數(shù)據(jù)封裝到用戶傳遞進(jìn)來的類的各個(gè)屬性上去,但是這里實(shí)現(xiàn)的并不是很好,必須讓表字段名和bean中的屬性名一致。

  • 4.當(dāng)然很顯然,有時(shí)候查出來的數(shù)據(jù)是一個(gè)集合,而不是像上面那種一個(gè)單一對(duì)象,于是我們又實(shí)現(xiàn)了針對(duì)集合的處理類:
    BeanListHandler.java
package cn.itcast.dao;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
import cn.itcast.utils.ResultSetHandler;
public class BeanListHandler implements ResultSetHandler {
    
    private Class clazz;
    public BeanListHandler() {
    }
    
    public BeanListHandler(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public Object handler(ResultSet result) {
        try {
            List list = new ArrayList();
            while(result.next()){
                Object bean = clazz.newInstance();
                ResultSetMetaData metaData = result.getMetaData();
                int count = metaData.getColumnCount();
                for(int i = 0; i < count; i++){
                    String name = metaData.getColumnName(i + 1);
                    Object value = result.getObject(name);
                    Field f = bean.getClass().getDeclaredField(name);
                    f.setAccessible(true);
                    f.set(bean, value);
                }
                list.add(bean);
            }
            return list.size() > 0? list:null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
  • 5.此時(shí)我們的增刪改查工具類就可以簡化為如下:
    AccountDao.java
package cn.itcast.dao;
import java.sql.SQLException;
import java.util.List;
import cn.itcast.domain.Account;
import cn.itcast.utils.JdbcUtils;
public class AccountDao {
    public void add(Account account) throws SQLException{
        String sql = "insert into account(name , money) values(?, ?)";
        Object[] params = {account.getName(), account.getMoney()};
        JdbcUtils.update(sql, params);
    }
    
    public void delete(int id ) throws SQLException{
        String sql = "delete from account where id = ?";
        Object[] params = {id};
        JdbcUtils.update(sql, params);
    }
    
    public void update(Account account) throws SQLException{
        String sql = "update account set name = ?, money = ? where id = ?";
        Object params[] = {account.getName(), account.getMoney(), account.getId()};
        JdbcUtils.update(sql, params);
    }
    
    public Account find(int id ) throws SQLException{
        String sql = "select * from account where id = ?";
        Object params[] = {id};
        return (Account) JdbcUtils.query(sql, params, new BeanHandler(Account.class));
    }
    
    public List getAll() throws SQLException{
        String sql = "select * from account";
        Object params[] = {};
        return (List)JdbcUtils.query(sql, params, new BeanListHandler(Account.class));
    }
}
  • 6.測(cè)試
    Demo7.java
package cn.itcast.demo;
import java.sql.SQLException;
import java.util.List;
import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
public class Demo7 {

    public static void main(String[] args) throws SQLException {
        AccountDao dao = new AccountDao();
        Account a = dao.find(1);
        System.out.println(a.getName());
        List list = dao.getAll();
        System.out.println(list.size());
    }
}

最后:這里編寫一個(gè)JDBC框架的目的并不是要實(shí)現(xiàn)一個(gè)真正可用的框架,主要是加深對(duì)JDBC各類框架實(shí)現(xiàn)的原理的理解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容