mybatis原始碼閱讀筆記-卷四(註解)
十.annotations註解包
Mybatis使用註解的方式可以減少使用xml配置sql,方便用斷點的形式檢測生成的sql,程式碼的可讀性更強,更利於維護。
10.1Param註解
用於定義介面入參的別名,方便程式碼執行時獲取介面入參,而不至於讀取引數表中的引數時因為引數型別相同而導致引數獲取紊亂。
用法:
List<T> selectByIdAndName (@Param("id") Integer id, @Param("name") String name);
10.2Select註解
給指定的mapper介面增加sql語句實現查詢功能,使用方法為:
@Select("select * from users") List<User> getAllUsers();
10.3SelectProvider註解
方便動態生成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語句執行前(後)插入執行,如在執行前(後)查詢當前自增張id的值,註解中宣告的屬性為:
//sql子句 String[] statement(); //屬性名,將查詢得到列中的資料賦值屬性名對應的屬性 String keyProperty(); //查詢返回的列名 String keyColumn() default ""; //表示在是否在主sql執行前執行 boolean before(); //返回的值的型別 Class<?> resultType(); //狀態 StatementType statementType() default StatementType.PREPARED;
具體使用方式為:
(1)sql語句執行後
@Update("update table2 set name = #{name} where id = #{nameId}") //在更新語句後執行,查詢nameId對應的name_fred,並賦值給Name類的generatedName屬性(入參name的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語句執行前
@Insert("insert into table3 (id, name) values(#{nameId}, #{name})") //在插入語句執行之前,查詢資料庫表中下一個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,且根據註解定義的sql語句實現功能。不過這個註解的載入不是在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註解
在與資料庫查詢結果對應的model類的構造器上增加這個註釋,可以讓框架自動使用這個構造器。
10.11 ResultType註解
用於註明結果型別,使用方式為:
@Select("select * from users") @ResultType(User.class) //雖然方法的返回結果為void,但是入參resultHandler可以接受返回結果 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註解
該註解用於標註使用的自定義語言指令碼驅動(自定義實現自LanguageDriver介面的類),方便於解析sql語言,使用時只需要在對應方法上增加註解,如:
@Lang(XMLLanguageDriver.class)
10.14Flush註解
使用這個註解的方式應該是在對應的方法上增加註解@Flush,然後呼叫方法時會將快取中的操作批量重新整理到資料庫中,因為官方給的文件不夠詳細,本人也沒有實踐過,如果存在理解錯誤,請諒解。
10.13Results、ResultMap、Result註解
這幾個註解用於查詢結果的對映,用於將資料庫中的欄位名和java物件中的欄位名對應上,不過目前也可以使用資料庫提供的別名定義的方式實現名稱對映,同時也可以使用自定義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會被定義成資料庫中的序列化主鍵 boolean id() default false; //資料庫查詢結果中的列名 String column() default ""; //需要對映的物件中的屬性名 String property() default ""; //java的型別 Class<?> javaType() default void.class; //資料庫中的型別 JdbcType jdbcType() default JdbcType.UNDEFINED; //自定義型別處理器 Class<? extends TypeHandler<?>> typeHandler() default UnknownTypeHandler.class; //標註為單條結果對映 One one() default @One; //表示為多條結果對映 Many many() default @Many;
10.13.3Results應用
具體使用時,在需要定義結果對映的方法前增加註釋,就可以將查詢到的結果按照定義好的結果對映樣式完成對映。
//這裡的註解中的引數只是為了展示方便定義的,目的是為了展示註解的使用方法,可能存在錯誤 @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
這個註解用於配置查詢結果配置,內部具有如下屬性:
//是否使用快取 boolean useCache() default true; //是否清除快取 FlushCachePolicy flushCache() default FlushCachePolicy.DEFAULT; //結果集的型別 ResultSetType resultSetType() default ResultSetType.FORWARD_ONLY; //執行語句生成方式型別 StatementType statementType() default StatementType.PREPARED; //返回結果條目數 int fetchSize() default -1; //查詢超時時間 int timeout() default -1; //是否使用自增長key boolean useGeneratedKeys() default false; //key在結果物件中的屬性名 String keyProperty() default ""; //key在資料庫中的列名 String keyColumn() default ""; //不清楚怎麼用 String resultSets() default ""; 常見的使用方式為返回插入資料對應的自增長序列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使用的建構函式和入參。
10.17.1Arg註解
註解中的具體屬性如下:
//標註是否是id boolean id() default false; //列名 String column() default ""; //java的資料型別 Class<?> javaType() default void.class; //jdbc中的資料型別 JdbcType jdbcType() default JdbcType.UNDEFINED; //資料型別處理器 Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class; //select子句,可以複用別的sql方法獲取結果,賦值給當前查詢 //感覺這種複合查詢定位問題時可能不怎麼方便 String select() default ""; //結果型別,可以填寫別的ResultMap的id的值,複用ResultMap String resultMap() default ""; //建構函式中@Param註解中的value值 String name() default ""; //標註列字首,在3.5.0版本中才引入的 String columnPrefix() default "";
10.17.2ConstructorArgs註解
這個註解結合Arg註解用於標註使用指定的建構函式,如:
(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)複雜巢狀,配置子查詢,感覺類似於關聯查詢吧。
@Select("SELECT * FROM " + "blog WHERE id = #{id}") @ConstructorArgs({ @Arg(column = "id", javaType = int.class, id = true), @Arg(column = "title", javaType = String.class), //這裡指定了一個查詢方法,且指定查詢結果中的author_id作為子查詢的入參 @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 {}; //待對映物件的構造器的構造入參 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);