ddd数据是怎么存储的
㈠ 什么叫数据的逻辑结构 什么叫数据的存储结构
一、数据的逻辑结构。
系统的逻辑结构是从思想的角度上对系统分类,把系统分成若干个逻辑单元,不同逻辑单元分别实现自己的功能。数据的逻辑结构是对数据之间关系的描述,有时就把逻辑结构简称为数据结构,数据的逻辑结构分为以下四种:
1、集合结构:集合结构的集合中任何两个数据元素之间都没有逻辑关系,组织形式松散。
2、线性结构:数据结构中线性结构指的是数据元素之间存在着“一对一”的线性关系的数据结构。
3、树状结构:树状结构是一个或多个节点的有限集合。
4、网络结构:网络结构是指通信系统的整体设计,它为网络硬件、软件、协议、存取控制和拓扑提供标准。
二、数据的存储结构。
数据的存储结构是指数据的逻辑结构在计算机中的表示。数据的存储结构分为顺序存储结构和链接存储结构两种。
1、顺序存储结构:顺序存储方法它是把逻辑上相邻的结点存储在物理位置相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现,由此得到的存储表示称为顺序存储结构。
2、链接存储结构:链接存储方法它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示的。由此得到的存储表示称为链式存储结构,链式存储结构通常借助于程序设计语言中的指针类型来实现。
(1)ddd数据是怎么存储的扩展阅读:
顺序储存结构的原理
在顺序存储中,每个存储空间含有所存元素本身的信息,元素之间的逻辑关系是通过数组下标位置简单计算出来的线性表的顺序存储,若一个元素存储在对应数组中的下标位置为i,则它的前驱元素在对应数组中的下标位置为i-1,它的后继元素在对应数组中的下标位置为i+1。
㈡ java程序中数据的储存方法有哪些
java程序中数据储存方法如下:
一种是栈内存,另一种是堆内存
(1)在函数中定义的基本类型变量(即基本类型的局部变量)和对象的引用变量(即对象的变量名)都在函数的栈内存中分配;
(2)堆内存用来存放由new创建的对象和数组以及对象的实例变量(即全局变量)。
在函数(代码块)中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间;
在堆中分配的内存由java虚拟机的自动垃圾回收器来管理
堆和栈的优缺点
堆的优势是可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的。
缺点就是要在运行时动态分配内存,存取速度较慢;
栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。
㈢ 浮点数的存储结构是怎样的
浮点数存储时有符号位,阶数位和尾数三部分组成。
解:最大的正数= (1-2 ^ (7))x 2 ^ (2 ^ (3) - 1) = (1-2 ^ (7)) x 2 ^(7) = 127,规则最小的正数=2×2^(-1)(或2^(3))x^2=2-1=2^(8)(9)=1/512。
最明显的绝对值是-1*2^(2^3-1)也就是-1*2^7,也就是-128。
(3)ddd数据是怎么存储的扩展阅读:
浮点数A由两个数字m和e表示:A=m*b^e。在任何这样的系统中,我们选择基数b(计数系统的基础)和精度p(要存储的比特数)。
M(即尾数)的形状陆雹为±d.dd…DDD的p位(每个位早尘帆是0和b-1之间的整数,包括0和b-1)。如果m的第一个数字是一个非零整数,那么m就被归一化了兄纤。
一些描述使用单个符号位(s表示+或-)表示加号或减号,因此m必须是正数。E是a的指数。
结构:
表示计算机中的一个浮点数,其结构如下:
尾数部分(定点小数)指令码部分(定点整数)
㈣ 浮点型数据在内存中实际的存放形式(储存形式)
浮点型数据在内存中存储不是按补码形式,是按阶码的方式存储,所以虽然int和float都是占用了4个字节,如果开始存的是int型数据,比如是个25,那么用浮点的方式输出就不是25.0,也许就变的面目全非。
你可以用共用体的方式验证一下。在公用体中定义一个整形成员变量和一个浮点型成员变量,给整形赋值25,输出浮点成员变量,你就知道了。
㈤ 科力达rtk手部5,点测量数据,怎么保存
科力达rtk手部5,点测量数据保存方法为:
1、打开RTK手簿昌肢察开关打开工饥让程之星。
2、找到工程并点击进入文件导入导出,然后点击文件导出。
3、进入导出界面,选择导出类型,一般习惯使用南方CASS的,A会选择dat类型,B经纬度默认,C选择测量文件(所需的数据保存的文件)。
4、找到上一步的测量文件以后,点击确定。
5、点击成果文件,最好是新建一个文件,方便查询,这儿我新建的ddd文件名作为演示。
6、测量文件和成果文件选择好了以后,点击导出。
7、此时,界面现实导出成功,并且现实你所保存耐茄的文件目标地址,你需要记住这个地址,方便电脑上查找。
㈥ DDD领域驱动设计-DDD概览
# DDD概览
## 启迪
领域可以理解为业务,领域专家就是对业务很了解的人。
限界上下文也就是微服务的边界,也可以理解为微服务,一个限界上下文=一个微服务。
个人理解领域驱动设计就是微服务驱动设计,从战略上先进行微服务的划分,从战术上针对某个微服务进行领域模型的设计也就是业务模型的设计。
领域模型包括:
- 实体
- 值对象
- 聚合
- 领域服务
- 领域事件
- 资源库
- 应用服务
## 什么是领域驱动设计?
理解领域驱动设计是什么之前,我们先来理解下什么是领域?
领域可以理解为业务,领域专家就是对业务很了解的人。
领域驱动设计的核心就是和最了解业务的人也就是领域专家一起通过领域建模的方式去设计我们的软件程序。
- 那么领域如何驱动设计?或者说业务如何驱动设计?
传统开发过程我们都是基于面向数据开发,拿到产品原型脑海里想着都是应该创建哪些表和哪些字段才能满足需求。
而领域驱动设计开发过程是让拍誉蚂我们基虚瞎于面向业务开发、面向领域模型开发。
领域模型的核心是通过承载和保存领域知识,并通过模型与代码的映射将这些领域知识保存在程序代码中,
在传统开发中,当业务被转换为一张张数据表时,丢失最多的就是领域知识(领域知识也就是我们在模型中定义的一些业务逻辑行为)。
面向领域模型开发的优点:
- 存储方便,统一使用JSON进行存储。
> 例:
> 订单领域包含基础信息、商品信息、金额信息、支付信息等包含订单全生命周期的子域,
> 对于传统面向数据的开发模式我们需要创建N张表进行存储订单的信息,但是面向领域开发时我们
> 可以通过利用nosql数据库(mongo、es等)进行保存整个订单域的信息,提高查询、更新效率,简化代码
- 复用性高,引用某个领域模型,就可以拥有该领域模型的所有行为。
> 例:
> 基于微服务架构下,某个电商应用需要一个判断某个订单是否是在线支付订单的逻辑时,
> 对于传统的开发模式我们需要调用订单中心的服务查询订单信息,然后写一个判断是否在线支付订单的方法。
> 如果有多个应用都需要这个逻辑时,每个应该都需要重复写相同的方法。
> 但面向领域开发时,只需要引用订单中心的jar包,然后统一调用订单领域内的方法即可。
> 这样就实现了业务的高内聚
## DDD可以做什么
DDD主要分为两个部分,战略设计与战术设计
- 战略设计
- 围绕微服务拆分
- 战术设计
- 微服务内部设计
## DDD怎么做
- 战略设计
- 和领域专家一起通过(过往经验、事物联系、事件风暴等)划分【限界上下文】
> 限界上下文也就是微服务的边界,也可以理解为微服务。
> 一个限界上下文=一个微服务
- 战术设计
- 开发人员通过(领域模型)保存【领域知识】
> 领域知识也就是事物(角色)、行为(规则)和关系
>
## DDD领域模型
领域模型包含什么?
- 实体
> 具有唯一标识,包含着业务知识的【充血模型】对象,用于对唯一性事物进行建模。
> 例袭埋:
> ```
> public class Order {
> private long orderId;
> private OrderAmount amount;
> private List item;
> }
> ```
- 值对象
> 生成后即不可变对象,通常作为实体的属性,用于描述领域中的事物的某种特征。
> 例:
> ```
> public class OrderItem {
> private long orderId;
> private String proctCode;
> private String proctName;
> }
> ```
- 聚合
> 将实体和值对象在一致性边界之内组成聚合,使用聚合划分微服务(限界上下文)内部的边界
- 领域服务
> 分担实体的功能,承接部分业务逻辑,做一些实体不变处理的业务流程。不是必须的
> 主要承接内部领域服务调用和外部微服务调用,及一些聚合业务逻辑处理。
> 例:
> ```
> @Service
> public class ShoppingcartDomainService {
> private final ShoppingcartRepository shoppingcartRepository;
> private final ProctFacade proctFacade;
> private final UserFacade userFacade;
> private final PromotionFacade promotionFacade;
>
>
> // 1.查询购物车信息
> ShoppingcartDO entity = shoppingcartRepository.loadShoppingcart(userId);
>
> // 2.调用【用户中心】服务查询用户信息
> User user = userFacade.getUser(userId);
>
> // 3.调用【商品中心】服务查询商品信息
> Proct proct = proctFacade.getProct(proctCode);
>
> // 4.调用【活动中心】服务查询活动信息
> Promotion promotion = promotionFacade.getPromotionByProctCode(proctCode);
>
> // 5.创建购物车实体
> Shoppingcart shoppingcart = new Shoppingcart(entity.getId, user, proct, promotion);
>
> // 6.购物车按活动分组
> shoppingcart.groupby4Promotion();
> }
> ```
>
>
- 领域事件
> 表示领域中发生的事情,通过领域事件可以实现本地微服务(限界上下文)内的信息同步,同时也可以实现对外部系统的解耦
- 资源库
> 保存聚合的地方,将聚合实例存放在资源库(Repository)中,之后再通过该资源库来获取相同的实例。
>
- 应用服务
> 应用服务负责流程编排,它将要实现的功能委托给一个或多个领域服务来实现,
> 本身只负责处理业务用例的执行顺序以及结果的拼装同时也可以在应用服务做些权限验证等工作。
> ![](images/application-service.png)
㈦ 领域驱动设计(DDD)实践之路(第二篇)
在领域驱动里面,infrastructure作为基础设施,是提供技术细节的模块。需要强调的是,很多人会误以为infrastructure就是传统的DAO层,其实infrastructure包括但氏明闭不限于DAO层,比如文件处理,三方调用,使用缓存,发送异步消息等具体的技术细节实现都存在于infrastructure层。那么技术细节是什么呢。在我们看来,技术细节包含以下特征
案例1:我们的实体需要持久化(存储),所以我们需要提供存储的实现。领域层的repository.save等方法提供了持久化接口约定,对于infrastructure来说,如何实现这个方法的代码,就是技术细节。那么我们如何实现这个过程呢?自然是选择缓存,OSS存或者数据库存。如果选择数据库,则进而需要选择orm框架,配置...,实现repository.save的接口,这些都属于持歼裂久化所需的技术细节代码。
案例2:我们的槐扒应用需要导出资产包相关的excel形式数据,那么当导出资产包数据时,文件领域模块提供了导出的统一接口,资产领域模块提供了资产包的适配接口,而导出excel的代码需要使用easyExcel或者POI等第三方框架,属于技术细节代码。
案例3: 接案例2,为了实现导出时所需的excel排版格式,排版本身的格式与业务有关,比如在我们的业务场景下,我们导出调解明细(我们项目特定的一个领域模型)的时候,只需要按照常见的导出方式即可,而导出资产明细(我们项目特定的一个领域模型)则需要解析拼接所有的动态数据列,合并显示每条数据不同的动态列,而这一切是由业务决定的。根据业务不同有不同的排版要求这一点体现了资产域需要提供文件域的导出策略,调解域也需要实现文件域的导出策略。这些都属于描述业务信息的约定,而这些约定的具体实现比如怎么把实体的那一个属性映射到excel的哪一行哪一列,则属于技术细节。这种区分方式显性化了业务的概念,同时又将实现放在了基础设施层,提供了一定的解耦性。
说完了infrastructure的技术细节的定义,我们接下来聊几个在采用DDD研发模式下,infrastructure层开发过程中经常会遇到的一些问题及我们的解决方案。
为了让业务逻辑和代码实现解耦,在repository的约定中,我们通常用“save保存”代替我们通常说的“insert(插入)“,”update(更新)”这样的技术术语,以屏蔽技术细节。这样带来的一个副作用是,在save时就需要根据策略判断调用insert还是update,我们使用的策略是根据id是否是空决定,即我们所有的实体对象都有一个属性,类型为Id类的子类,id对象的属性(数据库里面实际存放的id值)可能为null,但是id对象,本身不会为null,根据这个对象可以判断当前实体id是否为空。
对于聚合场景,子实体是需要知道聚合根的id的,因为在存储到数据库时可能需要以外键的方式存储对象间的映射关系。
然而,在具体实现中,我们认为,实体之间的对象关系才是标识两个实体之间关系的方式,而不是id,所以生成实体时,先通过对象引用关联对象,表明聚合和实体之间的关系,在保存到数据库的时候,通过实体生成数据库映射类的时候就可以知道当前数据的id是否为空,同时又能知道当前数据之间的关系。
对象之间的关系在1:1聚合保存的时候可能体现不明显,但是当1:N或者N:N批量保存聚合的时候,作用就比较明显了。在我们的系统中发起调解业务就需要批量保存调解批次。代码如下(欢迎吐槽,拥抱进步)
通过这种方式就解决了批量插入不能返回id,同时又能继续复用id.isNew()判断是否为新数据的方式(这里我们没有创建entity基类,所以判断放在了Id上)。
以上方法提供了批量保存时如何区分是新增还是更新。下面我们来谈谈我们项目内提供的插入和更新模板代码。
对于领域来说,save是基本的保存代码。方法传入的参数往往是一个存在于内存中的聚合根对象,有时包含全量的子实体,VO和全量的字段,而在插入场景,对批量请求我们希望支持批量插入,减少对数据库的IO频率,在更新场景下,我们希望减少update时的更新字段的数量(只更新需要更新的字段),这有助于减少数据库IO次数、binlog大小和mysql数据库索引变更带来的开销,所以是非常有必要的。因此对于infrastructure来说,可以提供统一的定制化模板方便repository定制化更新字段的方法快速实现。
由于我们的系统使用的是mybatisplus的ORM方案,所以我们根据api和mysql的批量语句开关提供了一个批量插入和批量更新的Mapper基类,其中insertBatchSomColumn是mybatisplus自带的,updateBatchById则是我们实现的,文档链接如下https://mp.toutiao.com/profile_v4/graphic/preview?pgc_id=7062223527654916621通过这种方式可以轻松地提供定制化更新某几列的sql,减轻sql编写负担。
这一次要讲的其实就是上面提到过的excel导入导出的案例。对于我们的系统来说,具有资产域,文件域,调解域等。其中资产域、调节域等三个域需要导入导出excel。但是我们在设计的时候认为文件的操作属于文件域的概念,所以应当由文件的domain提供功能。但是很明显,具体的导入导出的策略根据数据的不同是可以变化的。所以针对这种情况,我们回归到领域驱动的实现的本质------面向对象技术来思考这个问题的优雅解法。以导入为例
代码如下
上面4份代码是domain的,最下面的是infrastructure的,这里我们只讲infrastructure的(但是我个人认为领域分层后还是需要整体考虑的,所以才会贴上domain的代码)。
这是我们对于跨域业务逻辑的处理办法。
为了保证各领域模型间的解耦,我们经常通过最轻量级的领域事件的方式实现,而不是类似metaq,msgbroker这样的异步分布式消息中间件。领域事件的发送有很多的实现方案,我们倾向于直接使用spring的功能,因为我们需要同步保证事务。但是spring的event发送需要继承ApplicationEvent而领域事件我们又希望独立于spring的event体系,所以我们通过对spring的了解发现了spring已经提供了 PayloadApplicationEvent 可以实现这种功能实现上和其他的spring的event一致,获取我们自己定义的event的方法如下
这里的getPayload()可以获取到我们放进去的领域事件TimeoutEvent
在任何系统中都会有批处理的业务。可能是批处理聚合,可能是批处理聚合内的实体类。这里说一下我之前遇到的一个帖子(jdon)上的讨论。帖子上说的是有一个排班业务,一条班表数据作为聚合存在着每日排班子实体,每日排班下又存在着排班明细子实体,当日期逐渐增加时一条排班需要加载好几年的数据用于生成聚合,而实际上则仅仅只需要计算最近几周的数据。这里存在两点问题
第一点自然不用多说,技术实现以提供业务功能为核心是我一直以来的主张。所以当数据量可能会不断增大的情况下不用加载完整自然是必须的(哪怕内存存储的下也应当尽可能少的消耗)。第二点来说帖子的一位回复者倾向于DomainService提供专门的适配方法,用于加载几周的数据。
我们的系统中存在一个有一些类似的业务。我们的系统需要每隔几分钟就运行一次批处理任务,获取所有已经过期的调解明细,并且设置为过期。调解明细属于调解批次的聚合,所以我们有同样的需求。
我们在此提供一种我们的实现,供参考。
repository的实现根据面向对象原则,仅仅提供如何查询过滤数据库数据
迭代器的实现提供了迭代职责实现
至此实现了批处理加载聚合的逻辑,同时可以提供聚合的部分加载(需要注意业务的正确性不会因为聚合的不完全加载而产生问题)。
最后总结一下