views:

51

answers:

3

I have a POJO class, say Foo, which has a Set of other entity instances, say bars. Also there are standart misc classes for such project: service and dao for both Foo and Bar.

I want BarService to get the Set of Bar instances associated with some Foo. Now I have the following code, wich I believe is conceptually bad.

 
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 questions: Where it is better to initialize Foo's Set? What specific Sets and Lists are better for such purposes? What conceptual issues has my current implementation, and how to do better?

Thanks in advance.

A: 

i tend to initialize the collections in the service layer where i keep the transaction handling as well. So i can have a method in my BaseDAO which lets me initialize any collection of any Entity in my projects using reflection, by passing the collection names into the method which are to be fetched eagerly (initialized):

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;
    }

then you can initialize any collection from the service layer using this method:

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

A more detailed example: http://objecthunter.congrace.de/tinybo/blog/articles/69

smeg4brains
Usefull code snippet, and I definitely will use this approach in my future projects. But now I'm working on veeery small self-taught project, king of Spring-hibernate hello world, so just for now it is very complicated for me. Anyway, thank you.
edio
+2  A: 

Your entity could be simple set of fields with getters and setters. What you need to take care of is how you relate your objects and approach you take to populate objects.
ORM APIs gives liberty of using Objects rather than SQL. You should be careful as to when you should choose to initialize the fields of an Object. For example if you have an object person, which comprises of name, age, collection of contacts and cities visited. In a situation where you are interested in name and age of the person and not contact and cities, you should load name and age only. Which implies that contacts and cities should be lazy loaded.
When interested in contact you load only contacts and not the entire person object or through person object. You would want to load Set of contacts only using Dao/Service and explicitly defining methods to load specific aspect of the object (use of reverse association).
Some best hibernate practices can be found at Best Practices. Updated: 1) Entity does not populate on its own. One of the popular approach is to have DAO to do this Job. Your Entity could simple be

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) You can have transaction managed in another layer also referred as Service layer.

Ck-
Thanks. So, if I understand you correctly, I have to define method in DAO, which will take User ID as an argument, and perform a hibernate query to load needed aspects? Right?
edio
Exactly. As you get to know more about it, read about how to load particular set of fields, lazy loading, inverse association, criteria and HQL. I find HQL to be very effective to control what gets loaded in the object. Depending on situation you can choose what all fields get loaded thus avoiding unnecessary SQL joins or n+1 problem.
Ck-
+1  A: 

Where it is better to initialize Foo's Set?

Most of time, I initialize a collections when declaring it, which is what Hibernate recommends. Quoting the documentation:

6.1. Persistent collections

Hibernate requires that persistent collection-valued fields be declared as an interface type. For example:

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; }
}

The actual interface might be java.util.Set, java.util.Collection, java.util.List, java.util.Map, java.util.SortedSet, java.util.SortedMap or anything you like ("anything you like" means you will have to write an implementation of org.hibernate.usertype.UserCollectionType.)

Notice how the instance variable was initialized with an instance of HashSet. This is the best way to initialize collection valued properties of newly instantiated (non-persistent) instances. When you make the instance persistent, by calling persist() for example, Hibernate will actually replace the HashSet with an instance of Hibernate's own implementation of Set.

If leaving it null is part of your business, my suggestion would be to initialize it in a (common) link management methods:

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);
    }
}

What specific Sets and Lists are better for such purposes?

It all depends on the semantics you need. A Set doesn't allow duplicates, a List allows duplicates and introduces positional indexing.

What conceptual issues has my current implementation, and how to do better?

  1. I wouldn't perform an assignment in the getter.
    • If a collection is supposed to be null at that point, let it be null.
  2. I don't see the added value of your service
    • why not just calling foo.getBars()?
    • why converting the collection?
Pascal Thivent
Converting collection is needed to implement jsf view tier, but if I want to use datatable with set I have to implement custom datamodel.
edio
As for "why not just calling foo.getBars()?" isn't it bad, that my view tier will communicate with entity directy, but not with it's service? And as for initialisation at the point of declaration: isn't it bad, that on every entity creation memory will be first allocated for empty collection, and then for retrieved collection from DB?
edio
`isn't it bad, that my view tier will communicate with entity directy` No, unless you enjoy making things more complicated than needed. `isn't it bad, that on every entity creation memory will be first allocated for empty collection, and then for retrieved collection from DB?` Dude, this should be the latest of your concern.
Pascal Thivent