NHibernate - ISession.Clear() vs. ISession.GetSessionImplementation().PersistenceContext.Clear() - session

What would be the main difference between doing a session.Clear() vs. session.GetSessionImplementation().PersistenceContext.Clear()?
In my case, ideally, I would only clear the cache for a context switch in my code because I want to prevent lazyloading objects from previous context. And also, I wouldn't get the error that I am getting:
NHibernate.LazyInitializationException : Initializing[...#34]-failed to lazily initialize a collection of role: [...], no session or session was closed

session.Clear() clears PersistenceContext and also cancels all pending actions (saves, updates and deletions).

Related

Hibernate uses old closed SessionImpl instead of given SessionImpl

I have a problem with Hibernate Enverse (Version 5.2.0-Final).
Context:
I'm auditing some entities with some lazy relations. I have a jsf-page that loads one version of one entity with all relations of that version. That works fine. So now I have a page that shows a revision of the entity with all relations of that revision. On this page I can open a fieldset, that triggers an AJAX. In this request we reattach all relations by calling entityManager.merge(entity) to be able to fetch the lazy relations in this fieldset. (The EntityManager is RequestScoped)
The Problem:
The AJAX is a new request. The server calls entityManager.merge(entity), what enforces creation of a new EntityManager (So a new org.hibernate.internal.SessionImpl is created). On this object hibernate calls SessionImpl.merge(...). But in the method org.hibernate.internal.AbstractSharedSessionContract.createQuery(String) a other SessionImpl object is used, which is already closed in the request before. That enforces an java.lang.IllegalStateException: Session/EntityManager is closed.
In one sentence: Although a new entityManager was created and a merge was called on that new entityManger, Hibernate uses an old Session/EntityManager of the request before.
I debugged the problem and found following:
Debug1: Shows the Stacktrace of the SessionImpl.merge(...) with the session's object id
Debug2: Shows the last method with the correct SessionImpl object (see it's id). This object is not used in next methods.
Debug3: The step after Debug2 does not know the given SessionImpl object. It has it's own SessionImpl object in collection.initializor.versionsReader. This session was created and closed in the request before (on loading the page).
Debug4: Now Hibernate wants to create the query wit the closed SessionImpl
Debug5: This enforces the exception, as the session is closed.
My questions:
Is this a bug of Hibernate?
Why is the given SessionImpl in method org.hibernate.type.CollectionType.getElementIterater(...) not used?
Anyone knows a solution or workaround for this problem?
Tank you very much for any idea. I spent days on this bug.
Why is the Session arg in o.h.type.CollectionType.getElementIterator not used?
The short answer is it isn't required, its simply a backward compatibility concern from 8 years ago.
The long answer is the type-system used to actually deviate some behavior based on whether or not the user had specified the session to operate in EntityMode.MAP or EntityMode.POJO and therefore the types needed to know what mode the session was in; hence why it was passed.
But even back in 2011 when this was changed, the session argument only ever influenced behavior if and only if the session was operating in EntityMode.MAP. In other words, all other modes always routed directly to the underlying collections Collection#iterator() method.
All this aside however, this doesn't have any impact on what you experience in your Debug3 screen-shot.
Is this a bug in Hibernate?
No, based on what I have read, I believe you're mixing concerns.
In Hibernate (no Envers), you can basically do this
// Request 1
request1EntityManager = getEntityManager();
sessionScopeEntity = request1EntityManager.find( MyEntity.class, myEntityId );
// Request 2
request2EntityManager = getEntityManager();
sessionScopeEntity = request2EntityManager.merge( sessionScopeEntity );
for ( SomeCollectionItem Item : sessionScopeEntity.getSomeCollection() ) {
// do things here
}
The above works because you reassociate the entity with the new session which in-turn injects the session into all the uninitialized proxies the entity maintains. But you can also rewrite the above as
// Request 1
request1EntityManager = getEntityManager();
sessionScopeEntity = request1EntityManager.find( MyEntity.class, myEntityId );
sessionScopeEntity.getSomeCollection().size() // initialize collection w/request1Session
// Request 2
request2EntityManager = getEntityManager();
for ( SomeCollectionItem Item : sessionScopeEntity.getSomeCollection() ) {
// do things here
}
The difference is the collection gets initialized with the first session and therefore when you attempt to access it with the second session, the entity doesn't necessarily need a merge because the collection is no longer a proxy but actually populated like a normal fetched collection would be.
The major difference between an entity instance returned by Hibernate and an audited entity instance returned by Envers is that the audited entity instance is NOT a managed persistent entity.
Depending on your scenario, you may decide to only audit a subset of fields on an entity mapping. This is why you cannot nor should not use things like merge with that instance as it could easily lead to unintended side effects with your real data.
If you intend to pass the audited entity instance across sessions, i would highly suggest that you instead consider initializing the collections you need up-front with the first session where you fetched the instance because presently there is no way to re-associate an audited entity instance with a new session.

Write call/transaction is dropped in TransactionalEventListener

I am using spring-boot(1.4.1) with hibernate(5.0.1.Final). I noticed that when I try to write to the db from within #TransactionalEventListener handler the call is simply ignored. A read call works just fine.
When I say ignore, I mean there is no write in the db and there are no logs. I even enabled log4jdbc and still no logs which mean no hibernate session was created. From this, I reckon, somewhere in spring-boot we identify that its a transaction event handler and ignore a write call.
Here is an example.
// This function is defined in a class marked with #Service
#TransactionalEventListener
open fun handleEnqueue(event: EnqueueEvent) {
// some code to obtain encodeJobId
this.uploadService.saveUploadEntity(uploadEntity, encodeJobId)
}
#Service
#Transactional
class UploadService {
//.....code
open fun saveUploadEntity(uploadEntity: UploadEntity, encodeJobId: String): UploadEntity {
// some code
return this.save(uploadEntity)
}
}
Now if I force a new Transaction by annotating
#Transactional(propagation = Propagation.REQUIRES_NEW)
saveUploadEntity
a new transaction with connection is made and everything works fine.
I dont like that there is complete silence in logs when this write is dropped (again reads succeed). Is there a known bug?
How to enable the handler to start a new transaction? If I do Propogation.Requires_new on my handleEnqueue event, it does not work.
Besides, enabling log4jdbc which successfully logs reads/writes I have following settings in spring.
Thanks
I ran into the same problem. This behavior is actually mentioned in the documentation of the TransactionSynchronization#afterCompletion(int) which is referred to by the TransactionPhase.AFTER_COMMIT (which is the default TransactionPhase attribute of the #TransactionalEventListener):
The transaction will have been committed or rolled back already, but the transactional resources might still be active and accessible. As a consequence, any data access code triggered at this point will still "participate" in the original transaction, allowing to perform some cleanup (with no commit following anymore!), unless it explicitly declares that it needs to run in a separate transaction. Hence: Use PROPAGATION_REQUIRES_NEW for any transactional operation that is called from here.
Unfortunately this seems to leave no other option than to enforce a new transaction via Propagation.REQUIRES_NEW. The problem is that the transactionalEventListeners are implemented as transaction synchronizations and hence bound to the transaction. When the transaction is closed and its resources cleaned up, so are the listeners. There might be a way to use a customized EntityManager which stores events and then publishes them after its close() was called.
Note that you can use TransactionPhase.BEFORE_COMMIT on your #TransactionalEventListener which will take place before the commit of the transaction. This will write your changes to the database but you won't know whether the transaction you're listening on was actually committed or is about to be rolled back.

Reset cache after transaction commit in WSGI application

Seems, it should be a common problem.
My Flask application has middleware that commits (if request processed normally) or rolls back (if request processing fails) SQLAlchemy session after each request. I need to implement cache for some object in DB, let's say User.
I'd like to implement manual invalidation, so when I change User object, I should remove it from cache. If next request needs User and it's not found in cache, it retrieve it from DB and cache.
OK, I changed User object and flush it (no commit yet, because next part of code can fail and transaction should be reverted). Seems, I can't invalidate object in cache immediately, because transaction's operation are still in pending mode, so if somebody else will try to access User's info, he will not find him in cache, so will retrieve outdated user from DB and cache him. So I need to remove User from cache only after transaction commit.
I thought about placing information about User needs to be removed from cache and executing removal in middleware, after DB transaction commits:
class DbSessionMiddleware:
def __init__(self, flask_app):
self.app = flask_app.wsgi_app
def __call__(self, environ, start_response):
try:
return self.app(environ, start_response)
except BaseException:
dbs().rollback()
raise
finally:
dbs().commit()
# Check if error occurred and reset cache, if not?
Am I thinking in the right direction? How to pass data to middleware?
May be, use Flask-SQLAlchemy signals for this?

Fluent Nhibernate working with entities after session disposed

I've got a question about working with entities which were received from db.
Currently I've a lot of operations, where I need to get entities from db, and pass them to another service. Simplified version of such code are is like this:
List<Entity> list;
using(var session = SessionFactory.OpenSession())
{
list = Session.QueryOver<Entity>.Future().ToList();
}
So now I don't know, if list of objects isn't disposed for a long time, will it cause memory lear accordint to stored sessions. Does nhibernate sessions exist while exist objects which were received during the session?
Update:
Found some session setting Session.ActiveEntityMode - POCO, does it solves my problem?
the session is disposed as soon as the using ends. All entities loaded are still valid except not initialized lazyloaded collections/references/properties.
Also the Future in Session.QueryOver<Entity>.Future().ToList(); is a noop when there are no other operations befor which have Future/futurevalue on them.

when castle activerecord closes a connection

I started using nhibernate the same time I started using castle activerecord so I have those mixed up questions sometimes
I was wondering when activerecord (or nhibernate) closes a connection when the following code runs:
Dim entity = TABLE_Y.TryFind(id)
if not entity is nothing then
'long running process here
response.redirect("...")
end if
I'm asking this because this long running process takes more than one hour to finish and whenever the code redirects to another page I get an ORA-03135 (connection lost contact) telling me the connection was lost, this other page has the following active record query:
Dim entity = TABLE_X.FindAll(Order.Desc(...), _
Expression.Eq(...) And _
Expression.Eq(...)).FirstOrDefault
which then returns the ora-03135
so I was thinking if couldn't be any connection from activerecord that didn't close before the long running process
this long running process is literally another process started by the application which wait for it to end before redirecting to another page, so even if the other process uses active records it doesn't use the same connection string or whatsoever
it's funny because I'm starting a new query on a completely different table, is activerecord trying to reuse an existing connection that timed out? I tried to add "Pooling=False" and "Validate Connection=true" with no luck
thanks in advance
The connection will be closed when the session is disposed of, where this happens depends on your application. If you're using the module that comes with ActiveRecord then it will happen when the Application.EndRequest event fires (ie. at the end of your request), if you aren't, then you need to see where a SessionScope or TransactionScope is created and disposed (the dispose is where the connection is closed).
If you want to start a long running task and redirect before it's complete you will need to start it in another thread (eg. using ThreadPool or Tasks). You will also need to configure ActiveRecord to use the HybridWebThreadScopeInfo so that it will store the session in a thread local when the HttpContext is unavailable (which is what will happen in your background thread).
<activerecord threadinfotype="Castle.ActiveRecord.Framework.Scopes.HybridWebThreadScopeInfo, Castle.ActiveRecord">
Then in your task, wrap it in a TransactionScope or SessionScope (I prefer the former):
using(var trans = new TransactionScope()) {
// do your stuff here...
trans.VoteCommit();
}

Resources