示例環境:JDK1.8+Spring Boot2+MP+Druid+Oracle
<java.version>1.8</java.version>
<lombok.version>1.18.6</lombok.version>
<druid.version>1.1.13</druid.version>
<jodd.version>5.0.10</jodd.version>
<mybatis-plus.version>3.3.1.tmp</mybatis-plus.version>
文章目錄
一、插入主鍵策略
1.主鍵類型爲NUMBER
當表的主鍵類型爲NUMBER時,插入主鍵採用Oracle序列方式
創建序列如下:
create sequence emp_sequence
minvalue 1
maxvalue 999999999999999999999999
start with 1
increment by 1;
在實體類中採用@KeySequence註解,並且在註解中指定序列名稱,主鍵生成策略必須使用INPUT,如下:
@Setter
@Getter
@ToString
@TableName("EMP")
@KeySequence(value = "EMP_SEQUENCE",clazz = Long.class)
public class Emp {
/**
* 員工編號
*/
@TableId(type = IdType.INPUT)
private Long empno;
/**
* 員工姓名
*/
@TableField("ename")
private String empname;
........
同時還要 配置Oracle序列生成器
@Configuration
public class MybatisPlusConfig {
@Bean
public IdentifierGenerator idGenerator() {
//自定義ID生成器
return new CustomIdGenerator();
}
@Bean
public IKeyGenerator keyGenerator(){
//
return new OracleKeyGenerator();
}
}
這樣在保存的時候,會先執行查詢序列:
SELECT EMP_SEQUENCE.NEXTVAL FROM DUAL
然後再執行插入:INSERT INTO EMP ( empno, ename, job, mgr, hiredate, sal, comm, deptno ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )
2.主鍵類型爲VARCHAR2
(1)配置全局ID生成策略
mybatis-plus.global-config.db-config.id-type=assign_uuid
(2) 實現自定義的ID生成器
@Slf4j
public class CustomIdGenerator implements IdentifierGenerator {
@Override
public Number nextId(Object entity) {
return null;
}
@Override
public String nextUUID(Object entity) {
log.info("調用了UUID 生成!");
return PlatStringUtil.randomUUID();
}
}
(3) MybatisPlusConfig中 註冊UUID生成器
/**
* 註冊UUID生成器
* @return
*/
@Bean
public IdentifierGenerator idGenerator() {
//自定義ID生成器
return new CustomIdGenerator();
}
二、@TableField
這個註解在MP中是字段註解(非主鍵) 主要用途如下
- 對象中的屬性名和表字段名稱不一致的時候(非駝峯)使用
- 對象中的屬性名稱在表中不存在的時候使用
- 某個字段查詢的時候不返回值
示例如下:假設表EMP中字段有:員工姓名-ENAME ;薪水-SAL等字段,對象Emp中有屬性empAddress 不在表中,則可以使用該註解如下
@Setter
@Getter
@ToString
@TableName("EMP")
public class Emp {
/**
* 員工編號
*/
@TableId(type = IdType.ASSIGN_ID)
private Long empno;
/**
* 員工姓名
*/
@TableField("ename")
private String empname;
/**
* 工作崗位
*/
private String job;
/**
* 領導者編號
*/
private int mgr;
/**
* 入職時間
* 自定義輸出格式
*/
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Timestamp hiredate;
/**
* 薪水 查詢的時候不返回這個字段值
*/
@TableField(select = false)
private double sal;
/**
* 獎金
*/
private double comm;
/**
* 所在部門編號
*/
private int deptno;
/**
* 地址 不是數據庫表字段
*/
@TableField(exist = false)
private String empAddress;
}
三、更新操作
一種是根據ID更新,還有一種是根據條件更新
@RequestMapping("/updateByid")
public String updateById(HttpServletRequest request){
Emp emp = new Emp();
emp.setEmpno(1L);
emp.setHiredate(new Timestamp(System.currentTimeMillis()));
emp.setSal(100);
boolean result = empService.updateById(emp);
logger.info("the exec result is {}",result);
return "success";
}
根據ID更新的語句是: UPDATE EMP SET mgr=?, hiredate=?, sal=?, comm=?, deptno=? WHERE empno=?
@RequestMapping("/update")
public String update(HttpServletRequest request){
Emp emp = new Emp();
emp.setHiredate(new Timestamp(System.currentTimeMillis()));
emp.setSal(100);
/**
* 根據條件更新需要使用 條件構造器 QueryWrapper或者 UpdateWrapper
*/
QueryWrapper<Emp> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("ename","程序員");
queryWrapper.ge("sal",200);
boolean result = empService.update(emp, queryWrapper);
logger.info("the exec result is {}",result+"");
return "success";
}
根據條件更新語句爲: UPDATE EMP SET mgr=?, hiredate=?, sal=?, comm=?, deptno=? WHERE (ename = ? AND sal >= ?)
根據條件更新也可以寫成這樣
/**
* 或者
*/
UpdateWrapper<Emp> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("sal", 9999).set("hiredate", new Timestamp(System.currentTimeMillis()))
.eq("ename", "fire2").ge("sal", 200);
boolean result = empService.update(updateWrapper);
四、分頁查詢
1.配置分頁插件
/**
* MybatisPlus配置
*
* @author David Lin
* @version: 1.0
* @date 2020-02-22 23:15
*/
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
2.自定義分頁查詢
mapper接口定義如下:
/**
* 分頁查詢
* @param page
* @param queryWrapper
* @return
*/
IPage<Emp> selectPageInfo(Page<Emp> page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper);
自定義SQL的同時也使用Wrapper,方法參數必須添加@Param(Constants.WRAPPER) ,這樣在XML中的${ew.customSqlSegment}纔會生效,更多用法看官方DEMO
XML定義如下:
<select id="selectPageInfo" resultType="Emp">
select e.*,d.dname from emp e
left join dept d on e.deptno = d.deptno
${ew.customSqlSegment}
</select>
service定義如下:
public IPage<Emp> selectPage() {
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("d.deptno","20");
Page<Emp> page = new Page<>(1,5);
IPage<Emp> resultPage = empMapper.selectPageInfo(page, queryWrapper);
return resultPage;
}
最終分頁執行的Sql如下:
SELECT *
FROM (SELECT TMP.*, ROWNUM ROW_ID
FROM (select e.*, d.dname
from emp e
left join dept d
on e.deptno = d.deptno
WHERE (d.deptno = ?)) TMP
WHERE ROWNUM <= ?)
WHERE ROW_ID > ?
五、常用配置
application.properties 配置如下
#指定MyBatis-Plus配置文件 複雜配置可以單獨寫在 一個xml
#全局配置不能和 mybatis-plus.configuration.xx.xx 配置共同使用
mybatis-plus.config-location=classpath:mybatis/mybatis-config.xml
#如果接口方法對應的XML放在resources目錄下 需要告訴MyBatis-Plus哪裏掃描Mapper
mybatis-plus.mapper-locations=classpath:mapper/**/*.xml
#定義別名掃描的包
mybatis-plus.type-aliases-package=com.soft.fire.platform.*.model
#全局ID生成策略 設置後 可以省略實體對象中的 @TableId(type = IdType.INPUT) 配置
mybatis-plus.global-config.db-config.id-type=assign_uuid
#是否開啓自動駝峯命名規則(camel case)映射
#此屬性在 MyBatis 中原默認值爲 false,在 MyBatis-Plus 中默認開啓
#mybatis-plus.configuration.map-underscore-to-camel-case=true
#MP 全局地開啓或關閉配置文件中的所有映射器已經配置的任何緩存,默認爲 true。
#mybatis-plus.configuration.cache-enabled=true
六、條件構造器
1.allEq
全部eq(或個別isNull)
@RequestMapping("list")
public List<Emp> listEmp(HttpServletRequest request) {
Map<String, Object> params = new HashMap<>(8);
params.put("deptno", 20);
params.put("comm", null);
QueryWrapper<Emp> queryWrapper = new QueryWrapper();
queryWrapper.allEq(params);
return empService.list(queryWrapper);
}
執行的Sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (comm IS NULL AND deptno = ?)
2.likeLeft
QueryWrapper<Emp> queryWrapper = new QueryWrapper();
queryWrapper.likeLeft("job","MAN");
return empService.list(queryWrapper);
執行的Sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (job LIKE ?)
參數爲: %MAN
3.in
QueryWrapper<Emp> queryWrapper = new QueryWrapper();
queryWrapper.in("deptno",10,20);
執行的Sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (deptno IN (?, ?))
4.or
QueryWrapper<Emp> queryWrapper = new QueryWrapper();
queryWrapper.eq("ename", "smith").or().eq("deptno", 10);
執行的sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (ename = ? OR deptno = ?)
5.exists
QueryWrapper<Emp> queryWrapper = new QueryWrapper();
queryWrapper.exists("select 1 from dept d where d.deptno =deptno ");
執行的sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (EXISTS (select 1 from dept d where d.deptno = deptno))
6.select
設置查詢字段
QueryWrapper<Emp> queryWrapper = new QueryWrapper();
queryWrapper.select("empno,ename,job,deptno");
執行的Sql如下:
SELECT empno,ename,job,deptno FROM EMP
七、代碼生成器
(1) 添加依賴
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
(2)設置模板
這裏底層採用Freemarke模板引擎生成代碼,從GitHub下載這些模板如下:
(3)編寫代碼
這個代碼來自MP官方的DEMO
稍加修改如下:
**
* 代碼生成器 示例
* @since 202012/29
*/
public class CodeGeneratorWithTemplateTest {
/**
* 是否強制帶上註解
*/
private boolean enableTableFieldAnnotation = false;
/**
* 生成的註解帶上IdType類型
*/
private IdType tableIdType = null;
/**
* 是否去掉生成實體的屬性名前綴
*/
private String[] fieldPrefix = null;
/**
* 生成的Service 接口類名是否以I開頭
* <p>默認是以I開頭</p>
* <p>user表 -> IUserService, UserServiceImpl</p>
*/
private boolean serviceClassNameStartWithI = true;
//運行這個方法 自動代碼生成
@Test
public void generateCode() {
String packageName = "com.soft.fire.platform";
enableTableFieldAnnotation = true;
tableIdType = null;
generateByTables(packageName , "PLAT_SYSTEM_SYSUSER");
}
private void generateByTables(String packageName, String... tableNames) {
// 代碼生成器
AutoGenerator autoGenerator = new AutoGenerator();
// 全局配置
GlobalConfig globalConfig = getGlobalConfig();
// 數據源配置
String dbUrl = "jdbc:oracle:thin:@127.0.0.1:1521:ORCL";
DataSourceConfig dataSourceConfig = getDataSourceConfig(dbUrl);
// 策略配置
StrategyConfig strategyConfig = getStrategyConfig(tableNames);
//配置模板
TemplateConfig templateConfig = getTemplateConfig();
//包配置
PackageConfig packageConfig = getPackageConfig(packageName);
// 自定義配置
InjectionConfig injectionConfig = getInjectionConfig(packageConfig);
autoGenerator.setGlobalConfig(globalConfig);
autoGenerator.setDataSource(dataSourceConfig);
autoGenerator.setStrategy(strategyConfig);
//配置自定義模板
autoGenerator.setTemplate(templateConfig);
//配置自定義屬性注入
autoGenerator.setCfg(injectionConfig);
autoGenerator.setPackageInfo(packageConfig);
//使用 Freemarker模版引擎
autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());
autoGenerator.execute();
}
/**
* 包配置
*
* @param packageName
* @return
*/
private PackageConfig getPackageConfig(String packageName) {
PackageConfig pc = new PackageConfig();
pc.setModuleName("system");
pc.setParent(packageName);
pc.setController("controller");
pc.setEntity("model");
pc.setService("service");
pc.setMapper("mapper");
return pc;
}
/**
* 自定義配置
*
* @return
*/
private InjectionConfig getInjectionConfig(PackageConfig pc) {
InjectionConfig injectionConfig = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
this.setMap(map);
}
};
// 自定義輸出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定義配置優先於默認配置生效
focList.add(new FileOutConfig("/templates/code/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定義mapper xml輸出目錄
return System.getProperty("user.dir") + "/src/main/resources/mapper/" + pc.getModuleName() +
"/" + tableInfo.getMapperName() + StringPool.DOT_XML;
}
});
injectionConfig.setFileOutConfigList(focList);
return injectionConfig;
}
/**
* 指定自定義模板路徑
*
* @return
*/
private TemplateConfig getTemplateConfig() {
//指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setEntity("templates/code/entity.java");
templateConfig.setController("templates/code/controller.java");
templateConfig.setMapper("templates/code/mapper.java");
//關閉默認的mapper xml生成
templateConfig.setXml(null);
templateConfig.setService("templates/code/service.java");
templateConfig.setServiceImpl("templates/code/serviceImpl.java");
return templateConfig;
}
/**
* 策略配置
*
* @return
*/
private StrategyConfig getStrategyConfig(String... tableNames) {
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setCapitalMode(true);
strategyConfig.setRestControllerStyle(true);
//設置數據庫表映射到實體的命名策略
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
//設置數據庫表字段映射到實體的命名策略, 未指定按照 naming 執行
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
//是否生成實體時,生成字段註解
strategyConfig.setEntityTableFieldAnnotationEnable(enableTableFieldAnnotation);
//是否啓用 Lombok
strategyConfig.setEntityLombokModel(true);
//表前綴,配置後 生成的的代碼都會把前綴去掉
strategyConfig.setTablePrefix("PLAT_SYSTEM");
//test_id -> id, test_type -> type
strategyConfig.setFieldPrefix(fieldPrefix);
//修改替換成你需要的表名,多個表名傳數組
strategyConfig.setInclude(tableNames);
return strategyConfig;
}
/**
* 數據源配置
*
* @param dbUrl
* @return
*/
private DataSourceConfig getDataSourceConfig(String dbUrl) {
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.ORACLE);
dataSourceConfig.setUrl(dbUrl);
dataSourceConfig.setUsername("scott");
dataSourceConfig.setPassword("tiger");
dataSourceConfig.setDriverName(Driver.class.getName());
return dataSourceConfig;
}
private GlobalConfig getGlobalConfig() {
// 全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setActiveRecord(false);
globalConfig.setAuthor("David");
//生成文件的輸出目錄
globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
//是否覆蓋已有文件
globalConfig.setFileOverride(true);
//開啓 BaseResultMap
globalConfig.setBaseResultMap(true);
//開啓 baseColumnList
globalConfig.setBaseColumnList(true);
//是否生成完成後打開資源管理器
globalConfig.setOpen(true);
//日期類型的字段使用哪個類型,默認是 java8的 日期類型,此處改爲 java.util.date
globalConfig.setDateType(DateType.ONLY_DATE);
//生成的Service 接口類名是否以I開頭
if (!serviceClassNameStartWithI) {
globalConfig.setServiceName("%sService");
}
return globalConfig;
}
八、ActiveRecord模式
實體對象繼承Model就可以開啓AR模式
@Setter
@Getter
@ToString
@TableName("EMP")
@KeySequence("EMP_SEQUENCE")
public class Emp extends Model<Emp> {
/**
* 員工編號
*/
@TableId(type = IdType.INPUT)
private Long empno;
@RequestMapping("list")
public List<Emp> listEmp(HttpServletRequest request) {
QueryWrapper<Emp> queryWrapper = new QueryWrapper();
queryWrapper.eq("deptno",20);
queryWrapper.select("empno,ename,job,deptno");
Emp emp = new Emp();
return emp.selectList(queryWrapper);
}
執行的SQL如下:
SELECT empno,ename,job,deptno FROM EMP WHERE (deptno = ?)
由於底層也是調用Mapper的方法,所以EmpMapper不能刪除掉
九、自動填充
當插入的時候自動填充CREATE_TIME 字段值,當更新的時候 自動填充UPDATE_TIME 字段值
1.指定要自動填充的字段
/**
* 創建時間
*/
@TableField(value = "CREATE_TIME" ,fill = FieldFill.INSERT,jdbcType = JdbcType.TIMESTAMP)
private Timestamp createTime;
/**
* 更新時間
*/
@TableField(value = "UPDATE_TIME",fill = FieldFill.UPDATE,jdbcType = JdbcType.TIMESTAMP)
private Timestamp updateTime;
如果日期類型使用LocalDateTime ,如會報:Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: 無效的列類型: 1111, ,日期類型改成 Timestamp類型
2.自定義填充器實現類
**
* 自定義填充器
*
* @author David Lin
* @version: 1.0
* @date 2020-03-15 17:28
*/
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入時填充
*
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
Object createTime = getFieldValByName("createTime", metaObject);
//如果爲空 才進行填充
if (null == createTime) {
this.strictInsertFill(metaObject, "createTime", Timestamp.class, new Timestamp(System.currentTimeMillis()));
}
}
/**
* 更新時填充
*
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
Object createTime = getFieldValByName("updateTime", metaObject);
//如果爲空 才進行填充
if (null == createTime) {
this.strictUpdateFill(metaObject, "updateTime", Timestamp.class, new Timestamp(System.currentTimeMillis()));
}
}
}
3.注入Spring BOOT
*/
@Configuration
public class MybatisPlusConfig {
/**
* 注入 自定義填充器
* @return
*/
@Bean
public MetaObjectHandler metaObjectHandler(){
return new MyMetaObjectHandler();
}
這樣在插入操作/更新操作的時候 如果沒有顯示設置時間值, 則會自動填充,插入的sql如下:
INSERT INTO EMP ( empno, ename, job, mgr, hiredate, sal, comm, deptno, CREATE_TIME ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )
自動填充只對操作實體對象時纔有效果
十、MybatisX
MybatisX 是一款基於 IDEA 的快速開發插件,爲效率而生。
1.Java代碼與XML之間跳轉
2.Mapper方法自動生成XML
具體使用參考官網