淺談Java常見設(shè)計(jì)模式(六)
對(duì)這個(gè)世界如果你有太多的抱怨
跌倒了就不敢繼續(xù)往前走
為什么人要這么的脆弱墮落
請(qǐng)你打開電視看看
多少人為生命在努力勇敢的走下去
我們是不是該知足
珍惜一切就算沒(méi)有擁有
很久沒(méi)有回顧過(guò)jdbc操作了,今天回顧下原生Java操作數(shù)據(jù)庫(kù)的流程,
1、首先載入數(shù)據(jù)庫(kù)驅(qū)動(dòng)
2、獲取數(shù)據(jù)庫(kù)連接
3、獲取PreparedStatement
4、執(zhí)行sql語(yǔ)句
5、處理結(jié)果集
6、關(guān)閉連接和PreparedStatement、ResultSet
那么對(duì)于查詢的話,通常來(lái)說(shuō),1.2.3.4這幾個(gè)步奏是肯定一致的,第5步,結(jié)果集處理,每個(gè)查詢的結(jié)果集處理可能是太一樣的,所以是一個(gè)可微調(diào)的因素。
那么基于此,來(lái)嘗試進(jìn)行一次原生的jdbc操作,平常ORM框架用多了,這里來(lái)熟悉一些底層一點(diǎn)的東西。
下面例子使用的數(shù)據(jù)庫(kù)是Mysql8.0.11
首先有一個(gè)實(shí)體類,用于一個(gè)查詢結(jié)果的封裝:

這里僅列舉了幾個(gè)字段,意思一下就好
下面定義一個(gè)RowMapper結(jié)果集映射器接口,擁有一個(gè)結(jié)果封裝的方法
package?com.lgli.behavior.template.jdbc;import?java.sql.ResultSet;/*** 結(jié)果集處理接口* @author lgli?*/public?interface?RowMapper<T>?{????T?mapRow(ResultSet?resultSet)?throws?Exception;}
下面定義一個(gè)jdbc操作類,因?yàn)橐陨蠋讉€(gè)操作步驟,除了某些細(xì)節(jié)有些許變化之外,大體上都是一致的,那么這里設(shè)計(jì)了一個(gè)類,具有操作數(shù)據(jù)庫(kù)的標(biāo)準(zhǔn)步驟方法,同時(shí),通過(guò)結(jié)果集處理接口RowMapper的具體實(shí)現(xiàn)類來(lái)實(shí)現(xiàn)對(duì)細(xì)節(jié)的微調(diào)。那么這個(gè)類,簡(jiǎn)單稱為JdbcTemplate類:
package com.lgli.behavior.template.jdbc;import java.sql.*;import java.util.ArrayList;import java.util.List;/*** jdbc操作模板* @author lgli*/public abstract class JdbcTemplate {/*** mysql數(shù)據(jù)驅(qū)動(dòng)*/private static final String DATA_SOURCE_DRIVER = "com.mysql.cj.jdbc.Driver";/*** 數(shù)據(jù)庫(kù)連接地址*/private static final String DATA_SOURCE_URL = "jdbc:mysql://localhost:3306/xttl?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT&useSSL=false";/*** 用戶名*/private static final String USER = "root";/*** 密碼*/private static final String PASSWORD = "soft01";public <T> List<T> executeQuery(String sql,RowMapper<T> rowMapper){try{//1、載入數(shù)據(jù)庫(kù)驅(qū)動(dòng)initDataSourceDriver();//2、獲取數(shù)據(jù)庫(kù)連接Connection connection = getConnection();//3、獲取PreparedStatementPreparedStatement preparedStatement = getPreparedStatement(sql,connection);//4、執(zhí)行sql語(yǔ)句ResultSet resultSet = preparedStatement.executeQuery();//5、處理結(jié)果集List<T> result = dealResult(resultSet,rowMapper);//6、關(guān)閉連接和PreparedStatementclose(connection,preparedStatement,resultSet);return result;}catch (Exception e){e.printStackTrace();}return null;}protected <T> List<T> dealResult(ResultSet resultSet, RowMapper<T> rowMapper) throws Exception{List<T> result = new ArrayList<>();while(resultSet.next()){result.add(rowMapper.mapRow(resultSet));}return result;}protected void close(Connection connection, PreparedStatement preparedStatement,ResultSet resultSet) throws Exception{if(connection != null){connection.close();}if(preparedStatement != null){preparedStatement.close();}if(resultSet != null){resultSet.close();}}protected PreparedStatement getPreparedStatement(String sql,Connection connection) throws Exception {return connection.prepareStatement(sql);}private Connection getConnection() throws Exception{return DriverManager.getConnection(DATA_SOURCE_URL,USER,PASSWORD);}private void initDataSourceDriver() throws Exception{this.getClass().getClassLoader().loadClass(DATA_SOURCE_DRIVER);????}}
這里我們簡(jiǎn)單用一個(gè)查詢舉例,其他的數(shù)據(jù)庫(kù)操作雷同
可以看到,標(biāo)準(zhǔn)的步驟都是一樣的,每個(gè)數(shù)據(jù)庫(kù)操作都是這么來(lái)的,唯一不一樣的,就是結(jié)果集的處理。
下面定義一個(gè)Dao,繼承自這個(gè)JdbcTemplate類:
package com.lgli.behavior.template.jdbc;import java.util.List;/*** dao* @author lgli*/public class Dao extends JdbcTemplate{public List<User> selectUser(){String sql = "select * from user_basic_info";return super.executeQuery(sql, resultSet -> {User user = new User();user.setUserName(resultSet.getString("USER_NAME"));user.setUserUniqueSign(resultSet.getString("USER_UNIQUE_SIGN"));user.setUserPassword(resultSet.getString("USER_PASSWORD"));return user;});????}}
這里定義一個(gè)查詢方法,調(diào)用模板類的executeQuery方法,傳入一個(gè)RowMapper的實(shí)現(xiàn)類,這里即是對(duì)查詢結(jié)果的封裝,每一個(gè)查詢的封裝可能存在差異,比如這里是查詢user_basic_info,假設(shè)查詢另外的表,其結(jié)果封裝肯定就是不一樣的了,這里主要利用鉤子方法實(shí)現(xiàn)對(duì)結(jié)果的自定義封裝。
然后是我們的測(cè)試類:
package?com.lgli.behavior.template.jdbc;import java.util.List;/*** test* @author lgli*/public class DaoTest {public static void main(String[] args) {Dao dao = new Dao();List<User> users = dao.selectUser();for (User user : users) {System.out.println(user.toString());}}}
測(cè)試類直接調(diào)用查詢方法,打印結(jié)果:

上面主要類JdbcTemplate類,我們稱為模板類,這種設(shè)計(jì)模式,稱為模板模式,即:一個(gè)抽象類公開定義了執(zhí)行它的方法的方式/模板。它的子類可以按需要重寫方法實(shí)現(xiàn),但調(diào)用將以抽象類中定義的方式進(jìn)行。
假定下面這個(gè)業(yè)務(wù)場(chǎng)景:在系統(tǒng)已經(jīng)存在上面例子中的Dao類,這個(gè)
selectUser也已經(jīng)成熟運(yùn)行了很久了,
與此同時(shí),系統(tǒng)中有一個(gè)對(duì)人員處理的方法,
package?com.lgli.construct.adapter;import java.util.List;/*** 人員處理類型* @author lgli*/public?class?OtherUserDeal?{private List<OtherUser> dealUser(List<OtherUser> users){for(OtherUser user : users){user.setPassword("******");}return users;????}}
這個(gè)處理類,是專門對(duì)OtherUser進(jìn)行的處理,這個(gè)地方的這個(gè)OtherUser和前面的User類是不一樣的對(duì)象。
這個(gè)時(shí)候,新的需求來(lái)了,需要利用OtherUserDeal?對(duì)上面例子中的查詢結(jié)果進(jìn)行處理。
此時(shí),是可以單獨(dú)寫個(gè)方法來(lái)進(jìn)行操作的,那么如果在兩個(gè)方法業(yè)務(wù)邏輯很復(fù)雜的情況下,顯然,從頭開始來(lái)捋代碼邏輯時(shí)一個(gè)不明智的選擇,同時(shí)可能出現(xiàn)其他的BUG。
這個(gè)時(shí)候,可以選擇新加入一個(gè)適配器類,讓這個(gè)適配器可以將這兩個(gè)毫無(wú)相關(guān)的方法(接口)建立一個(gè)橋梁,來(lái)解決這個(gè)問(wèn)題。
這里新建一個(gè)Adapter類:
package com.lgli.construct.adapter;import com.lgli.behavior.template.jdbc.Dao;import com.lgli.behavior.template.jdbc.User;import java.util.ArrayList;import java.util.List;/*** 適配器類* @author lgli*/public class Adapter extends Dao {private OtherUserDeal userDeal;public Adapter(OtherUserDeal userDeal) {this.userDeal = userDeal;}public List<OtherUser> dealWithUser(){List<User> users = super.selectUser();List<OtherUser> otherUsers = new ArrayList<>();for(User user : users){OtherUser otherUser = new OtherUser();otherUser.setPassword(user.getUserPassword());otherUser.setName(user.getUserName());otherUser.setUserAccount(user.getUserUniqueSign());otherUsers.add(otherUser);}return userDeal.dealUser(otherUsers);????}}
一般來(lái)說(shuō),在實(shí)際的工作中,還是有一個(gè)比較標(biāo)準(zhǔn)的適配器模式,這里僅僅做個(gè)演示說(shuō)明,其代碼結(jié)構(gòu)存在較多的可以改造的地方,就不在這兒優(yōu)化了。
package?com.lgli.construct.adapter;import java.util.List;/*** 適配器測(cè)試類* @author lgli*/public?class?AdapterTest?{public static void main(String[] args) {Adapter adaper = new Adapter(new OtherUserDeal());List<OtherUser> otherUsers = adaper.dealWithUser();for(OtherUser user : otherUsers){System.out.println(user);}}}
這里一個(gè)簡(jiǎn)單的測(cè)試類,輸出結(jié)果:

這里,我們的適配器類,將兩個(gè)毫無(wú)相干的方法,結(jié)合起來(lái)了:
看下類圖:

這里就比較粗超的描述了適配器和方法的基本關(guān)系,其實(shí)也就是一個(gè)簡(jiǎn)單的適配器的實(shí)現(xiàn),下面描述一個(gè)標(biāo)準(zhǔn)一些的場(chǎng)景:
假定有一個(gè)播放器及其播放器實(shí)現(xiàn),這個(gè)播放器只能播放Mp3的音樂(lè):
MediaPlay:
package?com.lgli.construct.adapter;/*** 媒體播放器接口* @author lgli*/public?interface?MediaPlay?{????void?play();}
播放器實(shí)現(xiàn)MediaPlayImpl,只能播放MP3:
package com.lgli.construct.adapter;/*** 媒體播放器實(shí)現(xiàn)* @author lgli*/public?class?MediaPlayImpl?implements?MediaPlay{public void play() {System.out.println("播放MP3!!!");}}
這個(gè)時(shí)候,忽然項(xiàng)目引進(jìn)了一個(gè)高級(jí)一點(diǎn)的播放器接口和實(shí)現(xiàn):
高級(jí)播放器接口AdvancedMediaPlay
package com.lgli.construct.adapter;/*** 高級(jí)播放器* @author lgli*/public interface AdvancedMediaPlay {void play();}
高級(jí)播放器接口的其中一個(gè)實(shí)現(xiàn)AdvancedMediaPlayImpl,可以播放MP4文件:
package com.lgli.construct.adapter;/*** 高級(jí)播放器實(shí)現(xiàn)* @author lgli*/public class AdvancedMediaPlayImpl implements AdvancedMediaPlay{public void play() {System.out.println("播放MP4!!!");}}
這時(shí)候,有一個(gè)需求,需要用只能播放MP3的MediaPlayImpl去播放MP4,怎么辦?此時(shí)最好的解決方案就是,寫一個(gè)適配器,來(lái)關(guān)聯(lián)這兩個(gè)實(shí)現(xiàn):
package com.lgli.construct.adapter;/*** 播放器適配器* @author lgli*/public?class?MediaPlayAdapter?{????public?void?play(String?type){if(type.contains(".Mp3")){new MediaPlayImpl().play();}else if(type.contains(".Mp4")){new AdvancedMediaPlayImpl().play();}else{System.out.println("can not play media");}????}}
寫個(gè)測(cè)試類:

看下類圖:

這里的播放器適配器,將2個(gè)毫無(wú)關(guān)系的方法實(shí)現(xiàn)關(guān)聯(lián)起來(lái)了,這也是簡(jiǎn)單的適配器實(shí)現(xiàn)。
點(diǎn)擊下面公眾號(hào)關(guān)注獲取更多。。。
歡迎關(guān)注點(diǎn)贊轉(zhuǎn)發(fā),謝謝
