前言
一般来说,业务系统对表的操作很少会使用物理删除,所以在建表的时候,会默认添加一个是否删除的状态来做逻辑删除。而如果已经删除了的数据,也不会在业务系统上体现,所以需要在每个查询条件上增加未删除这个条件,而如果每次查询都需要手动增加该条件,着实不方便。研究了一下通用mapper,该持久化框架是支持逻辑删除的。
mapper逻辑删除
mapper的逻辑删除配置比较简单,只需要在逻辑删除的字段上增加@LogicDelete注解即可。LogicDelete在包tk.mybatis.mapper.annotation下。
配置样例:
@LogicDelete(isDeletedValue=1,notDeletedValue=2)
private Integer isDeleted;
字段 | 类型 | 描述 |
---|---|---|
isDeletedValue | int | 已删除的值,默认1 |
notDeletedValue | int | 未删除的值,默认0 |
查询样例:
sysUserMapper.selectAll();
查询自动构建的sql:
SELECT id,user_name,real_name,avatar,email,mobile_phone,telephone,password,salt,sex,is_locked,create_time,update_time,is_deleted FROM sys_user WHERE is_deleted = 2
枚举类型规范
我们做开发的时候经常会遇到这样子的问题:数据库字段某些值是固定的几个值,常规做法是直接存int类型,然后业务代码上定义常量或者枚举类,这种方式其实确实也挺不错的,但是需要人工去维护常量或者枚举类,有点不方便。下面说一下本框架上的做法。
先看表:
CREATE TABLE `sys_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_name` varchar(32) NOT NULL COMMENT '用户名',
`real_name` varchar(32) DEFAULT NULL COMMENT '姓名',
`avatar` varchar(200) DEFAULT NULL COMMENT '头像',
`email` varchar(64) DEFAULT NULL COMMENT '邮箱',
`mobile_phone` varchar(11) DEFAULT NULL COMMENT '手机号',
`telephone` varchar(20) DEFAULT NULL COMMENT '电话',
`password` varchar(40) DEFAULT NULL COMMENT '密码',
`salt` varchar(10) DEFAULT NULL COMMENT '加盐',
`sex` int(6) unsigned DEFAULT '1' COMMENT '性别(1->男|MALE,2->女|FEMALE,3->未知|UNKNOWN)',
`is_locked` tinyint(1) unsigned DEFAULT '2' COMMENT '是否锁定(1->已锁定|YES,2->未锁定|NO)',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`is_deleted` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除(1->未删除|NO,2->已删除|YES)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='用户';
从上表看出有三个字段的注释和别的字段注释不太一样,sex、is_locked、is_deleted。这个注释是我自己制定的规范。这里详细说明一下。
字典(名称、唯一编码)
name | key | 枚举类 |
---|---|---|
性别 | sys_user_sex | SysUser.SexEnum |
是否锁定 | sys_user_is_locked | SysUser.IsLockedEnum |
是否删除 | sys_user_is_deleted | SysUser.IsDeletedEnum |
注:是否模式可以只定义一个全局的yes_no->YesNoEnum
名称即注释,唯一编码即表名_字段
字典项(名称、唯一编码、值)
以性别为例:
名称 | 唯一编码 | 值 |
---|---|---|
男 | MALE | 1 |
女 | FEMALE | 2 |
未知 | UNKNOWN | 3 |
当注释中有了这些元数据后,代码生成器会进行生成代码的处理,后面讲到代码生成器后会详细说明。
开始编码
目录结构
只罗列需要新增或修改的文件
├── mldong-admin 管理端接口
├── src/main/java
└──com.mldong.modules.sys
└── service 服务层
└── impl
└── SysUserServiceImpl
└── src/main/resources
└── application.yml
├── mldong-common 工具类及通用代码
├── mldong-generator 代码生成器
└── mldong-mapper 持久层
├── src/main/java
├── com.mldong.annotation
└── DictEnum.java
├── com.mldong.base
├── CodedEnum.java
├── CodedEnumTypeHandle.java
└── YesNoEnum.java
└── com.mldong.modules.sys.entity
└── SysUser.java
文件说明
mldong-mapper/src/main/java/com/mldong/annotation/DictEnum.java
定义注解,和之前的ErrEnum一样,为了扫描进内存,方便后续查询和做字典接口的。本次修改的文件应该放在mldong-framework层的,不过现在暂时没多少,所以就暂时放在mldong-mapper层。不放在mldong-common层的原因是后续mldong-common会引入mldong-mapper,模块不能相互依赖。所以。。。。
package com.mldong.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 字典枚举类注解,方便收集做为字典使用的
* @author mldong
*
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DictEnum {
/**
* 名称
* @return
*/
String name();
/**
* 唯一标识
* @return
*/
String key();
}
mldong-mapper/src/main/java/com/mldong/base/CodedEnum.java
字典码接口,并提供value转枚举的方法
package com.mldong.base;
import java.util.Arrays;
import java.util.Optional;
public interface CodedEnum {
int getValue();
String getName();
public static <E extends Enum<?> & CodedEnum> Optional<E> codeOf(Class<E> enumClass, int code) {
return Arrays.stream(enumClass.getEnumConstants()).filter(e -> e.getValue() == code).findAny();
}
}
mldong-mapper/src/main/java/com/mldong/base/CodedEnumTypeHandle.java
package com.mldong.base;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Optional;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
/**
* mybatis枚举处理类
* @author mldong
*
* @param <E>
*/
@MappedTypes({CodedEnum.class})
public class CodedEnumTypeHandler<E extends Enum<?> & CodedEnum> extends BaseTypeHandler<E> {
private Class<E> type;
public CodedEnumTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter,
JdbcType jdbcType) throws SQLException {
if (parameter == null) {
ps.setNull(i, Types.TINYINT);
} else {
ps.setInt(i, parameter.getValue());
}
}
@Override
public E getNullableResult(ResultSet rs, String columnName)
throws SQLException {
int columnValue = rs.getInt(columnName);
return rs.wasNull() ? null : enumOf(columnValue);
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
int columnValue = rs.getInt(columnIndex);
return rs.wasNull() ? null : enumOf(columnValue);
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
int columnValue = cs.getInt(columnIndex);
return cs.wasNull() ? null : enumOf(columnValue);
}
private E enumOf(int code) {
final Optional<E> codedEnumOpt = CodedEnum.codeOf(type, code);
if (codedEnumOpt.isPresent()) {
return codedEnumOpt.get();
} else {
throw new IllegalArgumentException("Cannot convert " + code + " to " + type.getSimpleName() + " by code value.");
}
}
}
mldong-mapper/src/main/java/com/mldong/base/YesNoEnum.java
yes or no模型,对应如:是否删除(1->未删除|NO,2->已删除|YES)
package com.mldong.base;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* yes_no
* @author mldong
*
*/
public enum YesNoEnum implements CodedEnum{
/**
* 是
*/
YES(1, "是"),
/**
* 否
*/
NO(2,"否")
;
private int value;
private String name;
/**
* 未删除
*/
public final static int Y=1;
/**
* 已删除
*/
public final static int N=2;
@JsonCreator
public static YesNoEnum forValue(int value) {
return CodedEnum.codeOf(YesNoEnum.class, value).get();
}
YesNoEnum(int value, String name) {
this.value = value;
this.name = name;
}
@JsonValue
public int getValue() {
return value;
}
public String getName() {
return name;
}
}
mldong-mapper/src/main/java/com/mldong/modules/sys/entity/SysUser.java
实体类对应修改
package com.mldong.modules.sys.entity;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Id;
import javax.persistence.Table;
import tk.mybatis.mapper.annotation.LogicDelete;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.mldong.annotation.DictEnum;
import com.mldong.base.CodedEnum;
import com.mldong.base.YesNoEnum;
/**
* 用户表实体
* @author mldong
*
*/
@Table(name="sys_user")
public class SysUser implements Serializable{
/**
*
*/
private static final long serialVersionUID = -2687095050668229447L;
@Id
@ApiModelProperty(value="主键")
private Long id;
@ApiModelProperty(value="用户名")
private String userName;
@ApiModelProperty(value="姓名")
private String realName;
@ApiModelProperty(value="头像")
private String avatar;
@ApiModelProperty(value="邮箱")
private String email;
@ApiModelProperty(value="手机号")
private String mobilePhone;
@ApiModelProperty(value="联系电话")
private String telephone;
@ApiModelProperty(value="密码")
private String password;
@ApiModelProperty(value="加盐")
private String salt;
@ApiModelProperty(value="性别")
private Integer sex;
@ApiModelProperty(value="是否锁定")
private YesNoEnum isLocked;
@ApiModelProperty(value="创建时间")
private Date createTime;
@ApiModelProperty(value="更新时间")
private Date updateTime;
@LogicDelete(isDeletedValue=YesNoEnum.Y,notDeletedValue=YesNoEnum.N)
@ApiModelProperty(value="是否删除")
private YesNoEnum isDeleted;
// get set 略
@DictEnum(name="性别", key="sys_user_sex")
public enum SexEnum implements CodedEnum {
/**
* 男
*/
MALE(1, "男"),
/**
* 女
*/
FEMALE(2, "女"),
/**
* 未知
*/
UNKNOWN(3, "未知"),
;
private int value;
private String name;
@JsonCreator // str->json的时候,由value->enums
public static SexEnum forValue(int value) {
return CodedEnum.codeOf(SexEnum.class, value).get();
}
SexEnum(int value, String name) {
this.value = value;
this.name = name;
}
@JsonValue // json->str的时候,enums->value
public int getValue() {
return value;
}
public String getName() {
return name;
}
}
}
mldong-admin/src/main/java/com/mldong/modules/sys/service/impl/SysUserServiceImpl.java
对应的增、删方法需要修改
package com.mldong.modules.sys.service.impl;
import java.util.Date;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Condition;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.mldong.base.YesNoEnum;
import com.mldong.common.base.CommonPage;
import com.mldong.modules.sys.dto.SysUserParam;
import com.mldong.modules.sys.entity.SysUser;
import com.mldong.modules.sys.mapper.SysUserMapper;
import com.mldong.modules.sys.service.SysUserService;
/**
* 用户管理业务
* @author mldong
*
*/
@Service
public class SysUserServiceImpl implements SysUserService{
@Autowired
private SysUserMapper sysUserMapper;
@Override
public int save(SysUserParam param) {
Date now = new Date();
SysUser user = new SysUser();
BeanUtils.copyProperties(param, user);
user.setCreateTime(now);
user.setUpdateTime(now);
user.setIsDeleted(YesNoEnum.NO);
return sysUserMapper.insertSelective(user);
}
@Override
public int update(SysUserParam param) {
Date now = new Date();
SysUser user = new SysUser();
BeanUtils.copyProperties(param, user);
user.setUpdateTime(now);
return sysUserMapper.updateByPrimaryKeySelective(user);
}
@Override
public int remove(List<Long> ids) {
Date now = new Date();
SysUser upUser = new SysUser();
upUser.setIsDeleted(YesNoEnum.YES);
upUser.setUpdateTime(now);
Condition condition = new Condition(SysUser.class);
condition.createCriteria().andIn("id", ids);
return sysUserMapper.updateByConditionSelective(upUser, condition);
}
@Override
public SysUser get(Long id) {
return sysUserMapper.selectByPrimaryKey(id);
}
@Override
public CommonPage<SysUser> list(SysUserParam param, int pageNum, int pageSize) {
Page<SysUser> page = PageHelper.startPage(pageNum, pageSize,true);
SysUser user = new SysUser();
BeanUtils.copyProperties(param, user);
sysUserMapper.select(user);
return CommonPage.toPage(page);
}
}
mldong-admin/src/main/java/resources/application.yml
配置default-enum-type-handler
# mybatis配置
mybatis:
type-aliases-package: com.mldong.modules.*.mapper.*,com.mldong.modules.*.dao.*,com.mldong.modules.*.repo.*
mapper-locations: classpath*:mapper/*/*.xml,classpath*:dao/*/*.xml,classpath*:repo/*/*.xml
configuration:
map-underscore-to-camel-case: true
default-enum-type-handler: com.mldong.base.CodedEnumTypeHandler
启动运行项目
MldongAdminApplication.java
右键->Run As -> Java Application
访问演示
略
项目源码地址
- 后端
- 前端
相关文章
打造一款适合自己的快速开发框架-集成swaggerui和knife4j