-->
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.  [ 14 posts ] 
Author Message
 Post subject: Test the deletion of an element from a collection property..
PostPosted: Thu Jun 02, 2011 9:05 am 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
I have a unit test to check for the correct deletion of an element in a collection property.

The deletion of the object from the collection should result in the deletion of the record from the table.

The test source code:
Code:
   public LinkCategoryDaoTest() {
      linkCategory0 = new LinkCategory();
      linkCategory0.setName("Images");
      linkCategory1 = new LinkCategory();
      linkCategory1.setName("Pdf");
   }

   @Before
   public void beforeAnyTest() throws Exception {
      linkCategory1 = linkCategoryDao.makePersistent(linkCategory1);
      linkCategory0 = linkCategoryDao.makePersistent(linkCategory0);
   }

   @Test
   public void testCollection() {
      link0 = new Link();
      link0.setName("Thalasoft");
      link0.setImage("image0.png");
      link0.setListOrder(3);
      linkCategory0.addLink(link0);
      link2 = new Link();
      link2.setName("Google");
      link2.setImage("image2.gif");
      link2.setListOrder(1);
      linkCategory0.addLink(link2);
      link1 = new Link();
      link1.setName("Libe");
      link1.setImage("image1.jpg");
      link1.setListOrder(2);
      linkCategory0.addLink(link1);
      linkCategoryDao.flush();
      linkCategoryDao.clear();
      LinkCategory linkCategory = linkCategoryDao.findWithId(linkCategory0.getId(), false);
      assertEquals(3, linkCategory.getLinks().size());
      assertEquals(link2.getName(), linkCategory.getLinks().get(0).getName());
      assertEquals(link1.getName(), linkCategory.getLinks().get(1).getName());
      assertEquals(link0.getName(), linkCategory.getLinks().get(2).getName());
      linkCategory0.removeLink(link1);
      linkCategory = linkCategoryDao.findWithId(linkCategory0.getId(), false);
      assertEquals(2, linkCategory.getLinks().size()); <<<==== FAILS HERE
      assertEquals(link2.getName(), linkCategory.getLinks().get(0).getName());
      assertEquals(link0.getName(), linkCategory.getLinks().get(1).getName());
      linkCategory0.removeLink(link2);
      linkCategory = linkCategoryDao.findWithId(linkCategory0.getId(), false);
      assertEquals(1, linkCategory.getLinks().size());
      assertEquals(link0.getName(), linkCategory.getLinks().get(0).getName());
      linkCategory0.removeLink(link0);
      linkCategory = linkCategoryDao.findWithId(linkCategory0.getId(), false);
      assertEquals(0, linkCategory.getLinks().size());
   }


The test fails at the line indicated in the source code.

The error message is:

Quote:
java.lang.AssertionError: expected:<2> but was:<3>
at org.junit.Assert.fail(Assert.java:74)
at org.junit.Assert.failNotEquals(Assert.java:448)
at org.junit.Assert.assertEquals(Assert.java:102)
at org.junit.Assert.assertEquals(Assert.java:323)
at org.junit.Assert.assertEquals(Assert.java:319)
at com.thalasoft.learnintouch.core.dao.LinkCategoryDaoTest.testCollection(LinkCategoryDaoTest.java:87)


The Hibernate mapping is:
Code:
        <list name="links" inverse="true" cascade="all">
            <key column="category_id" not-null="true" />
            <list-index column="list_order" base="1" />
            <one-to-many class="com.thalasoft.learnintouch.core.domain.Link" />
        </list>


The tables structures:
Code:
mysql> desc link;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| version     | int(10) unsigned | NO   |     |         |                |
| name        | varchar(50)      | YES  |     | NULL    |                |
| description | varchar(255)     | YES  |     | NULL    |                |
| image       | varchar(50)      | YES  |     | NULL    |                |
| url         | varchar(255)     | YES  |     | NULL    |                |
| category_id | int(10) unsigned | YES  | MUL | NULL    |                |
| list_order  | int(10) unsigned | YES  |     | NULL    |                |
+-------------+------------------+------+-----+---------+----------------+
8 rows in set (0.05 sec)

mysql> desc link_category;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| version     | int(10) unsigned | NO   |     |         |                |
| name        | varchar(50)      | YES  |     | NULL    |                |
| description | varchar(255)     | YES  |     | NULL    |                |
+-------------+------------------+------+-----+---------+----------------+

Clearly, the deletion does not occur as I hoped it would.

Any clue ?


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Thu Jun 02, 2011 6:48 pm 
Hibernate Team
Hibernate Team

Joined: Fri Oct 05, 2007 4:47 pm
Posts: 2536
Location: Third rock from the Sun
Quote:
Any clue ?

I don't see any transaction commit in your code..

_________________
Sanne
http://in.relation.to/


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Thu Jun 02, 2011 8:19 pm 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
I run in auto commit.

Shouldn't the statement

Code:
linkCategory = linkCategoryDao.findWithId(linkCategory0.getId(), false);


trigger the commit.?


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Thu Jun 02, 2011 8:59 pm 
Hibernate Team
Hibernate Team

Joined: Fri Oct 05, 2007 4:47 pm
Posts: 2536
Location: Third rock from the Sun
I can't know that: I have no clue what "linkCategoryDao" is supposed to to, how it's designed, how it's implemented.
I think you should share more code, and consider that "autocommit" might work at the database level, but if Hibernate doesn't know when to flush, it won't.

Code:
linkCategoryDao.flush();
what's that?

_________________
Sanne
http://in.relation.to/


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Fri Jun 03, 2011 6:33 am 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
Sorry for the missing code... Here is some more:

Code:
@Repository
@Transactional
public class LinkCategoryHibernateDao extends GenericHibernateDao<LinkCategory, Serializable> implements LinkCategoryDao {

   @SuppressWarnings("unchecked")
   @Override
   public List<LinkCategory> findAll() {
      Criteria criteria = getSession().createCriteria(getPersistentClass());
      criteria.addOrder(Order.asc("name"));
      return criteria.list();
   }
   
}


Code:
@Repository
@Transactional
public abstract class GenericHibernateDao<T, ID extends Serializable> implements GenericDao<T, ID> {

   private static Logger logger = LoggerFactory.getLogger(GenericHibernateDao.class);

   private Class<T> persistentClass;
   private SessionFactory sessionFactory;

   @SuppressWarnings("unchecked")
   public GenericHibernateDao() {
      this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
   }

   public void setSessionFactory(SessionFactory sessionFactory) {
      this.sessionFactory = sessionFactory;
   }

   public SessionFactory getSessionFactory() {
      if (sessionFactory == null) {
         throw new IllegalStateException("sessionFactory has not been set on DAO before usage");
      }
      return sessionFactory;
   }

   public Session getSession() {
      return getSessionFactory().getCurrentSession();
   }

   public Class<T> getPersistentClass() {
      return persistentClass;
   }

   @SuppressWarnings("unchecked")
   @Override
   public T findWithId(ID id, boolean lock) {
      T entity;

      if (lock) {
         entity = (T) getSession().load(getPersistentClass(), id, LockOptions.UPGRADE);
      } else {
         entity = (T) getSession().load(getPersistentClass(), id);
      }

      return entity;
   }

   @SuppressWarnings("unchecked")
   @Override
   public boolean isFoundById(ID id) {
      try {
         @SuppressWarnings("unused")
         T entity = (T) getSession().load(getPersistentClass(), id);
      } catch (ObjectNotFoundException e) {
         return false;
      }

      return true;
   }

   @Override
   public List<T> findAll() {
      return findObjectsByCriteria();
   }

   @Override
   public long countAllRows() {
      Criteria criteria = getSession().createCriteria(getPersistentClass());
      criteria.setProjection(Projections.rowCount());
      return ((Long) criteria.list().get(0)).longValue();
   }

   @Override
   public long countFoundRows() {
      Criteria criteria = getSession().createCriteria(getPersistentClass());
      criteria.setProjection(Projections.rowCount());
      return ((Long) criteria.uniqueResult()).longValue();
   }

   @SuppressWarnings("unchecked")
   @Override
   public List<T> findWithExample(T exampleInstance, String... excludeProperty) {
      Criteria criteria = getSession().createCriteria(getPersistentClass());
      Example example = Example.create(exampleInstance);
      for (String exclude : excludeProperty) {
         example.excludeProperty(exclude);
      }
      criteria.add(example);
      return criteria.list();
   }

   @Override
   public T makePersistent(T entity) {
      getSession().saveOrUpdate(entity);
      return entity;
   }

   @Override
   public void makeTransient(T entity) {
      getSession().delete(entity);
      String message = "The entity " + entity.getClass().getCanonicalName() + " was deleted fine.";
      logger.debug(message);
   }

   @Override
   public void flush() {
      getSession().flush();
   }

   @Override
   public void clear() {
      getSession().clear();
   }

   @Override
   public void close() {
      getSession().close();
   }

   @SuppressWarnings("unchecked")
   @Override
   public List<T> findObjectsByCriteria(Criterion... criterion) {
      Criteria criteria = getSession().createCriteria(getPersistentClass());
      for (Criterion c : criterion) {
         criteria.add(c);
      }
      return criteria.list();
   }

   @Override
   public T findObjectByCriteria(Criterion... criterion) {
      T object = null;
      List<T> results = findObjectsByCriteria(criterion);

      if (results.isEmpty()) {
         if (logger.isDebugEnabled()) {
            logger.debug("No search results found for: " + criterion);
         }
      } else {
         if (results.size() > 1) {
            if (logger.isDebugEnabled()) {
               logger.debug("The criterion : " + criterion + " should return only one result");
            }
         }
         object = results.get(0);
      }

      return object;
   }

   @SuppressWarnings("unchecked")
   @Override
   public T findObjectByCriteria(Criteria criteria) {
      T object = null;
      List<T> results = criteria.list();

      if (results.isEmpty()) {
         if (logger.isDebugEnabled()) {
            logger.debug("No search results found for the criteria : " + criteria);
         }
      } else {
         if (results.size() > 1) {
            if (logger.isDebugEnabled()) {
               logger.debug("The criteria : " + criteria + " should return only one result");
            }
         }
         object = results.get(0);
      }

      return object;
   }

}


And my Spring Hibernate setup:

Code:
   <bean id="sessionFactory"
      class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
      <property name="dataSource">
         <ref bean="dataSource" />
      </property>
      <property name="mappingDirectoryLocations">
         <list>
            <value>classpath:com/thalasoft/learnintouch/core/domain</value>
         </list>
      </property>
      <property name="mappingResources">
         <list>
            <value>com/thalasoft/learnintouch/core/domain/typedef.hbm.xml</value>
         </list>
      </property>
      <property name="hibernateProperties">
         <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.connection.pool_size">0</prop>
            <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">true</prop>
         </props>
      </property>
   </bean>

   <!-- Setup the Spring transaction manager -->
   <bean id="transactionManager"
      class="org.springframework.orm.hibernate3.HibernateTransactionManager">
      <property name="sessionFactory">
         <ref local="sessionFactory" />
      </property>
   </bean>

   <tx:annotation-driven />

   <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
      <property name="sessionFactory">
         <ref bean="sessionFactory" />
      </property>
   </bean>

   <bean
      class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />

   <!--  Translate Hibernate exceptions into Spring exceptions -->
   <bean
      class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Fri Jun 03, 2011 6:35 am 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
This is to flush the Hibernate session.

Code:
linkCategoryDao.flush();


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Fri Jun 03, 2011 7:21 am 
Hibernate Team
Hibernate Team

Joined: Fri Oct 05, 2007 4:47 pm
Posts: 2536
Location: Third rock from the Sun
So, when adding a link you do
Quote:
linkCategory0.addLink(link1);
linkCategoryDao.flush();
linkCategoryDao.clear();

but when removing it you don't flush nor clear. So my guess is that your relation is bi-directional, and you're removing it from one side only?
Because you're not clearing the session the assert is checking on the same instances of object you where modifying before (same as in ==), they are not freshly loaded from the database, so it's not Hibernate controlling the entity state but your own code: it will merely applies to the database the final state of your entities at commit time (but as your test proved, your state is wrong before that point).

_________________
Sanne
http://in.relation.to/


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Fri Jun 03, 2011 7:39 am 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
You are right, I forgot to flush and clear the session when deleting an element from the collection.

So I added it, like:
Code:
      linkCategory0.removeLink(link1);
      linkCategoryDao.flush();
      linkCategoryDao.clear();


But I still get the same error message:
Quote:
java.lang.AssertionError: expected:<2> but was:<3>


I forgot to give you the source code for the domain classes, here they are:

Code:
public class LinkCategory implements java.io.Serializable {

   private Integer id;
   private int version;
   private String name;
   private String description;
   private List<Link> links = new LinkedList<Link>();

   public LinkCategory() {
   }

   public Integer getId() {
      return this.id;
   }

   public void setId(Integer id) {
      this.id = id;
   }

   public int getVersion() {
      return this.version;
   }

   public void setVersion(int version) {
      this.version = version;
   }

   public String getName() {
      return this.name;
   }

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

   public String getDescription() {
      return this.description;
   }

   public void setDescription(String description) {
      this.description = description;
   }

   public List<Link> getLinks() {
      return this.links;
   }

   @SuppressWarnings("unused")
   private void setLinks(List<Link> links) {
      this.links = links;
   }

   public void addLink(Link link) {
      LinkCategory currentCategory = link.getLinkCategory();
      if (currentCategory != this) {
         if (currentCategory != null) {
            currentCategory.getLinks().remove(link);
         }
         link.setLinkCategory(this);
         this.links.add(link);
      }
   }
   
   public void removeLink(Link link) {
      LinkCategory currentCategory = link.getLinkCategory();
      if (currentCategory == this) {
         link.setLinkCategory(null);
         this.links.remove(link);
      }
   }
   
}


Code:
package com.thalasoft.learnintouch.core.domain;

public class Link implements java.io.Serializable {

   private Integer id;
   private int version;
   private String name;
   private String description;
   private String image;
   private String url;
   private int listOrder;
   private LinkCategory linkCategory;

   public Link() {
   }

   public Integer getId() {
      return this.id;
   }

   public void setId(Integer id) {
      this.id = id;
   }

   public int getVersion() {
      return this.version;
   }

   public void setVersion(int version) {
      this.version = version;
   }

   public LinkCategory getLinkCategory() {
      return this.linkCategory;
   }

   public void setLinkCategory(LinkCategory linkCategory) {
      this.linkCategory = linkCategory;
   }

   public String getName() {
      return this.name;
   }

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

   public String getDescription() {
      return this.description;
   }

   public void setDescription(String description) {
      this.description = description;
   }

   public String getImage() {
      return this.image;
   }

   public void setImage(String image) {
      this.image = image;
   }

   public String getUrl() {
      return this.url;
   }

   public void setUrl(String url) {
      this.url = url;
   }

   public int getListOrder() {
      return this.listOrder;
   }

   public void setListOrder(int listOrder) {
      this.listOrder = listOrder;
   }

}


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Thu Jun 16, 2011 4:56 pm 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
The log shows not delete statement occurring.


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Thu Jun 16, 2011 5:58 pm 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
The documentation at http://docs.jboss.org/hibernate/core/3. ... child.html says: "In our case, a Child cannot exist without its parent. So if we remove a Child from the collection, we do want it to be deleted."

But in my case, the child CAN exist without its parent. And I still wanted the child to be deleted if it is removed from the collection.

Am I right in wanting this ?

If so, should I use the delete-orphan attribute ?


Top
 Profile  
 
 Post subject: Misleading Hibernate documentation ?
PostPosted: Thu Jun 16, 2011 6:11 pm 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
In the documentation, I find a statement at the page http://docs.jboss.org/hibernate/core/3. ... child.html to be confusing.

It reads: In our case, a Child cannot exist without its parent. So if we remove a Child from the collection, we do want it to be deleted. To do this, we must use cascade="all-delete-orphan".

In my opinion, the delete-orphan implies deleting any child whose parent has been deleted.

If the parent has not been deleted then the child is not an orphan and it should not be deleted.

But the above quoted statement seems to say that even if the child is not an orphan, it is still deleted if it is removed from the collection in the parent.

I think there are two different cascading at play here. But apparently Hibernate has chosen to merge the two together.

How should we do to NOT delete the orphans but still delete any child removed from the collection in the parent ? Or does this behavior make no sense ?


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Wed Jun 22, 2011 9:56 am 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
Is this not an interesting question ?

If so, I wonder why.. Its answer is already published somewhere ?


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Sun Jul 10, 2011 6:58 pm 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
There was one issue with my Dao and its test.

Using a List for the collection of links, inserted as many links necessary to have as many records as the greatest list order value specified. The list order values were causing additional insertions.

So I changed the List to a Set, as in:

<set name="links" inverse="true" order-by="list_order" cascade="all">
<key column="category_id" not-null="true" />
<one-to-many class="com.thalasoft.learnintouch.core.domain.Link" />
</set>

and got the appropriate number of inserted records, whatever their list order values were.

But I still have an issue in the removeLink method.

In it, I compare that the object about to be removed has the same parent than the this object.

I use a logger.debug to make sure this is the case, and indeed it looks like they are the same object, as the logger:

logger.debug("link.getLinkCategory: " + link.getLinkCategory() + " this: " + this);

says:

link.getLinkCategory: com.thalasoft.learnintouch.core.domain.LinkCategory@1342f5b this: com.thalasoft.learnintouch.core.domain.LinkCategory@1342f5b

But the if statement:

if (link.getLinkCategory() == this) {

returns false.

Here is the method source code:

public void removeLink(Link link) {
logger.debug("========================>> About to remove a link");
logger.debug("link.getLinkCategory: " + link.getLinkCategory() + " this: " + this);
if (link.getLinkCategory() == this) {
logger.debug("========================>> Removing a link");
link.setLinkCategory(null);
this.links.remove(link);
logger.debug("========================>> Removed a link");
}
}

Any clue ?


Top
 Profile  
 
 Post subject: Re: Test the deletion of an element from a collection property..
PostPosted: Mon Jul 11, 2011 8:36 am 
Pro
Pro

Joined: Mon Apr 16, 2007 8:10 am
Posts: 246
It really is puzzling...

In fact it feels like it's another problem altogether.

I will do a new post on the forum, there:

viewtopic.php?f=1&t=1011804


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 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.