title: why following code fails with org.springframework.dao.DuplicateKeyException
body: <pre><code>//following code is executed from non-transactional service
  Member member = Member.get(1)
  try{
      Member.withNewTransaction {
          throw new RuntimeException("error")
      }
  }catch(Exception e) {
      //handle exception
  }
  member.username = "Fred"
  member.save()
  </code></pre>
  
  <p>The following code fails on the <strong>last line with</strong></p>
  
  <pre><code>org.springframework.dao.DuplicateKeyException: a different object with the same identifier value was already associated with the session: [com.test.Member#1]; nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.test.Member#1]
  </code></pre>
  
  <p>I wouldn't expect this code to fail, because the exception causes TX to rollback, but its caught so the flow passes to next statements. The reality is that from the point where the exception and rollback happen, any subsequent save fails with <code>DuplicateKeyException</code>.</p>
  
  <p>I think this has to do something with hibernate session, which gets corrupted when the rollback happens and anything attached to this session can't be persisted by hibernate anymore (the error is misleading).</p>
  
  <p><strong>Can anyone please explain in more detail what is really happening and what is the best practice to handle this case ?</strong></p>
  
  <p>So far I've found 3 solutions:</p>
  
  <p><strong>Solution #1</strong> - put the transaction to its own session</p>
  
  <pre><code>Member.withNewSession {
      Member.withNewTransaction {
          throw new RuntimeException("error")
      }
  }
  </code></pre>
  
  <p><strong>Solution #2</strong> - re-select the member after the rollback happens</p>
  
  <pre><code>member = Member.get(member.id)
  member.username = "Fred"
  member.save()
  </code></pre>
  
  <p><strong>Solution #3</strong> - start a Master transaction</p>
  
  <pre><code>Member.withTransaction {
      Member member = Member.get(1)
      try {
          Member.withNewTransaction {
              throw new RuntimeException("error")
          }
      } catch(Exception e) {
          //handle exception
      }
      member.username = "Fred"
      member.save()
  }
  </code></pre>