单表的思考

149 阅读3分钟

最近在独立开发一个小项目,由于只有我一个人负责数据库,后端,前端,部署等所有环境。采用的技术栈是springboot+kotlin+mysql。写的过程中决定抛弃多表关联查询,所有的查询操作都采用单表来进行处理,看下是否可以完整覆盖整个业务。

优缺点

先说优点:

  1. 不需要额外的bo,vo等转换类。
  2. 都是单表,效率比较高,且SQL简单,mybatisPlus可以全程覆盖
  3. 由于采用了缓存,相同的关联字段可以直接复用

缺点:

  1. 如果单表的数据量很大时,会有一定的效率影响。(因为需要做缓存)
  2. 页面需要经过二次匹配处理,对加载速度有一定的影响。(实际操作基本无感知)

各种问题:

没有关联表,当需要展示多个表的数据时,需要查询关联表的字典数据,在前端进行匹配。这个时候就需要用到缓存技术,防止频繁调用数据库造成重复资源浪费。

关联表字段显示

后端

主要依赖缓存框架来实现增,删,改对内存数据的动态变化。

1. 配置缓存管理

@Bean
open fun cacheManager(): CacheManager {
    val caffeineCacheManager = CaffeineCacheManager()

    caffeineCacheManager.setCaffeine(
        Caffeine.newBuilder()
            .initialCapacity(100)
            .maximumSize(1000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
    )

    return caffeineCacheManager

}

默认的有效期是10分钟。

2. Service添加对应缓存注解

  • 更新数据

  • 查询数据

  • 新增数据

不做缓存处理,当查询不到时,会走查询数据缓存,为空会走一遍数据库,插入到缓存中。

前端

主要是在页面mounted时,加载我们需要关联数据。等列表需要渲染时,进行id值转换即可。

Vue2值转换匹配

  • 列表匹配
<div slot="busSlot" slot-scope="{text}">
  {{text == null ? '': busList.find(c => c.id == text).name}}
</div>
<div slot="lineSlot" slot-scope="{text}">
  {{text == null ? '': lineList.find(c => c.id == text).name}}
</div>
  • map匹配
<div slot="userId" slot-scope="{record}">
  {{userMap[record.userId]}}({{record.userId}})
</div>

Map匹配的效率要高于列表匹配,对于数据量大的表可以采用Map匹配。

数据插入

业务中有一个交易表,查询时需要显示关联的线路,车辆,学校三个信息。初始的交易表其实没有该数据。现在需要匹配单表,所以需要进行字段id冗余。分别是(lineId,busId,schoolId),这也是单表比较麻烦的地方。

  • 增加字段

  • 插入时通过上述的缓存查询到关联的id插入到数据库中

val bus = equipment?.busId?.let { busService.selectOne(it) }

数据查询

查询时就比较简单了,不需要进行关联表查询,通过mybatisPlus进行操作即可。

fun faceTradePage(faceTradeRequest: FaceTradeRequest): Any {

    return faceTradeMapper.selectPage(
        Page(faceTradeRequest.current.toLong(), faceTradeRequest.size.toLong()),
        KtQueryWrapper(FaceTrade::class.java)
            .eq(FaceTrade::merId, faceTradeRequest.customerId)
            .eq(faceTradeRequest.userId > 0, FaceTrade::userId, faceTradeRequest.userId)
            .eq(!faceTradeRequest.equipmentId.isNullOrEmpty(), FaceTrade::terId, faceTradeRequest.equipmentId)
            .eq(!faceTradeRequest.schoolCode.isNullOrEmpty(), FaceTrade::schoolCode, faceTradeRequest.schoolCode)
            .eq(faceTradeRequest.lineId != null, FaceTrade::lineId, faceTradeRequest.lineId)
            .eq(faceTradeRequest.busId != null, FaceTrade::busId, faceTradeRequest.busId)
            .gt(StrUtil.isNotEmpty(faceTradeRequest.startDate), FaceTrade::tradeTime, "${faceTradeRequest.startDate} 00:00:00")
            .lt(StrUtil.isNotEmpty(faceTradeRequest.endDate), FaceTrade::tradeTime, "${faceTradeRequest.endDate} 23:59:59")
            .orderByDesc(FaceTrade::createTime)
    )

}

总结

把后端的一部分工作转移到前端处理,小型项目这样操作还是很方便,便于接口的开发,提升代码开发速度。推荐使用kotlin来开发springboot项目,以较小的代码量,更快的速度完成项目的开发迭代。