Mybatis的缺陷

Mybatis是業界很是流行的持久層框架,輕量級、易用,在金融IT領域徹底是領軍地位,比Hibernate更受歡迎,優點很是多,也是很是值得咱們學習的。但Mybatis並不盡善盡美,其自身的設計、編碼也還有許多不足,甚至是缺陷,這篇文章來簡要討論一下這些缺陷:sql

1.Mybatis使用DTD做爲XML配置文件的校驗文件,可是很明顯,DTD差很少是快被淘汰的技術了,功能很是有限,擴展性很是差,擴展性很是差,擴展性很是差,可讀性也很差,Spring可以從DTD到XSD華麗轉身,但Mybatis始終沒這個魄力。數據庫

2.版本兼容性作的很差,就拿3.3.0—>3.4.0來講,按業界通用規範,第2級版本號升級,能夠添加功能,可是要保證向下兼容性,然而Mybatis的作法並不徹底是這樣的,看一下關鍵接口StatementHandler的關鍵方法prepare:緩存

// 3.3.0
Statement prepare(Connection connection)
      throws SQLException;

// 3.4.0
Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

這裏沒有添加一個方法,而是直接在原方法中添加了一個參數!相似例子還有很多,就不一一列舉了。app

3.Mybatis的插件,採用一個通用的Interceptor接口,配以@Intercepts、@Signature等註解,實現對多個組件的多種方法的攔截,看似很是靈活,在我看來實際上是結構不夠清晰,實際開發時,你會把對StatementHandler和ResultSetHandler的攔截加強放在一個類裏面嗎?不會是吧(會?你當單一職責原則、開閉原則都是狗屎嗎),那有什麼必要強制使用同一個接口呢?框架

另外,使用@Signature註解來設別須要被攔截的組件方法,若是註解有錯,編譯也是不會報錯的,而只能等到運行時才能發現,再看上面的例子:dom

假設我針對3.3.0版本實現了一個插件:ide

@Intercepts({ 
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class })
})
public class StatementHandlerInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        
    }
}

而後,升級爲3.4.0,結果呢,編譯一直正常,可是等到運行,卻拋出異常了。函數

4.Mybatis的緩存簡直就是雞肋,並且無論有沒有配置須要使用緩存、是否更新緩存,都要去計算CacheKey,不使用緩存、也不更新緩存的狀況下,這種計算徹底是浪費。學習

5.Mybatis的批量執行,看下面的一個JDBC例子:this

public void testJdbcBatch(Connection conn) throws Exception {

try{ conn.setAutoCommit(false); batchUpdate(conn); clearTestData(conn); conn.commit(); conn.setAutoCommit(true); }catch(Exception e){ conn.rollback(); throw e; } } private void clearTestData(Connection conn) throws SQLException { PreparedStatement ps = null; try{ ps = conn.prepareStatement("delete TABLE_NAME1 where FIELD_NAME1 = ? "); ps.setString(1, "TEST"); int d = ps.executeUpdate(); System.out.println("delete counts : " + d); }finally{ try{ ps.close(); }catch(Exception e){} } } private void batchUpdate(Connection conn) throws SQLException { PreparedStatement ps = null; try{ String sql = "INSERT INTO TABLE_NAME2(FIELD_NAME1, FIELD_NAME2, FIELD_NAME2)VALUES(?,?,?)"; ps = conn.prepareStatement(sql); for(int i = 0; i < 10; i++){ String random = RandomStringUtils.randomAlphabetic(8); ps.setString(1, "TEST");//FIELD_NAME1 ps.setString(2, "數據" + random);//FIELD_NAME2 ps.setString(3, "參數" + random);//FIELD_NAME3 ps.addBatch(); } int[] rs = ps.executeBatch(); }finally{ try{ ps.close(); }catch(Exception e){} } }

代碼沒有什麼違和感,可以執行正常,也能夠按預期的回滾,也就是說同一個事務中的同一個connection,能夠同時運行普通sql和batch,可是你在同一個事務的SqlSession中試試,反饋給你的是——不能在同一個事務中切換執行方式!

六、數據庫產品的兼容性:Mybatis把SQL的控制權交給了開發人員,因而從道德上佔據了制高點——你寫的不兼容,那是你本身的水平不行!但,這是一個真正的優秀框架的正確姿式嗎?爲何就不能提供一些輔助性的兼容實施?好比說在Oracle中被奉爲神明的DECODE函數,是否能夠在SqlMapper中提供一個<decode>標籤,在後面默默的修改爲CASE WHEN?或者說,官方不提供沒有關係,但你得提供擴展方式啊,因而又回到了:擴展性很是差,擴展性很是差,擴展性很是差。重要的事說三遍,但,我已經說六遍了。