mybatis源碼閱讀筆記-卷四(注解)

十.annotations注解包

Mybatis使用注解的方式可以減少使用xml配置sql,方便用斷點的形式檢測生成的sql,代碼的可讀性更強,更利于維護。

10.1Param注解

用于定義接口入?yún)⒌膭e名,方便代碼運行時獲取接口入?yún)?,而不至于讀取參數(shù)表中的參數(shù)時因為參數(shù)類型相同而導致參數(shù)獲取紊亂。
用法:

List<T> selectByIdAndName (@Param("id") Integer id, @Param("name") String name);

10.2Select注解

給指定的mapper接口增加sql語句實現(xiàn)查詢功能,使用方法為:

@Select("select * from users")
List<User> getAllUsers();

10.3SelectProvider注解

方便動態(tài)生成sql語句,可以在注解中聲明自定義生成sql語句的方法和類的type:

   //聲明自定義的Provider的type,以及自定義Provider中的方法名
  @SelectProvider(type = StatementProvider.class, method = "provideSelect")
  S select(S param);
//自定義的sql生成方法
public String provideSelect(Object param) {
      StringBuilder sql = new StringBuilder("select * from ");
      if (param == null || param instanceof Person) {
        sql.append(" person ");
        if (param != null && ((Person) param).getId() != null) {
          sql.append(" where id = #{id}");
        }
      } else if (param instanceof Country) {
        sql.append(" country ");
        if (((Country) param).getId() != null) {
          sql.append(" where id = #{id}");
        }
      }
      sql.append(" order by id");
      return sql.toString();
    }

10.4SelectKey注解

用于在sql語句執(zhí)行前(后)插入執(zhí)行,如在執(zhí)行前(后)查詢當前自增張id的值,注解中聲明的屬性為:

   //sql子句
  String[] statement();
  //屬性名,將查詢得到列中的數(shù)據(jù)賦值屬性名對應的屬性
  String keyProperty();
  //查詢返回的列名
  String keyColumn() default "";
  //表示在是否在主sql執(zhí)行前執(zhí)行
  boolean before();
  //返回的值的類型
  Class<?> resultType();
  //狀態(tài)
  StatementType statementType() default StatementType.PREPARED;

具體使用方式為:
(1)sql語句執(zhí)行后

@Update("update table2 set name = #{name} where id = #{nameId}")
    //在更新語句后執(zhí)行,查詢nameId對應的name_fred,并賦值給Name類的generatedName屬性(入?yún)ame的generatedName屬性)
    @SelectKey(statement="select name_fred from table2 where id = #{nameId}", 
                keyProperty="generatedName", keyColumn="NAME_FRED", before=false, resultType=String.class)
int updateTable2WithSelectKeyWithKeyMap(Name name);

(2)sql語句執(zhí)行前

@Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
    //在插入語句執(zhí)行之前,查詢數(shù)據(jù)庫表中下一個id的值,并賦值給Name對象name的nameId屬性
    @SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
int insertTable3(Name name);

10.5 Insert、InsertProvider注解

使用方法與Select相似。

10.6 Update、UpdateProvider注解

使用方法和Select相似。

10.7 Delete、DeleteProvider注解

使用方法和Select相似。

10.8 Mapper注解

使用該注解時只需要在對應的mapper的接口上添加注解@Mapper即可讓spring加載這個Bean,且根據(jù)注解定義的sql語句實現(xiàn)功能。不過這個注解的加載不是在mybatis的主jar包中,而是在這個jar中找到了這個方法:

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    logger.debug("Searching for mappers annotated with @Mapper");
    //mapper類路徑掃描器,這個類在mybatis-spring中,繼承自spring框架下的ClassPathBeanDefinitionScanner類。
    //說明如果不引入mybatis-spring-boot-autoconfigure這個jar也可以用這個類加載mapper
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    try {
      if (this.resourceLoader != null) {
        //設置資源加載器
        scanner.setResourceLoader(this.resourceLoader);
      }
      //從工廠中知道哪些包路徑是需要自動掃描的
      List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
      if (logger.isDebugEnabled()) {
        for (String pkg : packages) {
          logger.debug("Using auto-configuration base package '{}'", pkg);
        }
      }
      //設置需要掃描的注解類型
      scanner.setAnnotationClass(Mapper.class);
      //注冊過濾器
      scanner.registerFilters();
      //開始掃描
      scanner.doScan(StringUtils.toStringArray(packages));
    } catch (IllegalStateException ex) {
      logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
    }
  } 

10.9 AutomapConstructor注解

在與數(shù)據(jù)庫查詢結果對應的model類的構造器上增加這個注釋,可以讓框架自動使用這個構造器。

10.11 ResultType注解

用于注明結果類型,使用方式為:

  @Select("select * from users")
  @ResultType(User.class)
  //雖然方法的返回結果為void,但是入?yún)esultHandler可以接受返回結果
  void getAllUsers(UserResultHandler resultHandler);
其中UserResultHandler為:
public class UserResultHandler implements ResultHandler {
//返回結果
  private List<User> users;
  
  public UserResultHandler() {
    super();
    users = new ArrayList<User>();
  }

  @Override
  public void handleResult(ResultContext context) {
    User user = (User) context.getResultObject();
    users.add(user);
  }

  public List<User> getUsers() {
    return users;
  }
}

10.12One、Many注解

用于注明查詢結果是1對1(1對多)的關系,fetchType屬性可以配置為懶加載或者立即加載:

  @Results({ 
      @Result(property = "author", column = "author_id", one = @One(select = "org.apache.ibatis.binding.BoundAuthorMapper.selectAuthor", fetchType=FetchType.EAGER)), 
      @Result(property = "posts", column = "id", many = @Many(select = "selectPostsById", fetchType=FetchType.EAGER))
  })
  List<Blog> selectBlogsWithAutorAndPostsEagerly();

10.13Lang注解

該注解用于標注使用的自定義語言腳本驅動(自定義實現(xiàn)自LanguageDriver接口的類),方便于解析sql語言,使用時只需要在對應方法上增加注解,如:
@Lang(XMLLanguageDriver.class)

10.14Flush注解

使用這個注解的方式應該是在對應的方法上增加注解@Flush,然后調用方法時會將緩存中的操作批量刷新到數(shù)據(jù)庫中,因為官方給的文檔不夠詳細,本人也沒有實踐過,如果存在理解錯誤,請諒解。

10.13Results、ResultMap、Result注解

這幾個注解用于查詢結果的映射,用于將數(shù)據(jù)庫中的字段名和java對象中的字段名對應上,不過目前也可以使用數(shù)據(jù)庫提供的別名定義的方式實現(xiàn)名稱映射,同時也可以使用自定義typeHandle處理

10.13.1 ResultMap應用

  //在這個結果集中定義映射名叫userResult,方便別的地方去使用
  @Results(id = "userResult", value = {
          //定義uid到id的映射
    @Result(id = true, column = "uid", property = "id"),
    @Result(column = "name", property = "name")
  })
  @Select("select * from users where uid = #{id}")
  User getUserById(Integer id);
  //在這里通過復用之前定義好的id=userResult的映射完成結果集映射
  @ResultMap("userResult")
  @Select("select * from users where name = #{name}")
  User getUserByName(String name);

10.13.2Result說明

  //標注是不是id,一般id會被定義成數(shù)據(jù)庫中的序列化主鍵
  boolean id() default false;
  //數(shù)據(jù)庫查詢結果中的列名
  String column() default "";
  //需要映射的對象中的屬性名
  String property() default "";
  //java的類型
  Class<?> javaType() default void.class;
  //數(shù)據(jù)庫中的類型
  JdbcType jdbcType() default JdbcType.UNDEFINED;
  //自定義類型處理器
  Class<? extends TypeHandler<?>> typeHandler() default UnknownTypeHandler.class;
  //標注為單條結果映射
  One one() default @One;
  //表示為多條結果映射
  Many many() default @Many;

10.13.3Results應用

具體使用時,在需要定義結果映射的方法前增加注釋,就可以將查詢到的結果按照定義好的結果映射樣式完成映射。

 //這里的注解中的參數(shù)只是為了展示方便定義的,目的是為了展示注解的使用方法,可能存在錯誤
    @Select("select * from post where author_id = #{id} order by id")
    @Results(id = "test",
            value = {@Result(property = "id", column = "id",javaType = int.class,jdbcType = JdbcType.INTEGER),
                    @Result(property = "subject", column = "subject"),
                    @Result(property = "body", column = "body"),
                    @Result(property = "tags", javaType = List.class, column = "id", typeHandler = ArrayTypeHandler.class,
                            many = @Many(select = "getTagsForPost"))
            })
    List<AnnoPost> getPosts(int authorId);

10.14Options

這個注解用于配置查詢結果配置,內(nèi)部具有如下屬性:

//是否使用緩存
  boolean useCache() default true;
  //是否清除緩存
  FlushCachePolicy flushCache() default FlushCachePolicy.DEFAULT;
  //結果集的類型
  ResultSetType resultSetType() default ResultSetType.FORWARD_ONLY;
  //執(zhí)行語句生成方式類型
  StatementType statementType() default StatementType.PREPARED;
  //返回結果條目數(shù)
  int fetchSize() default -1;
  //查詢超時時間
  int timeout() default -1;
  //是否使用自增長key
  boolean useGeneratedKeys() default false;
  //key在結果對象中的屬性名
  String keyProperty() default "";
  //key在數(shù)據(jù)庫中的列名
  String keyColumn() default "";
  //不清楚怎么用
  String resultSets() default "";
常見的使用方式為返回插入數(shù)據(jù)對應的自增長序列id:
  @Options(useGeneratedKeys = true, keyProperty = "id ")
  @Insert({ "insert into planet (name) values (#{name})" })
//注意,不是把id作為方法的返回值,而是將id賦值給plate對象的id屬性。
  int insertPlanet(Planet planet);

10.15MapKey

標注返回結果的map的鍵值為對象中的哪個字段,如Person中存在一個字段名為id的屬性,MapKey的value設置為id,那么返回的map中key的值就是id的值,如:

@Select("select * from person")
  @MapKey("id")
  Map<Integer, Person> selectAsMap();

10.16 CacheNamespace、Property、CacheNamespaceRef注解

這個應該是用于命名空間中的緩存讀取,具體使用方法為在類名上加CacheNamespace注解,然后通過Property設置值:

@CacheNamespace(implementation = CustomCache.class, properties = {
//用取值符號取值,用于賦值
    @Property(name = "stringValue", value = "${stringProperty}"),
    @Property(name = "integerValue", value = "${integerProperty}"),
    @Property(name = "longValue", value = "${longProperty}")
})

在類名上增加注解CacheNamespaceRef,引用別的命名空間緩存,有兩種方式:
(1)通過類名路徑

@CacheNamespaceRef(name = "org.apache.ibatis.submitted.cache.PersonMapper")

(2)通過類的type

@CacheNamespaceRef(PersonMapper.class)

10.16 Arg和ConstructorArgs注解

這兩個注解一般都結合在一起使用,聲明查詢結果對應的Bean使用的構造函數(shù)和入?yún)ⅰ?/p>

10.17.1Arg注解

注解中的具體屬性如下:
//標注是否是id
  boolean id() default false;
  //列名
  String column() default "";
  //java的數(shù)據(jù)類型
  Class<?> javaType() default void.class;
  //jdbc中的數(shù)據(jù)類型
  JdbcType jdbcType() default JdbcType.UNDEFINED;
  //數(shù)據(jù)類型處理器
  Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
  //select子句,可以復用別的sql方法獲取結果,賦值給當前查詢
  //感覺這種復合查詢定位問題時可能不怎么方便
  String select() default "";
  //結果類型,可以填寫別的ResultMap的id的值,復用ResultMap
  String resultMap() default "";
  //構造函數(shù)中@Param注解中的value值
  String name() default "";
  //標注列前綴,在3.5.0版本中才引入的
  String columnPrefix() default "";

10.17.2ConstructorArgs注解

這個注解結合Arg注解用于標注使用指定的構造函數(shù),如:
(1)簡單的指定

@ConstructorArgs({
      @Arg(column = "name", name = "name"),
      @Arg(id = true, column = "id", name = "id"),
      @Arg(column = "team", name = "team", javaType = String.class),
  })
  @Select("select * from users where id = #{id}")
  User mapConstructorWithParamAnnos(Integer id);

(2)復雜嵌套,配置子查詢,感覺類似于關聯(lián)查詢吧。

@Select("SELECT * FROM " +
      "blog WHERE id = #{id}")
  @ConstructorArgs({
      @Arg(column = "id", javaType = int.class, id = true),
      @Arg(column = "title", javaType = String.class),
//這里指定了一個查詢方法,且指定查詢結果中的author_id作為子查詢的入?yún)?      @Arg(column = "author_id", javaType = Author.class, select = "org.apache.ibatis.binding.BoundAuthorMapper.selectAuthor"),
      @Arg(column = "id", javaType = List.class, select = "selectPostsForBlog")
  })
  Blog selectBlogUsingConstructor(int id);

10.18 TypeDiscriminator和Case注解

類型鑒定器主要用于查詢結果判斷與映射

10.18.1 Case中的屬性

//查詢結果中的值
  String value();
  //java中的對象類型
  Class<?> type();
  //結果映射
  Result[] results() default {};
  //待映射對象的構造器的構造入?yún)?  Arg[] constructArgs() default {};

10.18.2 TypeDiscriminator中的屬性

//列名
  String column();
  //java類型
  Class<?> javaType() default void.class;
  //jdbc中的類型
  JdbcType jdbcType() default JdbcType.UNDEFINED;
  //類型處理器
  Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
  //結果情景處理
  Case[] cases();

10.18.3 使用方式

    @TypeDiscriminator(
            // target for test (ordinal number -> Enum constant)
            column = "personType", javaType = PersonType.class, typeHandler = EnumOrdinalTypeHandler.class,
            // Switch using enum constant name(PERSON or EMPLOYEE) at cases attribute
            cases = {
             @Case(value = "PERSON", type = Person.class,  
             results = {@Result(property = "personType",  column = "personType", typeHandler = EnumOrdinalTypeHandler.class)}) ,
             @Case(value = "EMPLOYEE", type = Employee.class, 
             results = { @Result(property = "personType", column = "personType", typeHandler = EnumOrdinalTypeHandler.class)})
            })
    @Select("SELECT id, firstName, lastName, personType FROM person WHERE id = #{id}")
    Person findOneUsingTypeDiscriminator(int id);

原創(chuàng)文章轉載請標明出處
更多文章請查看
http://www.canfeng.xyz

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容