-
2022-03-10 15:54:01
-
目录
-
概念介绍
- 领域驱动设计是一种设计思想,在落地时主要使用四层架构承接
- 面向数据表设计,一般使用三层架构承接,但是也可以使用四层架构承接
-
领域驱动设计(DDD)
- DDD 是一种在面向高度复杂的软件系统时,关于如何去建模的方法论。
- DDD解决了什么问题?
- DDD解决了对复杂应用建模的问题,通过DDD提高架构和代码的可理解性,从而降低维护成本。
- 适用场景
- DDD 适用于复杂业务建模以及业务场景不确定的任务 。
- 对于业务场景确定或者,业务场景简单的系统,面向数据库设计更适合。
- 承接DDD设计的分层架构
- 四层架构
-
- User Interface(用户接口层)
- 职责:
- 负责向用户显示信息、解释用户命令。这里的用户可以是真正的用户、自动化测试程序、批处理脚本等。
- 职责:
- Application Layer(应用层)
- 主要负责:
- 负责对领域服务、其他应用服务的封装、编排、组合。
- 主要特点:
- 这一层都很薄,主要是服务的组合与编排。
- 特别注意:
- 不要将领域层的逻辑放在应用层中实现,否则很容易导致应用层、领域层的边界混乱,最终导致DDD的四层架构变成三层架构。
- 主要负责:
- Domain Layer(领域层)
- 主要职能:
- 实现领域对象或者聚合自身的原子业务逻辑,关注领域模型的能力,不太关注外部用户操作或者流程方面的控制。
- 主要包括:
- 领域服务、值对象、实体、聚合、聚合根等。
- 主要职能:
- Infrastructure Layer(基础设施层)
- 主要职能:
- 为其他各层提供通用的技术和技术服务。
- 主要包括:
- 封装消息中间件、网关、缓存、文件服务、数据库等。
- 主要作用
- 封装基础资源的服务,实现应用层、领域层、基础层解耦,降低对外部资源依赖。例如:如果没有此层,当消息中间件、缓存变化时,将会大面积修改业务逻辑。
- 主要职能:
- User Interface(用户接口层)
-
- 六边形模型
- 洋葱模型
- 四层架构
-
面向数据表设计
- 传统开发,以设计数据表为中心。
- 在面向数据表设计中,系统的开发主要集中于数据表的设计,包括字段的设计以及表关系的设计。
- 数据集中存储在不同的数据表中,数据表的设计需要符合各种范式和标准。
- 业务逻辑通过一系列针对这些数据表的增删改查来实现。
- 适用场景
- 承接面向数据表设计的分层架构
- 三层架构
- User Interface layer(表示层, UI) 接受对用户的请求并返回数据,将结果呈现给用户。
- Business Logic Layer(业务逻辑层, BLL) 主要负责接受表现层的请求,进行各种业务逻辑运算,然后通过数据访问层完成数据的操作。
- Data Access Layer(数据访问层, DAL) 主要负责数据的查询、持久化等操作,关注数据一致性、准确性,不关注业务逻辑
- 四层架构
- 可以使用四层架构承接面向数据表设计,将三层架构中的service层进行拆分
- 三层架构
-
领域驱动设计优点
- 快速响应需求变化
- 例子
- 场景
- 业务新增需求变化
- 领域驱动设计的处理流程
- 分析需求
- 产出需求对应的领域事件,命令
- 领域建模,产出领域对象
- 优点
- 快速定位新增需求对应的领域对象,映射到对应的数据模型
- 面向数据表设计的处理流程
- 分析需求
- 分析需要新增的字段
- 确认数据表的变化
- 具体在哪个表格新增字段
- 编写代码,操作数据表格,完成业务
- 缺点
- 无法确认应在哪张表新增字段
- 场景
-
设计承接分层架构对比
- 三层架构vs四层架构
- 区别:
- 三层架构:
- 三层架构的业务处理都放在Service层,所有的Service对象都处在同一个逻辑层级,Service对象即负责领域内逻辑,又负责领域间协调(此处的领域泛指业务边界)。Service层的内部是一张网。
- 四层架构:
- 四层架构将领域间协调和领域内逻辑处理分开成两层,分别用application Service和domin service处理,这样使得整个业务处理层(application service + domain service )变成了一个树形结构。
- 在所有的数据结构中,树形结构是在能够承载比较多信息量的同时,逻辑结构最简单的数据结构。
- 三层架构:
- 区别:
- 三层架构vs四层架构
更多相关内容 -
-
数据库表设计
2020-10-16 15:03:06常用的邻接表设计,都会添加 一个 parent_id 字段,比如区域表(国、省、市、区): CREATE TABLE Area( [id][int]NOTNULL, [name][nvarchar](50)NULL, [parent_id][int]NULL, [type][int]NULL); name:...我们在设计数据库的时候,是否会突破常规,找到最适合自己需求的设计方案,下面来举个例子:
常用的邻接表设计,都会添加 一个 parent_id 字段,比如区域表(国、省、市、区):
CREATE TABLE Area (
[id] [int] NOT NULL,
[name] [nvarchar] (50) NULL,
[parent_id] [int] NULL,
[type] [int] NULL );
name:地域的名称, parent_id 是父ID,省的父ID是国,市的父ID 为省,以此类推。
type 是区域的阶级: 1:国,2:省,3:市,4:区
在层级比较确定的情况下,这么设计表格没有什么问题,调用起来也很方便。
但是使用这种邻接表设计方式,并不能满足所有的需求,当我们不确定层级的情况下,假设我有下面一个评论结构:
用邻接表记录这个评论的数据(comments 表):
大家有没发现,这么设计表,如果要查询一个节点的所有后代,是很难实现的,你可以使用关联查询来获取一条评论和他的后代:
SELECT c1.*, c2.* FROM comments c1 LEFT OUTER JOIN comments c2 ON c2.parent_id = c1.comment_id;
然而这个查询只能获取两层的数据。这种树的特性就是可以任意深地拓展,你需要有相应的方法来获取它的深度数据。比如,可能需要计算一个评论分支的数量,或者计算一个机械设备的所有的总开销。
某些情况下,在项目中使用邻接表正好适用。邻接表设计的优势在于能快速的获取一个给定节点的直接父子节点,它也很容易插入新节点。如果这样的需求就是你的项目对于分层数据的全部操作,那使用邻接表就可以很好的工作了。
遇到上述的树模型,有几种方案是可以考虑下的:路径枚举、嵌套集以及闭包表。这些解决方案通常看上去比邻接表复杂很多,但它们的确使得某些使用邻接表比较复杂或很低效的操作变得更简单。如果你的项目确实需要提供这些操作,那么这些设计会是邻接表更好的选择。
一、路径枚举
在comments 表中,我们使用类型varchar 的path 字段来替代原来的parent_id 字段。这个path 字段所存储的内容为当前节点的最顶层祖先到它的自己的序列,就像UNIX的路径一样,你甚至可以使用 ‘/’ 作为路径的分隔符。
你可以通过比较每个节点的路径来查询一个节点祖先。比如:要找到评论#7, 路径是 1/4/5/7一 的祖先,可以这么做:
SELECT * FROM comments AS c WHERE '1/4/5/7' LIKE c.path || '%' ;
这句话查询语句会匹配到路径为 1/4/5/%,1/4/% 以及 1/% 的节点,而这些节点就是评论#7的祖先。
同时还可以通过将LIKE 关键字两边的参数互换,来查询一个给定节点的所有后代。比如查询评论#4,路径path为 ‘1/4’ 的所有后代,可以使用如下语句:
SELECT * FROM comemnts AS c WHERE c.path LIKE '1/4' || '%' ;
这句查询语句所有能找到的后台路径分别是:1/4/5、1/4/5/6、1/4/5/7。
一旦你可以很简单地获取一棵子树或者从子孙节点到祖先节点的路径,你就可以很简单地实现更多的查询,如查询一颗子树所有节点上值的总和。
插入一个节点也可以像使用邻接表一样地简单。你所需要做的只是复制一份要插入节点的父亲节点路径,并将这个新节点的ID追加到路径末尾即可。
路径枚举也存在一些缺点,比如数据库不能确保路径的格式总是正确或者路径中的节点确实存在。依赖于应用程序的逻辑代码来维护路径的字符串,并且验证字符串的正确性开销很大。无论将varchar 的长度设定为多大,依旧存在长度的限制,因而并不能够支持树结构无限扩展。
二、 嵌套集
嵌套集解决方案是存储子孙节点的相关信息,而不是节点的直接祖先。我们使用两个数字来编码每个节点,从而表示这一信息,可以将这两个数字称为nsleft 和 nsright。
每个节点通过如下的方式确定nsleft 和nsright 的值:nsleft的数值小于该节点所有后代ID,同时nsright 的值大于该节点的所有后代的ID。这些数字和comment_id 的值并没有任何关联。
确定这三个值(nsleft,comment_id,nsright)的简单方法是对树进行一次深度优先遍历,在逐层深入的过程中依次递增地分配nsleft的值,并在返回时依次递增地分配nsright的值。得到数据如下:
一旦你为每个节点分配了这些数字,就可以使用它们来找到指定节点的祖先和后代。比如搜索评论#4及其所有后代,可以通过搜索哪些节点的ID在评论 #4 的nsleft 和 nsright 范围之间,例:
SELECT c2.* FROM comments AS c1 JOIN comments AS c2 ON c2.nsleft BETWEEN c1.nsleft
AND c1.nsright WHERE c1.comment_id = 4;
比如搜索评论#6及其所有祖先,可以通过搜索#6的ID在哪些节点的nsleft 和 nsright 范围之间,例:
SELECT c2.* FROM comments AS c1 JOIN comments AS c2 ON c1.nsleft BETWEEN c2.nsleft
AND c2.nsright WHERE c1.comment_id = 6;
使用嵌套集设计的主要优势是,当你想要删除一个非叶子节点时,它的后代会自动替代被删除的节点,成为其直接祖先节点的直接后代。就是说已经自动减少了一层。
然而,某些在邻接表的设计中表现得很简单的查询,比如获取一个节点的直接父亲或者直接后代,在嵌套集设计中会变得比较复杂。在嵌套集中,如果需要查询一个节点的直接父亲,我们会这么做,比如要找到评论#6 的直接父亲:
SELECT parent.* FROM comments AS c JOIN comments AS parent ON c.nsleft BETWEEN parent.nsleft AND parent.nsright
LEFT OUTER JOIN comments AS in_between ON c.nsleft BETWEEN in_between.nsleft AND in_between.nsright
AND in_between.nsleft BETWEEN parent.nsleft AND parent.nsright WHERE c.comment_id = 6
AND in_between.comment_id IS NULL;
总之有些复杂。
对树进行操作,比如插入和移动节点,使用嵌套集会比其它设计复杂很多。当插入一个新节点时,你需要重新计算新插入节点的相邻兄弟节点、祖先节点和它祖先节点的兄弟,来确保他们的左右值都比这个新节点的左值大。同时,如果这个新节点时一个非叶子节点,你还要检查它的子孙节点。
如果简单快速查询是整个程序中最重要的部分,嵌套集是最好的选择,比操作单独的节点要方便快捷很多。然而,嵌套集的插入和移动节点是比较复杂的,因为需要重新分配左右值,如果你的应用程序需要频繁的插入、删除节点,那么嵌套集可能并不合适。
三、闭包表
闭包表是解决分级存储的一个简单而优雅的解决方案,它记录了树中所有节点间的关系,而不仅仅只有那些直接的父子节点。
在设计评论系统时,我们额外创建了一个叫 tree_paths 表,它包含两列,每一列都指向 comments 中的外键。
我们不再使用comments 表存储树的结构,而是将树中任何具有(祖先 一 后代)关系的节点对都存储在treepaths 表里,即使这两个节点之间不是直接的父子关系;同时,我们还增加一行指向节点自己。
通过treepaths 表来获取祖先和后代比使用嵌套集更加的直接。例如要获取评论#4的后代,只需要在 treepaths 表中搜索祖先是评论 #4的行就行了。同样获取后代也是如此。
要插入一个新的叶子节点,比如评论#6的一个子节点,应首先插入一条自己到自己的关系,然后搜索 treepaths 表中后代是评论#6 的节点,增加该节点和新插入节点的“祖先一后代”关系(新节点ID 应该为8):
INSERT INTO treepaths (ancestor, descendant)
SELECT t.ancestor, 8
FROM treepaths AS t
WHERE t.descendant = 6
UNION ALL SELECT 8, 8;
要删除一个叶子节点,比如评论#7, 应删除所有treepaths 表中后代为评论 #7 的行:
DELETE FROM treepaths WHERE descendant = 7;
要删除一颗完整的子树,比如评论#4 和它所有的后代,可删除所有在 treepaths 表中后代为 #4的行,以及那些以评论#4后代为后代的行。
闭包表的设计比嵌套集更加的直接,两者都能快捷地查询给定节点的祖先和后代,但是闭包表能更加简单地维护分层信息。这两个设计都比使用邻接表或者路径枚举更方便地查询给定节点的直接后代和祖先。
然而你可以优化闭包表来使它更方便地查询直接父亲节点或者子节点: 在 treepaths 表中添加一个 path_length 字段。一个节点的自我引用的path_length 为0,到它直接子节点的path_length 为1,再下一层为2,以此类推。这样查询起来就方便多了。
总结:你该使用哪种设计?
每种设计都各有优劣,如何选择设计,依赖于应用程序的哪种操作是你最需要性能上的优化。
层级数据设计比较
1、邻接表是最方便的设计,并且很多程序员都了解它
2、如果你使用的数据库支持WITH 或者 CONNECT BY PRIOR 的递归查询,那能使得邻接表的查询更高效。
3、枚举路径能够很直观地展示出祖先到后代之间的路径,但同时由于它不能确保引用完整性,使得这个设计非常脆弱。枚举路径也使得数据的存储变得比较冗余。
4、嵌套集是一个聪明的解决方案,但可能过于聪明,它不能确保引用完整性。最好在一个查询性能要求很高而对其他要求一般的场合来使用它。
5、闭包表是最通用的设计,并且以上的方案也只有它能允许一个节点属于多棵树。它要求一张额外的表来存储关系,使用空间换时间的方案减少操作过程中由冗余的计算所造成的消耗。
这几种设计方案只是我们日常设计中的一部分,开发中肯定会遇到更多的选择方案。选择哪一种方案,是需要切合实际,根据自己项目的需求,结合方案的优劣,选择最适合的一种。
我遇到一些开发人员,为了敷衍了事,在设计数据库表时,只考虑能否完成眼下的任务,不太注重以后拓展的问题,不考虑查询起来是否耗性能。可能前期数据量不多的时候,看不出什么影响,但数据量稍微多一点的话,就已经显而易见了(例如:可以使用外联接查询的,偏偏要使用子查询)。
我觉得设计数据库是一个很有趣且充满挑战的工作,它有时能体现你的视野有多宽广,有时它能让你睡不着觉,总之痛并快乐着。
-
评论表设计
2021-09-01 15:14:52MySQL评论表设计1. 表设计2. Mybatis 一对多映射3. SQL 查询语句4. 查询结果 1. 表设计 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; DROP TABLE IF EXISTS `article_comment`; CREATE TABLE `article_comment...MySQL评论表设计
1. 表设计
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; DROP TABLE IF EXISTS `article_comment`; CREATE TABLE `article_comment` ( `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, `article_id` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '文章id', `comment_id` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '评论id', `content` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '评论内容', `like_num` int(11) UNSIGNED NULL DEFAULT 0 COMMENT '点赞数', `user_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户id', `article_user_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文章用户id', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 23 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '文章评论' ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
2. Mybatis 一对多映射
注意 在Model里面需要加属性
List<ArticleCommentModel> replys;
<resultMap id="commentResultMap" type="com.xxx.Model"> <result column="id" property="id"/> <result column="article_id" property="articleId"/> <result column="comment_id" property="commentId"/> <result column="content" property="content"/> <result column="like_num" property="likeNum"/> <result column="user_id" property="userId"/> <result column="article_user_id" property="articleUserId"/> <result column="create_time" property="createTime"/> <result column="nick_name" property="nickName"/> <result column="head_img" property="headImg"/> <collection property="replys" ofType="com.xxx.Model"> <result column="r_id" property="id"/> <result column="r_article_id" property="articleId"/> <result column="r_comment_id" property="commentId"/> <result column="r_content" property="content"/> <result column="r_like_num" property="likeNum"/> <result column="r_user_id" property="userId"/> <result column="r_article_user_id" property="articleUserId"/> <result column="r_create_time" property="createTime"/> <result column="r_nick_name" property="nickName"/> <result column="r_head_img" property="headImg"/> </collection> </resultMap>
3. SQL 查询语句
SELECT ft.*, rft.id r_id, rft.article_id r_article_id, rft.comment_id r_comment_id, rft.content r_content, rft.like_num r_like_num, rft.user_id r_user_id, rft.article_user_id r_article_user_id, rft.create_time r_create_time, rft.nick_name r_nick_name, rft.head_img r_head_img FROM ( SELECT fac.*, fu.nick_name, fu.head_img FROM `article_comment` fac LEFT JOIN user fu ON fu.id = fac.user_id < include refid = "whereEqual" /> ) ft LEFT JOIN ( SELECT fac.*, fu.nick_name, fu.head_img FROM `article_comment` fac LEFT JOIN user fu ON fu.id = fac.user_id WHERE fac.comment_id IS NOT NULL < IF test = "articleId != null and articleId != ''" > AND fac.article_id = #{articleId} </ IF > ) rft ON ft.id = rft.comment_id ORDER BY ft.id DESC, rft.id DESC
4. 查询结果
-
数据库-库表设计 【分享一些库表设计经验】
2018-05-20 19:48:15本文的核心内容:记录积累一些库表设计方案与技巧 数据库实体与实体间的对应关系 1)数据库表的菜单【分类】设计:如省市关联、图书的一、二级分类。 2)数据库表设计之树形结构的表 3)表的简化方案(特定情况,例如...本文的核心内容:记录积累一些库表设计方案与技巧
数据库实体与实体间的对应关系
1)数据库表的菜单【分类】设计:如省市关联、图书的一、二级分类。
2)数据库表设计之树形结构的表
3)表的简化方案(特定情况,例如,用户触发过的场景记录表)
4)数据库表设计之购物车,利用Session暂时存储购物车信息。
实体与实体间的对应关系
一对一
一对一,一般用于对主表的补充。假设A表为用户信息表,存储了用户的姓名、性别、年龄等基本信息。用户的家庭住址信息也属于用户的基本信息。我们可以选择将用户的家庭住址信息放到用户信息表,也可以单独建一张表,存储用户的家庭住址信息,以用户信息表的主键作为关联。
需不需要拆分取决:表信息的关联程度、表的字段个数限度。
一对多
一对多,是最常见的一种设计。就是 A 表的一条记录,对应 B 表的多条记录,且 A 的主键作为 B 表的外键。举几个 例子:
-
- 班级表 与 学生表,一个班级对应多个学生,或者多个学生对应一个班级。
- 角色表 与 用户表,一个角色对应多个用户,或者多个用户对应一个角色。
-
- 商品表 与 图片表,一个商品对应多张图片,或者多张图片对应一个商品。
多对多
构建一张关系表将两张表进行关联,形成多对多的形式。例如:
-
- 老师表、学生表;一个学生可以选修多个老师的课程、同时一个老师也可以教多个学生。
--教师表 CREATE TABLE #Teacher(TeacherId int, Name nvarchar(20)); INSERT INTO #Teacher VALUES(1, '张老师'), (2, '王老师'); --学生表 CREATE TABLE #Student(StudentId int, Name nvarchar(20)); INSERT INTO #Student VALUES(1, '小张'), (2, '小赵'); --老师学生关系表 CREATE TABLE #Teacher_Student(StudentId int, TeacherId int); INSERT INTO #Teacher_Student VALUES(1, 1), (1, 2),(2, 1), (2, 2);
一:数据库表的菜单【分类】设计:如省市关联、图书的一、二级分类
BookType 一级分类: 少儿、外语、计算机
BookClass 二级分类: 少儿[0-2岁、3-6岁、7-10岁、11-14岁、儿童文学]
外语[英语、日语、韩语、俄语、德语]
计算机[计算机理论、计算机考试、数据库、人工智能、程序设计]
BookInf 图书详情 : 图书信息的详细字段。。。
基于以上关系:我们建表有两种方法
①:建立三张表 一级分类表,二级分类表、图书详情表
一级分类ID->作为二级分类的外键
二级分类ID->作为图书详情的外键
这一种依赖外键,实体模型也比较简单。(不再过多描述)
查询语句:可以采用 left join on 或者 等值连接 将二级分类的外键与一级分类的主键等值连接即可查询。
②:建立两张表 一级分类和二级分类合并成一张表
图书详情表(引用TypeID为外键)
TypeID 指一级二级分类的ID(唯一标识、主键) 序列自增从1开始。
TypeName 指一级二级分类的名字
ParentID 指二级分类所属一级分类TypeID (若为一级分类则填”0”与二级分类加以区分)
countNumber 指一级图书包含二级图书的个数
二级分类所包含详细图书的个数
数据库查询一级分类信息的SQL
select typeid,typename,parentid,countnumber from t_booktype where parentid='0'
数据查询二级分类信息(利用表的自连接)
select child.typeid,child.typename, child.parentid,child.countnumber from t_booktype child ,t_booktype parent where child.parentid=parent.typeid
二:MySQL 表中存储树形结构数据
由上面设计技巧引出,如果数据层级有多级呢?简言之就像一棵树一样,我们如何存储树形的数据到数据库。
存储父节点
存储于数据库中,最简单直接的方法,就是存储每个元素的父节点ID,即parent_Id->父节点Id。这种方式方便了插入,但是在某些情况下的查询会束手无策。我们可以增加两个字段(deep,is_leaf)帮助我们更快的查询。
deep=1表示父节点,deep>1 表示子节点。
id parent_id deep //当前树的深度 is_leaf //是否叶子节点
查询所有父节点deSQL如下:
select * from tree where deep=1
查询某个父节点下的所有子节点:
select * from tree where parent_id=""
查询某个父节点下的所有后代节点,采用这种库表设计方式,这个需要依靠程序才能实现。
存储路径
将存储根结点到每个节点的路径,这种数据结构,可以一眼就看出子节点的深度。要插入自己,然后查出父节点的Path,并且把自己生成的ID更新到path中去。
如果要查询某个节点下的子节点,只需要根据path的路径去匹配,比如要查询D节点下的所有子节点。
select * from tree where path like '1/4/%'
总结
我建议存储树形结构可以将两者结合起来。
id parent_id deep //当前树的深度 path //根路径 is_leaf //是否叶子节点
三:MySQL 表的简化(特定情况,例如:用户触发过的场景记录表)
假设业务中有N多道具,比如用户首次使用某个道具触发特效。根据表里有没有用户相关的道具触发记录来完成判断。
id user_id //用户Id item_id //道具ID flag //是否触发过特效 0-1 add_time update_time
毫无疑问,上述表结构是能够满足并实现我们需求的,但是如果有20种道具,那么每个用户最终将有20条数据,数据冗余,如何简化?
解决方案:
定义道具枚举值
public enum ItemOnceFlagEnum { NONE(0), ITEM_ONE(1),//道具1 ITEM_TWO(1 << 1), //道具2 ITEM_Three(1 << 2), //道具3 ; private int code; ItemOnceFlagEnum(int code) { this.code = code; } public int getCode() { return code; } public static ItemOnceFlagEnum valueOf(int code) { ItemOnceFlagEnum[] values = ItemOnceFlagEnum.values(); for (ItemOnceFlagEnum flag : values) { if (flag.getCode() == code) { return flag; } } return NONE; } }
建表
id user_id //用户Id flag //是否触发过特效 2的0次幂、2的1次幂。。。 add_time update_time user_id设置为唯一索引
判断是否使用过某道具与添加道具使用记录
//判断是否使用过某项道具 public static boolean isHasThisFlag(long flag, ItemOnceFlagEnum itemOnceFlagEnum) { return (flag & itemOnceFlagEnum.getCode()) > 0; } //添加道具使用记录 public Result<Boolean> addOnceFlag(long userId, itemOnceFlagEnum flag) { ItemOnceFlagDO itemOnceFlagDO = itmeOnceFlagService.getOnceFlagMap(Collections.singletonList(userId)); long calculateFlag = (Objects.isNull(blockOnceFlagDO) ? 0L : itemOnceFlagDO.getFlag()) | flag.getCode(); itemOnceFlag.setFlag(calculateFlag); boolean res = itemOnceFlagService.addOrUpdateOnceFlag(itemOnceFlag); }
添加记录SQL
"INSERT INTO xxxx ( ) VALUES ( :1.userId, :1.flag ) " + " ON DUPLICATE KEY UPDATE flag = :1.flag "
某一项道具为具体2次幂值(仅能够维护2种状态,有或无),flag代表所有触发道具2次幂和。
通过 (flag & itemOnceFlagEnum.getCode())>0 判断是否有某项道具
通过 flag | temOnceFlagEnum.getCode(); 添加道具记录
四:购物车模块的库表设计
在电商软件,必不可少的模块就是购物车。
我分享两种设计方法:
①:维护一张购物车表,以用户ID为外键
一个用户一个购物车,用户注册成功的同时,为用户在购物车表内维护一个专属于用户的购物车。(根据我以前学到的知识,这一步可以为用户表创建Insert触发法器,当用户注册成功[触发器将用户ID作为外键插入购物车表],用户即拥有了唯一的购物车)
T_Car
字段
类型
说明
Car_ID
Varchar2(36)
购物车编号 主键
User_ID
Varchar2(36)
外键 用户唯一标识
Car_Status
Varchar2(4)
购物车状态
T_Shop_Item
字段
类型
说明
Shop_item_ID
Varchar2(36)
购物项编号 主键
Car_ID
Varchar2(36)
购物车编号 外键
Product_ID
Varchar2(36)
商品编号 外键
Count
Number(4)
数量
Price
number(8,2)
价格
ProductName
Varchar(30)
商品名
这么实现购物车的弊端:①:非该网站的注册用户无法将商品加入购物车。这与现实的情况不符合。一般我们访问某宝,某东,我们可以以游客的方式将商品加入购物车,直到下订单、付款时才要求我们必须登录。②:每个用户维护一下购物车似乎不太明智,顾客将商品加入购物车到下订单,完成交易,这一需求对数据库更改频繁。
②:所有用户共用一个”购物车”
我们可以直接以用户ID为标识,区分购物车商品所属的用户。
T_Shop_Item
字段
类型
说明
Shop_item_ID
Varchar2(36)
购物项编号 主键
User_ID
Varchar2(36)
用户编号 外键
ProductID
Varchar2(36)
商品编号 外键
Count
Number(4)
数量
Price
number(8,2)
价格
ProductName
Varchar(30)
商品名
即使减少了一张购物车表,但是这表还需要用户登录才能记录用户添加商品情况。
③:利用Session暂时存储购物车内的东西
[用户不登录就能添加商品到购物车;用户登录状态将Session中的信息存入非关系型数据库、关系型数据库。将购物车内的东西持久化存储]
明确一点:Session -> 一次会话有效(在用户不关闭浏览器的前提下,默认存在30分钟;用户关闭浏览器再次打开后,未登录用户的购物车将清空。)
解决如何用Session存储购物车内信息。
第一个难点:那么如何准确区分不同的商品? (自然是商品ID)
第二个难点:那么如何准确标记一个进入购物车的商品? (只有商品ID是不行的)
商品ID,通过商品ID,我们可以查询到商品详情。(价格、名称等等)
同一商品的购买数量。这一信息是不存在于商品详情的。【重点,不能忽略】
这两个信息必须存储。于是我想到Map(两个原因)。
①:存储两个值
Map<String,Integer> map=new HashMap<String,Integer>();
key存储 商品ID, value存储 商品数量(购买数量)
②:保证商品ID(键)的唯一性
Map的特点:键唯一,值可以重复。我们以商品ID为键,这样就可以保证商品的唯一性。
我们再将map存入Session中就可以了。
当用户添加商品时,只需先从Session中取出map,迭代遍历判断key是否已经存在,若存在取value值加1;若不存在则将商品ID作为key,value数量默认为1,Put进map。
当用户(未登录)查看购物车时,只需从Session中取商品ID和数量,就可以显示购车内商品的详细信息,计算购物车内的商品总价格。
这种存储方式简化了添加商品进入购物车和删除购物车里商品的操作。但是却不得不再次封装一个Map对象将购物车详情页面的信息存储进去,以供购物车展示页面显示数据。
(这是我第一次考虑的存储方案,写到查看购物车详情页面才发现不合理之处。)
改进版本:
Map <String,Goods> map=new HashMap<String,Goods> ();
Key ->商品id
Value->购物车页面需要展示的商品详情(商品名、商品ID、商品数量、商品价格等)
核心:value值维护不再是一个商品数量,而是我们封装的模拟购物车实体对象
JSP页面的效果图
源代码:添加商品的Action
//添加商品Action public String addToCar() throws Exception{ //尝试从Session取出购物车 ValueStack vs=ActionContext.getContext().getValueStack(); Map<String,Goods> car=(Map<String,Goods>)vs.findValue("#session.car"); BookService service=new BookServiceImpl(); if(car==null){ //判断购物车是否存在 //不存在则创建,将用户添加的商品加入Map 存入Session Map<String,Goods> map=new HashMap<String,Goods>(); //查询数据将商品详情查询出出来,把我们感兴趣的属性封装到Goods实体中 Book book=service.getSingleBook(bookid); Goods goods=new Goods(); goods.setBookid(book.getBookid()); goods.setCount(1); goods.setPrice(book.getPrice()); goods.setCurrentprice(book.getCurrentprice()); goods.setProductname(book.getProductname()); map.put(bookid, goods); vs.setValue("#session.car", map); }else{ //判断用户添加的商品是否已经存在于购物车里 //若存在根据key取出Goods->修改商品选购的数量 //不存在则是首次添加,数量默认为1 ,将商品Put入map if(car.containsKey(bookid)){ Goods product=car.get(bookid); product.setCount(product.getCount()+1); }else{ Book book=service.getSingleBook(bookid); Goods goods=new Goods(); goods.setBookid(book.getBookid()); goods.setCount(1); goods.setPrice(book.getPrice()); goods.setCurrentprice(book.getCurrentprice()); goods.setProductname(book.getProductname()); car.put(bookid, goods); } } return Action.SUCCESS; }
-
-
流程审批表设计
2020-05-19 14:39:01这段时间公司做一个项目,需要设计一套流程,所以设计了以下的流程方案。 流程表workflow 流程节点表workflow_node 流程线表workflow_link 审批人员表approver 财务申请表fina... -
商城数据库表设计介绍
2020-07-18 23:21:48使用PowerDesigner对数据表以及他们之间的关系进行了粗略的设计得出了如下E-R图: 1.用户表 2. 商品表 3.商品类别表 4.购物车表 5. 订单表 6.订单明细表 7.支付信息表 8.收货信息表... -
mysql 树形结构表设计与优化
2021-09-20 09:15:15稍稍有点开发和表结构设计经验的同学,设计出这样一张表,应该很容易,只需要在depart表中,添加一个pid/字段即可满足要求,参考下表: CREATE TABLE `depart` ( `depart_id` varchar(32) NOT NULL COMMENT '部门ID... -
数据表设计与mysql入门(一)
2018-07-18 22:17:33这篇文章旨在带领新进的同学对基本的数据库知识、数据表逻辑设计、mysql数据库有一个基本了解,以便快速上手实际业务。 -
数据库订单表设计
2020-12-18 14:40:07订单表 (order) |-- 自动编号(order_id, 自增长主键) |-- 订单单号(order_no, 唯一值,供客户查询) |-- 商店编号(shop_id, 商店表自动编号) |-- 订单状态 (order_status,未付款,已付款,已发货,已签收,退货申请... -
MongoDB 表设计
2018-08-17 11:28:2612月12日上午,TJ在开源中国的年终盛典会上分享了文档模型设计的进阶技巧,就让我们来回顾一下吧: ——————————————————————————————————————————————————————... -
数仓建模—事实表和维度表设计规范
2021-12-06 22:59:29事实表和维度表设计规范 前面我们介绍了数仓建模—数仓开发规范,我们也介绍了事实表和维度表你可以参考数仓建模—事实表和数仓建模—维度表,但是我们的数仓设计其实很大一部分就是在设计维度表和事实表,所以我们... -
一键导出PostgreSQL数据库表设计为word文档
2021-06-09 18:45:36项目开始时,数据库表设计是从概要设计到详细设计,再到数据库中的表结构,有一套完整的文档; 然而,随着项目的演进,需求的变更,导致数据库表结构发生了比较大的变化(加表,加字段,该类型等),对于大多数小... -
HBase表设计介绍
2017-11-28 11:14:34那么hbase的表设计就和关系数据库表设计有很大不同之处。设计hbase表需要回答下面的问题: 1.行键的结构是什么的并且要包含什么内容? 2.表有多少个列族? 3.列族中都要放什么数据? 4.每个列族中有多少... -
正交表设计法设计测试用例
2020-03-23 21:35:08概念:正交试验设计是研究多因素多水平的一种设计方法,它是根据正交性从全面试验中挑选出部分有代表性的点进行试验,这些有代表性的点具备了“均匀分散,齐整可比”的特点,正交试验设计是一种基于正交表的、高效率... -
数据库用户签到表设计
2021-01-02 14:31:161、签到表(累计签到,写定时任务每晚23:30查询最后一次签到时间是否等于今天,不等于增清空连续签到时间) user_sign 签到表 字段 数据类型 长度 注释 id int 11 主键 user_id int ... -
权限管理数据表设计
2020-10-27 20:00:15权限管理数据表的设计 需要五张表,分别为(此处以MySQL为例) 用户表 t_user 角色表 t_role 权限表 t_permission 用户角色表 t_user_rolet_user_role 角色权限表 t_role_... -
优惠券表设计
2019-01-22 11:06:26CREATE TABLE `coupon` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `title` varchar(64) NOT NULL COMMENT '优惠券标题(有图片则显示...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠券表'; -
常用字典表设计
2019-07-27 13:51:32数据库字典表设计,支撑多层字典表。 CREATE TABLE `sys_dict` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `pid` int(11) DEFAULT '0' COMMENT ' 父ID ', `dataType` varchar(50) NOT NULL COMMENT... -
关于国产数据库表设计PDManer工具的使用
2022-04-20 22:49:08关于国产数据库表设计PDManer工具的使用一 PDManer简介二 PDManer的使用1 简单案列说明1 界面说明2 内容说明1 数据表2 视图3 关系图4 数据字典 一直以来,对于数据库的表设计,都是采用PowerDesigner工具. 最近发现一... -
数据库的用户信息表设计
2021-05-12 19:06:26我参考该博文设计:浅谈数据库用户表结构设计 只是有些地方我实践之后需要补充一下: user表字段: user_auth表: 要补充说明的是,“nickname”是唯一不重复的,所以他可以作为主键,这样有好处: 减少一... -
字典表设计
2018-08-09 09:37:44本篇的字典表主要针对前端展示多层目录数据的一个设计。 相信我们在金山毒霸上看电影时,会有很多电影分类,可以更具国家的不同,年限的不同亦或电影种类的不同去查询自己想要看的电影类型,那么这么多分类在设计... -
简易数字电压表设计 单片机 仿真 ADC0809
2020-11-04 18:47:42课程设计题二十九:简易数字电压表设计 设计要求: 1. 利用ADC0809设计一个简易数字电压表,要求可以测量0~5V之间8路输入电压 值,电压值由4位LED数码管显示,并在数码管上轮流显示或单路选择显示; 2. 测量... -
单片机秒表设计
2012-07-26 12:30:11单片机秒表设计 2010 年 12 月 10 日 目录 1 课程设计的目的和任务 1.1 单片机秒表课程设计的概述 1.2 课程设计思路及描述 1.3 课程设计任务和要求 2 硬件与软件的设计流程 2.1 系统硬件方案设计 2.2 软件方案设计 3... -
带过期时间的积分系统表设计
2020-04-08 09:52:55积分表设计 表结构设计 积分记录表 integral_record 此表对应的是业务操作引起的积分变化记录 字段名 类型 长度 说明 id bigint 20 主键id type smallint 11 积分记录类型 original_id varchar 40 原始... -
电商之商品表设计
2020-08-11 14:49:37今天就来巴拉主要的五张表供大家参考,有不足的地方可以互相讨论。先上一张图,然后细说 1.商品表 几乎大多数表都围绕商品表来转。...与商品表,规格表,sku表关联,一对多关系,多对多关系 ... -
数据库多级联动表设计
2018-03-06 17:23:52设计社区数据库时,遇到有关 物业公司 社区 楼栋 单元 房屋 的基础表设计。 这几张表之间都是包含关系,故设计为此形式。有点类似于链表。为了叙述方便,暂且称大的概念为高级表,小的概念为低级表。低级表关系着... -
(二)购物商城数据库设计-商品表设计
2018-05-29 20:54:08大家好,今天我们来设计一下购物商城的商品表。 我们的目标是表结构能够满足下面这张图的搜索: 在设计表之前,我们先来了解下商品中的一个概念:SPU和SKU SPU SPU(Standard Product Unit):标准化产品单元。... -
数据库表设计(一)——基本原则和概念
2019-02-25 11:05:43数据库表设计的技巧 1. 原始单据与实体之间的关系 可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对 应且只对应一个实体。在特殊情况下,它们可能是一对多或多对一的... -
电商-支付相关表的表设计
2018-10-09 18:01:02基础元素 支付关系 ...支付流水表结构 联合订单主表表结构 联合支付订单子表表结构 主订单表 子订单表 子订单分账表 子订单优惠减免表 ...