本文来自王在祥先生的博客(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 模式以及复杂的事务处理模型等。如果能够在有限的范围内进行尝试,进而进行推广。或许能够在接下来能进一步的提高工作效率。