使用说明
项目引入
maven
<dependency>
<groupId>com.github.wpyuan</groupId>
<artifactId>mybatis-crud</artifactId>
<version>${latest.version}</version>
</dependency>
gradle
implementation 'com.github.wpyuan:mybatis-crud:版本号'
使用设置
实体类(以下称entity)创建
可以先用Mybatis-Generator生成表对应entity类,在做下面的修改,需保证字段不缺类型一致
主键字段添加@com.github.mybatis.crud.annotation.Id注解、类上添加@com.github.mybatis.crud.annotation.Table注解,举例如下:
mysql数据库的employee表,对应的entity
@Table(name = "employee")
public class Employee {
@Id
private Long id;
...
}
oracle数据库的employee表,对应的entity
@Table("employee")
public class EmployeeOra {
/**
* @mbg.generated Fri Dec 25 11:16:58 CST 2020
*/
@Id(sequence = "EMPLOYEE_ID_S")
private Long id;
...
}
需要注意的是:需在@com.github.mybatis.crud.annotation.Table填入name值指定表名,oracle数据库需在@com.github.mybatis.crud.annotation.Id填入sequence值指定序列
mapper创建
组件已封装多个通用mapper,可选择继承使用,比如com.github.mybatis.crud.mapper.DefaultMapper包含默认CRUD实现,如下例子:
public interface EmployeeMapper extends DefaultMapper<Employee>, BatchInsertMapper<Employee> {
}
其中com.github.mybatis.crud.mapper.BatchInsertMapper是批量插入实现,可选择继承,然后配置此mapper扫描路径(这个不做赘述)
开始使用
创建表对应entity和mapper后,以插入、删除、修改和查询介绍
插入
insert
全字段插入
int insert(E entity);
insertSelective
有值的字段插入
int insertSelective(E entity);
删除
根据主键删除记录
int deleteByPrimaryKey(E entity);
根据条件删除记录
int delete(Condition<E> condition);
其中Condition<E> condition包含有
| 方法 | 说明 |
|---|---|
| andBetween, between, orBetween | between … and … 的实现 |
| diy | 自定义语句实现 |
| andEq, andEq, eq, eq, orEq, orEq | 等于=的实现 |
| andGt, andGt, gt, gt, orGt, orGt | 大于>的实现 |
| andGtEq, andGtEq, gtEq, gtEq, orGtEq, orGtEq | 大于等于>=的实现 |
| andIn, andIn, in, in, orIn, orIn | in的实现 |
| andNotNull, notNull, orNotNull | is not null的实现 |
| andNull, isNull, orNull | is null的实现 |
| andLt, andLt, lt, lt, orLt, orLt | 小于<的实现 |
| andLtEq, andLtEq, ltEq, ltEq, orLtEq, orLtEq | 小于等于<=的实现 |
| andLeftLike, andLeftLike, andLike, andLike, andLikeDiy, andRightLike, andRightLike, like, like, likeDiy, lLike, lLike, orLeftLike, orLeftLike, orLike, orLike, orLikeDiy, orLLike, orLLike, orRightLike, orRightLike, orRLike, orRLike, rLike, rLike | like的实现 |
| andNotBetween, notBetween, orNotBetween | not between … and … 的实现 |
| andNotEq, andNotEq, nEq, nEq, orNotEq, orNotEq | 不等于!=的实现 |
| andNotIn, andNotIn, notIn, notIn, orNotIn, orNotIn | not in 的实现 |
| andLeftNotLike, andLeftNotLike, andNotLike, andNotLike, andNotLikeDiy, andRightNotLike, andRightNotLike, lNotLike, lNotLike, notLike, notLike, notLikeDiy, orLeftNotLike, orLeftNotLike, orLNotLike, orLNotLike, orNotLike, orNotLike, orNotLikeDiy, orRightNotLike, orRightNotLike, orRNotLike, orRNotLike, rNotLike, rNotLike | not like 的实现 |
删除employeeName like '%1/%/2'、删除id=1的记录举例,更多用法会在后续Condition、LikeEscapeHandler小节讲述:
Employee employee = new Employee();
int updates = mapper.delete(new Condition<Employee>(employee).lLike("employeeName", "1/%/2"));
employee.setId(1L);
updates = mapper.delete(new Condition<Employee>(employee).eq("id"));
修改
主要方法如下:
/**
* 根据主键更新覆盖
*
* @param entity 实体类
* @return 影响条数
*/
int updateByPrimaryKey(E entity);
/**
* 根据主键更新覆盖有值列
*
* @param entity 实体类
* @return 影响条数
*/
int updateByPrimaryKeySelective(E entity);
/**
* 根据主键更新覆盖指定列
*
* @param entity 实体类
* @param fields 指定列
* @return 影响条数
*/
int updateField(@Param("entity") E entity, @Param("fields") String... fields);
/**
* 根据条件更新覆盖指定列
*
* @param update 条件
* @return 影响条数
*/
int update(Update<E> update);
这里说明int update(Update<E> update);方法的使用:
以指定id条件为例子,这里条件可任意,具体使用更多用法在后续Condition小节介绍
- 更新指定列:
更新
id=1记录的employeeName和bornDate为abs、当前日期,举例:Employee employee = new Employee(); employee.setId(1L); employee.setEmployeeName("abs"); employee.setBornDate(new Date()); int updates = mapper.update(Update.<Employee>builder() .fields(Arrays.asList("employeeName", "bornDate")) .condition(Condition.<Employee>builder(employee).build().eq("id")) - 更新有值列:
更新
id=1记录的employeeName和bornDate为abs、当前日期,即无须指定fields属性,举例:Employee employee = new Employee(); employee.setId(1L); employee.setEmployeeName("abs"); employee.setBornDate(new Date()); int updates = mapper.update(Update.<Employee>builder() .isUpdateSelective(true) .condition(Condition.<Employee>builder(employee).build().eq("id")) .build()); - 更新全字段:
更新
id=1记录的所有字段,直接覆盖,举例:Employee employee = new Employee(); employee.setId(1L); employee.setEmployeeName("abs"); employee.setBornDate(new Date()); updates = mapper.update(Update.<Employee>builder() .isUpdateSelective(false) .condition(Condition.<Employee>builder(employee).build().eq("id")) .build());
查询
主要方法如下:
/**
* 根据主键查询
*
* @param entity 实体类
* @return 查询结果
*/
E selectByPrimaryKey(E entity);
/**
* 根据条件查询列表
*
* @param condition 条件
* @return 查询结果
*/
List<E> list(Condition<E> condition);
/**
* 根据条件查询一个结果
*
* @param condition 条件
* @return 查询结果
*/
E detail(Condition<E> condition);
/**
* 根据条件复杂查询出结果
*
* @param leftJoin 查询条件
* @return 查询结果
*/
List<Map<String, Object>> select(LeftJoin<E> leftJoin);
/**
* 根据条件复杂查询出结果并转换格式
*
* @param resultTypeClass 返回结果类型class
* @param leftJoin 查询条件
* @return 查询结果
*/
<R> List<R> select(Class<R> resultTypeClass, LeftJoin<E> leftJoin);
这里讲下两个select方法的区别:
两个都提供了左外连接的实现,目前出于可维护性考虑,不考虑继续推出多外联表方法实现,建议写入xml文件,增加可读性
- 返回值类型为
List<Map<String, Object>>这个是直接查询数据库,返回的类型都是数据库类型,未经转换为javaType,注意使用,需自行转换 - 返回值类型为
List<R>这个根据传入的Class<R> resultTypeClass转换为指定的结构返回
这里讲下两个select方法的使用:
- 返回值类型为
List<Map<String, Object>>
Employee employee = new Employee();
UserInfo userInfo = UserInfo.builder().build();
List<Map<String, Object>> data = mapper.select(new LeftJoin<Employee>(Employee.class)
.field(As.of(UserInfo.class, "userId", "userId"))
.field(UserInfo.class, "userName")
on(On.of(Employee.class, "id"), On.of(UserInfo.class, "employeeId"))
);
其中涉及LeftJoin左外连接、As别名设置,即查询列返回的列别名,具体含义在后续LeftJoin、As、On小节说明
- 返回值类型为
List<R>
Employee employee = new Employee();
employee.setId(1L);
List<EmployeeVO> employeeVOS = mapper.select(EmployeeVO.class,
new LeftJoin<Employee>(Employee.class)
.field(As.of(UserInfo.class, "userId", "userId"))
.field(UserInfo.class, "userName")
.on(On.of(Employee.class, "id"), On.of(UserInfo.class, "employeeId"), OnCondition.builder(employee).build().eq("id"))
.where(WhereCondition.builder(employee).build().eq("id"))
);
其中where指定where条件,涉及OnCondition、WhereCondition均在后续小节说明
其他类说明
LikeEscapeHandler
like 转义处理器,处理like语句条件值中的%,_通配符为文本处理,若不加处理器则作为原通配符处理
可选择性启用,启用方式:
@Configuration
public class PluginInterceptor {
@Bean
public DefaultMybatisInterceptor defaultMybatisPlugin() {
DefaultMybatisInterceptor defaultMybatisInterceptor = new DefaultMybatisInterceptor();
defaultMybatisInterceptor.setPluginHandlers(new LikeEscapeHandler());
return defaultMybatisInterceptor;
}
}
@Configuration
public class MyBatisConfig {
@Autowired
private DefaultMybatisInterceptor defaultMybatisInterceptor;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setPlugins(defaultMybatisInterceptor);
...
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
...
return sqlSessionFactory;
}
}
Condition
单表条件构造器,构造方法和功能方法如下
构造方法
| 构造器 | 说明 |
|---|---|
| Condition(Class |
使用 new Condition(Employee.class)创建条件构造器,其中Employee更换为条件对应的表的entity |
| Condition(Condition |
复制Condition方法,不是同一引用 |
| Condition(E entity) | 由entity初始化条件构造器 |
也可以用builder创建条件构造器,如:
/**
* 有条件值的entity,构造Condition如下
*/
Employee employee = new Employee();
employee.setId(1L);
Condition.<Employee>builder(employee).build()
/**
* 无条件值的entity,构造Condition如下
*/
Condition.<Employee>builder(Employee.class).build()
功能方法
连接符方法
支持group、and、or连接符,其中:
group分组,即(...),举例:where (1=1)and且,即and (...),举例:where 1=1 and (1=0)or或,即or (...),举例:where 1=1 or (1=0)
语句方法
| 方法 | 说明 |
|---|---|
| andBetween, between, orBetween | between … and … 的实现 |
| diy | 自定义语句实现 |
| andEq, andEq, eq, eq, orEq, orEq | 等于=的实现 |
| andGt, andGt, gt, gt, orGt, orGt | 大于>的实现 |
| andGtEq, andGtEq, gtEq, gtEq, orGtEq, orGtEq | 大于等于>=的实现 |
| andIn, andIn, in, in, orIn, orIn | in的实现 |
| andNotNull, notNull, orNotNull | is not null的实现 |
| andNull, isNull, orNull | is null的实现 |
| andLt, andLt, lt, lt, orLt, orLt | 小于<的实现 |
| andLtEq, andLtEq, ltEq, ltEq, orLtEq, orLtEq | 小于等于<=的实现 |
| andLeftLike, andLeftLike, andLike, andLike, andLikeDiy, andRightLike, andRightLike, like, like, likeDiy, lLike, lLike, orLeftLike, orLeftLike, orLike, orLike, orLikeDiy, orLLike, orLLike, orRightLike, orRightLike, orRLike, orRLike, rLike, rLike | like的实现 |
| andNotBetween, notBetween, orNotBetween | not between … and … 的实现 |
| andNotEq, andNotEq, nEq, nEq, orNotEq, orNotEq | 不等于!=的实现 |
| andNotIn, andNotIn, notIn, notIn, orNotIn, orNotIn | not in 的实现 |
| andLeftNotLike, andLeftNotLike, andNotLike, andNotLike, andNotLikeDiy, andRightNotLike, andRightNotLike, lNotLike, lNotLike, notLike, notLike, notLikeDiy, orLeftNotLike, orLeftNotLike, orLNotLike, orLNotLike, orNotLike, orNotLike, orNotLikeDiy, orRightNotLike, orRightNotLike, orRNotLike, orRNotLike, rNotLike, rNotLike | not like 的实现 |
其中andBetween与between类似没有前缀and等效,是为了方便书写和简洁,这里默认了and前缀连接符
具体使用
根据如上构造方法、连接符和语句方法,可写出高度灵活复杂的条件,举例如下:
Condition<Employee> condition = new Condition<Employee>(Employee.class)
.and(
c -> c.group(
c1 -> c1.diy(null, null, "1", "=", 1, null)
.and(
c2 -> c2.diy(null, null, "1", "=", 1, null)
.diy(null, "or", "1", "=", 1, null)
)
)
.or(
c3 -> c3.group(
c4 -> c4.diy(null, null, "1", "=", 1, null)
.diy(null, "or", "1", "=", 1, null)
).diy(null, "and", "1", "=", 0, null)
)
);
即实现语句:
and( ( 1 = 1 and ( 1 = 1 or 1 = 1 ) ) or ( ( 1 = 1 or 1 = 1 ) and 1 = 0 ) )
其中的diy为自定义语句,可替换上述语句方法小点表格中任意方法
为了可读性和易维护性,不建议复杂的语句用组件写,建议写在xml文件,这里只是功能展示
更多Condition方法尽请期待…
OnCondition
外连接On条件构造器,与Condition单表条件构造器不同,专注与构造外连接时,on后面的连接条件,即是涉及两表的条件构造器
构造方法
含有Condition单表条件构造器所有构造方法外,还有
| 构造器 | 说明 |
|---|---|
| OnCondition(Class<?> eClass, Class<?> eClass2) | 使用new OnCondition(Employee.class, UserInfo.class)即初始化出Employee和UserInfo对应两表的条件构造器 |
| OnCondition(Object entity, Object entity2) | 使用new OnCondition(employee, userInfo)即初始化出employee和userInfo对应两表的条件构造器 |
也可以用builder创建条件构造器,与Condition单表条件构造器用法一样不做赘述
功能方法
连接符方法
与Condition单表条件构造器一样不做赘述
语句方法
含有Condition单表条件构造器所有语句方法外,还有
| 方法 | 说明 |
|---|---|
| andEq, eq, orEq | 两表等于=的实现 |
| andNotEq, nEq, orNotEq | 两表不等于!=的实现 |
| diy | 两表自定义语句实现 |
| andGt, gt, orGt | 两表大于>的实现 |
| andGtEq, gtEq, orGtEq | 两表大于等于>=的实现 |
| andLt, lt, orLt | 两表小于<的实现 |
| andLtEq, ltEq, orLtEq | 两表小于等于<=的实现 |
具体使用
根据如上方法,可写出两表联表查询,举例如下:
Employee employee = new Employee();
employee.setId(1L);
List<Map<String, Object>> data = mapper.select(new LeftJoin<Employee>(Employee.class)
.field(UserInfoOra.class,"userName", "userId")
.on(On.of(Employee.class, "id"), On.of(UserInfo.class, "employeeId"),
OnCondition.<UserInfo>builder(UserInfo.class).build().eq("isEnable", false),
).where(WhereCondition.builder(employee).build().eq("id"))
);
即实现语句:
select e.*, u.user_id, u.user_name
from employee e
left join user_info u on u.employee_id = e.id and u.is_enable = 0
where e.id = 1
其中LeftJoin、On下文单独小节说明
更多OnCondition方法尽请期待…
WhereCondition
外连接Where条件构造器,也是涉及两表的条件构造器,与OnCondition不同的是,WhereCondition专注于where的条件,其余全部和OnCondition雷同,这里不作赘述
As
别名标注,用法如下:
new LeftJoin<Employee>(Employee.class)
.field(As.of(UserInfo.class, "userId", "createdUserId"))
即,查询UserInfo对应表的user_id字段返回createdUserId别名,As需配合LeftJoin等外连接类使用
On
连接条件标注,用法如下:
new LeftJoin<Employee>(Employee.class)
.on(On.of(Employee.class, "id"), On.of(UserInfo.class, "employeeId"))
即,查询Employee对应表左外连UserInfo对应表记录,连接条件是Employee的id字段等于UserInfo的employeeId字段
LeftJoin
左外连接构造器
构造方法
| 构造器 | 说明 |
|---|---|
| LeftJoin(Class |
即使用new LeftJoin(Employee.class)创建左外连接构造器 |
| LeftJoin(E entity) | 即使用new LeftJoin(employee)创建左外连接构造器 |
目前两种方法推荐使用第一种创建
功能方法
| 限定符和类型 | 方法 | 说明 |
|---|---|---|
| LeftJoin |
field(As… fieldAs) | 查询两表的字段别名设置 |
| LeftJoin |
field(Class<?> eClass, String… field) | 指定查询字段,默认全查主表字段,这里供外连接表查询字段设置 |
| LeftJoin |
on(On onLeft, On onRight, OnCondition… onCondition) | 外连接on条件,onLeft与onRight指定连接关系,OnCondition其他连接条件,on上面的条件 不支持( )符号 |
| LeftJoin |
where(WhereCondition… whereConditions) | where条件, 不支持( )符号 |
如下举例:
new LeftJoin<Employee>(Employee.class)
.field(UserInfoOra.class,"userName", "userId")
.on(On.of(Employee.class, "id"), On.of(UserInfo.class, "employeeId"),
OnCondition.<UserInfo>builder(UserInfo.class).build().eq("isEnable", false),
).where(WhereCondition.builder(employee).build().eq("id"))
扩展
本插件经过一系列封装,具有高度的扩展性,利于应对各种场景,即可在sql各种阶段加入自定义逻辑,以下仅供参考,更多场景自行判断使用。
可支持扩展的几种类型
- 删除前置处理
- 插入前置处理
- 查询前置处理
- 更新前置处理
- prepare前置处理
目前仅支持前置处理,后置处理等,敬请期待后续更新。
具体操作
下面分别给上述几种类型举例,如何在指定类型加入自定义逻辑处理器
删除前置处理
自定义删除前置处理器,需实现DeleteMybatisInterceptorHandler接口,例子:
public class DelAllHandler implements DeleteMybatisInterceptorHandler {
@Override
public void before(Invocation invocation) {
//do something
}
}
上述例子是删除全部数据前置处理器,这个用于拦截些错误语句或sql注入导致的全表删除数据的异常操作,可在before前置方法中加入处理逻辑。
注意:com.github.mybatis.crud.handler.DeleteMybatisInterceptorHandler
插入前置处理
自定义删除前置处理器,需实现InsertMybatisInterceptorHandler接口,例子:
public class WhInsertHandler implements InsertMybatisInterceptorHandler {
@Override
public void before(Invocation invocation) {
//do something
}
}
上述例子是插入数据wh字段填入前置处理器,这个用于插入记录前,填充与业务无关的、便于审计运维的who、when等wh字段,可在before前置方法中加入处理逻辑。
注意:com.github.mybatis.crud.handler.InsertMybatisInterceptorHandler
查询前置处理
自定义查询前置处理器,需实现SelectMybatisInterceptorHandler接口,例子:
public class TenantHandler implements SelectMybatisInterceptorHandler {
@Override
public void before(Invocation invocation) {
//do something
}
}
上述例子是租户查询前置处理器,这个用于查询前,追加租户id条件做数据屏蔽使用,可在before前置方法中加入处理逻辑。
注意:com.github.mybatis.crud.handler.SelectMybatisInterceptorHandler
更新前置处理
自定义更新前置处理器,需实现UpdateMybatisInterceptorHandler接口,例子:
public class VersionHandler implements UpdateMybatisInterceptorHandler {
@Override
public void before(Invocation invocation) {
//do something
}
}
上述例子是更新版本号前置处理器,这个用于更新前,校对记录版本号、升版本号处理,可在before前置方法中加入处理逻辑。
注意:com.github.mybatis.crud.handler.UpdateMybatisInterceptorHandler
prepare前置处理
自定义prepare前置处理器,需实现PrepareMybatisInterceptorHandler接口,例子:
public class ConnHandler implements PrepareMybatisInterceptorHandler {
@Override
public void before(Invocation invocation) {
//do something
}
}
上述例子是连接prepare前置处理器,这个用于对连接Connection做处理操作,可在before前置方法中加入处理逻辑。
注意:com.github.mybatis.crud.handler.PrepareMybatisInterceptorHandler