`

Hibernate实体类 == 领域模型 ?

阅读更多
Hibernate实体类==领域模型 ?

http://www.iteye.com/topic/11608



自从Martin Fowler的DDD提出来之后,无数的人就开始非议ORM方式下的持久化实体类,抨击这种方式下的实体类是“贫血”的,缺乏丰富业务语义的。其实他们都犯了一个最基本的逻辑错误 - 偷换概念。

概念是如何被偷换的呢?请注意,领域模型(Domain Model)是一个商业建模范畴的概念,他和软件开发并无一丝一毫的关系,即使一个企业他不开发软件,他也具备他的业务模型,所有的同行业的企业他们的业务模型必定有非常大的共性和内在的规律性,由这个行业内的各个企业的业务模型再向上抽象出来整个行业的业务模型,这个东西即“领域模型”。一个掌握了行业领域模型的软件公司,根本不需要再给人家开发项目了,根本不需要靠软件开发养活自己了,你光给这个行业的企业提供业务咨询已经赚得非常丰厚的利润了。以我现在兼职所在的公司来说,就是这样一家软件公司,在行业内积累了足够的领域模型,成立了一个专门的咨询部门,这个部门下面都是咨询师,他们是不管软件开发的,也不懂软件开发,他们就专门教这个行业的客户,教他们怎么去做自己的业务,他们比客户还精通客户的业务,光是业务咨询已经可以为公司带来很多的收入。

而软件开发呢?一个并没有行业经验积累的软件公司,它开发的软件,基本上完全是需求驱动,而不是领域模型驱动。只有具备了领域模型积累的公司才有资格去谈领域模型驱动软件开发。在由领域模型往某种编程语言如Java上来实现的时候,绝对不会是1:1的对应关系,即使是粗颗粒度的EJB2模型都做不到,更不要说更加强调细颗粒度的POJO模型呢?用面向对象的语言如Java来编写一个领域模型,如果是用EJB2模型,你需要使用最少两个以上的EJB,即一个 Session Bean,处理面向流程的控制逻辑,一个Entity Bean,处理面向持久化的实体逻辑(持久化操作附着在Entity Bean的Home接口上)。如果是更加复杂的领域模型,那么你需要更多的EJB,也许是一个领域模型需要多个Entity Bean和多个Session Bean。现在我们使用基于POJO模型的实现,那么粗颗粒度的EJB还要继续细分:一个Entity Bean要剥离出来至少三个以上的POJO,即一个或者多个实体类,一个或者多个DAO接口类,一个或者多个DAO接口实现类;一个Session Bean要切分为多个业务Bean。

由此我们终于看出来概念是怎样被偷换的了,一个商业概念的抽象领域模型被一个Java持久化实体类替代了。但是我们应该看到,Martin批评的贫血的领域模型并不是Hibernate实体类,Martin指的贫血的领域模型实际上是缺乏丰富业务逻辑概念的领域抽象模型,这和Hibernate实体类完全是风牛马不相及的东西。而Hibernate实体类只是具体编码过程中,为了实现一个领域模型而编写的一组基于POJO的对象中的,完成领域模型某个特征的类。而这个领域模型完整的特征并不应该,也不可能由一个非常粗颗粒度的单类完成,而是由一组互相协作的类完成:即Hibernate的实体类保持领域模型的状态;DAO接口实现类完成领域模型的持久化操作;Spring Bean类完成领域模型的逻辑控制功能。




POJO指的就是非EJB那种重量级,高侵入性的组件模型,关于POJO的定义,你同样可以在Martin Fowler的bliki上面找到。

Spring的Bean是不是POJO? 是的!
Hibernate的entity是不是POJO?是的!
DAO接口是不是POJO?是的!
EJB是不是POJO? 不是的!

我没有看过Martin的DDD,我按照自己的理解, POJO domain models指的就是轻量级的领域模型。何为轻量级? 把领域模型的各个特征,各个属性,各个逻辑都塞到一个class里面叫做轻量级吗?

我认为,Martin批评的贫血的领域模型是指只关注了领域模型持久化特征方面,而忽略了领域模型其他特征方面的模型,这样的模型是贫血的。因为这种模型只关注了模型在技术层面的外在表现,也就是说只关注了数据的存取操作,而忽视了模型蕴含的业务核心价值。

举例来说,我们编一个银行软件,如果你只关注了账户的增删改查,这叫做贫血!而实际上你应该关注的是账户的业务特征,而不是数据特征,你应该关注的是账号开立的业务,账户注销的业务,账号过户的业务等等,这才是领域模型。这种领域模型在一个单纯的技术实现层面来说,对于最简单的业务,你可能只是Account类的增删改查,但是对于复杂的业务来说,他就不单但是一个类,一个表的简单操作了,例如开立账户,你要收手续费,以及考察个人财务状况,那么此时你需要的就是一组协作的类。

Martin提到领域模型,意在强调我们应该关注软件的业务,关注行业知识的内在规律,并且把这种规律建模为领域模型,批评拿到一个软件,脑子里面光想到数据库增删改查的人。这和我们的Hibernate持久化类毫无关系!

我的看法是:一个抽象的领域模型具备多方面的特征,你需要用一组互相协作的类来完成它,每一个或者一组类承担这个领域模型的某个特征。例如某个领域模型,例如上面的账户,你需要一组Hibernate持久化类:包括Account类,User类,Finance类,一组SpringBean类,AccountManager,FinanceManager,一组DAO接口和实现类。由这些POJO的类互相协作来共同完成这个领域模型。如果你仅仅关注Account的增删改查,那就贫血了,而如果你关注了账户的业务规则,并且考虑一组互相协作的类去完成它,就不是贫血的。



问题:
很赞同“商业概念的抽象领域模型”的概念。概念搞清楚固然重要,不过,这些“商业概念的抽象领域模型”在我们的应用开发中处在一个什么层次中呢,如何表现呢?在使用hibernate的系统中,这些“商业概念的抽象领域模型”不是通过hibernate的实体类表现出来的么,如果不是,那么用什么其他表现手段合适呢。

回复:
我上面已经重点强调过了,这种商业概念的抽象领域模型是独立于软件开发体系之外的,你就算不开发软件,你也可以并且需要去抽象你的领域模型。
具体到软件开发活动,你应该由领域模型来驱动你的软件内在规则,由需求驱动你的软件外在交互。
这个领域模型的代码实现我上面也强调过了你需要用一组互相协作的类来完成它,每一个或者一组类承担这个领域模型的某个特征。而Hibernate的实体类只不过是其中的一组类,它承担的职责就是保持领域模型的状态的。



Martin Fowler提出Domain Model的本意就是希望能提升业务领域模型的重用性,也就可知Domain Model是针对业务而抽象产生的模型,此模型和数据源层的持久模型并不一定完全相同,Domain Model中关注的层面更多的是业务方面,Domain Model由Domain Object构成,Domain Object共同协作完成业务逻辑的处理,觉得概念上而言Hibernate的实体类并不等同于Domain Model中的Domain Object,但由于很多应用的业务逻辑几乎是没有,在此时Hibernate的实体类同时也就可以充当Domain Object,觉得这也是为什么两者容易搞混的原因吧!



这实际上是因为术语的不同定义和理解导致的问题。
Martin Fowler提到的Domain Model并不是你这里所说的概念。
你所说的概念是RUP的概念,领域模型是作为业务建模的简化版本来定义的。
想想看,Martin Fowler是把TransactionScript,TableModule,DomainModel三者是放在并列的位置讨论的。用你的观点来解释能解释得通吗?
下面是martin Fowler认为 贫血的DomainModel的问题所在。
The fundamental horror of this anti-pattern is that it's so contrary to the basic idea of object-oriented design; which is to combine data and process together. The anemic domain model is really just a procedural style design, exactly the kind of thing that object bigots like me (and Eric) have been fighting since our early days in Smalltalk. What's worse, many people think that anemic objects are real objects, and thus completely miss the point of what object-oriented design is all about.
数据与行为分离,违反OO封装的基本原理。我想这是最明显不过了。


robbin :

把我的数据和你的行为整合到一起,这就是OO封装的基本原理? 你要搞清楚什么才是一个数据类的内在行为,而不要把本来不是这个数据类的行为误以为是这个数据类的行为。

我可以给你一个准则: 实体类是有状态的类,凡是针对实体类有状态的操作,你可以考虑一下是否是该实体类的行为,而凡是无状态的操作,它基本上就不是该实体类的行为。

还是就上面举的这个例子来看,Order类的addItem方法和removeItem方法是和Order类的某个具体实例紧密绑定的,换句话说就是,这两个方法是有状态的行为,所以他们是Order类的行为,必须放在Order类里面,而其他的所谓updateOrder,saveOrder,findOrders等等统统都不是和具体Order类实例绑定的,即全部都是无状态的操作。这些无状态的操作并不是实体类的行为,他们和实体类的关系只是消息的消费者和消息之间的依赖关系,并不是类内部属性和方法之间的紧密关联关系。。

这些无状态的方法只不过接收实体类作为消息来消费他们而已。我们对比一下Struts Action的
 
execute(HttpServletRequest request, HttpServletResponse response, ActionFormBean formBean, ...);


Action接收request和response,请注意,Struts的action是无状态的操作,而request和response是有状态的类,在这里request,response,formBean都是消息,而Action的execute是这些消息的消费者,你不会认为应该把Action的execute方法放到request对象里面吧?

好吧,那么我们再看看实体类和实体类的DAO接口:
AccountDao: 
updateAccount(Account account); 


比较一下,完全没有区别!这就说明updateAccount这个行为他和Account之间只存在消息的消费者和消息,而不是类方法的关系。

请记住上面这个Struts Action的例子,按照你们的逻辑,execute方法就不应该放在Action里面,而应该整合到request对象里面去。看起来很荒谬吧,其实你们的逻辑推导的结果就是这样。本质上你们又犯了偷换概念的错误,混淆了类的行为和类作为消息的外部消费者。





partech :

首先,我还是建议你去研究一下RUP同MartinFowler关于DomainModel的不同定义。否则大家讨论就是鸡同鸭讲了。
你说的这个原则我可以不可以这样理解?
有状态的操作,指改变实体属性的操作。
无状态操作,不改变实体属性的操作。
没错,你的这种观点是对的。持久化的方法是不能放入DomainObject中。
看看MartinFowler的大作11章的UnitOfWork和18章的Separated Interface。DomainObject实际上只需要知道UnitOfWork的接口。

分享到:
评论

相关推荐

    Hibernate实体层设计

    《Hibernate实体层设计详解》 Hibernate,作为Java领域中的一款著名持久化框架,它将对象关系映射(ORM)的概念引入到数据库操作中,极大地简化了数据库编程工作。本篇文章将深入探讨Hibernate实体层设计的核心概念...

    使用Hibernate逆向生成实体类的方法(注解和hbm.xml映射文件)

    在Java开发领域中,Hibernate作为一种流行的ORM(对象关系映射)框架,被广泛应用于将对象模型映射到基于SQL的关系型数据库上。通过Hibernate,开发者能够更加高效地处理数据库操作,同时减少对原生SQL的依赖,提升...

    hibernate E-R模型关系配置

    2. **属性(Attributes)**:实体类中的成员变量代表了E-R模型中的属性。这些属性可以使用`@Column`注解来定义其在数据库表中的列名、长度、是否允许为空等属性。 3. **关系(Relationships)**:E-R模型中的联系在...

    领域模型项目示例

    1. **实体类(Entity)**:领域模型的核心是实体,这些类代表了业务中的关键对象,比如用户(User)、订单(Order)、产品(Product)等。实体类通常包含了属性(属性对应业务领域的关键信息)和行为(方法,用于体现业务...

    用MyEclipse自动生成hibernate映射文件和实体类

    MyEclipse作为一款强大的Java集成开发环境,提供了便捷的功能来帮助开发者自动生成Hibernate的映射文件(.hbm.xml)和对应的实体类,大大提高了开发效率。 首先,我们来看看如何在MyEclipse中创建数据库和表。这...

    实体类生成器1很好用的

    实体类生成器是一种高效实用的开发工具,尤其在Java编程领域中广泛应用。它能够自动根据数据库的表结构生成对应的实体类代码,极大地提高了开发效率,减少了手动编写代码的工作量。"实体类生成器1"是这类工具的一个...

    C#,Java生成实体类工具源码

    在Java领域,Hibernate是另一个著名的ORM框架,同样支持从数据库生成实体类。另外,MyBatis也提供了逆向工程功能,可以从数据库表生成Java POJOs(Plain Old Java Objects)。还有一些第三方工具,如Apache Velocity...

    hibernate-release-5.2.10.Final

    JPA(Java Persistence API)元模型生成器是Hibernate 5.2.10.Final中的一个重要组件,它能自动生成JPA实体类的元模型,帮助开发者减少手动编写元模型代码的工作量。元模型是JPA中的一个重要概念,它包含了实体类的...

    hibernate annotation hibernate3

    在Java开发领域,Hibernate作为一种强大的对象关系映射(ORM)框架,极大地简化了数据库操作。本篇将深入探讨Hibernate 3版本中的注解使用,帮助开发者理解如何利用注解进行数据持久化,提高开发效率。 一、...

    hibernate模板类详解

    在Java开发领域中,Hibernate框架是进行对象关系映射(Object-Relational Mapping,简称ORM)的一种常用工具,它能够将面向对象模型的数据与关系型数据库之间的数据进行转换,从而简化了数据访问层的开发工作。...

    spring3+hibernate4+maven=springMVC(curd)

    它通过实体类和配置文件定义了数据库表与Java对象之间的映射,同时提供了事务管理和缓存功能。 **Maven** 是一个项目管理工具,它帮助开发者管理项目的构建、依赖关系和文档。在本项目中,Maven通过其配置文件pom....

    实体类查找数据库

    总结来说,实体类查找数据库是一种通过ORM框架将面向对象的编程模型与关系型数据库相结合的技术,它提供了更高级别的抽象,降低了数据库操作的复杂性,提高了开发效率。理解并熟练掌握这一技术,对于任何IT开发者来...

    hibernate映射文件生成数据库

    在提供的压缩包`FanxiangShengCheng`中,可能包含了具体的实体类、映射文件以及示例代码,用于演示如何通过Hibernate映射文件生成数据库。案例可能会展示如何配置Hibernate环境,如何编写映射文件,以及如何运行`...

    hibernate-tool 插件

    1. **代码生成器**:能够根据数据库表结构自动生成Java实体类和Hibernate的映射文件(.hbm.xml)。 2. **逆向工程**:通过现有数据库生成模型类和映射文件,帮助快速搭建项目。 3. **Hibernate配置文件验证**:检查...

    Hibernate5.2.8提取包

    - **Hibernate Tools**:提供了IDE集成,如Hibernate逆向工程,可以自动生成实体类和映射文件。 - **Hibernate Search**:允许在数据库中进行全文本搜索,提供了基于Lucene的搜索引擎。 4. **使用步骤** - 配置...

    hibernate第一个hibernate

    在Hibernate中,每个数据库表对应一个Java实体类,类的属性与表的字段相对应。使用注解或者XML文件(如`User.hbm.xml`)来定义这种映射关系。例如,一个名为`User`的实体类,可以有`id`、`username`、`password`等...

    hibernate相关插件全集

    它基于数据库模式,可以自动创建Hibernate的XML映射文件和Java实体类,大大减少了手动编写这些代码的工作量。Middlegen通过分析数据库表结构,为开发者提供了便捷的逆向工程功能,使得在项目初期快速建立模型成为...

    Java Hibernate

    - **配置 Hibernate**:首先需要设置 Hibernate 的核心配置文件 `hibernate.cfg.xml`,并定义实体类与数据库表之间的映射关系。 - **创建实体类**:定义 Java 类,并使用注解或 XML 配置文件指定这些类与数据库表的...

    实体类生成器源码

    实体类是面向对象编程中的一个重要概念,它代表了业务领域中的一个实体,如用户、订单或产品。在Java中,实体类通常是POJO(Plain Old Java Object),即简单的Java类,包含属性和getter/setter方法。通过注解,如...

    hibernate父亲与孩子

    在实体类中,部门类会有一个Employee类型的属性,表示其负责人;而在Employee类中,可能会有一个Department类型的属性,表示所属部门。在映射文件或注解中,我们需要指定这种关联,并配置外键,通常是员工表中的一个...

Global site tag (gtag.js) - Google Analytics