对于一个快速开发框架来说,离不开一个很重要的功能模块那就是数据库访问,目前在日常开发中用的最多的当然就是mybatis了,但是mybatis同样也会给我们带来很多重复的开发工作,例如一般的CRUD,也有很多开源框架在mybatis的基础上又封装了基础CRUD的基础类,但是这样的坏处也显而易见,和mybatis耦合很强,这里在我们的框架中,我们直接集成国内比较好的mybatis组件-mybatis plus,这个组件在github上的人气也很高,因为其底层原理很简单,所以大家也可以借助于该组件做一些深度二开,并且与mybatis没有强耦合,是两个独立的jar包。mybatis plus基于mybatis定义了很多Base范性基类,开发者只要将自己的Mapper和service继承自这些范性基类就可以直接继承基础的CRUD的方法,可以大幅减少代码量。
下面我们就拿上一篇提到的系统日志记录数据库的功能来举例子,让大家体验下在自己的框架中集成mybatis plus。
POM增加依赖
在之前框架的POM中新增以下依赖,因为我们框架是基于springboot开发所以这里我们直接使用mybatis plus为springboot开发的组件‘mybatis-plus-boot-starter’,可以更加方便和springboot集成,当然你也可以依赖mybatis plus基础库,但是要做些配置,具体可以参考官网文档,所以这里偷懒直接用了boot组件。
除了依赖mybatis plus的库,当然访问数据库还要依赖相关数据库的connector,只要是mybatis支持的数据库,当然plus也都可以支持,例子中我们用了大家日常用的最多的mysql,使用其他数据库的需要替换掉依赖的数据库connector,yml或property里的配置也需要进行调整。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
mybatis plus相关资料可以查看github,里面的文档还是比较完善的:https://github.com/baomidou/mybatis-plus
配置
因为mybatis plus功能太完善,框架中不需要做任何二开,直接使用,我们直接拿上一篇提到的日志模块来举例,上一篇是写到文件中,这里我们将同样的数据写到数据库中。
首先我们在数据库中建立一张名叫sys_log的表,具体的建表sql可以参看sql目录下的建表语句,里面的字段和上一篇定义的SysLog实体类的字段一样,具体参看源码。
接下来在application-xxx.yml中配置datasource:
spring:
datasource:
url: jdbc:mysql://123.206.118.12:3306/mkframework?useUnicode=true&characterEncoding=utf-8
username: root
password: Dnn198411!
driverClassName: com.mysql.jdbc.Driver
在application.yml中配置mybatis-plus相关信息,plus中有很多配置参数,这里只是配置了日常会用到的一些参数配置,还有其他的配置大家可以参考plus的github官方文档资料。
#mybatis
mybatis-plus:
mapper-locations: classpath:/mapper/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.monkey01.common.domain
# typeEnumsPackage: com.baomidou.springboot.entity.enums
global-config:
# 数据库相关配置
db-config:
#主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: id_worker
#字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
field-strategy: not_empty
#驼峰下划线转换
column-underline: true
#数据库大写下划线转换
#capital-mode: true
#逻辑删除配置
logic-delete-value: 0
logic-not-delete-value: 1
db-type: h2
#刷新mapper 调试神器
refresh: true
# 原生配置
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
定义DO、Mapper、Service类
数据库sys_log我们需要定义一个对应的DO和它进行ORM映射。
@TableName("sys_log")
public class SysLogDO implements Serializable {
@TableId
private Long id;
//用户名
private String username;
//用户操作
private String operation;
//请求方法
private String method;
//请求URL
private String url;
//请求参数
private String params;
//执行时长(毫秒)
private Long time;
//IP地址
private String ip;
//创建时间
private Date createDate;
//...省略setter、getter
}
大家一定注意到这里和其他的POJO的区别,那就是在类名前面和id字段定义前面都有注解,这里简单解释下。和mybatis一样,我们日常将一个DO类和数据库表关联起来有两种方法,一种是使用传统的mapper.xml将DO类和数据库表关联起来,字段名如果不一样的,也需要进行字段之间的映射关系定义,还有一定方法是在DO类中定义注解,来将原先定义在XML中的信息定义到注解里。当然plus会对这种传统的定义方法进行改良,我们只需要在DO类前面加上@TableName(“XXX”)在注解的value中写上数据库表名就可以将该DO类与对应的数据库表映射起来了。如果表中的字段和我们定义的DO类的字段名称完全一样或者数据库表字段只是用下划线替代了DO类中字段的驼峰结构,那么plus会自动进行映射字段,如果是将下划线转为驼峰定义需要在上面的配置中将转换设置为true,column-underline: true
。
还可以看到我们在id字段定义的前面加上了@TableID,该注解只可以定义在使用int或者Long定义的字段上用于自增id生成,在plus的配置中还可以配置生成id的算法,这里plus官方推荐的是使用开源sequence项目生成的id,只需要在yml中plus配置下配置id-type: id_worker
就可以了。
定义好了DO后,还需要和mybatis一样,还需要定义mapper接口,下面我们看下我们要实现SysLog的增删改查需要怎样定义一个mapper接口。
@Mapper
public interface SysLogMapper extends BaseMapper<SysLogDO> {
}
就是这么简单,如果只是基本的增删改查就是这么简单,一个方法都不需要定义,只需要让mapper接口继承自BaseMapper这个范性基类,里面的范型定义为需要操作的DO类。我们看下BaseMapper源码就知道为什么这么简单就可以实现常用的增删改查功能了。
public interface BaseMapper<T> {
Integer insert(T var1);
Integer deleteById(Serializable var1);
Integer deleteByMap(@Param("cm") Map<String, Object> var1);
Integer delete(@Param("ew") Wrapper<T> var1);
Integer deleteBatchIds(@Param("coll") Collection<? extends Serializable> var1);
Integer updateById(@Param("et") T var1);
Integer update(@Param("et") T var1, @Param("ew") Wrapper<T> var2);
T selectById(Serializable var1);
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> var1);
List<T> selectByMap(@Param("cm") Map<String, Object> var1);
T selectOne(@Param("ew") Wrapper<T> var1);
Integer selectCount(@Param("ew") Wrapper<T> var1);
List<T> selectList(@Param("ew") Wrapper<T> var1);
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> var1);
List<Object> selectObjs(@Param("ew") Wrapper<T> var1);
IPage<T> selectPage(IPage<T> var1, @Param("ew") Wrapper<T> var2);
IPage<Map<String, Object>> selectMapsPage(IPage<T> var1, @Param("ew") Wrapper<T> var2);
}
是不是从这里就能发现,我们常用的一些数据库操作都包涵在里面了,包括分页也都在里面,是不是一下子效率提升了很多。
当然dao的mapper类定义好了以后就是定义service层的service类了。
@Service("sysLogService")
public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLogDO> implements SysLogService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void saveSysLog(SysLogDO sysLogDO) {
this.saveSysLogFile(sysLogDO);
this.saveSysLogDB(sysLogDO);
}
private void saveSysLogFile(SysLogDO sysLogDO){
logger.info(sysLogDO.toString());
}
private boolean saveSysLogDB(SysLogDO sysLogDO) {
return this.save(sysLogDO);
}
}
从这里可以发现只需要让service实现类继承plus的ServiceImpl范型类就可以了,在范型类中需要定义上这个service对应的mapper类和实体DO类,然后在service实现类中就可以通过this调用上面我们看到的常用的数据库表操作方法了。
能解决大量重复代码的开发工作的核心就是这个ServiceImpl范型类和上面提到的BaseMapper接口,这里建议大家都可以看看ServiceImpl的源码,这里的设计方法其实可以用到很多我们常用的代码中。
自动代码生成
当然大家也会发现上面写的DO、Mapper、Service类也是一个重复的过程,我们可以通过plus为我们提供的自动代码生成方法来自动生成这些DO、Mapper、Service代码,这里我们可以很方便的实现这些重复的代码,具体可以参考test目录下的GeneratorServiceEntity类,在类里面需要配置数据库的相关信息,还有需要自动生成的类信息。都配置好厚直接执行这个Test方法就可以自动生成这些类了,减少了很多重复的工作。
总结
因为目前的mybatis和mybatis plus在orm这块已经做的非常完善了,真的没必要再自研一套,直接拿来用,对于mybatis的源码建议大家有空也可以读读,不难,但是里面涉及到很多好的框架设计方法,值得去学习。
本篇对应的代码tag是v0.2,大家以点击下载https://github.com/feiweiwei/MkFramework4java/releases/tag/v0.2,当然也可以通过git clone –b v0.2 https://github.com/feiweiwei/MkFramework4java.git
下载。