+1 on this work. We shouldn't be tied to a Hibernate only feature.

But I do wonder, how did IBM work around this issue with their iBatis
implementation.

- Dave


On 12/18/06, Mitesh Meswani <[EMAIL PROTECTED]> wrote:
Thanks for the review Craig. Please find attached a cumulative patch
that addresses your review comments.
Please note that this patch contains some additional changes that I had
missed last time. Following is a summary of additional changes.
HibernateBookMarkManagerImpl:
    removeBookmark(BookmarkData bookmark) now removes bookmark from the
parent folder's collection of bookmarks also
FolderData, UserData,WeblogEntryData:
   Replaced the cascade="all-delete-orphan" with cascade="all"

All the business unit tests passes with the attached patch. ( I am
getting a failure while running org.apache.roller.ui.UIPluginManagerTest
with or without these changes on a freshly checked out workspace)

Thanks,
Mitesh


Craig L Russell wrote:
> Hi Mitesh,
>
> This is a good analysis of a problem that I've seen as well. Comments
> follow...
>
> On Dec 18, 2006, at 11:03 AM, Mitesh Meswani wrote:
>
>> Apache mail host seemed to remove the text of the mail.
>> Resending.........
>>
>> Hi,
>>
>> I am working to port Roller's backend code to use JPA/JDO (see
>> trunk/sandbox/jdobackend). I ran across a dependency on Hibernate
>> specific feature that can be easily removed with some refactoring.
>
> This Hibernate feature is not portable to either JDO or JPA, and I
> think it's a bit confusing when reading the code, since you have to
> either be a Hibernate expert or look up the behavior in the Hibernate
> documentation to figure out what is going on.
>
> So I support the notion of making relationship handling explicit and
> thereby removing the confusion.
>
> For Hibernate's "orphan" relationships, both sides of the relationship
> are affected by the operation, which I'd characterize as a "delete"
> operation that has a side effect of removing the deleted instance from
> the collection on the "owner" side. This is in contrast to Hibernate's
> definition of removing an instance which has a side effect of deleting
> the instance from the database.
>
> If the deleted instance had its own XXXManager, I'd favor putting the
> behavior into the XXXManager interface, since there is supposed to be
> no knowledge of the persistence layer inside the pojo layer. And in
> fact, it's unlikely that such a pojo would have such a behavior since
> the nature of the relationship is a composition relationship, and it
> makes sense that the behavior be in the aggregate instance manager.
>
> So I agree with your proposal to remove the method from the pojo and
> instead add it to the XXXManager interface that can both remove the
> owned instance from the collection and remove the owned instance from
> the database.
>
> As a performance optimization, we should also consider changing the
> relationship from a Set to a Map. This would avoid the issue of
> iterating the entire Set just to find the right entry. This is a
> portable JPA/JDO/Hibernate construct (to be further looked at later).
>
> In the code that follows, sometimes you remove the entity from the
> collection and then remove it from the database. In other cases, you
> remove it from the database and then remove it from the collection. We
> should probably be consistent in the order (although it should not
> matter; I don't believe that either JDO or JPA requires an order).
>
>>
>> ...snipped
>
>> Index: src/org/apache/roller/pojos/UserData.java
>> ===================================================================
>> --- src/org/apache/roller/pojos/UserData.java    (revision 488141)
>> +++ src/org/apache/roller/pojos/UserData.java    (working copy)
>> @@ -434,38 +434,14 @@
>>         return false;
>>     }
>> //Moved the method revokeRole to HibernateUserManagerImpl
>> -       /**
>> -     * Revokes specified role from user.
>> -     */
>> -    public void revokeRole(String roleName) throws RollerException {
>> -        RoleData removeme = null;
>> -        Iterator iter = roles.iterator();
>> -        while (iter.hasNext()) {
>> -            RoleData role = (RoleData) iter.next();
>> -            if (role.getRole().equals(roleName)) {
>> -                removeme = role;
>> -            }
>> -        }
>> -       -        /*
>> -         * NOTE: we do this outside the loop above because we are
>> not allowed
>> -         * to modify the contents of the Set while we are iterating
>> over it.
>> -         * doing so causes a ConcurrentModificationException
>
> This looks like a bug to me. The algorithm iterates the collection and
> actually ignores all but the last role found. Since the semantic is
> that there should be only one such instance, it should stop at the
> first match of role name and therefore there is no
> ConcurrentModificationException.
>
>>     public void savePage(WeblogTemplate data) throws RollerException;
>> Index:
>> src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
>> ===================================================================
>> ---
>> src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
>> (revision 488141)
>> +++
>> src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
>> (working copy)
>> @@ -26,6 +26,8 @@
>> import java.util.List;
>> import java.util.Map;
>> import java.util.TreeMap;
>> +import java.util.Collection;
>> +
>> import org.apache.roller.pojos.StatCount;
>> import org.hibernate.Criteria;
>> import org.hibernate.HibernateException;
>> @@ -58,6 +60,7 @@
>> import org.apache.roller.pojos.WeblogCategoryData;
>> import org.apache.roller.pojos.WeblogEntryData;
>> import org.apache.roller.pojos.WebsiteData;
>> +import org.apache.roller.pojos.RoleData;
>> import org.hibernate.Query;
>> @@ -406,6 +409,29 @@
>>         website.removePermission(target);
>>         this.strategy.remove(target);
>>     }
>> // Method moved from UserData to here
>> +
>> +    public void revokeRole(String roleName, UserData user) throws
>> RollerException {
>> +        RoleData removeme = null;
>> +        Collection roles = user.getRoles();
>> +        Iterator iter = roles.iterator();
>> +        while (iter.hasNext()) {
>> +            RoleData role = (RoleData) iter.next();
>> +            if (role.getRole().equals(roleName)) {
>> +                removeme = role;
>
> I'd think that the remove operations should be here, followed by break;
>
> The rest of the code looks good.
>
> Craig
>
> Craig Russell
> Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
> 408 276-5638 mailto:[EMAIL PROTECTED]
> P.S. A good JDO? O, Gasp!
>


Index: src/org/apache/roller/pojos/FolderData.java
===================================================================
--- src/org/apache/roller/pojos/FolderData.java (revision 488141)
+++ src/org/apache/roller/pojos/FolderData.java (working copy)
@@ -274,7 +274,7 @@
      *
      * @roller.wrapPojoMethod type="pojo-collection" 
class="org.apache.roller.pojos.BookmarkData"
      *
-     * @hibernate.set lazy="true" order-by="name" inverse="true" 
cascade="all-delete-orphan"
+     * @hibernate.set lazy="true" order-by="name" inverse="true" cascade="all"
      * @hibernate.collection-key column="folderid"
      * @hibernate.collection-one-to-many 
class="org.apache.roller.pojos.BookmarkData"
      */
@@ -296,15 +296,6 @@
         getBookmarks().add(bookmark);
     }

-
-    /**
-     * Remove a boomkark from folder.
-     */
-    public void removeBookmark(BookmarkData bookmark) {
-        getBookmarks().remove(bookmark);
-    }
-
-
     /**
      * @roller.wrapPojoMethod type="pojo-collection" 
class="org.apache.roller.pojos.BookmarkData"
      *
Index: src/org/apache/roller/pojos/UserData.java
===================================================================
--- src/org/apache/roller/pojos/UserData.java   (revision 488141)
+++ src/org/apache/roller/pojos/UserData.java   (working copy)
@@ -404,7 +404,7 @@


     /**
-     * @hibernate.set lazy="true" inverse="true" cascade="all-delete-orphan"
+     * @hibernate.set lazy="true" inverse="true" cascade="all"
      * @hibernate.collection-key column="userid"
      * @hibernate.collection-one-to-many 
class="org.apache.roller.pojos.RoleData"
      */
@@ -434,38 +434,14 @@
         return false;
     }

-
     /**
-     * Revokes specified role from user.
-     */
-    public void revokeRole(String roleName) throws RollerException {
-        RoleData removeme = null;
-        Iterator iter = roles.iterator();
-        while (iter.hasNext()) {
-            RoleData role = (RoleData) iter.next();
-            if (role.getRole().equals(roleName)) {
-                removeme = role;
-            }
-        }
-
-        /*
-         * NOTE: we do this outside the loop above because we are not allowed
-         * to modify the contents of the Set while we are iterating over it.
-         * doing so causes a ConcurrentModificationException
-         */
-        if(removeme != null) {
-            roles.remove(removeme);
-        }
-    }
-
-
-    /**
      * Grant to user role specified by role name.
      */
     public void grantRole(String roleName) throws RollerException {
         if (!hasRole(roleName)) {
             RoleData role = new RoleData(null, this, roleName);
             roles.add(role);
+            role.setUser(this);
         }
     }

Index: src/org/apache/roller/pojos/WeblogEntryData.java
===================================================================
--- src/org/apache/roller/pojos/WeblogEntryData.java    (revision 488141)
+++ src/org/apache/roller/pojos/WeblogEntryData.java    (working copy)
@@ -353,7 +353,7 @@
      *
      * @roller.wrapPojoMethod type="pojo-collection" 
class="org.apache.roller.pojos.EntryAttributeData"
      * @ejb:persistent-field
-     * @hibernate.set lazy="true" order-by="name" inverse="true" 
cascade="all-delete-orphan"
+     * @hibernate.set lazy="true" order-by="name" inverse="true" cascade="all"
      * @hibernate.collection-key column="entryid" type="String"
      * @hibernate.collection-one-to-many 
class="org.apache.roller.pojos.EntryAttributeData"
      */
@@ -404,12 +404,8 @@
             att.setValue(value);
         }
     }
-    public void removeEntryAttribute(String name) throws RollerException {
-        EntryAttributeData att = (EntryAttributeData)attMap.get(name);
-        if (att != null) {
-            attMap.remove(att);
-            attSet.remove(att);
-        }
+    public void onRemoveEntryAttribute(EntryAttributeData att) throws 
RollerException {
+        attMap.remove(att);
     }
     //-------------------------------------------------------------------------

@@ -609,7 +605,7 @@
      *
      * @ejb:persistent-field
      *
-     * @hibernate.set lazy="true" order-by="name" inverse="true" 
cascade="all-delete-orphan"
+     * @hibernate.set lazy="true" order-by="name" inverse="true" cascade="all"
      * @hibernate.collection-key column="entryid"
      * @hibernate.collection-one-to-many 
class="org.apache.roller.pojos.WeblogEntryTagData"
      */
@@ -654,16 +650,10 @@
         addedTags.add(name);
     }

-    public void removeTag(String name) throws RollerException {
-        for (Iterator it = tagSet.iterator(); it.hasNext();) {
-            WeblogEntryTagData tag = (WeblogEntryTagData) it.next();
-            if (tag.getName().equals(name)) {
-                removedTags.add(name);
-                it.remove();
-            }
-        }
+    public void onRemoveTag(String name) throws RollerException {
+        removedTags.add(name);
     }
-
+
     public Set getAddedTags() {
         return addedTags;
     }
@@ -696,9 +686,10 @@
                 newTags.remove(tag.getName());
             }
         }
-
+
+        WeblogManager weblogManager = 
RollerFactory.getRoller().getWeblogManager();
         for (Iterator it = removeTags.iterator(); it.hasNext();) {
-            removeTag((String) it.next());
+            weblogManager.removeWeblogEntryTag((String) it.next(), this);
         }

         for (Iterator it = newTags.iterator(); it.hasNext();) {
Index: src/org/apache/roller/business/UserManager.java
===================================================================
--- src/org/apache/roller/business/UserManager.java     (revision 488141)
+++ src/org/apache/roller/business/UserManager.java     (working copy)
@@ -27,7 +27,6 @@
 import org.apache.roller.pojos.UserData;
 import org.apache.roller.pojos.WebsiteData;

-
 /**
  * Manages users, weblogs, permissions, and weblog pages.
  */
@@ -325,8 +324,15 @@
     public void retireUser(WebsiteData website, UserData user)
         throws RollerException;

-
     /**
+     * Retire user from a website
+     * @param roleName Name of the role to be revoked
+     * @param user    User for whom the role is to be revoked
+     */
+    public void revokeRole(String roleName, UserData user)
+        throws RollerException;
+
+    /**
      * Store page.
      */
     public void savePage(WeblogTemplate data) throws RollerException;
Index: src/org/apache/roller/business/WeblogManager.java
===================================================================
--- src/org/apache/roller/business/WeblogManager.java   (revision 488141)
+++ src/org/apache/roller/business/WeblogManager.java   (working copy)
@@ -194,9 +194,22 @@
      * @return Collection of WeblogEntryData objects.
      */
     public List getWeblogEntriesPinnedToMain(Integer max) throws 
RollerException;
-
-
+
     /**
+     * Remove attribute with given name from given WeblogEntryData
+     * @param name Name of attribute to be removed
+     */
+    public void removeWeblogEntryAttribute(String name, WeblogEntryData entry)
+            throws RollerException;
+
+    /**
+     * Remove tag with given name from given WeblogEntryData
+     * @param name Name of tag to be removed
+     */
+    public void removeWeblogEntryTag(String name, WeblogEntryData entry)
+            throws RollerException;
+
+    /**
      * Save weblog category.
      */
     public void saveWeblogCategory(WeblogCategoryData cat) throws 
RollerException;
Index: 
src/org/apache/roller/business/hibernate/HibernateBookmarkManagerImpl.java
===================================================================
--- src/org/apache/roller/business/hibernate/HibernateBookmarkManagerImpl.java  
(revision 488141)
+++ src/org/apache/roller/business/hibernate/HibernateBookmarkManagerImpl.java  
(working copy)
@@ -81,10 +81,13 @@


     public void removeBookmark(BookmarkData bookmark) throws RollerException {
+        //Remove the bookmark from its parent folder
+        bookmark.getFolder().getBookmarks().remove(bookmark);
+        //Now remove it from database
         this.strategy.remove(bookmark);
-
         // update weblog last modified date.  date updated by saveWebsite()
-        
RollerFactory.getRoller().getUserManager().saveWebsite(bookmark.getWebsite());
+        RollerFactory.getRoller().getUserManager()
+                .saveWebsite(bookmark.getWebsite());
     }


Index: src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java
===================================================================
--- src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java      
(revision 488141)
+++ src/org/apache/roller/business/hibernate/HibernateUserManagerImpl.java      
(working copy)
@@ -26,6 +26,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
+import java.util.Collection;
+
 import org.apache.roller.pojos.StatCount;
 import org.hibernate.Criteria;
 import org.hibernate.HibernateException;
@@ -58,6 +60,7 @@
 import org.apache.roller.pojos.WeblogCategoryData;
 import org.apache.roller.pojos.WeblogEntryData;
 import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.pojos.RoleData;
 import org.hibernate.Query;


@@ -406,6 +409,20 @@
         website.removePermission(target);
         this.strategy.remove(target);
     }
+
+    public void revokeRole(String roleName, UserData user) throws 
RollerException {
+        RoleData removeme = null;
+        Collection roles = user.getRoles();
+        Iterator iter = roles.iterator();
+        while (iter.hasNext()) {
+            RoleData role = (RoleData) iter.next();
+            if (role.getRole().equals(roleName)) {
+                iter.remove();
+                this.strategy.remove(role);
+            }
+        }
+    }
+

     public WebsiteData getWebsite(String id) throws RollerException {
         return (WebsiteData) this.strategy.load(id,WebsiteData.class);
Index: src/org/apache/roller/business/hibernate/HibernateWeblogManagerImpl.java
===================================================================
--- src/org/apache/roller/business/hibernate/HibernateWeblogManagerImpl.java    
(revision 488141)
+++ src/org/apache/roller/business/hibernate/HibernateWeblogManagerImpl.java    
(working copy)
@@ -50,6 +50,7 @@
 import org.apache.roller.pojos.WeblogEntryTagData;
 import org.apache.roller.pojos.WeblogEntryTagAggregateData;
 import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.pojos.EntryAttributeData;
 import org.apache.roller.util.DateUtil;
 import org.hibernate.Criteria;
 import org.hibernate.HibernateException;
@@ -60,13 +61,7 @@
 import org.hibernate.criterion.MatchMode;
 import org.hibernate.criterion.Order;
 import org.hibernate.criterion.Restrictions;
-import org.hibernate.dialect.DerbyDialect;
-import org.hibernate.dialect.Dialect;
-import org.hibernate.dialect.OracleDialect;
-import org.hibernate.dialect.SQLServerDialect;
-import org.hibernate.engine.SessionFactoryImplementor;

-
 /**
  * Hibernate implementation of the WeblogManager.
  */
@@ -547,10 +542,39 @@
             throw new RollerException(e);
         }
     }
-
-
-    public WeblogEntryData getWeblogEntryByAnchor(WebsiteData website, String 
anchor)
+
+    public void removeWeblogEntryAttribute(String name, WeblogEntryData entry)
             throws RollerException {
+        for (Iterator it = entry.getEntryAttributes().iterator(); 
it.hasNext();) {
+            EntryAttributeData entryAttribute = (EntryAttributeData) it.next();
+            if (entryAttribute.getName().equals(name)) {
+                //Call back the entity to adjust its internal state
+                entry.onRemoveEntryAttribute(entryAttribute);
+                //Remove it from the collection
+                it.remove();
+                //Remove it from database
+                this.strategy.remove(entryAttribute);
+            }
+        }
+    }
+
+    public void removeWeblogEntryTag(String name, WeblogEntryData entry)
+            throws RollerException {
+        for (Iterator it = entry.getTags().iterator(); it.hasNext();) {
+            WeblogEntryTagData tag = (WeblogEntryTagData) it.next();
+            if (tag.getName().equals(name)) {
+                //Call back the entity to adjust its internal state
+                entry.onRemoveTag(name);
+                //Remove it from the collection
+                it.remove();
+                //Remove it from database
+                this.strategy.remove(tag);
+            }
+        }
+    }
+
+    public WeblogEntryData getWeblogEntryByAnchor(WebsiteData website, String 
anchor)
+            throws RollerException {

         if (website == null)
             throw new RollerException("Website is null");
Index: 
src/org/apache/roller/ui/authoring/struts/actions/WeblogEntryFormAction.java
===================================================================
--- 
src/org/apache/roller/ui/authoring/struts/actions/WeblogEntryFormAction.java    
    (revision 488141)
+++ 
src/org/apache/roller/ui/authoring/struts/actions/WeblogEntryFormAction.java    
    (working copy)
@@ -513,10 +513,11 @@
         }
         if (!valid || empty) {
             mLogger.debug("Removing MediaCast attributes");
+            WeblogManager weblogManager = 
RollerFactory.getRoller().getWeblogManager();
             try {
-                entry.removeEntryAttribute("att_mediacast_url");
-                entry.removeEntryAttribute("att_mediacast_type");
-                entry.removeEntryAttribute("att_mediacast_length");
+                weblogManager.removeWeblogEntryAttribute("att_mediacast_url", 
entry);
+                weblogManager.removeWeblogEntryAttribute("att_mediacast_type", 
entry);
+                
weblogManager.removeWeblogEntryAttribute("att_mediacast_length", entry);
             } catch (RollerException e) {
                 mLogger.error("ERROR removing invalid MediaCast attributes");
             }
Index: src/org/apache/roller/ui/admin/struts/formbeans/UserAdminForm.java
===================================================================
--- src/org/apache/roller/ui/admin/struts/formbeans/UserAdminForm.java  
(revision 488141)
+++ src/org/apache/roller/ui/admin/struts/formbeans/UserAdminForm.java  
(working copy)
@@ -23,6 +23,9 @@

 import org.apache.struts.action.ActionMapping;
 import org.apache.roller.RollerException;
+import org.apache.roller.business.Roller;
+import org.apache.roller.business.RollerImpl;
+import org.apache.roller.business.RollerFactory;
 import org.apache.roller.pojos.UserData;
 import org.apache.roller.ui.authoring.struts.forms.UserForm;

@@ -118,7 +121,7 @@
         }
         else
         {
-            user.revokeRole("admin");
+            RollerFactory.getRoller().getUserManager().revokeRole("admin", 
user);
         }
     }

Index: tests/org/apache/roller/business/WeblogEntryTest.java
===================================================================
--- tests/org/apache/roller/business/WeblogEntryTest.java       (revision 
488141)
+++ tests/org/apache/roller/business/WeblogEntryTest.java       (working copy)
@@ -415,8 +415,8 @@
         TestUtils.endSession(true);

         entry = mgr.getWeblogEntry(id);
-        entry.removeTag("testtag");
-        entry.removeTag("testtag2");
+        mgr.removeWeblogEntryTag("testtag", entry);
+        mgr.removeWeblogEntryTag("testtag2", entry);
         mgr.saveWeblogEntry(entry);
         TestUtils.endSession(true);

Index: tests/org/apache/roller/business/BookmarkTest.java
===================================================================
--- tests/org/apache/roller/business/BookmarkTest.java  (revision 488141)
+++ tests/org/apache/roller/business/BookmarkTest.java  (working copy)
@@ -328,7 +328,7 @@
         bookmarkb = (BookmarkData)testFolder.getBookmarks().iterator().next();

         // Remove one bookmark
-        testFolder.removeBookmark(bookmarka);
+        bmgr.removeBookmark(bookmarka);
         bmgr.saveFolder(testFolder);
         TestUtils.endSession(true);

Index: tests/org/apache/roller/business/UserTest.java
===================================================================
--- tests/org/apache/roller/business/UserTest.java      (revision 488141)
+++ tests/org/apache/roller/business/UserTest.java      (working copy)
@@ -186,7 +186,7 @@
         assertTrue(user.hasRole("admin"));

         // remove role
-        user.revokeRole("admin");
+        mgr.revokeRole("admin",user);
         mgr.saveUser(user);
         TestUtils.endSession(true);




Reply via email to