Index: core/src/test/java/org/jbpm/db/DeleteProcessInstanceDbTest.java
===================================================================
--- core/src/test/java/org/jbpm/db/DeleteProcessInstanceDbTest.java	(revision 6912)
+++ core/src/test/java/org/jbpm/db/DeleteProcessInstanceDbTest.java	(working copy)
@@ -112,7 +112,58 @@
     newTransaction();
     assertDeleted(processInstance);
   }
+  
+  // Test related to JBPM-3171: if the ProcessInstance.hashCode function contains the process instance key,
+  // the variable handling is broken, which results in a Integrity constraint violation FK_TKVARMAP_CTXT table: JBPM_TOKENVARIABLEMAP
+  // at process instance deletion.
+  public void testDeleteProcessInstanceWithSubProcessInstanceAndVariableAndSetKey() {
+    ProcessDefinition buyCheese = ProcessDefinition.parseXmlString("<process-definition name='buy cheese'>"
+        + "  <start-state>"
+        + "    <transition to='find shop' />"
+        + "      <event type='node-leave'>"
+        + "        <script name='set_key'>"
+        + "          executionContext.getProcessInstance().setKey(\"fondue_purchase\");"
+        + "        </script>"
+        + "      </event>"      
+        + "  </start-state>"
+        + "  <state name='find shop' >"
+        + "      <event type='node-enter'>"
+        + "        <script name='change_variable'>"
+        + "          executionContext.setVariable(\"cheese\", \"comte\");"
+        + "        </script>"
+        + "      </event>"      
+        + " </state>"
+        + "</process-definition>");
+    deployProcessDefinition(buyCheese);
 
+    ProcessDefinition makeFondue = ProcessDefinition.parseXmlString("<process-definition name='make fondue'>"
+        + "  <start-state>"
+        + "    <transition to='buy cheese' />"
+        + "      <event type='node-leave'>"
+        + "        <script name='add_variable'>"
+        + "          executionContext.setVariable(\"cheese\", \"vacherin\");"
+        + "        </script>"
+        + "      </event>"
+        + "  </start-state>"
+        + "  <process-state name='buy cheese'>"
+        + "    <sub-process name='buy cheese'/>"
+        + "    <variable name='cheese' access='read,write' mapped-name='cheese'/>"
+        + "  </process-state>"
+        + "</process-definition>");
+    deployProcessDefinition(makeFondue);
+
+    ProcessInstance processInstance = jbpmContext.newProcessInstance("make fondue");
+    processInstance.signal();
+
+    processInstance = saveAndReload(processInstance);
+    jbpmContext.getGraphSession().deleteProcessInstance(processInstance);
+
+    newTransaction();
+    assertDeleted(processInstance.getRootToken().getProcessInstance());
+    assertDeleted(processInstance);
+  }
+
+  
   private void assertDeleted(ProcessInstance processInstance) {
     long processInstanceId = processInstance.getId();
     assertNull("process instance not deleted: " + processInstanceId,
Index: core/src/test/java/org/jbpm/jbpm3171/JBPM3171Test.java
===================================================================
--- core/src/test/java/org/jbpm/jbpm3171/JBPM3171Test.java	(revision 0)
+++ core/src/test/java/org/jbpm/jbpm3171/JBPM3171Test.java	(revision 0)
@@ -0,0 +1,86 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This 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 software 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.jbpm3171;
+
+import java.util.Date;
+
+import org.jbpm.AbstractJbpmTestCase;
+import org.jbpm.context.exe.ContextInstance;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.ProcessInstance;
+
+/**
+ * Test hashCode implementation of ProcessInstance and - as a result -
+ * correct variable handling
+ * 
+ * @see <a href="https://jira.jboss.org/browse/JBPM-3171">JBPM-3171</a>
+ * @author Martin Weiler
+ */
+public class JBPM3171Test extends AbstractJbpmTestCase {
+
+  ProcessDefinition processDefinition;
+
+  protected void setUp() throws Exception {
+    super.setUp();
+    processDefinition = ProcessDefinition.createNewProcessDefinition();    
+  }
+
+  // test normal variable handling
+  public void testVariableWithoutKey() {
+    ProcessInstance processInstance = new ProcessInstance(processDefinition);
+    ContextInstance contextInstance = processInstance.getContextInstance();
+    contextInstance.setVariable("red", new String("hat"));
+    assertEquals("hat", contextInstance.getVariable("red"));
+  }
+
+  // test variable handling when process instance has been created with a key
+  public void testVariableWithKey() {
+    ProcessInstance processInstance = new ProcessInstance(processDefinition, null, "key_at_instance_creation");
+    ContextInstance contextInstance = processInstance.getContextInstance();      
+    contextInstance.setVariable("j", new String("boss"));
+    assertEquals("boss", contextInstance.getVariable("j"));
+  }   
+
+  // test variable handling when setKey is called on the process instance after creation  
+  public void testVariableAfterSettingKey() {
+    ProcessInstance processInstance = new ProcessInstance(processDefinition);
+    ContextInstance contextInstance = processInstance.getContextInstance();      
+    contextInstance.setVariable("hiber", new String("nate"));
+    contextInstance.getProcessInstance().setKey("key_set_on_existing_instance");
+    assertEquals("nate", contextInstance.getVariable("hiber"));
+  } 
+
+  // test if hashCode changes after setting a key on the process instance
+  public void testHashCodeAfterSettingKey() {  
+    ProcessInstance processInstance = new ProcessInstance(processDefinition);
+    int hashCode = processInstance.hashCode();
+    processInstance.setKey("key_set_on_existing_instance");
+    assertEquals(hashCode, processInstance.hashCode());
+  }
+  
+  // basic ProcessInstance.hashCode test
+  public void testProcessInstanceHashCode() {
+    ProcessInstance processInstance1 = new ProcessInstance(processDefinition);
+    ProcessInstance processInstance2 = new ProcessInstance(processDefinition);
+    assertTrue(processInstance1.hashCode()!=processInstance2.hashCode());      
+  }
+}
Index: core/src/main/java/org/jbpm/graph/exe/ProcessInstance.java
===================================================================
--- core/src/main/java/org/jbpm/graph/exe/ProcessInstance.java	(revision 6912)
+++ core/src/main/java/org/jbpm/graph/exe/ProcessInstance.java	(working copy)
@@ -502,14 +502,18 @@
       && processDefinition.equals(other.getProcessDefinition());
   }
 
+   /**
+   * Computes the hash code for this process instance. Process instances without an id
+   * (not persisted to db) will return their {@linkplain System#identityHashCode(Object) identity
+   * hash code}.
+   */
   public int hashCode() {
-    if (key == null) return super.hashCode();
-
-    int result = 295436291 + key.hashCode();
-    result = 1367411281 * result + processDefinition.hashCode();
-    return result;
+    if (id != 0) 
+        return (int) (id ^ (id >>> 32));
+    else
+        return System.identityHashCode(this);
   }
-
+  
   public String toString() {
     return "ProcessInstance"
       + (key != null ? '(' + key + ')' : id != 0 ? "(" + id + ')'
