Search code examples
javahibernatelazy-initialization

Error Initializing Lazy Collection


I'm trying to write some code that determines if a customer has certain feature. I have this method for that:

@Transactional(readOnly = true)
public boolean customerHasFeature(String customerId, String feature) {
    Customer customer = customerDAO.findByCid(customerId);
    if(customer != null) {
        return customer.hasFeatureNamed(feature);
    }

    return false;
}

The customerDAO method is here

@Transactional(readOnly = true)
public Customer findByCid(String cid) {
    List<Customer> customers = findByCriteriaImpl(Restrictions.eq("cid", cid));
    if(customers.size() > 0)
        return customers.get(0);
    return null;
}

In customerHasFeature after I retrieve the customer it doesn't load the features and I get the error

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.socialware.model.Customer.features, no session or session was closed

When debugging findbyCid, I can see the features loaded after the criteria retrieves the customer, but when returned customer gets to customerHasFeature, it has an error.

I tried adding

Hibernate.initialize(customer.getFeatures());

after I call the customerDAO in customerHasFeature method but then I get

org.hibernate.HibernateException: collection is not associated with any session

I'm using hibernate 3,I appreciate any help or guides.

EDIT

Here's the findByCriteriaImpl method.

List<T> findByCriteriaImpl(Criterion... criterion) {
       Criteria crit = createCriteria(getPersistentClass());
       if (criterion != null) {
          for (Criterion c : criterion) {
               crit.add(c);
          }
       }
       long startTime = System.currentTimeMillis();
       List<T> toReturn = crit.list();
       reportQueryTimeForMonitoring(System.currentTimeMillis() - startTime, "findByCriteriaImpl", "for criteria " + crit);
       return toReturn;
}

And the Customer class

public class Customer implements Serializable{
    private static final long serialVersionUID = -1L;

    @Field(index=Index.UN_TOKENIZED)
    private long customerId;

    private String cid;

    //@Field
    private String name;

    private Set<Feature> features;
    private boolean deleted = false;
    private String randomKey;

    public Customer() {
    }

    @Override
    public String toString() {
        return new StringBuilder()
            .append("Customer{")
            .append(customerId).append(", ")
            .append(cid).append(", ")
            .append(name).append(", ")
            .append(deleted)
            .append("}").toString();
    }

    @Override
    public boolean equals(Object obj) {
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(getClass() != obj.getClass())
            return false;
        Customer other = (Customer) obj;
        if(cid == null) {
            if(other.cid != null)
                return false;
        }
        else if(!cid.equals(other.cid))
            return false;
        if(customerId != other.customerId)
            return false;
        if(name == null) {
            if(other.name != null)
                return false;
        }
        else if(!name.equals(other.name))
            return false;
        return true;
    }

    public long getCustomerId() {
        return customerId;
    }

    public void setCustomerId(long customerId) {
        this.customerId = customerId;
    }

    public void addFeature(Feature feature) {
        if(null == getFeatures()) {
            features = new HashSet<Feature>();
        }
        features.add(feature);
    }

    public Set<Feature> getFeatures() {
        return features;
    }

    public void setFeatures(Set<Feature> features) {
        this.features = features;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCid() {
        return cid;
    }

    public void setCid(String cid) {
        this.cid = cid;
    }

    public boolean isDeleted() {
        return deleted;
    }

    public void setDeleted(boolean deleted) {
        this.deleted = deleted;
    }

    public String getRandomKey() {
        return randomKey;
    }

    public void setRandomKey(String randomKey) {
        this.randomKey = randomKey;
    }

    public boolean hasFeatureNamed(String name) {
        Set<Feature> features = getFeatures();

        if (features == null) {
            return false;
        }
        for (Feature feature : features) {
            if (feature != null && feature.getName().equals(name)) {
                return true;
            }
        }
        return false;
    }

    public void removeFeature(String name) {
        Set<Feature> features = getFeatures();

        if (features == null) {
            return;
        }
        for (Iterator<Feature> i = features.iterator(); i.hasNext();) {
            Feature feature = i.next();
            if (feature.getName().equals(name)) {
                i.remove();
            }
        }
    }
}

Solution

  • Looking at your Customer class, I cannot see how did you map the features set, but note that, however you did, the default fetching type is LAZY, meaning that your collection will not be initialized during the read.

    Please try the following:

    List<T> findByCriteriaImpl(List<String> fetches, Criterion... criterion) {
           Criteria crit = createCriteria(getPersistentClass());
           if (criterion != null) {
              for (Criterion c : criterion) {
                   crit.add(c);
              }
              for (String s : fetches) {
                   crit.setFetchMode(s, FetchMode.JOIN);
              }
           }
           long startTime = System.currentTimeMillis();
           List<T> toReturn = crit.list();
           reportQueryTimeForMonitoring(System.currentTimeMillis() - startTime, "findByCriteriaImpl", "for criteria " + crit);
           return toReturn;
    }
    

    And call the findByCriteriaImpl like:

    List<Customer> customers = findByCriteriaImpl(Collections.asList("features"), Restrictions.eq("cid", cid));
    

    This way you can tell your method which collections you want to fetch together with your entity.