mybatis-plus插件是對mybatis作出系列加強插件,後面簡稱MP,MP可免去開發者重複編寫xml、mapper、service、entity等代碼,經過MP提供的實體註解來完成單表的CRUD簡單操做,MP一樣配套有代碼生成工具,可經過簡單的配置來生成xml、mapper、service、entity等文件,極大提高了開發速度,本文是在spring-cloud的環境下集成mybatis-plus。html
spirng-cloud的基礎環境搭建可參考http://www.javashuo.com/article/p-ytxyscfm-dc.html。java
進入正文。mysql
——————————————————————————————————————————————————————————————————————————————————————————————————————————————git
先來pom.xml文件web
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>custom-authorize</groupId> <artifactId>custom-authorize</artifactId> <version>0.0.1-SNAPSHOT</version> <!-- 引入spring boot的依賴 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.7.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- junit的spring-boot依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- alibaba fastjson 格式化對 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.1.41</version> </dependency> <!-- feign組件依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- jwt認證協議依賴 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> <!-- redis依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!-- eureka服務組件 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- MYSQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- Mybatis依賴 --> <!-- <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0-gamma</version> </dependency> <!-- 引入Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- mybatis-plus代碼生成器模板引擎 --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> </dependency> </dependencies> <!-- 引入spring cloud的依賴,不能少,主要用來管理Spring Cloud生態各組件的版本 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- 添加spring-boot的maven插件,不能少,打jar包時得用 --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
裏面部分jar包是微服務所需jar包,核心jar包commons-pool二、mysql-connector-java、mybatis-plus-boot-starter、lombok、freemarker、spring-cloud、spring-boot這幾個,其餘的不須要能夠刪掉。redis
lombok能夠極大減小實體重複代碼的編寫,例如get/set/toString/constrator等,感興趣的朋友能夠去度娘一下,基本就是幾個註解。spring
這裏使用的是3.x版本的MP插件,支持jdk1.8的特性,文檔地址:https://baomidou.gitee.io/mybatis-plus-doc/#/quick-start。sql
再來編寫入口類:數據庫
package com.authorize; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; /** * 入口類 * @title AuthorizeApplication.java * @package com.authorize * @description TODO(一句話描述該類做用) * @author Pandong * @date 2019年2月22日 */ @SpringBootApplication @EnableFeignClients // 服務之間通訊所需註解 @EnableEurekaClient // 基於spring-boot環境的能夠刪掉上面及本行註解 @MapperScan("com.authorize.dao") public class AuthorizeApplication { public static void main(String[] args) { SpringApplication.run(AuthorizeApplication.class, args); } /** * MP分頁插件,後面會有說明 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
編寫application.yml文件:apache
server:
port: 8082
spring:
application:
# 指定註冊到eureka server上的服務名稱
name: custom-authorize
#################################redis配置########################################
redis:
host: 127.0.0.1
password: 123
port: 6379
timeout: 10000 # 鏈接超時時間(毫秒)
database: 0 # Redis默認狀況下有16個分片,這裏配置具體使用的分片,默認是0
lettuce:
pool:
max-active: 8 # 鏈接池最大鏈接數(使用負值表示沒有限制)默認 8
max-wait: -1 # 鏈接池最大阻塞等待時間(使用負值表示沒有限制)默認 -1
max-idle: 8 # 鏈接池中的最大空閒鏈接默認 8
min-idle: 0 # 鏈接池中的最小空閒鏈接默認 0
#################################redis配置########################################
#####################################################################################################
# mysql 屬性配置
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/web_custom
username: root
password: pd123
# jpa:
# hibernate:
# #ddl-auto: create #ddl-auto:設爲create表示每次都從新建表
# ddl-auto: update #ddl-auto:設爲update表示每次都不會從新建表
# show-sql: true
#####################################################################################################
#####################################################################################################
# mybatis mapper xml 配置
#mybatis:
# mybatis.type-aliases-package:指定domain類的基包,即指定其在*Mapper.xml文件中能夠使用簡名來代替全類名(看後邊的UserMapper.xml介紹)
#type-aliases-package:
#mapper-locations: classpath:mybatis/mapper/*.xml
#config-location: classpath:mybatis/mybatis-config.xml
# mybatis-plus 配置
mybatis-plus:
# 若是是放在src/main/java目錄下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
# 若是是放在resource目錄 classpath:/mapper/*Mapper.xml
config-location: classpath:/mybatis/mybatis-config.xml
mapper-locations: classpath:/mybatis/mapper/*.xml
#實體掃描,多個package用逗號或者分號分隔
typeAliasesPackage: com.authorize.entity
global-config:
#主鍵類型 0:"數據庫ID自增", 1:"用戶輸入ID",2:"全局惟一ID (數字類型惟一ID)", 3:"全局惟一ID UUID";
id-type: 3
# 熱加載mapper文件
refresh: true
db-config:
db-type: mysql
#####################################################################################################
eureka:
client:
service-url:
# 指定eureka server通訊地址,注意/eureka/小尾巴不能少
defaultZone: http://admin:123@localhost:8080/eureka/
instance:
# 是否註冊IP到eureka server,如不指定或設爲false,那就會註冊主機名到eureka server
prefer-ip-address: true
logging:
level:
root: INFO
org.hibernate: INFO
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
com.authorize: DEBUG
編寫mybatis-config.xml文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="callSettersOnNulls" value="true"/> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="NULL"/> </settings> <typeAliases> <typeAlias alias="Integer" type="java.lang.Integer" /> <typeAlias alias="Long" type="java.lang.Long" /> <typeAlias alias="HashMap" type="java.util.HashMap" /> <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" /> <typeAlias alias="ArrayList" type="java.util.ArrayList" /> <typeAlias alias="LinkedList" type="java.util.LinkedList" /> </typeAliases> </configuration>
編寫實體類:
1 package com.authorize.entity; 2 3 import java.math.BigDecimal; 4 import com.baomidou.mybatisplus.annotation.TableName; 5 import com.baomidou.mybatisplus.annotation.IdType; 6 import com.baomidou.mybatisplus.extension.activerecord.Model; 7 import com.baomidou.mybatisplus.annotation.TableId; 8 import com.baomidou.mybatisplus.annotation.TableField; 9 import java.io.Serializable; 10 11 import lombok.Data; 12 import lombok.EqualsAndHashCode; 13 import lombok.experimental.Accessors; 14 15 /** 16 * <p> 17 * 18 * </p> 19 * 20 * @author Pandong 21 * @since 2019-03-05 22 */ 23 @Data 24 @EqualsAndHashCode(callSuper = false) 25 @Accessors(chain = true) 26 @TableName("ct_user") 27 public class CtUser extends Model<CtUser> { 28 29 private static final long serialVersionUID = 1L; 30 31 /** 32 * 主鍵 33 */ 34 @TableId(value = "user_id", type = IdType.AUTO) 35 private Long userId; 36 37 /** 38 * 用戶名稱 39 */ 40 @TableField("user_name") 41 private String userName; 42 43 /** 44 * 年齡 45 */ 46 private Integer age; 47 48 /** 49 * 價格 50 */ 51 private BigDecimal balance; 52 53 54 @Override 55 protected Serializable pkVal() { 56 return this.userId; 57 } 58 59 }
能夠看到實體類上面使用了與hibernate類似的註解,屬性上面使用了TableId註解,屬性還有其餘註解,可自行去查看文檔。
實體上其餘註解都是lombok的註解,是否是減小了不少代碼。
編寫mapper:
package com.authorize.dao; import com.authorize.entity.CtUser; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * <p> * Mapper 接口 * </p> * * @author Pandong * @since 2019-03-05 */ public interface CtUserMapper extends BaseMapper<CtUser> { }
沒看錯,只須要繼承BaseMapper就可實現CRUD,裏面有大量的基礎方法提供調用,能知足大部分的單表以及分頁操做。
mapper上面未使用@Mapper註解,是由於在入口類中增長了註解@MapperScan註解。
編寫Service:
package com.authorize.service.authorize; import com.authorize.entity.CtUser; import com.baomidou.mybatisplus.extension.service.IService; /** * <p> * 服務類 * </p> * * @author Pandong * @since 2019-03-05 */ public interface ICtUserService extends IService<CtUser> { }
編寫Impl:
package com.authorize.service.impl; import com.authorize.entity.CtUser; import com.authorize.dao.CtUserMapper; import com.authorize.service.authorize.ICtUserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; /** * <p> * 服務實現類 * </p> * * @author Pandong * @since 2019-03-05 */ @Service public class CtUserServiceImpl extends ServiceImpl<CtUserMapper, CtUser> implements ICtUserService { }
service一樣繼承MP提供的頂級基類,裏面包含了大量的基礎方法提供調用。
編寫完成後總體的目錄結構:
generatorTemplate目錄是代碼生成的自定義模板目錄,後面說代碼生成會說到。
最後編寫測試類:
package com.authorize.controller; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import com.authorize.AuthorizeApplication; import com.authorize.service.authorize.ICtMenuService; /** * * @filename CtSysyserControllerTest.java * @pakage com.authorize.controller * @descption TODO(用一句話表述類的做用) * @author Pandong * @date 2019年3月5日 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = AuthorizeApplication.class) public class CtSysyserControllerTest { @Autowired private ICtUserService ss;
// 因爲我數據庫中已經有數據,因此插入數據的代碼就不寫了 @Test public void test() { CtUser CtUser = ss.selectById(1); System.out.println(user); } }
到這裏MP已經集成進來了,單表基本不用本身去寫任何代碼,固然,上面只是基本的配置,如今來講分頁插件如何使用。
官方文檔中有多個分頁的實現,這裏只講官方推薦的方式,在上面的入口類中已經添加了分頁插件的初始化(使用spring同志請自行去看文檔)。
@Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }
編寫測試類:
package com.authorize.controller; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.authorize.AuthorizeApplication; import com.authorize.entity.CtUser; import com.authorize.service.authorize.ICtUserService; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; /** * * @filename CtSysyserControllerTest.java * @pakage com.authorize.controller * @descption TODO(用一句話表述類的做用) * @author Pandong * @date 2019年3月5日 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = AuthorizeApplication.class) public class CtUserControllerTest { @Autowired private ICtUserService ss; @Test public void test() { IPage<CtUser> page = new Page<>(1, 20); QueryWrapper<CtUser> wapper = new QueryWrapper<>(); page = ss.selectPage(page, wapper); System.out.println(page.getRecords()); } }
MP實現分頁只須要new Page就完了,這樣就完成一個簡單的分頁查詢。
可能有的朋友看不明白QueryWrapper是什麼,QueryWrapper是MP對查詢的封裝,這裏作一些簡單的說明,詳細的去文檔中查看,國人大牛開發的,註釋都是中文的。
@Test public void test1() { IPage<CtUser> page = new Page<>(1, 20); QueryWrapper<CtUser> wapper = new QueryWrapper<>(); LambdaQueryWrapper<CtUser> lambda = wapper.lambda(); lambda.and( obj -> obj.eq(CtUser::getUserName, "張三").eq(CtUser::getAge, "30")); page = ss.selectPage(page, lambda); System.out.println(page.getRecords()); }
上面代碼是一個簡單的查詢分頁操做,MP支持lambda表達式以及1.8的一些新特性,看不明白函數接口、方法引用的可參考http://www.runoob.com/java/java8-new-features.html。
LambdaQueryWrapper中封裝了不少方法來構造各類不一樣的查詢條件,上面所用到的and方法,查詢的sql語句:SELECT user_id AS userId,user_name AS userName,age,balance FROM ct_user WHERE ( user_name = ? AND age = ? ) ,
裏面還封裝了eq、or、like等等,詳細的能夠去看官方文檔,上文中就給出地址。
到這裏MP基本的CURD已經算是完成了,最後再來講一下代碼生成:
package com.authorize.utils.generator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.InjectionConfig; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.FileOutConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.config.rules.DbColumnType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; /** * 基於mybatis-plus的代碼成功工具 * * @filename EntityGenerator.java * @pakage com.authorize.utils.generator * @descption TODO(用一句話表述類的做用) * @author Pandong * @date 2019年3月1日 */ public class EntityGenerator { public static void main(String[] args) { generator(); } public static void generator() { AutoGenerator mpg = new AutoGenerator(); // 選擇 freemarker 引擎,默認 Veloctiy mpg.setTemplateEngine(new FreemarkerTemplateEngine()); // 全局配置 GlobalConfig gc = new GlobalConfig(); gc.setAuthor("Pandong"); gc.setOutputDir("D:\\workspace\\springcloud_learning\\custom-authorize\\src\\main\\java"); gc.setFileOverride(false);// 是否覆蓋同名文件,默認是false gc.setActiveRecord(true);// 不須要ActiveRecord特性的請改成false gc.setEnableCache(false);// XML 二級緩存 gc.setBaseResultMap(true);// XML ResultMap gc.setBaseColumnList(false);// XML columList /* 自定義文件命名,注意 %s 會自動填充表實體屬性! */ // gc.setMapperName("%sDao"); // gc.setXmlName("%sDao"); // gc.setServiceName("MP%sService"); // gc.setServiceImplName("%sServiceDiy"); // gc.setControllerName("%sAction"); mpg.setGlobalConfig(gc); // 數據源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setDbType(DbType.MYSQL); dsc.setTypeConvert(new MySqlTypeConvert() { // 自定義數據庫表字段類型轉換【可選】 public DbColumnType processTypeConvert(String fieldType) { System.out.println("轉換類型:" + fieldType); // 注意!!processTypeConvert 存在默認類型轉換,若是不是你要的效果請自定義返回、非以下直接返回。 return super.processTypeConvert(gc, fieldType); } }); dsc.setDriverName("com.mysql.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("pd123"); dsc.setUrl("jdbc:mysql://localhost:3306/web_custom?useUnicode=true&characterEncoding=utf8"); mpg.setDataSource(dsc); // 策略配置 StrategyConfig strategy = new StrategyConfig(); // strategy.setCapitalMode(true);// 全局大寫命名 ORACLE 注意 // strategy.setTablePrefix(new String[] { "ct_" });// 此處能夠修改成您的表前綴 strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略 strategy.setInclude(new String[] { "ct_authorize","ct_btn","ct_menu","ct_role","ct_sysuser","ct_user"}); // 須要生成的表 strategy.setEntityLombokModel(true); // 生成lombox模型實體 strategy.entityTableFieldAnnotationEnable(true); // // strategy.setExclude(new String[]{"test"}); // 排除生成的表 // 自定義實體父類 // strategy.setSuperEntityClass("com.baomidou.demo.TestEntity"); // 自定義實體,公共字段 // strategy.setSuperEntityColumns(new String[] { "test_id", "age" }); // 自定義 mapper 父類 // strategy.setSuperMapperClass("com.baomidou.demo.TestMapper"); // 自定義 service 父類 // strategy.setSuperServiceClass("com.authorize.service.authorize.IDGeneratorService"); // 自定義 service 實現類父類 // strategy.setSuperServiceImplClass("com.authorize.service.impl.IDGeneratorServiceImpl"); // 自定義 controller 父類 strategy.setSuperControllerClass("com.authorize.controller.BaseController"); // 【實體】是否生成字段常量(默認 false) // public static final String ID = "test_id"; // strategy.setEntityColumnConstant(true); // 【實體】是否爲構建者模型(默認 false) // public User setName(String name) {this.name = name; return this;} // strategy.setEntityBuilderModel(true); mpg.setStrategy(strategy); // 包配置 PackageConfig pc = new PackageConfig(); pc.setParent("com.authorize"); pc.setService("service.authorize"); pc.setServiceImpl("service.impl"); pc.setXml(null); pc.setMapper("dao"); // pc.setModuleName("test"); mpg.setPackageInfo(pc); // 注入自定義配置,能夠在 VM 中使用 cfg.abc 【可無】 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { } }; // // // 自定義 xxList.jsp 生成 List<FileOutConfig> focList = new ArrayList<>(); // focList.add(new FileOutConfig("/template/list.jsp.vm") { // @Override // public String outputFile(TableInfo tableInfo) { // // 自定義輸入文件名稱 // return "D://my_" + tableInfo.getEntityName() + ".jsp"; // } // }); // cfg.setFileOutConfigList(focList); // mpg.setCfg(cfg); // // // 調整 xml 生成目錄演示 focList.add(new FileOutConfig("/mybatis/generatorTemplate/mapper.xml.ftl") { @Override public String outputFile(TableInfo tableInfo) { return "src/main/resources/mybatis/mapper/" + tableInfo.getEntityName() + "Mapper.xml"; } }); cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // // // 關閉默認 xml 生成,調整生成 至 根目錄 // TemplateConfig tc = new TemplateConfig(); // tc.setXml(null); // mpg.setTemplate(tc); // 自定義模板配置,能夠 copy 源碼 mybatis-plus/src/main/resources/templates 下面內容修改, // 放置本身項目的 src/main/resources/templates 目錄下, 默認名稱一下能夠不配置,也能夠自定義模板名稱 // TemplateConfig tc = new TemplateConfig(); // tc.setController("..."); // tc.setEntity("..."); // tc.setMapper("..."); // tc.setXml("..."); // tc.setService("..."); // tc.setServiceImpl("..."); // 如上任何一個模塊若是設置 空 OR Null 將不生成該模塊。 // mpg.setTemplate(tc); // 執行生成 mpg.execute(); // 打印注入設置【可無】 // System.err.println(mpg.getCfg().getMap().get("abc")); } }
上面是官網給出的例子改的DEMO,上面我本身改了代碼生成的位置,使用的模板也改過了,模板能夠從源碼的templates中拷貝。
這裏配置生成文件的位置,可自行修改位置。
這段代碼就是修改xml生成的位置,以及使用本身修改過的模板。
說到這裏基本集成已經完成了,包含了生成的系列代碼,更詳細的就去看官方文檔。。。