MyBatis源码简读——1.2 基础支持模块(一)

上一章讲了比较简单的基础模块,现在看下那些和业务逻辑有一定关系的支持模块。

  • 注解类(annotations包);
  • 绑定模块类(binding包);
  • 配置解析(builder包);
  • 事务(transaction包);

注解模块

注解模块主要在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的关联。

目前绑定模块几个类的关系。

  • MapperMethod:mapper接口中,每个方法对应一个MapperMethod对象,维护SqlCommand,SQL命令对象
  • MapperProxy:接口方法的代理类
  • MapperProxyFactory:主要是mapper代理工厂,主要维护mapper下的方法和MapperMethod对象的关系
  • MapperRegistry:主要作用是维护配置类,以及mapper接口和其mapper代理工厂的映射关系

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));

 

 

你可能感兴趣的:(#,Mybatis源码,源码)