对充血模型的疑问

小弟我在坛子上看到关于贫血模型和充血模型的讨论后一直再想这对于我这种代码小工意味着啥?
是否可以更快更迅速更敏捷的完成代码完成开发。
一直做的项目都是SSH 然后分层 action,service,dao,entity;有时候我觉得dao层没什么必要啊,既然
hibernate是面向对象为何还要 dao.save(entity)   dao.update(entity) 这样操作?
感觉如果去掉dao层 直接entity.update(),entity.delete();  这样是否会更形象?
然后小弟开始尝试,首先搭了个struts2+spring3+Hibernate3.6的ssh工程用的是全注解搭法这样开发就不用写配置文件了还是相当的爽啊

首先我觉得应该有一个公共的类充当所有entity的父类


import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

import javax.annotation.Resource;

import org.hibernate.SessionFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Repository;

@Repository("MagicH")
public class MagicH extends HibernateTemplate {

@Resource(name = "sessionFactory")
public void setSuperSessionFactory(SessionFactory sessionFactory) {
    super.setSessionFactory(sessionFactory);
}

private Serializable id; // 标示
private Class clazz; // 实体
private Object entity; // 数据库实体
private PropertyDescriptor targetPds[];  //初始化后属性
private HashMap setMethodMap = new HashMap();   //set方法映射
private HashMap getMethodMap = new HashMap();  //get方法映射

/**
 * 模型初始化
 * 
 * @param clazz
 * @param id
 */
public boolean init(Class clazz, Serializable id) {
    this.id = id;
    this.clazz = clazz;
    PropertyDescriptor targetPds[] = BeanUtils.getPropertyDescriptors(clazz); 
      for (PropertyDescriptor pdObj : targetPds)
        {
           setMethodMap.put(pdObj.getName().toLowerCase(), pdObj.getWriteMethod());
           getMethodMap.put(pdObj.getName().toLowerCase(), pdObj.getReadMethod());
        }
      this.entity = get(clazz, id);
    return this.entity==null?false:true;  
}

/**
 * 获取对象实体
 * 
 * @return
 */
public Object getObj() {
    if (entity == null) {
        this.entity = get(clazz, id);
    }
    return entity;
}

/**
 * 删除对象实体
 */
public void delObj() {
    if (entity != null)
        delete(entity);
    else
        delete(get(clazz, id));
}

/**
 * 设置数据库实体属性
 * @param propertyName
 * @param propertyValue
 */
public void setProperty(String propertyName, Object propertyValue) {

      Method writeMethod = (Method) setMethodMap.get(propertyName.toLowerCase());
      try {
        writeMethod.invoke(entity, new Object[] {propertyValue});
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

/**
 * 获取属性的值
 * @param propertyName
 * @return
 */
public Object getPropertyValue(String propertyName)
{
    Method readMethod = (Method)getMethodMap.get(propertyName.toLowerCase());
    Object returnValue = null;
    try {
        returnValue = readMethod.invoke(entity,null);
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return returnValue;
}
/**
 * 更新对象
 */
public void updateObj()
{
    update(entity);
}


/**
 * 保存对象
 * @param obj
 * @return
 */
public Serializable addObj(Object obj)
{
    Serializable id =  save(obj);
    init(obj.getClass(),id);
    return  id;
}

}



建立一张用户表测试,对应实体如下

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.stereotype.Repository;

import com.usoft.core.dao.impl.MagicH;

/**

  • MagicUser entity. @author MyEclipse Persistence Tools
    */
    @Repository("MagicUser")
    @Entity
    @Table(name = "MAGIC_USER")
    public class MagicUser extends MagicH implements java.io.Serializable {

    // Fields

    private String userId;
    private String userNam;
    private String userSex;
    private String userPhone;

    // Constructors

    /** default constructor */
    public MagicUser() {
    }

    /** full constructor */
    public MagicUser(String userNam, String userSex, String userPhone) {
    this.userNam = userNam;
    this.userSex = userSex;
    this.userPhone = userPhone;
    }

    // Property accessors
    @GenericGenerator(name = "generator", strategy = "uuid.hex")
    @Id
    @GeneratedValue(generator = "generator")
    @Column(name = "USER_ID", unique = true, nullable = false, length = 32)
    public String getUserId() {
    return this.userId;
    }

    public void setUserId(String userId) {
    this.userId = userId;
    }

    @Column(name = "USER_NAM", length = 500)
    public String getUserNam() {
    return this.userNam;
    }

    public void setUserNam(String userNam) {
    this.userNam = userNam;
    }

    @Column(name = "USER_SEX", length = 1)
    public String getUserSex() {
    return this.userSex;
    }

    public void setUserSex(String userSex) {
    this.userSex = userSex;
    }

    @Column(name = "USER_PHONE", length = 11)
    public String getUserPhone() {
    return this.userPhone;
    }

    public void setUserPhone(String userPhone) {
    this.userPhone = userPhone;
    }

}


这是本人测试用的service


import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.usoft.magich.bs.ImagicServiceTest;
import com.usoft.magich.vo.MagicUser;

@Service("MagicService")
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public class MagicServiceTestImpl implements ImagicServiceTest {

@Resource
private MagicUser magicUser;

public void testMagicH() {
    //保存
    magicUser.setUserNam("用户123");
    magicUser.setUserPhone("12345");
    magicUser.setUserSex("1");
    magicUser.addObj(magicUser);

   //更新
    magicUser.setProperty("UserNam", "1234567");
    magicUser.updateObj();

    System.out.print(magicUser.getPropertyValue("UserNam"));

    boolean b1 = magicUser.init(magicUser.getClass(), "aaa");  //id 不存在初始化失败
    System.out.println(b1);

    boolean b2 = magicUser.init(magicUser.getClass(),"402881862da6ec3b012da6fb4f640004"); //id存在为 true
    System.out.println(b2);
    magicUser.delObj(); //删除 对象
}

}



只是中午吃饭时脑子那么想了一下,然后回来就尝试写了个单表的增删改查,我个人举得无论是单表还是多表最后数据库查出来的无非是个二叉表,而其中的一条记录应该对应一个java类 多条记录则是该java类的List集合,然后所有的数据库操作不再是dao.doSomething 而是entity.doSomething
我这样做了后写的代码就只有actiong,service,entity层了,不知道这样是否可行,谁能解释一下我的疑问。


问题补充
看了大家的回复深受启发,感觉自己这个想法似乎不可取就不再钻牛角尖了。
可能一直处于开发人员的角色,所以关系的核心问题无非就是 写最少的代码做最多的事

21个回答

一个问题的作用域将决定一个问题的解决方案是否合适。其实这也是大多数人对于领域模型各种变种的误解。

当一个简单类具备了field和method之后,大师们告诉你,这个类的实例叫做对象,基于这种方式写出来的程序叫OO。大师们还告诉你,只有field和它们对应的setter/getter方法的类,不怎么OO,因为这叫贫血。在类上加上method,赋予类逻辑执行能力,这叫领域模型,比较OO。

恩,我们不妨考虑一个拥有3000w行代码的大系统,假设它需要1000个领域模型类。这相当于把一个最简单的问题放大了1000倍,那么我们等于把我们的逻辑重复1000遍。当然,这个系统的逻辑不再是增删改查那么简单,而是有复杂的业务流程。这个时候,你会发现,一个领域模型类可能将达到20K,甚至30K那么大。里面可能有超过40个method,对应30种不同的业务流程。

某天,项目经理说,hey,小伙子,客户说这业务改了,它们要在所有涉及到数据库保存的地方加条日志,并通知另外一个系统。想象一下你的工作?在一个超过2000行的类里面,找到那些方法,一个个加过来。恩,这就是专家们说的,这很OO。

哈哈,你这个Entity充血的解释比较有意思。你这种做法,我们几年前的时候就这样做了,你那个Entity类只不过是个Dao的基类,提供了通用的增删改查改功。这并不是什么充血模型,至多算伪充血,还容易误导人概念。我觉得Entity名字换成BaseDao名比较好。充血模型更多应该从领域对象出发。

你这个MagicUser类无法作为模型,因为他无法通过new的方式创建。

你的做法,说白了就是把model层和dao层合并了,没有带来任何别的变化,这种做法不会让你的开发变得更快。

此外还引入了以下问题:
1.你的领域对象和Hibernate框架完全耦合
2.领域对象承担了不属于它的职责,即持久化。好吧,或许你会说“持久化”也可以算它的职责,那么至少“写入数据库”不是它的职责,为了剥离这个职责,演化之后你还是需要一个DAO层。
3.从第2个问题就衍生出第3个问题,你知道有些领域对象是不需要持久化的,只在内存中,但按照你这个框架来的话,随随便便就要抛exception

holdmydream
holdmydream 精辟,精华
7 年多之前 回复

我不如用一个基于反射的基础数据库操作的BASEDAO

在SERVICE层里 我自动注入这个基操作DAO,如:

@Resource
BASEdao userDao;

个人偏向于贫血,但是特殊业务下,如文章的图片在创建时建创UUID文件名的文件夹,删除时自动删除,== 我会在POJO中注血的

ObjectWeb的DODS就如你想象的“充血模型”,以前改shark源码的时候接触过这个持久层框架一段时间,但不得不说,比起实体与DAO分离的“贫血模型”来说,实在是很不好用。

充血模型,领域驱动个人觉得最好的解决之道在于DOMAIN EVENT 的应用

为什么Rod不把HibernateTemplate定义为final类型的!

用SSH做一个应用是多么麻烦啊,要写这么多代码,长此下去,必成代码工人

共21条数据 1 3 尾页
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问