-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Hibernate search: endless loop in tree structures
PostPosted: Sun Mar 23, 2008 1:41 pm 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
Hi,

I seem to be running into an issue where an indexed class which also references itself to create a tree structure leads to an endless loop on indexing the item with Lucene.

This is the relevant class (abbreviated:
Code:
@Entity
@Table(name="Categories")
@Indexed
public class Category{

   @Column(nullable=false)
   @Field(index=Index.TOKENIZED,store=Store.YES)
   public String getCategory() {
      return category;
   }
   
   @ManyToOne(cascade=CascadeType.MERGE)
   @JoinColumn(name="FK_ParentID",nullable=true)
   public Category getParent() {
      return parent;
   }
   
   public void setParent(Category parent) {
      this.parent = parent;
   }

   @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
   public Set<Category> getChildren() {
      return children;
   }

   public void setChildren(Set<Category> children) {
      this.children = children;
   }

   public void addChild(Category category){
      category.setParent(this);
      children.add(category);
   }
   
   public void delChild(Category category){
      this.children.remove(category);
   }

   /**
    * @see java.lang.Object#hashCode()
    */
   public int hashCode() {
      return new HashCodeBuilder(765233521, 1856390359).appendSuper(
            super.hashCode()).append(this.category).append(this.views)
            .append(this.hidden).append(this.isPremium).append(
                  this.categoryId).append(this.catLookup).append(this.parent).append(this.country)
            .toHashCode();
   }

   /**
    * @see java.lang.Object#equals(Object)
    */
   public boolean equals(Object object) {
      if (!(object instanceof Category)) {
         return false;
      }
      Category rhs = (Category) object;
      return new EqualsBuilder().appendSuper(super.equals(object)).append(
            this.category, rhs.category).append(this.views, rhs.views)
            .append(this.hidden, rhs.hidden).append(this.isPremium,
                  rhs.isPremium).append(this.categoryId, rhs.categoryId)
            .append(this.catLookup, rhs.catLookup).append(this.children,
                  rhs.children).append(this.parent, rhs.parent).append(
                  this.country, rhs.country).isEquals();
   }

   /**
    * @see java.lang.Comparable#compareTo(Object)
    */
   public int compareTo(Object object) {
      if(this == object)
         return 0;
      Category myClass = (Category) object;
      return new CompareToBuilder().append(this.category, myClass.category)
            .append(this.views, myClass.views).append(this.hidden,
                  myClass.hidden).append(this.isPremium,
                  myClass.isPremium).append(this.categoryId,
                  myClass.categoryId).append(this.catLookup,
                  myClass.catLookup).append(this.children,
                  myClass.children).append(this.parent, myClass.parent)
            .append(this.country, myClass.country).toComparison();
   }

}




Cheers,

Marc


Top
 Profile  
 
 Post subject: Perhaps not Search
PostPosted: Sun Mar 23, 2008 4:06 pm 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
Perhaps this is not behaviour for Search. I commented out all the Search annotations and I still get the loop.

Basically I:
* open a fileinput stream for a record with 138 lines
* read the file line by line
* insert records and parent references based on each line until the file ends: 1 line = 1 record
* close the inputstream
* end the test

Stepping through the code, my code ends ok and then it continues inserting records until I count 2382 inserted records. Should have been 138. Not a clue why. Must have something to do with that self referencing parent relationship.

Cheers,

Marc


Top
 Profile  
 
 Post subject: Code
PostPosted: Mon Mar 24, 2008 1:33 pm 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
I hope I can get a respons for this, because as I'm walking through the code, I keep ending up with the same result and I can't see anything wrong with it.

in the code below, the CategoryLookup is basically a translation table. The input is a text file structured as a tree using tabs. The idea is to create the database records from this file. The file contains 138 records. From the database content it would seem that it inserts content like this:

In the database, the following record structure emerges:
Item 1
Item 1
Child 1
Item 1
Child 1
Item 1
Child 2
Child 1
Item 1
Child 1
Child 2
Item 1
Child 1
Child 3
Child 2
[...]

Best that I can make of it, is that it inserts Parents and children as new records in stead of identifying them as already existing. I use Commons utils equals and hashcode methods. Could it be that because the Commons utils hashcode builder includes all fields including the id field that is only set after insertion, that it recognizes the objects as somehow different?

Code:
      BufferedReader br = new BufferedReader(new InputStreamReader(is));
      String line = null;
      Category prevCat = null;
      int previous = 0;

      try {
         while ((line = br.readLine()) != null) {
            
            if(line.indexOf('=') == -1)
               //Reached end of file
               break;
            
            String[] ra = line.split("=");
            CategoryLookup cl = new CategoryLookup();
            Category cat = new Category();
            cat.setCatLookup(cl);
            cat.setCountry(targetCountry);

            if (log.isDebugEnabled())
               log.debug(line);
            if (line.charAt(0) == '\t' && line.charAt(1) == '\t') {
               // level 2
               if (log.isDebugEnabled())
                  log.debug("level 2");
               if (previous == 2) {
                  // parent is same category parent
                  cat.setParent(prevCat.getParent());
                  prevCat.getParent().addChild(cat);
               } else {
                  // previous = 1
                  cat.setParent(prevCat);
                  prevCat.addChild(cat);
               }

               if (ra.length > 1) {
                  cat.setCategory(ra[1].trim());
               } else {
                  cat.setCategory(ra[0].trim());
               }

               previous = 2;

            } else if (line.charAt(0) == '\t') {
               // level 1
               if (log.isDebugEnabled())
                  log.debug("level 1");

               if (previous == 2) {
                  // parent is previos category parent.parent
                  cat.setParent(prevCat.getParent().getParent());
                  prevCat.getParent().getParent().addChild(cat);
               } else if (previous == 1) {
                  // parent is the same
                  cat.setParent(prevCat.getParent());
                  prevCat.getParent().addChild(cat);
               } else {
                  // parent is root
                  cat.setParent(prevCat);
                  prevCat.addChild(cat);
               }

               if (ra.length > 1) {
                  cat.setCategory(ra[1].trim());
               } else {
                  cat.setCategory(ra[0].trim());
               }
               previous = 1;

            } else {
               // level 0
               if (log.isDebugEnabled())
                  log.debug("level 0");

               if (ra.length > 1) {
                  cat.setCategory(ra[1]);
               } else {
                  cat.setCategory(ra[0]);
               }

               previous = 0;
            }
            CategoryLookup clTmp = catLookupDao.getByLabel(ra[0]);
            if(clTmp != null){
               // the lookup already exists. Use existing one
               cl = clTmp;
               cat.setCatLookup(cl);
            }else{
               cl.setCategoryName(ra[0].trim());
            }
            if(prevCat != null)
               prevCat = categoryDao.save(prevCat);
            prevCat = cat;
            cl = catLookupDao.save(cl);
            cat = categoryDao.save(cat);
         }
         
         br.close();
      } catch (IOException io) {
         log.error(io);
      }


Cheers,

Marc


Top
 Profile  
 
 Post subject: Some insights please?
PostPosted: Fri Mar 28, 2008 11:29 am 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
Hi all,

I really don't understand this. I changed the code so the save operation happens outside the loop generating the tree structure. This leads to a more structure record structure in the database but still I get double entries.

My test results have been like this:
* Individual save operation on CategoryLookup and Category inside the loop: quadruple entries on all tables
* Move Category save outside of initial loop: double entries on categories quadruple entries on CategoryLookup
* Move CategoryLookup outside of initial loop: correct entries on categories and double entries on CategoryLookup

The point is
1. I don't see the difference between any of the cases above. They should all yield the same result
2. I don't see why there are double entries on CategoryLookup. The list contains a single and correct set of entries. Why is it saved twice?

The fact that moving the CategoryLookup outside of the loop influences the number of entries on the categories table is also weird, since there's only a manyToOne relation from Category=>CategoryLookup but not the other way around.

Any ideas?

Kind regards,

Marc

Code:
Category
   @ManyToOne(cascade=CascadeType.MERGE)
   @JoinColumn(name="FK_ParentID",nullable=true)
   public Category getParent() {
      return parent;
   }

   @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
   public Set<Category> getChildren() {
      return children;
   }

   public void addChild(Category category){
      if(children == null)
         children = new HashSet<Category>();
      children.add(category);
   }

   @ManyToOne(cascade=CascadeType.MERGE)
   @JoinColumn(name="FK_CategoryLookupID",nullable=false)
   public CategoryLookup getCatLookup() {
      return catLookup;
   }

   public int hashCode() {
      return new HashCodeBuilder(765233521, 1856390359).appendSuper(
            super.hashCode()).append(this.category).append(this.views)
            .append(this.hidden).append(this.isPremium).append(this.catLookup).append(this.parent).append(this.country)
            .toHashCode();
   }

   public boolean equals(Object object) {
      if (!(object instanceof Category)) {
         return false;
      }
      Category rhs = (Category) object;
      return new EqualsBuilder().append(
            this.category, rhs.category).append(this.views, rhs.views)
            .append(this.hidden, rhs.hidden).append(this.isPremium,
                  rhs.isPremium).append(this.categoryId, rhs.categoryId)
            .append(this.catLookup, rhs.catLookup).append(this.children,
                  rhs.children).append(this.parent, rhs.parent).append(
                  this.country, rhs.country).isEquals();
   }


CategoryLookup

   public int hashCode() {
      return new HashCodeBuilder(-1431736281, -818998207).append(this.categoryName).append(this.Id)
            .toHashCode();
   }

   /**
    * @see java.lang.Object#equals(Object)
    */
   public boolean equals(Object object) {
      if(this == object)return true;
      if (!(object instanceof CategoryLookup)) {
         return false;
      }
      CategoryLookup rhs = (CategoryLookup) object;
      return new EqualsBuilder().append(
            this.categoryName, rhs.categoryName).append(this.Id, rhs.Id)
            .isEquals();
   }



Code:
CategoryManager
      BufferedReader br = new BufferedReader(new InputStreamReader(is));
      String line = null;
      Category prevCat = null;
      int previous = 0;
      List<Category> rootCats=new ArrayList<Category>();
      List<CategoryLookup> lookups =new ArrayList<CategoryLookup>();

      try {
         while ((line = br.readLine()) != null) {
            
            if(line.indexOf('=') == -1)
               //Reached end of file
               break;
            
            String[] ra = line.split("=");
            CategoryLookup cl = new CategoryLookup();
            Category cat = new Category();
            cat.setCatLookup(cl);
            cat.setCountry(targetCountry);

            if (log.isDebugEnabled())
               log.debug(line);
            if (line.charAt(0) == '\t' && line.charAt(1) == '\t') {
               // level 2
               if (log.isDebugEnabled())
                  log.debug("level 2");
               if (previous == 2) {
                  // parent is same category parent
                  cat.setParent(prevCat.getParent());
                  prevCat.getParent().addChild(cat);
               } else {
                  // previous = 1
                  cat.setParent(prevCat);
                  prevCat.addChild(cat);
               }

               if (ra.length > 1) {
                  cat.setCategory(ra[1].trim());
               } else {
                  cat.setCategory(ra[0].trim());
               }

               previous = 2;

            } else if (line.charAt(0) == '\t') {
               // level 1
               if (log.isDebugEnabled())
                  log.debug("level 1");

               if (previous == 2) {
                  // parent is previos category parent.parent
                  cat.setParent(prevCat.getParent().getParent());
                  prevCat.getParent().getParent().addChild(cat);
               } else if (previous == 1) {
                  // parent is the same
                  cat.setParent(prevCat.getParent());
                  prevCat.getParent().addChild(cat);
               } else {
                  // parent is root
                  cat.setParent(prevCat);
                  prevCat.addChild(cat);
               }

               if (ra.length > 1) {
                  cat.setCategory(ra[1].trim());
               } else {
                  cat.setCategory(ra[0].trim());
               }
               previous = 1;

            } else {
               // level 0
               rootCats.add(cat);
               if (log.isDebugEnabled())
                  log.debug("level 0");

               if (ra.length > 1) {
                  cat.setCategory(ra[1]);
               } else {
                  cat.setCategory(ra[0]);
               }

               previous = 0;
            }
            CategoryLookup clTmp=null;
            for(CategoryLookup cl2 : lookups){
               if(cl2.getCategoryName().equals(ra[0])){
                  clTmp = cl2;
                  break;
               }
            }
            if(clTmp != null){
               // the lookup already exists. Use existing one
               cl = clTmp;
               cat.setCatLookup(cl);
            }else{
               cl.setCategoryName(ra[0].trim());
            }

            prevCat = cat;
            lookups.add(cl);
//            cl = catLookupDao.save(cl);
//            cat = categoryDao.save(cat);
         }
         
         br.close();
         
         for (CategoryLookup cl : lookups){
            catLookupDao.save(cl);
         }
         catLookupDao.flush();
         for(Category myCat : rootCats){
            categoryDao.save(myCat);
         }
         
      } catch (IOException io) {
         log.error(io);
      }


Top
 Profile  
 
 Post subject: Records
PostPosted: Fri Mar 28, 2008 12:35 pm 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
When I monitor the SQL, I see 138 inserts after the flush statement. But then, for each Category insert, it also inserts a CategoryLookup. When I look at the tree category structure however, I see that the categorylookup reference is accurate. So, the same object is inserted twice. Any ideas? Bueller, bueller?

Cheers,

Marc


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.