关键:
可能读写的关系扩展为能读不能写的关系;; hibernate private set 方法 ;
讲设计不能脱离具体业务,不然别人无法理解,自己也记忆不长久, 而且不能举一反三;;
一种情况:原有实体--增加isDeleted 含义, 使数据库表的实体含义增加了一种新的含义, 有效的 和 过去虚拟删除的;
楼B218 7110 15:33:31
我现在从oo角度来思考了一下:目前一个materialUnit有多个materials,这个关系绑定在Material实体中的materialUnit这个属性上;现在需要有historyMaterialsList ,这个其实是新的关系, 在ormapping的时候,想到这个关系需要绑定新的字段,Material实体需要一个新的属性来帮带这个关系(或者也可以用一个关系表来绑定)historyMaterialUnit
楼B218 7110 15:39:05
之前直接从schema入手,增加一个isHistory字段,然后期望通过where把原来的materials修改;;
直接把O关系和表结构破坏掉的原因在于:我用一个属性,绑定了两种关系;; 所以就需要第二个字段where来将两者区分开来
楼B218 7110 15:40:15
用Material上的materialUnit字段绑定了 现在的materials 和 historyMaterialsList
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "materialUnit")
@MapKey(name = "materialLayoutIndex")
@OrderBy("id")
public Map<MaterialLayoutIndex, Material> getMaterials() {
return materials;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "historyMaterialUnit")
@OrderBy("id")
public Map<Materials> gethistoryMaterials() {
return historyMaterials;
}
//这里不能以MaterialLayoutIndex作为map里;;; 因为可能会有多个,既然是1对多,那么就需要用material作为key ;;
public void setMaterials(Map<MaterialLayoutIndex, Material> materials) {
this.materials = materials;
}
第二种方案
有了OO设计之后,绕过oo设计直接改数据表,增加一个is_history 字段,然后反过来把对原有oo设计的getMaterials()增加过滤条件 @where() ;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "materialUnit")
@MapKey(name = "materialLayoutIndex")
@OrderBy("id")
@Where(clause = "is_history='1'")
public Map<MaterialLayoutIndex, Material> getMaterials() {
return materials;
}
从oo设计角度来说,这是一个bad smell ,不符合oo规范;; 主要原因在于并不是先oo,再数据库表; 而是先数据库表,再OO;;
数据库表更多的是新的类型字段, 而OO角度更多的是关系,映射为关系表或者新的外键字段;;;;
从另一个角度来说,获得历史数据我并不期望在OO角度实现,我只是期望在schema角度实现, 增加is_history字段很正常;;
从OO角度,历史物料并不是物料,而是一种关系;; 被删除的物料也是一种关系,这种关系不应该放在原有的Material的materialUnit属性上, 而应该捆绑在另一个属性上deletedMaterialUnit 上面;
如果你增加isDelete 属性,而不是增加deletedMaterialUnit 属性相当于把 原有的物料含义添加了invalid entity含义,失效实体;; MaterialUnit.getMaterials 自然要获取到所有的实体,包括invalid entity ; 如果你要进行过滤,也可以加上@where ;;
我也觉得挺合理的;;
但是getMaterials就会延伸出 getMaterialsNotDeleted() , getMaterialsAll() , getMaterialsDeleted() 多种关系,如果都通过新增一个外键来表征这种关系的话,感觉没必要;;
但从实体世界来说确实是这样的,invalid entity 确实要和原有的materialUnit分开
最佳的方案是 getMaterials() 改成 getMaterialsNotDeleted() ,
另外一种情况;
原有实体--增加一个普通字段isdeleted(非外键)历史含义,被覆盖,被删除 使数据库表的对应实体含义增加了一种新的含义, 目前的, 和 历史的;; 这样与原有的实体已经对应不起来了;;
这个会导致一个问题:
1. materialUnit原有的getMaterials的含义变掉了, 对service层影响很大;; (一个数据表字段引起的数据表含义和实体含义不一致导致实体世界的崩塌 )
1.2 如果把之前的所有getMaterials进行改造 ,即使之前的改造完毕, 也会预留下一个坑, 以后的开发者会自然而然的getMaterials进行操作,然后save级联save,把 历史记录都消除掉,无法获取一个历史物料到底属于谁的;; 基于业务是,历史的物料不应该被消除关系,虽然service层可以谨慎处理,避免挂掉,但这个提供了一个很方便的方法,是一个坑 应该提供一个private的 set方法,就不会是坑了;
缺点2: 这个带有了业务关系, 其他产品线可能根本没有isdeleted这个字段,你莫名奇妙的给我一个这样的过滤,我会觉得很奇怪的;;
如果用Dao,不同的业务产品线完全可以忽略这个字段,在sql中忽略此字段;;
另外一个方法是不同的业务产品线子把getMaterials的实现下放到子类中,自己来控制怎么过滤;;; 父类中没有isDeleted这种方法,但问题在于把一个简单的materialUnit改成父子类, 这样太重了;;
2. 如果增加一个@where 过滤历史的 ,, 利用materialUnit实体,无法获得所有的表实体(数据);
另外一种方案,从oo世界着手; getMaterialNow , getMaterialOvrride() ,getMaterialDeleted() ,MaterialsALL () 多种关系, 都通过新增一个外键来表征这种关系的话,感觉没必要;;
总和考虑, 基于不改变原有接口定义的角度考虑getMaterials才是最高端的;; 增加一个利用
1. 普通字段history ,@where ,缺点 material实体和数据表一致,但是getMaterials并不是单纯的实体关系了;;利用materialUnit 和material实体,以及对应的hql无法操作整个数据库世界;
但是可以通过增加新的接口 getMaterialsAll 这个关系来操作整个数据库世界; 但是这个就显得很不符合逻辑, invalid entity 不应该如此方便的获取,修改,关键是历史的关系不应该修改;;所以就不应该被关系化;; hibernate要搞一个不能被修改的关系;;; 需要 set方法是private就好了;;;
2. 在getMaterials()外键的基础上 ,在material 实体和表上 增加 对应getMaterialOvrride() 的一个外键 ( 这个很像 一个material 有一个广告主和一个代理商 ;)
=========
我明白你刚才说的1 1 的意思了, 你是指 一个杯子被一个人拥有过, 还可以再次被人所拥有;;
这种逻辑, 这两个字段是放在material表上,更合理的含义应该是上一次(某一次)被谁拥有,现在又被谁拥有. 主要点是我思考的逻辑是一个物料只能被一个人占有一次, 你思考的逻辑是一个杯子可以被丢掉再捡回来;所以认为 1 1 在曾经的关系 和 现在的关系 这两种关系中不可能存在;;
第二个字段,曾经被谁拥有过,应该独立出一个关系表出来,实现多对多的关系,1个杯子不仅能够我占用,还能被我占用多次; 以 杯子(物料)id 和时间 为unique key;
我思考的逻辑是一个物料被丢掉后就不能捡回来;所以我认为 1 1 在曾经的关系 和 现在的关系 这两种关系中不可能存在;
如果一个杯子被一个拥有过,还可以再次被拥有,应该是 是一个关系表 materialId 和 时间 是uniqueKey , 一个物料即可以被同一个物料单元拥有, 又可以被同一个物料单元拥有多次;;
一个物料单元同一时间可以拥有多个物料单元;
一种新型的多对多;; 加入时间维度