顶呱呱啊!更便捷的Mybatis增强插件——EasyMybatis
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了顶呱呱啊!更便捷的Mybatis增强插件——EasyMybatis相关的知识,希望对你有一定的参考价值。
参考技术A项目主页:https://mybatis.zuoyu.top
API文档地址:https://mybatis.zuoyu.top/doc/index.html
GitHub地址:https://github.com/zuoyuip/easy-mybatis
Gitee地址:https://gitee.com/zuoyuip/easy-mybatis
Maven依赖引入
Gradle依赖引入
对于开发人员来说:
如今已很难看到单体架构的项目(感兴趣的可以查看我对架构演变的描述《浅谈微服务》),目前的项目大都是通过 RESTful 、 MQ 、 Socket 的方式(协议)进行数据传输。
这让我开始质疑传统 JavaWeb 项目中的数据库操作模式——即 Model(DTO) 存在的意义。理论上,数据库设计是不可能完全遵循视图模型的,这就导致“正确”的做法是在项目中引入 VO ,由多个 DTO 来组装。
那么,为什么不能用灵活的Map来替代呢?
对一个 Map 的方法进行拓展,增加其对 Json 的解析能力,那么是不是就可以摆脱 POJO 的各种麻烦组装。
我在思考如何设计这个框架的时候,被需要考虑的方方面面给阻挡住了。
因为一个数据库框架需要考虑的东西实在太多了,比如:
···
思来想去,发现自己方向跑偏了,我只是希望 统一数据库操作的接口 + 摆脱Model ,没必要重新平地起墙,完全可以在一个现有的框架基础上进行封装。
那么,对这个现有框架的选择就尤为重要了。
目前Java中主流的数据库操作框架:
选择现有框架有一个原则——“ 统一数据库操作的接口 + 摆脱Model ”是对该框架的加强,而非变异;不能因为“ 统一数据库操作的接口 + 摆脱Model ”而无法使用原框架的部分功能。
“ 摆脱Model ”这个特点,首先就要排除重度 ORM 框架,也就是支持 JPA 操作的数据库—— Spring Data JPA 、 Hibernate ;原因很简单,这两个框架的强大之处恰恰就在它完全面向 Model 操作。
剩下的就只有两个框架了, Spring JDBC 和 Mybatis 。其中, Spring JDBC 留给了开发人员大量的可操作空间,更加自由,但恰恰是这种自由使得它更加繁琐。而 Mybatis 是一个轻量 ORM 框架,准确来说 Mybatis 不能称为 ORM 框架,因为它并不是面向 Model 操作数据库,仅仅是将数据库字段与 Model 字段互相赋值,并没有做到 ORM 定义的关系映射。
由以上各框架的特点,结合国内Java语言中数据库操作框架的热度,毫无疑问的选择了 Mybatis 。
考虑到 SpringBoot 对 Mybatis 优秀的支持级别,我决定基于 mybatis-spring-boot-starter 开发这款框架,准备来说应该称其为“ 插件 ”。
摒弃传统 mybatis 的 model 、 xml 、 dao 数据库操作模式,避繁就简,快速开发。
采用预编译 SQL ,拒绝运行期间反射生成 SQL ,性能更高效。
只是对Mybatis-Spring的增强插件,对已有工程不做任何修改,仍可使用原生框架的功能,仅仅是简化了开发阶段对数据库的操作。
对数据库的所有操作共用一个接口,降低使用门槛,轻松操作数据库。
使用 JsonObject 为数据对象,提供一系列操作方法,方便从持久化对象组装为视图对象。
整个框架只提供了一个接口、一个注解、两个对象,仅仅一行配置便可完成对数据库进行常用操作。
mybatis-spring-boot的Maven依赖
mybatis-spring-boot的Gradle依赖
Maven依赖引入
Gradle依赖引入
关于 springBoot 的配置,这里不多赘述,更多移步springBoot官网。
这里的 table-names 配置,表示需要 easy-mybatis 框架支持的数据表名,多个表名使用逗号隔开。
即可使用 easy-mybatis 框架操作 teacher 和 student 两个数据表, 如果需要支持其他数据表,需要在此配置 。
使用 MapperRepository 接口对数据库进行操作,需要使用 @Magic("表名称") 标记该接口的数据表归属。
在本例中, @Magic("teacher") 表示该 MapperRepository 为 "teacher" 数据表的操作接口,可以通过 teacherRepository 调用一系列方法完成对 "teacher" 数据表的操作。
该框架(插件)的全部相关配置
此配置表示需要载入的数据表,多个表名由逗号隔开,只有在此处配置了的数据表,才能在项目中使用 @Magic("表名称") 标记 MapperRepository 接口的数据表归属。
此配置表示在 JsonObject 中的日期存在格式, JsonObject 中对日期的存储类型为 String 类型,可通过此配置自定义存储格式,默认格式为 yyyy-MM-dd HH:mm:ss 。
注意
当 data-format 的格式修改,请务必将 oracle-date-format 的格式与之匹配。
此配置表示当数据库为Oracle时,每个数据表的主键生成器名称,以键值对的格式进行参数配置。若此处没有对数据表的主键生成器进行配置,那么在做该数据表的数据插入行为时需自定义填充主键数值。
此配置表示当数据库为Oracle时,传入日期格式字段时的参数格式,即 TO_DATE(field, \'$dateFormat\') 。
注意
当 oracle-date-format 的格式修改,请务必将 data-format 的格式与之匹配。
此类是根据 JsonObject 类型的参数 example 中键值对,对符合要求的数据进行操作,例如:
这里的 selectListByExample 方法就是根据参数 example 中键值对查找符合要求的数据。
同理, countByExample 方法就是获取符合参数 example 中键值对的数据总量...
此类是根据 String 类型的参数 suffixSql 对方法追加参数中SQL语句的内容,例如:
这里的 selectListBy 方法就是查找出符合参数中SQL语句要求的内容, updateBy 方法就是将符合参数中SQL语句要求的数据修改为 example 定义的数值。
同理, countBy 方法就是获取符合参数中SQL语句要求的数据总量...
当数据库为mysql时,如果数据表主键自增,则无需定义主键数值;若主键为非自增,则需定义主键数值。
当数据库为Oracle时,如果在配置中定义了数据表的主键生成器,则无需定义主键数值;反之,则需定义主键数值。
键的类型为 java.lang.String 类型,值的类型可以为任何类型。
JsonObject 提供了丰富的方法,以方便操作键值对和类型转换,更多关于 JsonObject 的使用说明请移步 JsonObject说明 、 JsonObject的API文档 。
每个单位可以为任何类型,且各单位类型允许与其他单位类型不同。
JsonArray 提供了丰富的方法,以方便操作键值对和类型转换,更多关于 JsonArray 的使用说明请移步 JsonArray说明 、 JsonArray的API文档 。
具体方法可参考 API文档 。
top.zuoyu.mybatis.json.JsonObject 为键值对容器,以键值的形式装载,数据结构为:
键的类型为 java.lang.String 类型,值的类型可以为任何类型。
内部默认已包含常规的类型转换,默认支持以下类型:
如果默认的转换器无法满足需求,或者需要转换为其他类型,可通过自定义转换器 ConvertClass 进行实现,接口方法:
本示例也可使用 StringConvert 转换器, StringConvert 为 ConvertClass 的子接口。
也可以通过 toClass 方法,将 JsonObject 对象转换为指定类型的实体类。
更多方法请移步 JsonObject的API文档 。
top.zuoyu.mybatis.json.JsonArray 为数组容器,以多个单位的形式装载,数据结构为:
每个单位可以为任何类型,且各单位类型允许与其他单位类型不同。
具体使用方法与 JsonObject 在类型转换操作一致,参考 JsonObject的类型转换操作
SpringBoot2 配置多数据源,整合MybatisPlus增强插件
本文源码:GitHub·点这里 || GitEE·点这里
一、项目案例简介
1、多数据简介
实际的项目中,经常会用到不同的数据库以满足项目的实际需求。随着业务的并发量的不断增加,一个项目使用多个数据库:主从复制、读写分离、分布式数据库等方式,越来越常见。
2、MybatisPlus简介
MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。
插件特点
无代码侵入:只做增强不做改变,引入它不会对现有工程产生影响。
强大的 CRUD 操作:通过少量配置即可实现单表大部分 CRUD 操作满足各类使用需求。
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件。
支持主键自动生成:可自由配置,解决主键问题。
内置代码生成器:采用代码或者 Maven 插件可快速生成各层代码。
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作。
内置性能分析插件:可输出 Sql 语句以及其执行时间。
二、多数据源案例
1、项目结构
注意:mapper层和mapper.xml层分别放在不同目录下,以便mybatis扫描加载。
2、多数据源配置
spring:
# 数据源配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
admin-data:
driverClassName: com.mysql.jdbc.Driver
dbUrl: jdbc:mysql://127.0.0.1:3306/cloud-admin-data?useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: root
password: 123
initialSize: 20
maxActive: 100
minIdle: 20
maxWait: 60000
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 30
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 30000
maxEvictableIdleTimeMillis: 60000
validationQuery: SELECT 1 FROM DUAL
testOnBorrow: false
testOnReturn: false
testWhileIdle: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
filters: stat,wall
user-data:
driverClassName: com.mysql.jdbc.Driver
dbUrl: jdbc:mysql://127.0.0.1:3306/cloud-user-data?useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: root
password: 123
initialSize: 20
maxActive: 100
minIdle: 20
maxWait: 60000
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 30
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 30000
maxEvictableIdleTimeMillis: 60000
validationQuery: SELECT 1 FROM DUAL
testOnBorrow: false
testOnReturn: false
testWhileIdle: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
filters: stat,wall
这里参数的形式是多样的,只需要配置参数扫描即可。
3、参数扫描类
@Component
@ConfigurationProperties(prefix = "spring.datasource.admin-data")
public class DruidOneParam {
private String dbUrl;
private String username;
private String password;
private String driverClassName;
private int initialSize;
private int maxActive;
private int minIdle;
private int maxWait;
private boolean poolPreparedStatements;
private int maxPoolPreparedStatementPerConnectionSize;
private int timeBetweenEvictionRunsMillis;
private int minEvictableIdleTimeMillis;
private int maxEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
private String filters;
private String connectionProperties;
// 省略 GET 和 SET
}
4、配置Druid连接池
@Configuration
@MapperScan(basePackages = {"com.data.source.mapper.one"},sqlSessionTemplateRef = "sqlSessionTemplateOne")
public class DruidOneConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(DruidOneConfig.class) ;
@Resource
private DruidOneParam druidOneParam ;
@Bean("dataSourceOne")
public DataSource dataSourceOne () {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(druidOneParam.getDbUrl());
datasource.setUsername(druidOneParam.getUsername());
datasource.setPassword(druidOneParam.getPassword());
datasource.setDriverClassName(druidOneParam.getDriverClassName());
datasource.setInitialSize(druidOneParam.getInitialSize());
datasource.setMinIdle(druidOneParam.getMinIdle());
datasource.setMaxActive(druidOneParam.getMaxActive());
datasource.setMaxWait(druidOneParam.getMaxWait());
datasource.setTimeBetweenEvictionRunsMillis(druidOneParam.getTimeBetweenEvictionRunsMillis());
datasource.setMinEvictableIdleTimeMillis(druidOneParam.getMinEvictableIdleTimeMillis());
datasource.setMaxEvictableIdleTimeMillis(druidOneParam.getMaxEvictableIdleTimeMillis());
datasource.setValidationQuery(druidOneParam.getValidationQuery());
datasource.setTestWhileIdle(druidOneParam.isTestWhileIdle());
datasource.setTestOnBorrow(druidOneParam.isTestOnBorrow());
datasource.setTestOnReturn(druidOneParam.isTestOnReturn());
datasource.setPoolPreparedStatements(druidOneParam.isPoolPreparedStatements());
datasource.setMaxPoolPreparedStatementPerConnectionSize(druidOneParam.getMaxPoolPreparedStatementPerConnectionSize());
try {
datasource.setFilters(druidOneParam.getFilters());
} catch (Exception e) {
LOGGER.error("druid configuration initialization filter", e);
}
datasource.setConnectionProperties(druidOneParam.getConnectionProperties());
return datasource;
}
@Bean
public SqlSessionFactory sqlSessionFactoryOne() throws Exception{
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
factory.setDataSource(dataSourceOne());
factory.setMapperLocations(resolver.getResources("classpath*:/dataOneMapper/*.xml"));
return factory.getObject();
}
@Bean(name="transactionManagerOne")
public DataSourceTransactionManager transactionManagerOne(){
return new DataSourceTransactionManager(dataSourceOne());
}
@Bean(name = "sqlSessionTemplateOne")
public SqlSessionTemplate sqlSessionTemplateOne() throws Exception {
return new SqlSessionTemplate(sqlSessionFactoryOne());
}
}
注意事项
- MapperScan 在指定数据源上配置;
- SqlSessionFactory 配置扫描的Mapper.xml地址 ;
- DataSourceTransactionManager 配置该数据源的事务;
- 两个数据源的配置手法相同,不赘述 ;
5、操作案例
- 数据源一:简单查询
@Service public class AdminUserServiceImpl implements AdminUserService { @Resource private AdminUserMapper adminUserMapper ; @Override public AdminUser selectByPrimaryKey (Integer id) { return adminUserMapper.selectByPrimaryKey(id) ; } }
- 数据源二:事务操作
@Service public class UserBaseServiceImpl implements UserBaseService { @Resource private UserBaseMapper userBaseMapper ; @Override public UserBase selectByPrimaryKey(Integer id) { return userBaseMapper.selectByPrimaryKey(id); } // 使用指定数据源的事务 @Transactional(value = "transactionManagerTwo") @Override public void insert(UserBase record) { // 这里数据写入失败 userBaseMapper.insert(record) ; // int i = 1/0 ; } }
注意:这里的需要指定该数据源配置的事务管理器。
三、MybatisPlus案例
1、核心依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.7.1</version>
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.0.7.1</version>
</dependency>
2、配置文件
mybatis-plus:
mapper-locations: classpath*:/mapper/*.xml
typeAliasesPackage: com.digital.market.*.entity
global-config:
db-config:
id-type: AUTO
field-strategy: NOT_NULL
logic-delete-value: -1
logic-not-delete-value: 0
banner: false
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
cache-enabled: false
call-setters-on-nulls: true
jdbc-type-for-null: ‘null‘
3、分层配置
mapper层
UserBaseMapper extends BaseMapper<UserBase>
实现层
UserBaseServiceImpl extends ServiceImpl<UserBaseMapper,UserBase> implements UserBaseService
接口层
UserBaseService extends IService<UserBase>
4、mapper.xml文件
<mapper namespace="com.plus.batis.mapper.UserBaseMapper" >
<resultMap id="BaseResultMap" type="com.plus.batis.entity.UserBase" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="user_name" property="userName" jdbcType="VARCHAR" />
<result column="pass_word" property="passWord" jdbcType="VARCHAR" />
<result column="phone" property="phone" jdbcType="VARCHAR" />
<result column="email" property="email" jdbcType="VARCHAR" />
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP" />
<result column="state" property="state" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List" >
id, user_name, pass_word, phone, email, create_time, update_time, state
</sql>
<select id="selectByParam" parameterType="com.plus.batis.entity.QueryParam" resultMap="BaseResultMap">
select * from hc_user_base
</select>
</mapper>
注意事项
BaseMapper中的方法都已默认实现;这里也可以自定义实现一些自己的方法。
5、演示接口
@RestController
@RequestMapping("/user")
public class UserBaseController {
private static final Logger LOGGER = LoggerFactory.getLogger(UserBaseController.class) ;
@Resource
private UserBaseService userBaseService ;
@RequestMapping("/info")
public UserBase getUserBase (){
return userBaseService.getById(1) ;
}
@RequestMapping("/queryInfo")
public String queryInfo (){
UserBase userBase1 = userBaseService.getOne(new QueryWrapper<UserBase>().orderByDesc("create_time")) ;
LOGGER.info("倒叙取值:{}",userBase1.getUserName());
Integer count = userBaseService.count() ;
LOGGER.info("查询总数:{}",count);
UserBase userBase2 = new UserBase() ;
userBase2.setId(1);
userBase2.setUserName("spring");
boolean resFlag = userBaseService.saveOrUpdate(userBase2) ;
LOGGER.info("保存更新:{}",resFlag);
Map<String, Object> listByMap = new HashMap<>() ;
listByMap.put("state","0") ;
Collection<UserBase> listMap = userBaseService.listByMap(listByMap) ;
LOGGER.info("ListByMap查询:{}",listMap);
boolean removeFlag = userBaseService.removeById(3) ;
LOGGER.info("删除数据:{}",removeFlag);
return "success" ;
}
@RequestMapping("/queryPage")
public IPage<UserBase> queryPage (){
QueryParam param = new QueryParam() ;
param.setPage(1);
param.setPageSize(10);
param.setUserName("cicada");
param.setState(0);
return userBaseService.queryPage(param) ;
}
@RequestMapping("/pageHelper")
public PageInfo<UserBase> pageHelper (){
return userBaseService.pageHelper(new QueryParam()) ;
}
}
这里pageHelper方法是使用PageHelper插件自定义的方法。
四、源代码地址
GitHub·地址
https://github.com/cicadasmile/middle-ware-parent
GitEE·地址
https://gitee.com/cicadasmile/middle-ware-parent
以上是关于顶呱呱啊!更便捷的Mybatis增强插件——EasyMybatis的主要内容,如果未能解决你的问题,请参考以下文章
更快更强,来试试 Mybatis 的增强版——EasyMybatis