一点小感悟

从JPA和MyBatis的开发过程来看其设计思路区别就有点大了

MyBatis的工具主要都是面向现有表结构,然后才有对象的设计方式,其最佳实践环境应是在有专门的DBA帮助完成表设计,然后依据表设计来构建业务代码

而JPA的思路是,我先有对象,然后仅仅是分析对象之间的关系,并持久化对象,其两者的思路不一致

二者都可以应用DDD做设计,但因为分工原因,会导致其DBA的设计思路是按着最少冗余设计,而开发时候只能迁就其设计思路

而对象设计者很可能是对象的实现者,这时候,将持久化的过程交给一套方法或者规则来完成,能最大化匹配其实现业务代码的思路

不是问题的问题

做起开发发现浑身不对劲就很难受了

需求变本来就是常态,为什么品控部门必须要有确定的文档才能进行需求开发

难道开发面对变更,测试就不需要面对变更吗?

测试参与到开发来,而不应该是测试只管根据文档测试其他都不用管

目标难道不是交付合格的软件么,没有写好的文档就不能交付了么,无非就是撕逼的时候为了个责任划分而吵吵吵吗

一点都感受不到大家是为了交付或者是有共同的目标,就单纯的是为了工作而工作,能做出什么像样的东西么

还有,需求日期这种东西,不应该是开发人员预估个时间,然后才能是对外宣传的呢,怎么能说是随口说出去,然后层层下压呢

最少也应该在开发给出一个不算太靠谱的时间之后进行压缩吧

一个稳定的软件不应该是要留足开发空间么,而非只是为了收钱而交付啊

就算是为了满足生存的需求,那生存本身不应该是压缩成本吗?没有做好亏一段时间的准备,难道创业是不需要成本的?

这些看起来不像是问题而像是控诉的话题,应该就是所谓的问题了吧

这些问题问了自己好久,得不到答案

JPA+Springboot小限制

JPA+Springboot小限制

限制一

  • 通过JPA(Hibernate)在MySQL环境下自动建表要注意,JPA的默认字段长度为255,而如果数据库中存在索引不能超过768bytes的限制,则需要考虑自己手工维护表

  • 限制来源于JPA或默认新建一个记录序号的表,此表的主键为Varchar(255),且程序员无法修改,只要编码长度是超过三字节编码,则建表失败

  • 可以通过SpringData/Hibernate自动调用classpath下特定的sql文件特性绕过

  • 但如果抛弃了由hibernate做表维护而转为程序员手工维护表关系则不如采用mybatis方案更为直接

限制二

  • hibernate自动建表时候,是没法设置在哪个schame下或者哪个catalog下建表,如果DB的其他schame有同名表,则建表会失败

  • 此限制来源于Hibernate做数据库元数据查询时候只使用了table字段,且我无法找到可以让查询元数据时候指定schame+table联合确定table是否存在的办法

  • 此限制的场景会在Junit测试时候如果想做schame隔离是无法做到的

  • 一并发现此问题的还有activiti的自动建表也是

MariaDB单机双实例配置说明

MariaDB单机双实例配置说明

安装

  • 推荐操作系统,CentOS7, yum install mariadb-server

配置

以下配置均是以CentOS7来进行

  • 在/etc/my.cnf.d/建立两个文件3306.cnf,3307.cnf
[mysqld0]
basedir                               = /usr                        
character_set_server                  = utf8                        
collation_server                      = utf8_unicode_ci             
datadir                               = /opt/data0                  
default_storage_engine                = InnoDB                      
default_time_zone                     = '+8:00'                     
event_scheduler                       = 1                           
init_connect                          = 'set names utf8;'           
lower_case_table_names                = 1                           
memlock                               = 0                           
pid_file                              = /opt/data0/mysql.pid        
port                                  = 3306                        
skip_external_locking                 = 1                           
socket                                = /opt/data0/mysql.sock       
tmpdir                                = /dev/shm                    
user                                  = mysql                       


general_log                           = 0                           
general_log_file                      = /opt/data0/general_query.log
log_output                            = FILE                        
log_error                             = /opt/data0/mysqld.log       
log_warnings                          = 2                           
log_queries_not_using_indexes         = 1                           
log_slow_admin_statements             = 1                           
log_slow_verbosity                    = query_plan                  
long_query_time                       = 1                           
min_examined_row_limit                = 100                         
slow_query_log                        = 1                           
slow_query_log_file                   = /opt/data0/slow_query.log   


gtid_strict_mode                      = 1                           
transaction_isolation                 = READ-COMMITTED              


innodb_data_home_dir                  = /opt/data0                  
innodb_log_group_home_dir             = /opt/data0                  


innodb_undo_directory                 = /opt/data0                  


binlog_cache_size                     = 4M                          
binlog_checksum                       = crc32                       
binlog_format                         = row                         
expire_logs_days                      = 30                          
log_bin                               = /opt/data0/mysql-bin        
max_binlog_cache_size                 = 128M                        
max_binlog_size                       = 1G                          
sync_binlog                           = 1                           


max_relay_log_size                    = 1G                          
relay_log                             = /opt/data0/relay-bin        
relay_log_index                       = /opt/data0/relay-bin.index  
relay_log_info_file                   = relay-log.info              
relay_log_purge                       = 1                           
relay_log_recovery                    = 1                           
sync_relay_log                        = 10000                       


  • 创建数据库文件,其中应区分06,07所在的文件夹
mysql_install_db --datadir=/opt/data0 --user=mysql --force --defaults-file=/etc/my.cnf.d/3306.cnf
mysql_install_db --datadir=/opt/data1 --user=mysql --force --defaults-file=/etc/my.cnf.d/3307.cnf
  • 创建Systemd控制文件
mkdir /etc/systemd/system/[email protected]



#
# /etc/systemd/system/[email protected]
#

[Unit]
Description=Multi-Instance MariaDB Service
After=network.target
After=syslog.target


[Install]
WantedBy=multi-user.target


[Service]
User=mysql
Group=mysql
Type=forking
# true is needed for the ExecStartPre
PermissionsStartOnly=true
ExecStartPre=/usr/bin/mkdir -p /opt/data%i
ExecStartPre=/usr/bin/chown mysql:mysql /opt/data%i -R
ExecStart=/usr/bin/mysqld_multi start %i
ExecStop=/usr/bin/mysqld_multi stop %i
LimitNOFILE=102400
Restart=on-failure
RestartPreventExitStatus=1
PrivateTmp=true
  • 启动服务
systemctl start mariadb@{0,1}
  • 配置服务器账户密码
mysqladmin -hlocalhost -P3306 --protocol=tcp password abc
mysqladmin -hlocalhost -P3307 --protocol=tcp password abc
  • 本地登陆数据库,此时与正常操作数据库一致
mysql -P3306 --protocol=tcp -uroot -pabc
mysql -P3307 --protocol=tcp -uroot -pabc

什么是锁(粗版)

什么是锁

  • 锁是一种特殊的数据结构+调度器,这个数据结构的维护通常是由操作系统维护的,当然如果语言自身也实现了线程管理的话,一般也会自己实现锁结构

  • 本质上锁是如下的一个数据结构

struct waitList{
    *ExecuteNode root;
    *ExecuteNode next;
}

每当一个进程/线程开始执行的时候,cpu会读取要操作的内存结构,其head中有一个字段是CPU保证当前过程不能发生软中断(基于语言虚拟机有相似的过程),当CPU发现这个字段置为1/true时候,CPU应要忽略其他请求,直到把该位置恢复为false,然后随机唤醒一个进程/线程(ExecuteNode)

基于以上事实在调度过程中,就能发现和我们在Java中使用锁的表现一致,但CPU不能保证其他进程/线程不能修改该标志位(系统或者语言调度器保证)

从业务到代码

从业务到代码

目的

所以我写这篇文章的目的很简单,为了给各位写代码的新手带来一点不同的视角

前言

代码不过是业务的表示,当你把代码看作一门语言(这里语言不是指java/C这些编码语言的意思,而是普适意义上的language)的时候,我们,作为编码人员,更多的是像是陈述事实一样,将业务陈述,然后通过代码落地。

问题

在新手中,经常有这么几个疑问
1. 这么写有什么用
2. 明明我一个函数就可以把所有工作做完,为什么要拆开来
3. 这个工具屌不屌
4. 这个语言如何如何

看法

结论

  1. 如果不能跳出只是代码堆砌工的眼界,那么永远不知道有什么用
  2. 拆分是为了将函数目的单纯化
  3. 工具只是生产力工具的一部分,不能说锤子一定比机床好
  4. 同样,都是锤子,不能说A类型的锤子一定好与B类型锤子

理由

不知道各位呆过几个公司,每家公司给人的感觉都是不一样的。提几个点让大家想一想
1. 有问题能不能找到确定的一个人能解决确定的一类问题
2. 出了事情,背锅是平摊锅还是说加权锅,还是看老板心情分
3. 简单点,如果电脑坏了要换,上报后多久能搞定
4. 工作的成果是否能够量化
让我们来换到代码的视角来看这个问题
1. 出了bug能否容易精确定位在什么类
2. 出错了,整个系统的其他部分是否依旧稳定
3. 某个部件如果因为市场因素全部重做,重做的成本是低是高
可能换到编码视角,各位一定能够简单的提出一些熟悉的概念,比如鲁棒性,比如重构,etc。但是不知道各位有没有做过思考,亦即这些概念套用到非编码视角的时候也是通用的,更或者说,这概念都是从其他行业或者领域移植到软件开发领域,毕竟软件开发到今天真正普及也就四十年?

所以

  1. 向业务学习,好的代码应该是和业务一致的
  2. 因为你的代码就是业务时候,业务的快速变动就能变成了代码变动
  3. 而一个公司的业务变动不可能连根本逻辑都改变了
  4. 加班就会少了
  5. 与业务人员沟通也会顺畅了

论目标

论目标

信息安全中经常需要验证一条信息,其做法是,定义一个函数f,f作用于信息得到一条简短的信息,即f(g(x),c) == f(h(x),c) == F 其中,g(x)是发送端的信息,h(x)是接收端信息,c是双方约定的一个信息,F是计算的结果,如果经过f作用后,得到的结果是相同,那么就可以认为,这条信息是真实的。

原理上这东西很简单,也非常实用,那么对应到不是信息安全领域中呢。

举个例子:

  1. 领导说,我们的当前目标是,在合法的范围内挣得一百万
  2. 所有人开始提方案
  3. 每个人根据目标,代入自己的方案,是否满足条件
  4. 如果所有方案都不能满足,和领导讨论是否tradeoff一部分要求

现在我经常性面对的问题是:

  1. 负责的人,提不出一个目标
  2. 执行的人,总能找到各种似是而非的理由,在一定条件下这又是成立的

举个例子:

  1. 提出做一个系统
  2. a提出A方案,方案A优点是1,2,3,4,5
  3. 但是大家都不同意,觉得这些操作相当冗余

那么这时候谁对谁错,作为一个负责人,这时候就要想到这么一个事情,这个事情的目标是什么:

  • A1,A2,A3,A4

那么上面的对错就容易判断了,如果a提出的1,2,3,4,5不满足A1,A2,A3,A4,一律是要砍掉,只保留能够满足A1,A2,A3,A4的设计

对应出来我们就得到了类似的和信息安全领域的结果即
f(1,2,3,4,5) == A1,A2,A3,A4
如此我们总能够简单的验证我们的工作成果,而不是陷入无止境的扯皮中

软件开发的三个层级

软件开发的三个层级

  1. 核心领域模型:该层级与实际业务应该一致,举个例子:我们在财务系统中有依据收款信息开票的选项,那么我们的领域模型就应该有个类叫做收款信息,它有一个动作叫做开票
  2. 领域模型对外的交互:到了这个层级,会有很多开源代码库帮助我们,例如我们需要发票对象需要对外暴露,那么通过mvc的形式,可以对外暴露发票的信息,或者是发票需要做持久化,有各种各样的Persistence的工具帮助我们
  3. 底层:例如数据库字段长度,报文的payload等等,到了这一层,应该是一个很具体的问题,具体来说,在这一层级不应该有任何业务相关的知识,而完全是计算机领域的知识

通过对软件开发进行划分层级,可以帮助我们开发过程中渐进式开发
比如我要关注业务代码的时候,就纯粹的业务思维,如果我是底层的开发,我只需要关注计算机领域的知识

为什么要这么划分

我在开发中观察到,大部分新手或者开发了很久的人,在写代码的时候,过早的关注到细节问题,比如写开发票的时候就开始关注到发票数据库字段长度,就直接陷进了细节中,具体对外表现出来就是业务实现统统一个函数打包,甚至还有全局变量等等
渐进式的分层开发有助于我们专注于一件事件,只有专注了,那么开发中错误才能减少

供应链系统开发(一)

供应链系统开发(一)

一些概念

  1. 四流
    1. 商流(以下三个角色通过商品物权转移就是商流)
      • 原材料商
      • 材料加工商
      • 成品销售商
    2. 物流
      • 商品的包装
      • 商品的运输
      • 商品的存储
    3. 信息流
      • 运输单据
      • 发货单据
      • 交易单据
      • 各种各样是的整个供应链能运行的单据成为信息流
    4. 资金流
      • 货款结算
  2. 四流合一
    1. 四流间联动
      1. 以物流与信息流相互影响为例
        1. 商品运输过程中若出现损坏
        2. 需要在运输单据中记录一条损坏商品的信息
        3. 当这个运输单据到达货主处,货主要根据单据来验收,从而得到应收数量减少

    那么四流合一就很明白了,四流的内容能够完全反映出整个供应链链条的运作
    即四流可以与一条供应链等价

  3. 为了实现四流合一,又提出了三库的说法

    1. 供应商库
    2. 采购商库
    3. 商品库
  4. 为了提高整个供应链商品的运转效率,这时候需要有个服务商使得三库的链条运转顺畅得到了以下
    1. 服务商库
      1. 资金
      2. 物流

供应链系统开发(二)

供应链系统开发(二)

基本要素(确定调研方向,即确定参与系统的角色)

  • 上游供货商
  • 下游采购商
  • 资金提供商
  • 商品
  • 信息掮客/平台

以上是构成现代供应链的几个基础要素

要达成交易需要的几个步骤(业务流程分析)

  1. 下游采购商提出采购商品
  2. 下游采购商提出用款需求
  3. 下游采购商提出指定供货商/或者不关心
  4. 平台发布信息
  5. 平台撮合商品
  6. 平台撮合资金需求
  7. 平台撮合供应商

如下故事可以将场景闭环(业务场景分析)

  1. 客户向平台提出采购需求
  2. 平台分析需求,若仅有采购需求(直营)
    1. 从自有平台信息中寻找可撮合的交易对象
    2. 若存在则撮合交易
      1. 平台与供货商确定配送方式
      2. 客户付款
      3. 平台确认付款后,通知/直接将货物配送到指定地点
      4. 平台通知客户提货
      5. 客户通知平台提货完成
      6. 三方票据结算
      7. 故事结束
    3. 若不存在则
      1. 与客户沟通让其提供参考供应商
      2. 平台考察客户提供供应商,若准入则添加供应商至平台信息返回故事2.1
      3. 若不允许准入,则故事结束
  3. 若还有融资需求(垫资代采业务)
    1. 从自有平台信息中寻找可撮合的资金提供商
      1. 若存在则撮合交易
        1. 平台与资金提供商确定资金占用费用
        2. 平台与资金提供商确定供应商是否准入
        3. 平台与资金提供商确定商品是否准入
        4. 平台向客户返回费用协商结果及供应商,商品准入结果(项目信息)
        5. 若客户同意项目结果,则签署合同返回故事2.1
        6. 若客户不同意则返回3.1继续执行,直到所有资金商都无法满足客户需求
    2. 若无,则故事结束

到了这一步则系统的概设完成

参与角色与参与要素(业务场景涉及的表单、文件、票据等分析)

对照直营故事来细化
1. 询价单
2. 供应商目录
3. 商品目录
4. 采购合同
5. 销售合同
6. 采购订单
7. 销售订单
8. 发票
9. 通知
对照垫资代采故事细化
1. 资金提供商目录
2. 项目信息
3. 垫资代采合同
4. 与客户确认单据
按照此思路逐项分析,直至形成一个业务需求文档