[hibernate-commits] Hibernate SVN: r11459 - in trunk/HibernateExt/shards/src: java/org/hibernate/shards/session and 3 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed May 2 03:38:01 EDT 2007


Author: max.ross
Date: 2007-05-02 03:38:01 -0400 (Wed, 02 May 2007)
New Revision: 11459

Added:
   trunk/HibernateExt/shards/src/test/org/hibernate/shards/integration/model/ModelIntegrationTest.java
Modified:
   trunk/HibernateExt/shards/src/java/org/hibernate/shards/Shard.java
   trunk/HibernateExt/shards/src/java/org/hibernate/shards/ShardImpl.java
   trunk/HibernateExt/shards/src/java/org/hibernate/shards/session/ShardedSessionImpl.java
   trunk/HibernateExt/shards/src/test/org/hibernate/shards/NonPermutedTests.java
   trunk/HibernateExt/shards/src/test/org/hibernate/shards/integration/model/ModelPermutedIntegrationTest.java
   trunk/HibernateExt/shards/src/test/org/hibernate/shards/session/ShardedSessionImplTest.java
Log:
Fix bugs in update, delete, and saveOrUpdate.
Add support for objects with ids that are base types.

Modified: trunk/HibernateExt/shards/src/java/org/hibernate/shards/Shard.java
===================================================================
--- trunk/HibernateExt/shards/src/java/org/hibernate/shards/Shard.java	2007-05-02 06:23:27 UTC (rev 11458)
+++ trunk/HibernateExt/shards/src/java/org/hibernate/shards/Shard.java	2007-05-02 07:38:01 UTC (rev 11459)
@@ -18,6 +18,11 @@
 
 package org.hibernate.shards;
 
+import org.hibernate.Criteria;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.classic.Session;
+import org.hibernate.engine.SessionFactoryImplementor;
 import org.hibernate.shards.criteria.CriteriaEvent;
 import org.hibernate.shards.criteria.CriteriaId;
 import org.hibernate.shards.criteria.ShardedCriteria;
@@ -26,12 +31,6 @@
 import org.hibernate.shards.query.ShardedQuery;
 import org.hibernate.shards.session.OpenSessionEvent;
 
-import org.hibernate.Criteria;
-import org.hibernate.LockMode;
-import org.hibernate.Query;
-import org.hibernate.classic.Session;
-import org.hibernate.engine.SessionFactoryImplementor;
-
 import java.io.Serializable;
 import java.util.List;
 import java.util.Set;
@@ -188,4 +187,8 @@
    * @see Query#uniqueResult()
    */
   Object uniqueResult(QueryId queryId);
+
+  void merge(Object object);
+
+  void merge(String entityName, Object object);
 }

Modified: trunk/HibernateExt/shards/src/java/org/hibernate/shards/ShardImpl.java
===================================================================
--- trunk/HibernateExt/shards/src/java/org/hibernate/shards/ShardImpl.java	2007-05-02 06:23:27 UTC (rev 11458)
+++ trunk/HibernateExt/shards/src/java/org/hibernate/shards/ShardImpl.java	2007-05-02 07:38:01 UTC (rev 11459)
@@ -18,6 +18,12 @@
 
 package org.hibernate.shards;
 
+import org.hibernate.Criteria;
+import org.hibernate.Interceptor;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.classic.Session;
+import org.hibernate.engine.SessionFactoryImplementor;
 import org.hibernate.shards.criteria.CriteriaEvent;
 import org.hibernate.shards.criteria.CriteriaId;
 import org.hibernate.shards.criteria.ShardedCriteria;
@@ -30,13 +36,6 @@
 import org.hibernate.shards.util.Preconditions;
 import org.hibernate.shards.util.Sets;
 
-import org.hibernate.Criteria;
-import org.hibernate.Interceptor;
-import org.hibernate.LockMode;
-import org.hibernate.Query;
-import org.hibernate.classic.Session;
-import org.hibernate.engine.SessionFactoryImplementor;
-
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.LinkedList;
@@ -382,5 +381,12 @@
     events.addLast(event);
   }
 
+  public void merge(Object object) {
+    establishSession().merge(object);
+  }
+
+  public void merge(String entityName, Object object) {
+    establishSession().merge(entityName, object);
+  }
 }
 

Modified: trunk/HibernateExt/shards/src/java/org/hibernate/shards/session/ShardedSessionImpl.java
===================================================================
--- trunk/HibernateExt/shards/src/java/org/hibernate/shards/session/ShardedSessionImpl.java	2007-05-02 06:23:27 UTC (rev 11458)
+++ trunk/HibernateExt/shards/src/java/org/hibernate/shards/session/ShardedSessionImpl.java	2007-05-02 07:38:01 UTC (rev 11459)
@@ -18,14 +18,34 @@
 
 package org.hibernate.shards.session;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.CacheMode;
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.Filter;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.ReplicationMode;
+import org.hibernate.SQLQuery;
+import org.hibernate.SessionException;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.TransientObjectException;
+import org.hibernate.classic.Session;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.proxy.HibernateProxy;
 import org.hibernate.shards.CrossShardAssociationException;
 import org.hibernate.shards.Shard;
 import org.hibernate.shards.ShardId;
 import org.hibernate.shards.ShardImpl;
 import org.hibernate.shards.ShardOperation;
 import org.hibernate.shards.ShardedTransaction;
-import org.hibernate.shards.util.Lists;
-import org.hibernate.shards.util.Sets;
 import org.hibernate.shards.criteria.CriteriaFactoryImpl;
 import org.hibernate.shards.criteria.CriteriaId;
 import org.hibernate.shards.criteria.ShardedCriteriaImpl;
@@ -43,32 +63,11 @@
 import org.hibernate.shards.strategy.selection.ShardResolutionStrategyDataImpl;
 import org.hibernate.shards.transaction.ShardedTransactionImpl;
 import org.hibernate.shards.util.Iterables;
+import org.hibernate.shards.util.Lists;
 import org.hibernate.shards.util.Maps;
 import org.hibernate.shards.util.Pair;
 import org.hibernate.shards.util.Preconditions;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.hibernate.CacheMode;
-import org.hibernate.Criteria;
-import org.hibernate.EntityMode;
-import org.hibernate.Filter;
-import org.hibernate.FlushMode;
-import org.hibernate.HibernateException;
-import org.hibernate.Interceptor;
-import org.hibernate.LockMode;
-import org.hibernate.Query;
-import org.hibernate.ReplicationMode;
-import org.hibernate.SQLQuery;
-import org.hibernate.SessionException;
-import org.hibernate.SessionFactory;
-import org.hibernate.Transaction;
-import org.hibernate.TransientObjectException;
-import org.hibernate.classic.Session;
-import org.hibernate.engine.SessionFactoryImplementor;
-import org.hibernate.id.IdentifierGenerator;
-import org.hibernate.metadata.ClassMetadata;
-import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.shards.util.Sets;
 import org.hibernate.stat.SessionStatistics;
 import org.hibernate.type.Type;
 
@@ -579,16 +578,14 @@
   }
 
   public Serializable save(String entityName, Object object) throws HibernateException {
-    ShardId shardId;
-    if(contains(object)) {
-      shardId = getShardIdForObject(object);
-    } else {
+    ShardId shardId = getShardIdForObject(object);
+    if(shardId == null) {
       shardId = selectShardIdForNewObject(object);
     }
     Preconditions.checkNotNull(shardId);
     setCurrentSubgraphShardId(shardId);
     log.debug(String.format("Saving object of type %s to shard %s", object.getClass(), shardId));
-     return shardIdsToShards.get(shardId).save(entityName, object);
+    return shardIdsToShards.get(shardId).save(entityName, object);
   }
 
   ShardId selectShardIdForNewObject(Object obj) {
@@ -725,83 +722,112 @@
         shard.saveOrUpdate(entityName, object);
       }
 
-      public void update(Shard shard, Object object) {
-        shard.update(entityName, object);
+      public void merge(Shard shard, Object object) {
+        shard.merge(entityName, object);
       }
     };
     applySaveOrUpdateOperation(op, object);
   }
 
   void applySaveOrUpdateOperation(SaveOrUpdateOperation op, Object object) {
-    List<ShardId> saveOrUpdateShardIds = determineShardIdsForObjectThatMightAlreadyExist(object);
-    if(saveOrUpdateShardIds.size() == 1) {
-      /**
-       * if there's only 1 shard id we don't need to worry about whether this is
-       * a save or an update because we know we've got the right shard.
-       * We'll let that shard figure out what the right thing to do is
-       */
-      ShardId shardId = saveOrUpdateShardIds.get(0);
-      setCurrentSubgraphShardId(shardId);
+    ShardId shardId = getShardIdForObject(object);
+    if(shardId != null) {
+      // attached object
       op.saveOrUpdate(shardIdsToShards.get(shardId), object);
-    } else {
-      /**
-       * If we've got multiple shard ids we know it's an update.  However,
-       * we can't call saveOrUpdate() on multiple shards because if it
-       * already exists and we call saveOrUpdate() on the wrong shard we're
-       * going to end up with the same object in multiple shards, and that's
-       * really bad.  Instead we'll just figure out the single shard on which
-       * the object resides and perform the update there.
-       */
-      ShardId shardId = getShardIdForObject(object);
-      setCurrentSubgraphShardId(shardId);
-      Shard shard = shardIdsToShards.get(shardId);
-      op.update(shard, object);
+      return;
     }
-  }
+    List<Shard> potentialShards = determineShardsObjectViaResolutionStrategy(object);
+    if(potentialShards.size() == 1) {
+      op.saveOrUpdate(potentialShards.get(0), object);
+      return;
+    }
 
-  List<ShardId> determineShardIdsForObjectThatMightAlreadyExist(Object object) {
+    /**
+     * Too bad, we've got a detached object that could be on more than 1 shard.
+     * The only safe way to handle this is to try and lookup the object, and if
+     * it exists, do a merge, and if it doesn't, do a save.
+     */
     Serializable id = extractId(object);
-    if(id == null) {
-      ShardId shardId = selectShardIdForNewObject(object);
-      // if there's no id then it's clearly a save operation
-      return Collections.singletonList(shardId);
+    if(id != null) {
+      Object persistent = get(object.getClass(), id);
+      if(persistent != null) {
+        shardId = getShardIdForObject(persistent);
+      }
     }
-    /**
-     * there is an id so it might be a save operation (user provided the id)
-     * or it might be an update
-     */
-    ShardResolutionStrategyData srsd = new ShardResolutionStrategyDataImpl(object.getClass(), id);
-    return selectShardIdsFromShardResolutionStrategyData(srsd);
+    if(shardId != null) {
+      op.merge(shardIdsToShards.get(shardId), object);
+    } else {
+      save(object);
+    }
   }
 
-  private Serializable extractId(Object object) {
+  Serializable extractId(Object object) {
     ClassMetadata cmd = shardedSessionFactory.getClassMetadata(object.getClass());
     // I'm just guessing about the EntityMode
     return cmd.getIdentifier(object, EntityMode.POJO);
   }
 
-  public void update(Object object) throws HibernateException {
-    /*
-     * we might get back multiple shards if there isn't a sure-fire
-     * way to derive the shard from the object, but since the object
-     * only exists on one shard there's no harm in trying to update
-     * it multiple places because the update will only succeed in one place.
+  private interface UpdateOperation {
+    void update(Shard shard, Object object);
+    void merge(Shard shard, Object object);
+  }
+
+  private static final UpdateOperation SIMPLE_UPDATE_OPERATION =
+      new UpdateOperation() {
+        public void update(Shard shard, Object object) {
+          shard.update(object);
+        }
+
+        public void merge(Shard shard, Object object) {
+          shard.merge(object);
+        }
+      };
+
+  private void applyUpdateOperation(UpdateOperation op, Object object) {
+    ShardId shardId = getShardIdForObject(object);
+    if(shardId != null) {
+      // attached object
+      op.update(shardIdsToShards.get(shardId), object);
+      return;
+    }
+    List<Shard> potentialShards = determineShardsObjectViaResolutionStrategy(object);
+    if(potentialShards.size() == 1) {
+      op.update(potentialShards.get(0), object);
+      return;
+    }
+    /**
+     * Too bad, we've got a detached object that could be on more than 1 shard.
+     * The only safe way to perform the update is to load the object and then
+     * do a merge.
      */
-    // What about exceptions?
-    for(Shard shard : determineShardsForObjectThatExists(object)) {
-      shard.update(object);
-    }
+    Object persistent = get(object.getClass(), extractId(object));
+    shardId = getShardIdForObject(persistent);
+    op.merge(shardIdsToShards.get(shardId), object);
   }
 
-  public void update(String entityName, Object object)
+  public void update(Object object) throws HibernateException {
+    applyUpdateOperation(SIMPLE_UPDATE_OPERATION, object);
+  }
+
+  public void update(final String entityName, Object object)
       throws HibernateException {
-    for(Shard shard : determineShardsForObjectThatExists(object)) {
-      shard.update(entityName, object);
-    }
+    UpdateOperation op = new UpdateOperation() {
+      public void update(Shard shard, Object object) {
+        shard.update(entityName, object);
+      }
+
+      public void merge(Shard shard, Object object) {
+        shard.merge(entityName, object);
+      }
+    };
+    applyUpdateOperation(op, object);
   }
 
-  private List<Shard> determineShardsForObjectThatExists(Object object) {
+  List<Shard> determineShardsObjectViaResolutionStrategy(Object object) {
     Serializable id = extractId(object);
+    if(id == null) {
+      return Collections.emptyList();
+    }
     ShardResolutionStrategyData srsd = new ShardResolutionStrategyDataImpl(object.getClass(), id);
     List<ShardId> shardIds = selectShardIdsFromShardResolutionStrategyData(srsd);
     return shardIdListToShardList(shardIds);
@@ -837,32 +863,63 @@
     throw new UnsupportedOperationException();
   }
 
-  public void delete(Object object) throws HibernateException {
+  private interface DeleteOperation {
+    void delete(Shard shard, Object object);
+  }
+
+  private void applyDeleteOperation(DeleteOperation op, Object object) {
+    ShardId shardId = getShardIdForObject(object);
+    if(shardId != null) {
+      // attached object
+      op.delete(shardIdsToShards.get(shardId), object);
+      return;
+    }
     /**
-     * we might get back multiple shards if there isn't a sure-fire
-     * way to derive the shard from the object.  It turns out that if you have an
+     * Detached object.
+     * We can't just try to delete on each shard because if you have an
      * object associated with Session x and you try to delete that object in
-     * Sessoin y, and if that object has persistent collections, Hibernate will
+     * Session y, and if that object has persistent collections, Hibernate will
      * blow up because it will try to associate the persistent collection with
      * a different Session as part of the cascade.  In order to avoid this we
-     * need to be precise about the shard on which we perform the delete
+     * need to be precise about the shard on which we perform the delete.
+     *
+     * First let's see if we can derive the shard just from the object's id.
      */
-
-    // What about exceptions?
-    List<Shard> potentialShards = determineShardsForObjectThatExists(object);
+    List<Shard> potentialShards = determineShardsObjectViaResolutionStrategy(object);
     if(potentialShards.size() == 1) {
-      potentialShards.get(0).delete(object);
+      op.delete(potentialShards.get(0), object);
       return;
     }
-    ShardId shardId = getShardIdForObject(object, potentialShards);
-    shardIdsToShards.get(shardId).delete(object);
+    /**
+     * Too bad, we've got a detached object that could be on more than 1 shard.
+     * The only safe way to perform the delete is to load the object before
+     * deleting.
+     */
+    Object persistent = get(object.getClass(), extractId(object));
+    shardId = getShardIdForObject(persistent);
+    op.delete(shardIdsToShards.get(shardId), persistent);
   }
 
-  public void delete(String entityName, Object object)
+  private static final DeleteOperation SIMPLE_DELETE_OPERATION =
+      new DeleteOperation() {
+
+        public void delete(Shard shard, Object object) {
+          shard.delete(object);
+        }
+      };
+
+  public void delete(Object object) throws HibernateException {
+    applyDeleteOperation(SIMPLE_DELETE_OPERATION, object);
+  }
+
+  public void delete(final String entityName, Object object)
       throws HibernateException {
-    for(Shard shard : determineShardsForObjectThatExists(object)) {
-      shard.delete(entityName, object);
-    }
+    DeleteOperation op = new DeleteOperation() {
+      public void delete(Shard shard, Object object) {
+        shard.delete(entityName, object);
+      }
+    };
+    applyDeleteOperation(op, object);
   }
 
   /**
@@ -1258,7 +1315,7 @@
 
   interface SaveOrUpdateOperation {
     void saveOrUpdate(Shard shard, Object object);
-    void update(Shard shard, Object object);
+    void merge(Shard shard, Object object);
   }
 
   private static final SaveOrUpdateOperation SAVE_OR_UPDATE_SIMPLE = new SaveOrUpdateOperation() {
@@ -1266,8 +1323,8 @@
       shard.saveOrUpdate(object);
     }
 
-    public void update(Shard shard, Object object) {
-      shard.update(object);
+    public void merge(Shard shard, Object object) {
+      shard.merge(object);
     }
   };
 
@@ -1293,6 +1350,7 @@
   }
 
   public ShardId getShardIdForObject(Object obj, List<Shard> shardsToConsider) {
+    // TODO(maxr) optimize this by keeping an identity map of objects to shardId
     Shard shard = getShardForObject(obj, shardsToConsider);
     if(shard == null) {
       return null;

Modified: trunk/HibernateExt/shards/src/test/org/hibernate/shards/NonPermutedTests.java
===================================================================
--- trunk/HibernateExt/shards/src/test/org/hibernate/shards/NonPermutedTests.java	2007-05-02 06:23:27 UTC (rev 11458)
+++ trunk/HibernateExt/shards/src/test/org/hibernate/shards/NonPermutedTests.java	2007-05-02 07:38:01 UTC (rev 11459)
@@ -65,6 +65,7 @@
     classes.add(org.hibernate.shards.id.ShardedTableHiLoGeneratorTest.class);
     classes.add(org.hibernate.shards.id.ShardedUUIDGeneratorTest.class);
     classes.add(org.hibernate.shards.integration.model.MemoryLeakTest.class);
+    classes.add(org.hibernate.shards.integration.model.ModelIntegrationTest.class);
     classes.add(org.hibernate.shards.loadbalance.RoundRobinShardLoadBalancerTest.class);
     classes.add(org.hibernate.shards.query.SetBigDecimalEventTest.class);
     classes.add(org.hibernate.shards.query.SetBigIntegerEventTest.class);

Added: trunk/HibernateExt/shards/src/test/org/hibernate/shards/integration/model/ModelIntegrationTest.java
===================================================================
--- trunk/HibernateExt/shards/src/test/org/hibernate/shards/integration/model/ModelIntegrationTest.java	                        (rev 0)
+++ trunk/HibernateExt/shards/src/test/org/hibernate/shards/integration/model/ModelIntegrationTest.java	2007-05-02 07:38:01 UTC (rev 11459)
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package org.hibernate.shards.integration.model;
+
+import org.hibernate.shards.integration.BaseShardingIntegrationTestCase;
+import org.hibernate.shards.model.IdIsBaseType;
+
+/**
+ * @author maxr at google.com (Max Ross)
+ */
+public class ModelIntegrationTest extends BaseShardingIntegrationTestCase {
+
+  public void testSaveIdIsBaseType() {
+    IdIsBaseType hli = new IdIsBaseType();
+    session.beginTransaction();
+    hli.setValue("yamma");
+    session.save(hli);
+    commitAndResetSession();
+    hli = reload(hli);
+    assertNotNull(hli);
+  }
+
+  public void testSaveOrUpdateIdIsBasetype() {
+    IdIsBaseType hli = new IdIsBaseType();
+    session.beginTransaction();
+    hli.setValue("yamma");
+    session.saveOrUpdate(hli);
+    commitAndResetSession();
+    hli = reload(hli);
+    assertNotNull(hli);
+  }
+
+}

Modified: trunk/HibernateExt/shards/src/test/org/hibernate/shards/integration/model/ModelPermutedIntegrationTest.java
===================================================================
--- trunk/HibernateExt/shards/src/test/org/hibernate/shards/integration/model/ModelPermutedIntegrationTest.java	2007-05-02 06:23:27 UTC (rev 11458)
+++ trunk/HibernateExt/shards/src/test/org/hibernate/shards/integration/model/ModelPermutedIntegrationTest.java	2007-05-02 07:38:01 UTC (rev 11459)
@@ -18,6 +18,12 @@
 
 package org.hibernate.shards.integration.model;
 
+import org.hibernate.HibernateException;
+import org.hibernate.SessionFactory;
+import org.hibernate.TransactionException;
+import org.hibernate.classic.Session;
+import org.hibernate.criterion.Projections;
+import org.hibernate.proxy.HibernateProxy;
 import org.hibernate.shards.ShardId;
 import org.hibernate.shards.integration.BaseShardingIntegrationTestCase;
 import org.hibernate.shards.integration.MemoryLeakPlugger;
@@ -46,13 +52,6 @@
 import org.hibernate.shards.util.Maps;
 import org.hibernate.shards.util.Sets;
 
-import org.hibernate.HibernateException;
-import org.hibernate.SessionFactory;
-import org.hibernate.TransactionException;
-import org.hibernate.classic.Session;
-import org.hibernate.criterion.Projections;
-import org.hibernate.proxy.HibernateProxy;
-
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.List;
@@ -82,7 +81,7 @@
 
   @Override
   protected IdGenType getIdGenType() {
-    return IdGenType.SHARD_UUID;
+    return IdGenType.SIMPLE;
   }
   */
 
@@ -106,8 +105,7 @@
       session.save(b);
       buildings.add(b);
     }
-    session.getTransaction().commit();
-    resetSession();
+    commitAndResetSession();
     List<Integer> counts =  session.createCriteria(Building.class).setProjection(Projections.rowCount()).list();
     int total = 0;
     for(Integer count : counts) {
@@ -150,8 +148,7 @@
     for(List<Serializable> idList : shards.values()) {
       assertEquals(2, idList.size());
     }
-    session.getTransaction().commit();
-    resetSession();
+    commitAndResetSession();
     session.beginTransaction();
     for(Building b : buildings) {
       Building bReloaded = reloadAssertNotNull(b);
@@ -169,8 +166,7 @@
       // now let's get rid of the buildings
       session.delete(bReloaded);
     }
-    session.getTransaction().commit();
-    resetSession();
+    commitAndResetSession();
     for(Building b : buildings) {
       assertNull(reload(b));
       for(Floor f : b.getFloors()) {
@@ -179,20 +175,36 @@
     }
   }
 
-  public void testSaveOrUpdate() {
+  public void testSaveOrUpdateAttached() {
     session.beginTransaction();
     Building b = building("b");
     floor(b, 23);
-    session.save(b);
-    session.getTransaction().commit();
-    resetSession();
+    session.saveOrUpdate(b);
+    commitAndResetSession();
     session.beginTransaction();
     b = reload(b);
     b.setName("b2");
     session.saveOrUpdate(b);
-    session.getTransaction().commit();
+    commitAndResetSession();
+    b = reload(b);
+    assertEquals("b2", b.getName());
   }
 
+  public void testSaveOrUpdateDetached() {
+    session.beginTransaction();
+    Building b = building("b");
+    floor(b, 23);
+    session.saveOrUpdate(b);
+    commitAndResetSession();
+    // let's do saveOrUpdate on a detached entity
+    Building transientB = building("b2");
+    transientB.setBuildingId(b.getBuildingId());
+    session.saveOrUpdate(transientB);
+    commitAndResetSession();
+    b = reload(b);
+    assertEquals("b2", b.getName());
+  }
+
   public void testSavingOneToManyChildViaCascade() {
     session.beginTransaction();
     Building b = building("awesome building");
@@ -837,6 +849,60 @@
     }
   }
 
+  public void testUpdateOfAttachedEntity() {
+    session.beginTransaction();
+    Building b = building("b1");
+    session.save(b);
+    commitAndResetSession();
+    b = reload(b);
+    b.setName("other name");
+    session.update(b);
+    commitAndResetSession();
+    b = reload(b);
+    assertEquals("other name", b.getName());
+  }
+
+  // calling update on a detached entity should actually result in a merge
+  public void testUpdateOfDetachedEntity() {
+    session.beginTransaction();
+    Building b = building("b1");
+    session.save(b);
+    commitAndResetSession();
+    Building transientB = building("a different name");
+    transientB.setBuildingId(b.getBuildingId());
+    session.update(transientB);
+    commitAndResetSession();
+    b = (Building) session.get(Building.class, transientB.getBuildingId());
+    assertNotNull(b);
+    assertEquals("a different name", b.getName());
+  }
+
+  public void testDeleteOfAttachedEntity() {
+    session.beginTransaction();
+    Building b = building("b1");
+    session.save(b);
+    commitAndResetSession();
+    b = reload(b);
+    session.delete(b);
+    commitAndResetSession();
+    b = reload(b);
+    assertNull(b);
+  }
+
+  public void testDeleteOfDetachedEntity() {
+    session.beginTransaction();
+    Building b = building("b1");
+    session.save(b);
+    commitAndResetSession();
+    Building detached = new Building();
+    detached.setBuildingId(b.getBuildingId());
+    detached.setName("harold");
+    session.delete(detached);
+    commitAndResetSession();
+    b = reload(b);
+    assertNull(b);
+  }
+
   // this is a really good way to shake out synchronization bugs
   public void xtestOverAndOver() throws Exception {
     final boolean[] go = {true};

Modified: trunk/HibernateExt/shards/src/test/org/hibernate/shards/session/ShardedSessionImplTest.java
===================================================================
--- trunk/HibernateExt/shards/src/test/org/hibernate/shards/session/ShardedSessionImplTest.java	2007-05-02 06:23:27 UTC (rev 11458)
+++ trunk/HibernateExt/shards/src/test/org/hibernate/shards/session/ShardedSessionImplTest.java	2007-05-02 07:38:01 UTC (rev 11459)
@@ -18,15 +18,23 @@
 
 package org.hibernate.shards.session;
 
-import org.hibernate.shards.defaultmock.ClassMetadataDefaultMock;
-import org.hibernate.shards.defaultmock.InterceptorDefaultMock;
-import org.hibernate.shards.defaultmock.SessionFactoryDefaultMock;
-import org.hibernate.shards.defaultmock.TypeDefaultMock;
+import junit.framework.TestCase;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.Session;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.metadata.ClassMetadata;
 import org.hibernate.shards.Shard;
 import org.hibernate.shards.ShardDefaultMock;
 import org.hibernate.shards.ShardId;
 import org.hibernate.shards.ShardImpl;
 import org.hibernate.shards.ShardedSessionFactoryDefaultMock;
+import org.hibernate.shards.defaultmock.ClassMetadataDefaultMock;
+import org.hibernate.shards.defaultmock.InterceptorDefaultMock;
+import org.hibernate.shards.defaultmock.SessionFactoryDefaultMock;
+import org.hibernate.shards.defaultmock.TypeDefaultMock;
 import org.hibernate.shards.engine.ShardedSessionFactoryImplementor;
 import org.hibernate.shards.strategy.ShardStrategy;
 import org.hibernate.shards.strategy.ShardStrategyDefaultMock;
@@ -36,17 +44,9 @@
 import org.hibernate.shards.util.Maps;
 import org.hibernate.shards.util.Pair;
 import org.hibernate.shards.util.Sets;
-
-import junit.framework.TestCase;
-
-import org.hibernate.EntityMode;
-import org.hibernate.HibernateException;
-import org.hibernate.Interceptor;
-import org.hibernate.Session;
-import org.hibernate.engine.SessionFactoryImplementor;
-import org.hibernate.metadata.ClassMetadata;
 import org.hibernate.type.Type;
 
+import java.io.Serializable;
 import java.sql.Connection;
 import java.util.Collections;
 import java.util.List;
@@ -75,35 +75,62 @@
   }
 
   public void testApplySaveOrUpdateOperation() {
-    final List[] listToReturn = { Lists.newArrayList(new ShardId(0)) };
+    final List<ShardId> shardIdToReturn = Lists.newArrayList(new ShardId(0));
+    final List<Shard> shardListToReturn = Lists.<Shard>newArrayList(new ShardDefaultMock());
+    final Serializable[] idToReturn = {null};
+    final boolean[] saveCalled = {false};
     ShardedSessionImpl ssi = new MyShardedSessionImpl() {
       @Override
-      List<ShardId> determineShardIdsForObjectThatMightAlreadyExist(Object object) {
-        return listToReturn[0];
+      public ShardId getShardIdForObject(Object obj) {
+        return shardIdToReturn.get(0);
       }
+
+      List<Shard> determineShardsObjectViaResolutionStrategy(Object object) {
+        return shardListToReturn;
+      }
+
+      Serializable extractId(Object object) {
+        return idToReturn[0];
+      }
+
+      public Serializable save(String entityName, Object object)
+      throws HibernateException {
+        saveCalled[0] = true;
+        return null;
+      }
     };
 
     final boolean[] saveOrUpdateCalled = { false };
-    final boolean[] updateCalled = { false };
+    final boolean[] mergeCalled = { false };
     ShardedSessionImpl.SaveOrUpdateOperation op = new ShardedSessionImpl.SaveOrUpdateOperation() {
       public void saveOrUpdate(Shard shard, Object object) {
         saveOrUpdateCalled[0] = true;
       }
 
-      public void update(Shard shard, Object object) {
-        updateCalled[0] = true;
+      public void merge(Shard shard, Object object) {
+        mergeCalled[0] = true;
       }
+
     };
     ssi.applySaveOrUpdateOperation(op, null);
     assertTrue(saveOrUpdateCalled[0]);
-    assertFalse(updateCalled[0]);
+    assertFalse(mergeCalled[0]);
+    shardIdToReturn.set(0, null);
+    saveOrUpdateCalled[0] = false;
 
+    ssi.applySaveOrUpdateOperation(op, null);
+    assertTrue(saveOrUpdateCalled[0]);
+    assertFalse(mergeCalled[0]);
+    shardIdToReturn.set(0, null);
     saveOrUpdateCalled[0] = false;
-    listToReturn[0].add(new ShardDefaultMock());
 
+    shardListToReturn.add(new ShardDefaultMock());
     ssi.applySaveOrUpdateOperation(op, null);
     assertFalse(saveOrUpdateCalled[0]);
-    assertTrue(updateCalled[0]);
+    assertFalse(mergeCalled[0]);
+    assertTrue(saveCalled[0]);
+
+    //TODO(maxr) write test for when we call merge()
   }
 
   public void testClose() {




More information about the hibernate-commits mailing list