上一章讲了比较简单的基础模块,现在看下那些和业务逻辑有一定关系的支持模块。
注解模块主要在annotations
包下面,定义了mybatis中所有的注解。
CRUD类的注解:Select,Update,Delete,Insert
此注解我们使用最多,标识方法是进行查询,更新,删除,新增操作的注解
参数中的注解:@Param
和CRUD一样这个注解也是常用注解,我们一般在参数上使用此注解,代表对应SQL中的占位符
String selectDescription(@Param("p") String p);
结果集注解:@Results,@Result
@Results对应XML中的 标签为
@Select("{call sptest.getname(#{id,jdbcType=INTEGER,mode=IN})}")
@Results({ @Result(column = "ID", property = "id"),
@Result(column = "FIRST_NAME", property = "firstName"),
@Result(column = "LAST_NAME", property = "lastName") })
@Options(statementType = StatementType.CALLABLE)
Name getNameAnnotated(Integer id);
@One 复杂类型的单独属性值的注解;@Many 复杂类型的集合属性值的注解
@Select({"SELECT * FROM blog"})
@Results({
@Result(property = "author", column = "author_id",
one = @One(select = "org.apache.ibatis.binding.BoundAuthorMapper.selectAuthor")),
@Result(property = "posts", column = "id",
many = @Many(select = "selectPostsById"))
})
List selectBlogsWithAutorAndPosts();
// selectPostsById 是一个方法
Select("SELECT * FROM post WHERE id = #{id}")
List selectPostsById(int id);
@ResultMap,结果集的注解
@ResultMap("userResult")
@Select("select * from users where name = #{name}")
User getUserByName(String name);
而userResult在XML中配置
@ResultType,标识结果类型
@Select("select * from users")
@ResultType(User.class)
void getAllUsers(UserResultHandler resultHandler);
类标识注解:@Mapper
Mapper注解,标识此类是个mapper
@Mapper
public interface TestMapper extends BaseMapper {
}
CRUD类注解:@SelectProvider,@InsertProvider,@UpdateProvider,@DeleteProvider
标识不同的SQL语句提供器,以SelectProvider为例,指的是OurSqlBuilder类下的buildSelectByIdProviderContextOnly方法
@SelectProvider(type = OurSqlBuilder.class, method = "buildSelectByIdProviderContextOnly")
T selectActiveById(Integer id);
public class OurSqlBuilder {
public String buildSelectByIdProviderContextOnly(ProviderContext context) {
final boolean containsLogicalDelete = context.getMapperMethod().getAnnotation(BaseMapper.ContainsLogicalDelete.class) != null;
final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
return new SQL(){{
SELECT("*");
FROM(tableName);
WHERE("id = #{id}");
if (!containsLogicalDelete){
WHERE("logical_delete = ${Constants.LOGICAL_DELETE_OFF}");
}
}}.toString();
}
}
特殊注解:@CacheNamespace,@Property,@CacheNamespaceRef,@Options,@SelectKey,@MapKey,@Flush,@Lang
@CacheNamespace 缓存空间配置的注解,@Property 属性配置
@CacheNamespace(implementation = CustomCache.class, properties = {
@Property(name = "date", value = "2016/11/21")
})
private interface CustomCacheUnsupportedPropertyMapper {
}
public class CustomCache extends PerpetualCache {
private String stringValue;
private Integer integerValue;
private int intValue;
private Long longWrapperValue;
private long longValue;
private Short shortWrapperValue;
private short shortValue;
private Float floatWrapperValue;
private float floatValue;
private Double doubleWrapperValue;
private double doubleValue;
private Byte byteWrapperValue;
private byte byteValue;
private Boolean booleanWrapperValue;
private boolean booleanValue;
private Date date;
//.......
}
@CacheNamespaceRef,指向指定命名空间的注解
@CacheNamespaceRef(PersonMapper.class) // by type
public interface ImportantPersonMapper {
@Select("select id, firstname, lastname from person")
@Options(flushCache = FlushCachePolicy.TRUE)
List findWithFlushCache();
}
@Options,缓存的操作可选项
@Select("select id, firstname, lastname from person")
@Options(flushCache = FlushCachePolicy.TRUE)
List findWithFlushCache();
enum FlushCachePolicy {
/** false
for select statement; true
for insert/update/delete statement. */
DEFAULT,
/** Flushes cache regardless of the statement type. */
TRUE,
/** Does not flush cache regardless of the statement type. */
FALSE
}
@SelectKey,通过 SQL 语句获得主键的注解;@MapKey,Map 结果的键的注解
@Update("update table2 set name = #{name} where id = #{nameId}")
@SelectKey(statement="select name_fred from table2 where id = #{nameId}", keyProperty="generatedName", keyColumn="NAME_FRED", before=false, resultType=String.class)
int updateTable2WithSelectKeyWithKeyMap(Name name);
@Select({ "SELECT * FROM blog"})
@MapKey("id")
Map selectBlogsAsMapById();
@Flush
,Flush 注解,如果使用了这个注解,定义在 Mapper 接口中的方法能够调用 SqlSession#flushStatements()
方法。(Mybatis 3.3及以上)
@Flush
List flush();
@Lang
,语言驱动的注解
@Lang(RawLanguageDriver.class)
@Select("SELECT firstName, lastName FROM names WHERE lastName LIKE #{name}")
List selectRawWithMapper(Parameter p);
绑定模块的主要内容在binding
包下面,主要作用是自定义的mapper接口和SQL的关联。
目前绑定模块几个类的关系。
MapperRegistry的数据添加
// MapperRegistry.java
public void addMapper(Class type) {
// 判断,必须是接口
if (type.isInterface()) {
// 已经添加过,则抛出 BindingException 异常
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 添加到mapper中
knownMappers.put(type, new MapperProxyFactory<>(type));
// 解析mapper的注解配置
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
// 标记加载完成
loadCompleted = true;
} finally {
// 若加载未完成,从 knownMappers 中移除
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
MapperProxyFactory,代理工厂的创建
// MapperProxyFactory.java
public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
MapperProxy,mapper代理的创建
// MapperProxyFactory.java
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
// 创建mapper代理对象
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
// MapperProxy.java
public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
mapperInterface为此mapper对应的类。
mapper方法执行
当调用invoke方法时候,MapperProxy会通过cachedMapperMethod()获得方法对象,然后缓存起来,并开始执行
// MapperProxy.java
// 调用方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果是 Object 定义的方法,直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
// 见 https://github.com/mybatis/mybatis-3/issues/709 ,支持 JDK8 default 方法
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 获得mapper对象
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行mapper对象
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
MapperMethod ,mapper方法的创建
// MapperMethod.java
public MapperMethod(Class> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
this.command 是其SQL执行命令,this.method 是其接口方法。代理最终执行的就是MapperMethod的执行方法,主要参与者就是command ,method 当然这是后续业务上了解的内容了。
例子
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));