Scala的啟發:代碼本質與過度包裝的平衡
本文來自王在祥先生的博客(blogspot),原標題為《重溫 scala》。對于代碼簡潔化與可讀性矛盾的解決,Scala語言無疑是一種啟發。以下為原文:
51CTO編輯推薦:Scala編程語言專題
#t#最近,在閱讀項目組的代碼時,再次陷入了苦思:基于Java、Hibernate的商業應用開發確實陷入到了一個很痛苦的境界,這個問題實際上正在進行開發的大部分開發人員都不會感覺到,因為大家都覺得這就是正常的程序員生活。再說,幾乎所有的框架都在力捧Hibernate這樣的ORM工具能夠極大的簡化程序的開發,要不,你去使用使用 JDBC試試。
在Java中基于JDBC編程,確實有些匯編語言的感覺,摘抄一份實際項目的代碼:
- public void updateTerminalStatus(String timeout, String transtime) throws Exception {
- //更新超時終端的狀態值(9為超時)
- String updateTerminal = "update T_Terminal set Status=9 "
- + " where TerminalID in (select TerminalID from T_Terminal where TO_CHAR(latestDate, 'yyyymmddHH24miss')<=? and (status=0 or status=2) ) ";
- //查詢超時的終端信息,且該終端號在故障表中沒有未處理的超時記錄,(包含該終端號在故障表中沒有超時記錄的情況)。
- String queryTimeOutTerm = "SELECT te.TerminalID FROM T_TERMINAL te WHERE te.status=9 ";
- String queryTerm = "SELECT terminalid FROM T_TROUBLELOG tr " +
- " WHERE tr.DeviceID='COMMMODULE' AND tr.TroubleID='CommTimeOut' " +
- " AND tr.SolveDate IS NULL GROUP BY terminalid ";
- //向故障表中添加一條新的故障記錄。
- String insertTrouble = "insert into T_TroubleLog (LogID, TerminalID, DeviceID, TroubleID, HappenDate) "
- + " values (?, ?, 'COMMMODULE', 'CommTimeOut', TO_DATE(?,'yyyymmddHH24miss')) ";
- //更新故障表中的終端超時記錄,該終端號的狀態已經不超時,則超時故障處理完畢。
- String updateTrouble = "UPDATE T_TROUBLELOG tl SET tl.SolveDate=TO_DATE(?,'yyyymmddHH24miss') " +
- " WHERE terminalid IN" +
- " (SELECT tm.terminalid FROM T_TERMINAL tm,T_TROUBLELOG tl " +
- " WHERE tm.Status != 9 AND tm.TerminalID=tl.TerminalID )" +
- " AND tl.DeviceID='COMMMODULE' AND tl.TroubleID='CommTimeOut' AND tl.SolveDate IS NULL ";
- Connection conn = null;
- PreparedStatement pstmt = null;
- PreparedStatement pstmtInsert = null;
- ResultSet rs = null;
- List terlist = new ArrayList();
- List noterlist = new ArrayList();
- try {
- conn = transDataSource.getConnection();
- conn.setAutoCommit(false);
- pstmt = conn.prepareStatement(updateTerminal);
- pstmt.setString(1, timeout);
- pstmt.executeUpdate();
- pstmt.close();
- pstmt = conn.prepareStatement(updateTrouble);
- pstmt.setString(1, transtime);
- pstmt.executeUpdate();
- pstmt.close();
- log.debug("更新完成");
- //生成32位的隨機ID,使用Hibernate中的UUID算法。
- Properties props = new Properties();
- props.setProperty("separator", "");
- IdentifierGenerator gen = new UUIDHexGenerator();
- ( (Configurable) gen ).configure(Hibernate.STRING, props, null);
- pstmtInsert = conn.prepareStatement(insertTrouble);
- pstmt = conn.prepareStatement(queryTerm);
- rs = pstmt.executeQuery();
- while(rs.next()){
- String termid = rs.getString(1);
- terlist.add(termid);
- }
- log.debug("查詢終端號在故障表中沒有未處理的超時記錄完成");
- rs.close();
- rs = null;
- pstmt.close();
- pstmt = null;
- pstmt = conn.prepareStatement(queryTimeOutTerm);
- rs = pstmt.executeQuery();
- while (rs.next()) {
- String term = rs.getString(1);
- noterlist.add(term);
- }
- log.debug("查詢超時的終端信息完成" + noterlist.size());
- for(int j = 0; j < noterlist.size(); j++){
- String terminalid = noterlist.get(j).toString();
- if(terlist.contains(terminalid)){
- continue;
- }
- pstmtInsert.setString(1, (String) gen.generate(null, null));
- pstmtInsert.setString(2, terminalid);
- pstmtInsert.setString(3, transtime);
- pstmtInsert.addBatch();
- }
- pstmtInsert.executeBatch();
- conn.commit();
- log.debug("向故障表中添加新的故障記錄完成");
- } catch (Exception ex) {
- try{
- conn.rollback();
- }catch (Exception e) {
- }
- throw ex;
- } finally {
- if (rs != null) {
- try {
- rs.close();
- } catch (Exception ex) {
- }
- }
- if (pstmt != null) {
- try {
- pstmt.close();
- } catch (Exception ex) {
- }
- }
- if (pstmtInsert != null) {
- try {
- pstmtInsert.close();
- } catch (Exception ex) {
- }
- }
- if (conn != null) {
- try {
- conn.close();
- } catch (Exception ex) {
- }
- }
- }
- }
這份代碼長達120行,要去理解它還是得花一些時間的,如果使用 scala來寫的話,可以怎么寫呢?
- implicit val conn: Connection = transDataSource.getConnection()
- transaction {
- update("""update T_Terminal set Status=9 where TerminalID in
- (select TerminalID from T_Terminal where TO_CHAR(latestDate, 'yyyymmddHH24miss')<= ? and (status=0 or status=2))
- """, timeout);
- update("""UPDATE T_TROUBLELOG tl SET tl.SolveDate=TO_DATE(?,'yyyymmddHH24miss') WHERE terminalid IN
- (SELECT tm.terminalid FROM T_TERMINAL tm,T_TROUBLELOG tl WHERE tm.Status != 9 AND tm.TerminalID=tl.TerminalID )
- AND tl.DeviceID='COMMMODULE' AND tl.TroubleID='CommTimeOut' AND tl.SolveDate IS NULL
- """, transtime);
- val terlist: List[String] = List()
- query("""SELECT terminalid FROM T_TROUBLELOG tr
- WHERE tr.DeviceID='COMMMODULE' AND tr.TroubleID='CommTimeOut' AND tr.SolveDate IS NULL
- GROUP BY terminalid
- """).foreach { (row) =>
- terlist += row("terminalid")
- }
- log.debug("查詢終端號在故障表中沒有未處理的超時記錄完成");
- val noterlist: List[String] = List()
- query("SELECT te.TerminalID FROM T_TERMINAL te WHERE te.status=9").foreach { (row)=>
- noterlist += row("terminalid")
- }
- log.debug("查詢超時的終端信息完成" + noterlist.size());
- val psInsert = conn prepareStatement """insert into T_TroubleLog (LogID, TerminalID, DeviceID, TroubleID, HappenDate)
- values (?, ?, 'COMMMODULE', 'CommTimeOut', TO_DATE(?,'yyyymmddHH24miss'))"""
- for(val terminalId <- noterlist){
- psInsert << uuid() << terminalid << transtime <<!
- }
- }
這是一個直譯的版本,代碼函數為34行,瘦身到25%左右。不僅代碼行數更短,而且新的代碼的可讀性也更高得多。
最近一直在思考,我們對JDBC是否做了過度的包裝?包括事務處理,DAO等從EJB1.0時代產生的設計模式,到后續的O-R-M框架,看上去代碼是越發簡潔,實際上已經遠遠的遠離代碼的本質。這種基于Scala的JDBC簡單封裝,即便于我,也還遠不如原始的4GL的簡潔,但相比傳統的jdbc或者后續的orm、spring+dao等等,則要簡化得多。
scala到底能不能投入實際項目應用?開發人員的學習成本有多高?這個問題一直讓我痛苦,最近,看到 dcaoyuan 先生的netbeans scala ide,讓我重新點燃對scala的欲望,或許,在我們的平臺中,接下來就可以嘗試 scala了:至少在我看來,scala可以有效的消除我們目前代碼中不必要的 DAO 模式以及復雜的事務處理模型等。如果能夠在有限的范圍內進行嘗試,進而進行推廣。或許能夠在接下來能進一步的提高工作效率。