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。