Grails programmatic transaction handling

99封情书 提交于 2019-12-11 04:03:58

问题


My Grails app has a a service method that updates a list of artists from last.fm's web service.

@Transactional(propagation = Propagation.NOT_SUPPORTED)
void updateLastFmArtists(Range idRange = null) {

    Artist.list().each { Artist artist ->

        // We could be updating a lot of artists here, the process could take up 
        // to an hour and we don't want to wrap all that in a single transaction
        Artist.withTransaction { status ->
            try {
                updateArtistInfo(artist)

            } catch (IOException ex) {
                status.setRollbackOnly()
            }
        }
    }
}

Each individual artist is updated within it's own transaction, which should be rolled back if an IOException is thrown. However, I noticed the following behaviour:

If an attempt to update an artist throws an IOException - causing the transaction to be rolled back - then the update of the next artist always fails due to the following error

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.example.Artist.topTracks, no session or session was closed

If I change the code above so that each artist is updated within it's own session, this seems to fix the problem,

   Artist.withNewSession { session ->
       Artist.withTransaction { status ->
            try {
                updateArtistInfo(artist)

            } catch (IOException ex) {
                status.setRollbackOnly()
            }
        }
   }

But I don't understand why I need to do this, i.e. why is that rolling back a transaction seems to close the session?


回答1:


It's normal that rollback makes the session unusable, as it's an unrecoverable error like all Hibernate exceptions. See for example the javadoc of class ObjectNotFoundException:

/*
 * ...
 *
 * Like all Hibernate exceptions, this exception is considered 
 * unrecoverable.
 *
 */

The reason is that the session is a state synchronizer component between the database and objects in memory. The way to treat a rollback in the database would be to rolllback the changes in the objects in memory.

Because this functionality would be hard to implement and of limited use, the decision was taken to make these type of exceptions unrecoverable.

You can try to catch it and continue to use the session but there is no guarantee that the session will be in a consistent state.

EDIT:

Here is further references other than the Javadoc, found in the documentation:

An exception thrown by Hibernate means you have to rollback your database transaction and close the Session immediately (this is discussed in more detail later in the chapter). If your Session is bound to the application, you have to stop the application. Rolling back the database transaction does not put your business objects back into the state they were at the start of the transaction. This means that the database state and the business objects will be out of sync. Usually this is not a problem, because exceptions are not recoverable and you will have to start over after rollback anyway.

and also:

If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.



来源:https://stackoverflow.com/questions/21638706/grails-programmatic-transaction-handling

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!