从 crud 意识设计模式

  • 在业务零碎中增删改查(crud)是常常须要开发的内容,本文次要从增删改查来对设计模式进行一个学习或应用.
  • 个别咱们对一个表的增删改查有如下一些接口定义
boolean insert(Object o);Object byId(Integer id);boolean del(Integer id);boolean editor(Integer interfaces, Object o;
  • 再带上 redis 的 crud 操作,具体不进行列举.

整体操作

  • 在这里咱们仅针对单表而言

C-创立

  • 通常咱们的创立流程是这样子的.

    1. 接管申请,将申请转换为db对象,长久化
    2. 如果须要制作缓存,则放入缓存中
  • 伪代码如下
boolean b =  mapper.save(s)if (b){  redis.save(s)}

R-读取

  • 此处以byId为例. where 条件不易放入 redis 作为 key ,如果作为 key 数据量会过多
  • 通常操作

    1. 尝试从 redis 中读取
    2. 读取到了则返回,没有读取到通过db进行查问
  • 伪代码
Object o =nullo = redis.read(id)if (o ==null) {  o = mapper.read(id)}

U-更新

  • 通常操作

    1. 删除 redis 的记录(防止读到假数据)
    2. 更新db
    3. 放入 redis
  • 伪代码
redis.del()mapper.update(o)redis.save(o)

D-删除

  • 通常操作

    1. redis 删除
    2. db 删除
  • 伪代码
redis.del()mapper.del(o)

设计改进

  • 上述的操作大部分内容类似度很高, 在日常编写的时候咱们可能会常常写这样子的代码.代码量过多, 以及代码复用率不是那么好.因而对其做出改进.
  • 形象数据库操作和缓存操作. 其操作内容就是 CRUD 几个基本操作. 再次强调:单表操作
  • 在形象 crud 基本操作前须要在提出一个问题.

    • 咱们的主键可能是多种类型的. 常见有 int 、 varchar 这两种, 如何让咱们的 crud 接口更好的应用?
    • 上面是笔者提供的解决方案.

id 多类型的解决

  • 首先定义一个空接口 IdInterface 它用来标识这是一个 id 接口
public interface IdInterface {}
  • 依据罕用 int 和 varchar 设计出如下两个子接口. 他们的作用会在后续失去进一步的体现. 以后阶段仅须要晓得他们是 id 接口的继承即可.

    • 办法 id() 用来返回具体的 id 值
public interface IntIdInterface extends IdInterface {  int id();}public interface StrIdInterface extends IdInterface {  String id();}

CRUD 根底接口形象

  • 首先定义 db 操作接口
public interface DbOperation<T, I extends IdInterface> {  boolean insert(T t);  T byId(I interfaces);  boolean del(I interfaces);  boolean editor(I interfaces, T t);}
  • 代码解释, 此处应用泛型 T 来标识承受的类型, I 来标识接管的接口, 这里存在2中状况. 第一种 I = IntIdInterface , 第二种 I = StrIdInterface
  • 持续定义 redis 的操作
public interface RedisOperation<T, I extends IdInterface> {  void insert(T t);  void update(I i, T t);  void del(I i);  T byId(I i);}
  • redis 缓存的数据结构抉择. 这里为了方便使用 id 进行查问抉择 hash 进行开发. 如有需要可自定义抉择数据结构.
  • redis 操作 hash 的行为也能够形象进去.这里就不应用接口了. 应用一个父类来进行解决.
public abstract class RedisHashKeyOperation<T> {  Gson gson = new Gson();  @Autowired  private StringRedisTemplate redisTemplate;  protected void update(String key, String id, T t) {    T redisObj = this.byId(key, id, t.getClass());    if (!Objects.isNull(redisObj)) {      // 如果是redis中的类型和以后传入的类型雷同      if (redisObj.getClass().equals(t.getClass())) {        this.insert(key, id, t);      }    }  }  protected void insert(String key, String id, T t) {    redisTemplate.opsForHash().put(key, id, gson.toJson(t));  }  protected T byId(String key, String id, Class<?> clazz) {    String o = (String) redisTemplate.opsForHash().get(key, id);    return (T) gson.fromJson(o, clazz);  }  protected void delete(String key, String id) {    this.redisTemplate.opsForHash().delete(key, id);  }}
  • 个别的对 redis hash 操作根本就是这样子的. 不会有过多的批改.
  • 创立两个表来应用以下. 实体对象 mapper 不在此进行开展代码
CREATE TABLE `project_int` (  `id` int(32) NOT NULL AUTO_INCREMENT,  `name` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;CREATE TABLE `project_str` (  `id` varchar(50) COLLATE utf8mb4_bin NOT NULL,  `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
  • 贴出一个int为主键的操作
@Service("ProjectIntDbOperationImpl")public class ProjectIntDbOperationImpl implements DbOperation<ProjectInt, IntIdInterface> {  @Autowired  private ProjectIntMapper projectIntMapper;  @Override  public boolean insert(ProjectInt projectInt) {    return projectIntMapper.insert(projectInt) > 0;  }  @Override  public ProjectInt byId(IntIdInterface interfaces) {    return projectIntMapper.selectByPrimaryKey(interfaces.id());  }  @Override  public boolean del(IntIdInterface interfaces) {    return projectIntMapper.deleteByPrimaryKey(interfaces.id()) > 0;  }  @Override  public boolean editor(IntIdInterface interfaces, ProjectInt projectInt) {    // 更新存在策略    ProjectInt projectInt1 = this.byId(interfaces);    projectInt1.setName(projectInt.getName());    return this.projectIntMapper.updateByPrimaryKey(projectInt1) > 0;  }}@Service("ProjectIntRedisOperationImpl")public class ProjectIntRedisOperationImpl    extends RedisHashKeyOperation<ProjectInt>    implements    RedisOperation<ProjectInt, IntIdInterface> {  public static final String CACHE_PROJECT_INT = "cache:project_int";  public void insert(ProjectInt projectInt) {    super.insert(CACHE_PROJECT_INT, String.valueOf(projectInt.getId()), projectInt);  }  public void update(IntIdInterface strIdInterface, ProjectInt projectInt) {    super.update(CACHE_PROJECT_INT, String.valueOf(strIdInterface.id()), projectInt);  }  public void del(IntIdInterface strIdInterface) {    super.delete(CACHE_PROJECT_INT, String.valueOf(strIdInterface.id()));  }  public ProjectInt byId(IntIdInterface intIdInterface) {    return super.byId(CACHE_PROJECT_INT, String.valueOf(intIdInterface.id()), ProjectInt.class);  }}
  • varchar 作为主键
@Service("ProjectStrDbOperationImpl")public class ProjectStrDbOperationImpl implements DbOperation<ProjectStr, StrIdInterface> {  @Autowired  private ProjectStrMapper projectStrMapper;  @Override  public boolean insert(ProjectStr projectInt) {    return projectStrMapper.insert(projectInt) > 0;  }  @Override  public ProjectStr byId(StrIdInterface interfaces) {    return projectStrMapper.selectByPrimaryKey(interfaces.id());  }  @Override  public boolean del(StrIdInterface interfaces) {    return projectStrMapper.deleteByPrimaryKey(interfaces.id()) > 0;  }  @Override  public boolean editor(StrIdInterface interfaces, ProjectStr projectInt) {    // 更新存在策略    ProjectStr projectInt1 = this.byId(interfaces);    projectInt1.setName(projectInt.getName());    return this.projectStrMapper.updateByPrimaryKey(projectInt1) > 0;  }}@Service("ProjectStrRedisOperationImpl")public class ProjectStrRedisOperationImpl    extends RedisHashKeyOperation<ProjectStr>    implements    RedisOperation<ProjectStr, StrIdInterface> {  public static final String CACHE_PROJECT_INT = "cache:project_int";  public void insert(ProjectStr projectStr) {    super.insert(CACHE_PROJECT_INT, String.valueOf(projectStr.getId()), projectStr);  }  public void update(StrIdInterface strIdInterface, ProjectStr projectStr) {    super.update(CACHE_PROJECT_INT, String.valueOf(strIdInterface.id()), projectStr);  }  public void del(StrIdInterface strIdInterface) {    super.delete(CACHE_PROJECT_INT, String.valueOf(strIdInterface.id()));  }  public ProjectStr byId(StrIdInterface intIdInterface) {    return super.byId(CACHE_PROJECT_INT, String.valueOf(intIdInterface.id()), ProjectStr.class);  }}
  • 基本操作曾经具备. 然而对外的办法还须要再一次进行形象. 在整体操作中,CRUD的逻辑相似. 持续进行改良.
  • 对立对外提供的接口如下
public interface ByIdOperationFacade<T, I extends IdInterface> {  boolean insert(T t);  T byId(I i);  boolean del(I i);  boolean editor(I i, T t);  DbOperation getDbOperation();}
  • 光有这个还不够. 咱们提供的对立 crud 办法还须要失去两个要害对象, 1. DbOperation 2. RedisOperation. 定义出操作汇合类
public class OperationCollections {  private DbOperation dbOperation;  private RedisOperation redisOperation;}
  • 有了操作汇合. 短少一个生成它的工具
public interface ByIdOperationFactory {  OperationCollections factory(Class<?> clazz);  }
  • 实现如下. 留神:此处为了让泛型更好的应用, 这里采纳的是类型对应一个操作对象(DbOperation,RedisOperation).

    • 须要改良的中央: 给DbOperationRedisOperation 增加一个函数用来获取类型
@Service("ByIdOperationFactoryImpl")public class ByIdOperationFactoryImpl implements ByIdOperationFactory {  static Map<Class, DbOperation> dbOperationMap = new HashMap<>();  static Map<Class, RedisOperation> redisOperationHashMap = new HashMap<>();  @Autowired  private ApplicationContext context;  @PostConstruct  public void init() {    Map<String, DbOperation> beansOfType = context.getBeansOfType(DbOperation.class);    beansOfType.forEach(        (k, v) -> {          Class type = v.type();          dbOperationMap.put(type, v);        }    );    Map<String, RedisOperation> beansOfType1 = context.getBeansOfType(RedisOperation.class);    beansOfType1.forEach((k, v) -> {      Class type = v.type();      redisOperationHashMap.put(type, v);    });  }  @Override  public OperationCollections factory(Class<?> clazz) {    OperationCollections operationCollections = new OperationCollections();    DbOperation dbOperation = dbOperationMap.get(clazz);    RedisOperation redisOperation = redisOperationHashMap.get(clazz);    operationCollections.setDbOperation(dbOperation);    operationCollections.setRedisOperation(redisOperation);    return operationCollections;  }}
  • 实现一个通用的 id 操作对象
public abstract class CommonByIdOperation<T, I extends IdInterface>    implements ByIdOperationFacade<T, I> {  @Autowired  @Qualifier("ByIdOperationFactoryImpl")  ByIdOperationFactory byIdOperationFactory;  @Override  public boolean insert(T t) {    throw new RuntimeException("没有实现");  }  @Override  public DbOperation getDbOperation() {    throw new RuntimeException("没有实现");  }  public boolean editor(I i, T t) {    boolean editor = false;    OperationCollections operationCollections = this.operationCollection();    RedisOperation redisOperation = operationCollections.getRedisOperation();    if (redisOperation != null) {      redisOperation.del(i);    }    DbOperation dbOperation = operationCollections.getDbOperation();    if (dbOperation != null) {      editor = dbOperation.editor(i, t);    }    if (redisOperation != null) {      redisOperation.insert(t);    }    return editor;  }  public boolean del(I i) {    boolean del = false;    OperationCollections operationCollections = this.operationCollection();    RedisOperation redisOperation = operationCollections.getRedisOperation();    if (redisOperation != null) {      redisOperation.del(i);    }    DbOperation dbOperation = operationCollections.getDbOperation();    if (dbOperation != null) {      del = dbOperation.del(i);    }    return del;  }  public T byId(I i) {    T result = null;    RedisOperation redisOperation = this.operationCollection().getRedisOperation();    if (redisOperation != null) {      result = (T) redisOperation.byId(i);    }    DbOperation dbOperation = this.operationCollection().getDbOperation();    if (dbOperation != null) {      if (result == null) {        System.out.println("从数据库获取");        result = (T) dbOperation.byId(i);      }    }    return result;  }  public OperationCollections operationCollection() {    return byIdOperationFactory.factory(clazz());  }  protected Class<?> clazz() {    throw new IllegalArgumentException("类型异样");  }}
  • 阐明: 这里的 insert 操作不倡议在此进行解决. 应为id是在mybatis插入后才会有. 当然也能够通过一个bean—copy的形式在这里获取. 然而咱们的命名如果不同. beanCopy 就不行了.
  • 简略概述bean-copy的形式
public class BeanCopy {  public static void main(String[] args) {    StrId strId = new StrId();    strId.setId("str_id");    CpId cpId = new CpId();    BeanUtils.copyProperties(strId, cpId);    System.out.println(cpId.getId());  }  @Data  static class CpId {    private Object id;  }  @Data  static class StrId {    private String id;  }}
  • 最初的组装过程
@Service("ProjectIntDbOperationImpl")public class ProjectIntDbOperationImpl implements DbOperation<ProjectInt, IntIdInterface> {  @Autowired  private ProjectIntMapper projectIntMapper;  @Override  public boolean insert(ProjectInt projectInt) {    return projectIntMapper.insert(projectInt) > 0;  }  @Override  public ProjectInt byId(IntIdInterface interfaces) {    return projectIntMapper.selectByPrimaryKey(interfaces.id());  }  @Override  public boolean del(IntIdInterface interfaces) {    return projectIntMapper.deleteByPrimaryKey(interfaces.id()) > 0;  }  @Override  public boolean editor(IntIdInterface interfaces, ProjectInt projectInt) {    // 更新存在策略    ProjectInt projectInt1 = this.byId(interfaces);    projectInt1.setName(projectInt.getName());    return this.projectIntMapper.updateByPrimaryKey(projectInt1) > 0;  }  @Override  public Class<?> type() {    return ProjectInt.class;  }}
@Service("ProjectStrFacade")public class ProjectStrFacade extends CommonByIdOperation<ProjectStr, StrIdInterface> implements    ByIdOperationFacade<ProjectStr, StrIdInterface> {  @Override  public boolean insert(ProjectStr projectInt) {    DbOperation dbOperation = this.getDbOperation();    boolean insert = false;    if (dbOperation != null) {      insert = dbOperation.insert(projectInt);    }    RedisOperation redisOperation = this.operationCollection().getRedisOperation();    if (redisOperation != null) {      redisOperation.insert(projectInt);    }    return insert;  }  @Override  public ProjectStr byId(StrIdInterface strIdInterface) {    return super.byId(strIdInterface);  }  @Override  public boolean del(StrIdInterface strIdInterface) {    return super.del(strIdInterface);  }  public boolean editor(StrIdInterface strIdInterface, ProjectStr projectInt) {    return super.editor(strIdInterface, projectInt);  }  @Override  public DbOperation getDbOperation() {    return this.operationCollection().getDbOperation();  }  @Override  protected Class<?> clazz() {    return ProjectStr.class;  }}

测试

@SpringBootTestclass DemoApplicationTests {  Gson gson = new Gson();  @Autowired  private ProjectStrMapper projectStrMapper;  @Autowired  @Qualifier("projectIntFacade")  private ByIdOperationFacade<ProjectInt, IntIdInterface> byIdOperationFacade;  @Autowired  private StringRedisTemplate stringRedisTemplate;  @Test  void testInsert() {    ProjectInt projectInt = new ProjectInt();    projectInt.setName("JJJ");    this.byIdOperationFacade.insert(projectInt);  }  @Test  void testUpdate() {    ProjectInt projectInt = this.byIdOperationFacade.byId(new IntIdInterface() {      @Override      public int id() {        return 1;      }    });    projectInt.setName("update");    this.byIdOperationFacade.editor(new IntIdInterface() {      @Override      public int id() {        return projectInt.getId();      }    }, projectInt);  }  @Test  void testDel() {    this.byIdOperationFacade.del(new IntIdInterface() {      @Override      public int id() {        return 1;      }    });  }  @Test  void testById() {    ProjectInt projectInt = this.byIdOperationFacade.byId(new IntIdInterface() {      @Override      public int id() {        return 1;      }    });    System.out.println();  }}
  • 咱们对外裸露的类只有ByIdOperationFacade.
  @Autowired  @Qualifier("projectIntFacade")  private ByIdOperationFacade<ProjectInt, IntIdInterface> byIdOperationFacade;
  • 通过不同的实现来操作不同的数据库对象

总结

  • 本文应用了

    1. 模板模式
    2. 工厂模式
    3. 门面模式
  • 仓库地址: https://github.com/huifer/crud