mybatis爲javaer們提供了強大的數據庫訪問支持,但對於絕大多數場景來說,使用上仍然不夠簡單,比如:還是需要編寫包含sql語句的xml或註解。本文對mybatis的原理進行了簡單的介紹,並介紹了我們部門自己基於mybatis源代碼修改並開發的mybatis擴展框架appassist-dao.
1.Mybatis基礎
1.兩種Mybatis使用方式
- spring集成方式
- 直接調用初始化方式
- 下圖時兩種初始化方式之間的關係
圖1
- 下圖時兩種初始化方式之間的關係
2.Mybatis-spring的初始化流程
mybatis-spring是對mybatis的封裝,因此實際上mybatis-spring的初始化流程包含了標準的mybais初始化流程。下圖展示了mybatis-spring初始化的大體流程,其中紅框部分會進行mapper xml的掃描,也就是右側的流程部分。
圖2 mybatis-spring除了可以使用SqlSessionFacotryBean來初始化各個mapper,還可以用MapperScanConfigurer來掃描指定包路徑下的Mapper接口,自動向spring框架中注入對應的Mapper實現:
圖3
4. Mybatis兩種使用模式—— 傳統模式SqlSession和MapperProxy:
傳統模式,使用SqlSession進行訪問:
MapperProxy模式:
3. MapperProxy做了些什麼?
MapperProxy實際上是一個JDK代理:InvocationHandler。被代理的,則是與xxxMapper.xml中namespace對應的接口。
4.Mybatis定製——實現基礎Dao類:BaseDao,併爲繼承BaseDao類的Mapper接口自動注入mybatis sql xml。
1)需要定製的功能:
比如,目前在我們部門內測時候用的dao框架 appassist-dao,它定義了一個基礎接口,凡是集成自該接口的Dao對象,都會根據它的類型參數”T extends BaseQuery”去生成對應的sql語句。
public interface BaseDao<T extends BaseQuery, KEY extends Serializable> {
int insert(T instance);
int insertAll(Collection<T> instances);
int insertAllBatch(Collection<T> instances);
int deleteByKeys(KEY... keys);
int deleteByCondition(T condition);
List<T> selectListByCondition(T condition);
T selectOneByKey(KEY key);
T selectOneByCondition(T condition, boolean strictlyCheck) throws SelectOneException;
int selectCount(T condition);
List<T> selectListByKeys(KEY... keys);
int updateByKey(T instance);
int updateByCondition(@Param("update") T update, @Param("condition") T condition);
}
舉個例子:
我們定義一個Dao,並定義對應的Domain對象(即PO對象)。
public interface UserDao extends BaseDao<User, Long> {
}
@Table("sys_user")
public class User extends BaseQuery {
@Column(primaryKey = true)
private Long id;
@Column
private String name;
@Column("age")
private Integer age;
@Column("health_condition")
private String healthCondition;
}
appassist-dao可以使得數據庫訪問變得更簡單,就如上述兩個類:User和UserDao。只要將UserDao掃描並實例化注入到spring中,就能夠像如下使用:
@Resource
private UserDao userDao;
@Override
public void testUserDao() {
User user1 = new User();
user1.setName("haha");
User user2 = new User();
user2.setName("hehe");
// 將name爲"haha"和"hehe"的兩個User插入sys_user表中。
int insertCount = userDao.insertAll(Arrays.asList(user1, user2));
}
2)定製功能的實現
前面有講到,spring中,Mybatis的初始化入口爲:SqlSessionFactoryBean。爲了在mybatis初始化的時候能夠掃描UserDao和User類,並根據User類中的註解自動生成sql註冊到Mybatis中。我們需要對它進行改寫。
一共有兩個hack點:
1. 圖2中的綠色部分
對應mybatis源碼:
MybatisXMLMapperBuilder.parse();
- MapperScannerConfigurer將對象自動掃描注入後,對Dao接口方法進行註解掃描
對應mybatis源碼:
MybatisMapperAnnotationBuilder.parse();
其中紅框部分即爲我們自己添加的Sql自動生成並註冊mybatis的代碼。
AutoSqlInjector中,根據Domain對象中Table、Column註解等信息,生成對應的BaseDao中方法所對應的mybatis sql片段。再通過調用
builderAssistant.addMappedStatement
方法,來將生成好sql片段註冊到mybatis中。
3)這樣定製帶來的幾大好處
- mybatis orm化,對於大多數應用場景無需mapper xml文件,即可使用。
- 採用了比較好的Mybatis實踐來實現方法,由sql技術大牛統一編寫mybatis sql,避免大多數情況下自己編寫sql導致的問題。
- 能夠植入一些mybatis不具備的特別的功能,比如dao方法監控自動接入、分庫分表、過濾空條件update和delete。