<style>
code::before {
color:red
}
</style>
# Entity Life Cycle in Grails with Hibernate
###### tags: `Grails`
Alright, today I got some Exceptions from test server. **DuplicateKeyException** & **NonUniqueObjectException**. The former was defined by Spring, and the latter is from Hibernate. Explain it in a short way from below:
>"a different object with the same identifier value was already associated with the session"
Before we dive into this Exception, I want to introduce the methods that Grails/Hibernate session use to re-attached the entity after session was close after each request.
## Attach and Refresh
There are many ways to alter the state of the entity from **DETACHED** back to **MANAGE**, e.g `merge()` etc. Let us focus on attach and refresh first since I've been used these two method in the application mostly.
>Hibernate manages persistent instances in a persistence Session. A new Session is created per request and is closed at the end of the request. If an object is retrieved from the Session and placed into a web scope such as the HttpSession it will be "detached" from the Hibernate Session once the Session is closed and discarded. You can use the attach() method to re-attach an existing persistent instance to the persistence Session of the current request.
這裡提到說,如果是放在session中的persistent instance,在request結束後會變為detached狀態,這時候無法使用Lazyloading去取得該instance底層資料例如`user.userDetail`。
這時候可以使用 `attach()` or `refresh()`,取得當前 request 中的 hibernate session ,將 instance 轉為 persistent instance ,這時候才可以使用 Lazyloading 的方式取得下層資料。
If we retrieve an object value from entity, example code below :
```groovy=
class User {
Long id
String firstName
String lastName
UserDetail userDetail
}
class UserDetail {
String phoneNumber
String address
}
//When we get an eneity from the database.
def user = User.get(1)
def userDetail = user.userDetail
//discard the eneity, make it detached.
user.discard()
```
After calling `discard()`, our **user** entity status was changed from **MANAGE** to **DETACHED**. If you want to re-attach the entity to the current hibernate session, you could use `attach` or `refresh`.
`refresh` and `attach` act similarily, they both re-attach the enitity to the current hibernate session, but different in some detail that would crash your application if you don't understand it. ~~(since I spend lots of time deal with the bug)~~
### Refresh
`refresh()` method simply return a new instance with **MANAGE** state. So after we call the method, we can obtain the object value from the **user** entity.
```groovy=
def refreshedUserEntity = user.refresh()
def userDetail = refreshedUserEntity.userDetail
println userDetail.phoneNumber //get phone number wow
```
Also, `refresh()` refresh synchronize the entity with the database, so if the entity value was changed before refresh, it will be discard and set the be the same as database value.
```groovy=
//discard again from above code
refreshedUserEntity.discard()
//if our user last name is Chen
//set the entity value Lin
refreshedUserEntity.lastName = 'Lin'
def newUser = refreshedUserEntity.refresh()
println newUser.lastName //value will be Chen
```
### Attach
`attach()`, in semantic, means re-attach the entity. Hence it doesn't return a new instance like `refresh()` do, we can use the same entity.
But attach not just 'attach' the current eneity. In the code above, we've retrieved the `userDetail` from the **user** entity. And that was memorized in the meta-data of our entity and we don't know about it.
Once we use `attach()`, we really just attach the enitiy with the session and nothing more.
If the value of enitity was changed before attach, it will remain the changed value. So we must be careful about this, cause all the changes will be flushed into database if we pass through service layer or doing a flush mannualy.
In a more complex condition, although `userDetail` we taken from the user is not attached to the session(not at this moment), but once we flush, hibernate will take out what you've retrieve before, and put it into persistenceContext(it is where session managed the persistent eneities).
At this time, if you get the same identity value from from database during the process before flush, cause it will save the entity in the persistenceContext, when we flush the session, the same object value with same identity will cause **DuplicateKeyException**.
```groovy=
def user = User.get(1)
def userDetail = user.userDetail
//unbined from session
user.discard()
//change the value
user.lastName = "Lin"
//re-attach the entity to the session
user.attach()
//the print our value will be Lin but not changed to the database yet
println user.lastName
///some code here
def anotherDetail = userDetail.findByUser(user)
///some code here
//Exception wil occur if we try to flush into db
user.save(flush: true)
```
We must be careful using attach, as this was not memtioned in the grails document.