從零寫一個(gè)Java WEB框架(二)Server層 優(yōu)化

  • 該系列近她,其實(shí)是對(duì)《架構(gòu)探險(xiǎn)》這本書的實(shí)踐叉瘩。本人想記錄自己的學(xué)習(xí)心得所寫下的。
  • 從一個(gè)簡單的Servlet項(xiàng)目開始起步粘捎。對(duì)每一層進(jìn)行優(yōu)化薇缅,然后形成一個(gè)輕量級(jí)的框架危彩。
  • 每一篇,都是針對(duì)項(xiàng)目的不足點(diǎn)進(jìn)行優(yōu)化的泳桦。
  • 項(xiàng)目已放上github
  • 上一篇地址:上一篇

開篇

本篇針對(duì)項(xiàng)目中的Server層進(jìn)行優(yōu)化介紹汤徽。先來看看Server層的目前情況。

image.png

可以看到由于業(yè)務(wù)簡單灸撰。只有一個(gè)Server類谒府。
以下是CustomerService類的靜態(tài)代碼塊
image.png

以下是CustomerService類的其中一個(gè)方法

image.png

缺點(diǎn):

  • 將數(shù)據(jù)庫的加載是一個(gè)公共代碼塊,應(yīng)該提取出來浮毯,不應(yīng)該寫在某個(gè)類中完疫。
  • Service 層是對(duì)業(yè)務(wù)邏輯的處理,但是在方法中债蓝,對(duì)查詢的結(jié)果進(jìn)行了大量的操作壳鹤。這樣使業(yè)務(wù)層的代碼太過渾濁。應(yīng)該講查詢結(jié)果的處理封裝起來饰迹。

代碼實(shí)現(xiàn)

將Server層的對(duì)數(shù)據(jù)庫操作的代碼提取出來

要求:

  • 建立一個(gè)DatabaseUtil類 封裝對(duì)數(shù)據(jù)庫的加載
  • 再創(chuàng)建兩個(gè)方法getConnection()獲取數(shù)據(jù)庫連接 和closeConnection() 關(guān)閉數(shù)據(jù)庫連接芳誓。
  • 對(duì)數(shù)據(jù)庫操作的代碼實(shí)現(xiàn)封裝起來

實(shí)現(xiàn):

  • 為了能更好地封裝JDBC的操作。這里還使用了Apache Common項(xiàng)目中的DbUtils類庫蹦锋,這個(gè)類庫可以更好地幫我處理數(shù)據(jù)庫數(shù)據(jù)兆沙。
  • DatabaseUtil 類就實(shí)現(xiàn)了加載驅(qū)動(dòng)欧芽,獲取連接莉掂,和對(duì)數(shù)據(jù)庫進(jìn)行操作并對(duì)查詢結(jié)果進(jìn)行處理,然后結(jié)果給Server層的3個(gè)主要功能千扔。

代碼如下:


public class DatabaseUtil {
    private static final String DRIVER;
    private static final String URL;
    private static final String USERNAME;
    private static final String PASSWORD;

//    利用Apache的一個(gè)工具庫DbUtils 對(duì)JDBC的封裝
    private static final QueryRunner QUERY_RUNNER = new QueryRunner();


    private static final Logger logger = LoggerFactory.getLogger(DatabaseUtil.class);


    static{
        System.out.println("配置加載");
        Properties conf = PropsUtil.loadProps("config.properties");
        DRIVER = conf.getProperty("jdbc.driver");
        URL = conf.getProperty("jdbc.url");
        USERNAME = conf.getProperty("jdbc.username");
        PASSWORD = conf.getProperty("jdbc.password");

        //JDBC流程第一步 加載驅(qū)動(dòng)
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            logger.error("加載jdbc驅(qū)動(dòng)失敗",e);
            e.printStackTrace();
        }
    }


    public static Connection getConnection() {

        try {
            Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            return connection;
        } catch (SQLException e) {
            logger.error("獲取連接失敗",e);
            e.printStackTrace();
        }
        return null;
    }

    public static void closeConnection(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                logger.error("關(guān)閉連接失敗",e);
                e.printStackTrace();
            }
        }
    }


    /**
     * 查詢獲取實(shí)體列表
     * @param entityClass 返回對(duì)象
     * @param sql    查詢語句
     * @param params 查詢條件
     *
     *
     */
    public static <T> List<T> queryEntityList(Class<T> entityClass,String sql, Object... params) {
        List<T> entityList = null;
        Connection con = null;

        try {
            con = getConnection();
            entityList =
                    QUERY_RUNNER.query(con, sql, new BeanListHandler<T>(entityClass), params);

        } catch (SQLException e) {
            logger.error("查詢失敗",e);
            e.printStackTrace();
        }finally {
            closeConnection(con);
        }
        return entityList;
    }

    /**
     *  查詢實(shí)體
     * @param tClass
     * @param sql
     * @param params
     * @param <T>
     * @return
     */
    public static <T> T queryEntity(Class<T> tClass, String sql, Object... params) {
        T entity = null;
        Connection con = null;

        try {
            con = getConnection();
            entity =
                    QUERY_RUNNER.query(con, sql, new BeanHandler<T>(tClass), params);

        } catch (SQLException e) {
            logger.error("查詢失敗",e);
            e.printStackTrace();
        }finally {
            closeConnection(con);
        }
        return entity;
    }

    /**
     *  執(zhí)行查詢語句
     *  返回一個(gè)List對(duì)象憎妙。Map 表示列名與列值得映射關(guān)系
     * @param sql
     * @param params
     * @return
     */

    public static List<Map<String, Object>> executeQuery(String sql, Object... params) {
        List<Map<String,Object>> entity = null;
        Connection con = null;

        try {
            con = getConnection();
            entity =
                    QUERY_RUNNER.query(con, sql, new MapListHandler(), params);

        } catch (SQLException e) {
            logger.error("查詢失敗",e);
            e.printStackTrace();
        }finally {
            closeConnection(con);
        }
        return entity;
    }


    /**
     *  執(zhí)行更新語句(update,insert,delete)
     * @param sql
     * @param params
     * @return
     */
    public static int executeUpdate(String sql, Object... params) {
        int rows=0;
        Connection con = null;

        try {
            con = getConnection();
            rows =
                    QUERY_RUNNER.update(con, sql, new MapListHandler(), params);

        } catch (SQLException e) {
            logger.error("查詢失敗",e);
            e.printStackTrace();
        }finally {
            closeConnection(con);
        }
        return rows;
    }

    /**
     *  插入實(shí)體
     * @param entityClass
     * @param filedMap
     * @param <T>
     * @return
     */
    public static <T> boolean insertEntity(Class<T> entityClass, Map<String, Object> filedMap) {
        /*
        *  1. 判斷filedMap是否為空
        *  2. 對(duì)Map遍歷,拼接sql語句
        *  3. 執(zhí)行語句
        * */
        if (MapUtils.isEmpty(filedMap)) {
            logger.error("插入實(shí)體失敗,實(shí)體Map為空");
            return false;
        }
        Iterator<String> iterator = filedMap.keySet().iterator();
        StringBuffer columns = new StringBuffer("(");
        StringBuffer values = new StringBuffer("(");
        String sql=("insert into" + entityClass.getSimpleName());
        while (iterator.hasNext()) {
            String key = iterator.next();
            columns.append(key).append(", ");
            values.append("?, ");
        }
//        刪除最后一個(gè) , 然后加上 )
        columns.replace(columns.lastIndexOf(", "), columns.length(), ")");
        values.replace(values.lastIndexOf(", "), values.length(), ")");

        sql += columns + " values " + values;

        Object[] params = filedMap.values().toArray();



        return executeUpdate(sql,params)==1;
    }

    public static <T> boolean updateEntity(Class<T> entityClass, long id, Map<String, Object> fieldMap) {
        //TODO
        return false;
    }

    public static <T> boolean deleteEntity(Class<T> entityClass, long id) {
        String sql = "Delete From " + entityClass.getSimpleName() + " Where id = ?";
        return executeUpdate(sql, id) == 1;
    }
}


重建Server層

在構(gòu)建了DatabaseUtil類后曲楚,Server層的代碼如下:


/*
*  提供客戶數(shù)據(jù)服務(wù)
* */
public class CustomerService {

    private static final Logger logger = LoggerFactory.getLogger(CustomerService.class);



    /*
     *  獲取客戶列表
     * */
    public List<Customer> getCustomerList() {
        String sql = "select * from customer";
        List<Customer> list = DatabaseUtil.queryEntityList(Customer.class, sql,  null);
        return list;


    }

    /*
    *  獲取客戶
    * */

    public Customer getCustomer(long id) {
        String sql = "select * from customer where id = ?";
        return DatabaseUtil.queryEntity(Customer.class, sql, id);

    }

    /*
    *  創(chuàng)建客戶
    * */

    public  boolean createCustomer(Map<String,Object> fieldMap) {

        return DatabaseUtil.insertEntity(Customer.class,fieldMap);

    }

    /*
    *  更新客戶
    * */
    public boolean updateCustomer(long id,Map<String,Object> fieldMap) {
        //TODO
        return false;
    }

    /*
     *  刪除客戶
     * */
    public boolean deleteCustomer(long id) {
        return DatabaseUtil.deleteEntity(Customer.class, id);
    }
}

可以看到Server層的代碼非常簡潔厘唾,這樣就可以開心地寫業(yè)務(wù)邏輯代碼了。而不需要對(duì)數(shù)據(jù)庫的操作操心了龙誊。

缺陷

雖然Server層是非常的簡潔抚垃,舒服。但是相對(duì)地趟大,又創(chuàng)建了一個(gè)數(shù)據(jù)層鹤树。在這個(gè)剛建立的數(shù)據(jù)層上,顯然還是有很多不足的逊朽。

  • getConnection()closeConnection()上罕伯,每次調(diào)用數(shù)據(jù)庫操作,都需要重新連接數(shù)據(jù)庫和關(guān)閉數(shù)據(jù)庫叽讳,這樣的消耗很大追他。

總結(jié)

竟然我們已經(jīng)把數(shù)據(jù)庫處理這一塊跨分到一個(gè)層了坟募。那么我們就應(yīng)該對(duì)這一塊有一個(gè)更好地優(yōu)化才行。比如數(shù)據(jù)庫連接池 就是以后需要優(yōu)化的一個(gè)點(diǎn)邑狸。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懈糯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子单雾,更是在濱河造成了極大的恐慌昂利,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铁坎,死亡現(xiàn)場離奇詭異蜂奸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)硬萍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門扩所,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人朴乖,你說我怎么就攤上這事祖屏。” “怎么了买羞?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵袁勺,是天一觀的道長。 經(jīng)常有香客問我畜普,道長期丰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任吃挑,我火速辦了婚禮钝荡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舶衬。我一直安慰自己埠通,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布逛犹。 她就那樣靜靜地躺著端辱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虽画。 梳的紋絲不亂的頭發(fā)上舞蔽,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音狸捕,去河邊找鬼喷鸽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灸拍,可吹牛的內(nèi)容都是我干的做祝。 我是一名探鬼主播砾省,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼混槐!你這毒婦竟也來了编兄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤声登,失蹤者是張志新(化名)和其女友劉穎狠鸳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悯嗓,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡件舵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脯厨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铅祸。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖合武,靈堂內(nèi)的尸體忽然破棺而出临梗,到底是詐尸還是另有隱情,我是刑警寧澤稼跳,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布盟庞,位于F島的核電站,受9級(jí)特大地震影響汤善,放射性物質(zhì)發(fā)生泄漏什猖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一萎津、第九天 我趴在偏房一處隱蔽的房頂上張望卸伞。 院中可真熱鬧抹镊,春花似錦锉屈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至终佛,卻和暖如春俊嗽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铃彰。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國打工绍豁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人牙捉。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓竹揍,卻偏偏與公主長得像敬飒,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芬位,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1无拗、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,981評(píng)論 3 119
  • 我看過一句話說得很好:“我以為別人尊重我,是因?yàn)槲液軆?yōu)秀昧碉。慢慢的我明白了英染,別人尊重我,是因?yàn)閯e人很優(yōu)秀被饿∷目担”生活中很...
    心_472c閱讀 119評(píng)論 0 0
  • 在動(dòng)手編寫具體代碼的是時(shí)候,先寫好一套測試用例狭握,用來描述該代碼應(yīng)該實(shí)現(xiàn)什么功能箭养。 測試核心
    skoll閱讀 124評(píng)論 0 0
  • 2.1 庭院深幾許 童年的家,那一方深幽而美麗的農(nóng)家庭院哥牍,給我的印象太深刻了毕泌。時(shí)至今日,總是經(jīng)常浮現(xiàn)在我腦海里嗅辣。連...
    蔡喜源閱讀 597評(píng)論 2 3