示例环境:JDK1.8+Spring Boot2+MP+Druid+Oracle
1.8
1.18.6
1.1.13
5.0.10
3.3.1.tmp
当表的主键类型为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 ( ?, ?, ?, ?, ?, ?, ?, ? )
(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();
}
这个注解在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);
/**
* MybatisPlus配置
*
* @author David Lin
* @version: 1.0
* @date 2020-02-22 23:15
*/
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
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
全部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 = ?)
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
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 (?, ?))
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 = ?)
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))
设置查询字段
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开头
* 默认是以I开头
* user表 -> IUserService, UserServiceImpl
*/
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;
}
实体对象继承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 字段值
/**
* 创建时间
*/
@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类型
**
* 自定义填充器
*
* @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()));
}
}
}
*/
@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 是一款基于 IDEA 的快速开发插件,为效率而生。
具体使用参考官网