在Spring-Hibernate项目中初始化实体集合(POJO)的正确方法是什么?

时间:2010-10-29 08:36:40

标签: java hibernate spring architecture collections

我有一个POJO类,比如Foo,它有一组其他实体实例,比如说吧。 此类项目还有标准的misc类:Foo和Bar的服务和dao。

我希望BarService获取与某些Foo相关联的Bar实例集。现在我有以下代码,我相信它在概念上很糟糕。

 
public class Foo {
    Set<Bar> bars;

    public Set<Bar> getBars() {
        if (bars == null)
            return ( bars = new HashSet() );
        return bars;
    }
}
 
public class BarServiceImpl {
    public List<Bar> getListOfBars(Foo foo) {
        return new ArrayList(foo.getBars());
    }
}

3个问题: 在哪里初始化Foo的Set更好? 哪些特定的集合和列表更适合此类目的? 我目前的实施有哪些概念性问题,以及如何做得更好?

提前致谢。

3 个答案:

答案 0 :(得分:11)

  

最好初始化Foo的Set?

大多数时候,我在声明它时初始化集合,这是Hibernate推荐的。引用文档:

  

6.1. Persistent collections

     

Hibernate要求持久化   收集值字段被声明   作为接口类型。例如:

public class Product {
    private String serialNumber;
    private Set parts = new HashSet();

    public Set getParts() { return parts; }
    void setParts(Set parts) { this.parts = parts; }
    public String getSerialNumber() { return serialNumber; }
    void setSerialNumber(String sn) { serialNumber = sn; }
}
     

实际界面可能是   java.util.Set,   java.util.Collection,   java.util.Listjava.util.Map,   java.util.SortedSet,   java.util.SortedMap或任何你   喜欢(“你喜欢的任何东西”意味着你   将不得不写一个实现   的   org.hibernate.usertype.UserCollectionType。)

     

注意实例变量是怎样的   用一个实例初始化   HashSet。这是最好的方法   初始化集合值   新实例化的属性   (非持久性)实例。当你   使实例持久化   以persist()为例,   Hibernate实际上会取代   HashSet,其实例为   Hibernate自己的实现   Set

如果离开它null是您业务的一部分,我的建议是用(常见)链接管理方法初始化它:

public class Foo {
    ...
    private Set<Bar> bars;
    ...
    public void addBar(Bar bar) {
        if (this.bars == null) {
            this.bars = new HashSet<Bar>();
        }
        this.bars.add(bar);
    }
}
  

哪些特定的集合和列表更适合此类用途?

这一切都取决于你需要的语义。 Set不允许重复,List允许重复并引入位置索引。

  

我目前的实施有哪些概念性问题,以及如何做得更好?

  1. 不会在getter中执行赋值。
    • 如果该集合在该点应该是null,请将其设为null
  2. 我没有看到您服务的附加价值
    • 为什么不只是致电foo.getBars()
    • 为什么要转换这个系列?

答案 1 :(得分:2)

您的实体可以是带有getter和setter的简单字段集。您需要注意的是如何将对象与填充对象的方法联系起来 ORM API提供了使用Objects而不是SQL的自由。您应该在何时选择初始化Object的字段时要小心。例如,如果您有一个对象人员,其中包括姓名,年龄,联系人集合和访问过的城市。如果您对此人的姓名和年龄感兴趣而不是联系方式和城市,则应仅加载姓名和年龄。这意味着联系人和城市应该是懒惰的 当您对联系感兴趣时,您只加载联系人而不是整个人物对象或通过人物对象。您只想使用Dao / Service加载一组联系人,并明确定义加载对象特定方面的方法(使用反向关联)。
一些最好的休眠实践可以在Best Practices找到。 更新: 1)实体不会自行填充。一种流行的方法是让DAO做这个Job。你的实体很简单

public class Foo {
    private Set<Bar> bar=new HashSet<Bar>();
    public Set<Bar> getBar {
        return bar;
    }
    public void setBar(Bar bar) {
        this.bar = bar;
    }
}

2)您可以在另一个层中管理事务,也称为服务层。

答案 2 :(得分:1)

我倾向于初始化服务层中的集合,我也保持事务处理。所以我可以在我的BaseDAO中有一个方法,它允许我使用反射初始化我项目中任何实体的任何集合,方法是将集合名称传递给要急切获取的方法(初始化):

public <T extends Object> T getEntity(Class<T> clazz,long id,String[] collectionsToBeInitialized){
        T entity=(T) this.getCurrentSession().createCriteria(clazz).add(Restrictions.idEq(id)).setFetchMode(collectionsToBeInitialized[0], FetchMode.JOIN).uniqueResult();
        int length=collectionsToBeInitialized.length;
        for (int idx=1;idx<length;idx++){
            String collectionName=collectionsToBeInitialized[idx];
            try {
                Method m = clazz.getMethod("get" + collectionName.substring(0, 1).toUpperCase() + collectionName.substring(1),(Class<T>) null);
                Hibernate.initialize(m.invoke(entity,(Object[]) null));
            } catch (NoSuchMethodException e) {
                LOG.error("Could not initialize collection " + collectionName + " of class Event", e);
            } catch (InvocationTargetException e) {
                LOG.error("Could not initialize collection " + collectionName + " of class Event", e);
            } catch (IllegalAccessException e) {
                LOG.error("Could not initialize collection " + collectionName + " of class Event", e);
            }
        }
        return entity;
    }

然后您可以使用此方法从服务层初始化任何集合:

MyEntity ent=getEntity(MyEntity.class,id,new String[]{"collection1","collection2"});

更详细的例子: http://objecthunter.congrace.de/tinybo/blog/articles/69