I have a heavy operation, which is read only. This is all mapped by hibernate, in a spring boot application.
Hibernate spends 6seconds flushing my entities, when there is no mutation of any sort. This is simply a get operation.
I am trying to stop hibernate from spending that 6 second, flushing redundantly.
I put hibernate flush mode to MANUAL/NEVER. But it didn't make any difference.
The property is set correctly, but hibernate stats still show the flushing, count, and the time spent.
There are several ways to do it. You may try:
org.springframework.transaction.annotation.Transactional annotation set to read-only:
#Transactional(readOnly = true)
Which in case of Hibernate, it sets the JDBC transaction into a read-only mode and FlushMode.NEVER.
Here details: https://stackoverflow.com/a/1712328/5810648
Also, it is possible to disable dirty-check calling setReadOnly(true) on Hibernate query or call setHint("org.hibernate.readOnly", true) on JPA query. According to doc :
Hibernate will never dirty-check them or make changes persistent ( eg.
new Boolean(true) ), default to false
http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html/objectstate.html#d0e1215
Another way is to use stateless session for that heavy operation. Here is details: https://stackoverflow.com/a/5497077/5810648
Related
I've got a Spring application using Hibernate. I've implemented Envers into it, which is working fine. However, Hibernate will by default automatically flush before some transactions are committed.
For example, I have an MVC endpoint that will update a record, but before saving it, will have to make various other queries to retrieve some other data. Each time another query is run, Hibernate flushes and this results in there being multiple audit rows for each change. This creates some confusion, as there is already a modified date on my record which isn't changed in each update (as it's flushing before this property is changed).
What are my options for managing this more effectively, and creating a reliable audit log even with Hibernate flushing in this way? Is the only answer to implement my own listener with some custom logic to check if it should actually be committing an audit change or not?
You can detach the entity and merge when you are done. These queries are only executed if they touch tables that would be affected by pending inserts/updates/deletes. If you use native queries, this is a different topic. Hibernate has no SQL parser to figure out which tables you are touching so it is conservative and flushes all pending changes.
I am using hibernate envers for auditing.
It works fine but today I realized that it doesnt if I create entities in a for-loop.
After set log true for sql queries I figured out, that the rev-tables are not updated after each iteration. Somehow hibernate collects all changes and fires the audit command in the end of a request? How can I let hibernate to do auditing after each iteration in my for-loop?
What I already tried:
for (...) {
Obj a = new Obj();
objRepository.save(a);
entityManager.flush();
entityManager.clear();
}
As #gtosto points out, Hibernate Envers operates on a transaction boundary basis and therefore audit records won't be flushed and persisted until commit.
One way to synchronize this would be to manually control the transaction boundary yourself as a part of the for-loop so you basically persist small buckets of the list and commit.
The downside here is that can be performance intensive, particularly if the list of objects you're trying to persist is quite large.
The jira issue HHH-9622 outlines a request to make the AuditProcess flushable; however, there are consequences to introducing such behavior that need to be considered.
In fact the problem was that I added the #Transactional annotation to the respective class. Remove it and hibernate will fire the audit commands as soon as you call objRepository.save(a). No need for entity manager.
I'm not getting a clear idea of why autocommit is by default false in Hibernate when we have the Transaction management apis provided.
I have three questions
why autocommit mode is not recommended by Hibernate?
What happens when we use autocommit = true and then use the Hibernate Transaction apis for transaction management ?
When using spring declarative transaction management how #Transactional(readonly = true) will help the read only code (Hibernate code) we write?
I will answer one by one Starting with (2) as i don't know much about (1)
(2): autocommit=true means by default all queries get commited. In such case If
if there is a #Transactional on a method, it overrides the autocommit and encloses all queries into a single transaction, thus overriding the autocommit
if there is a #Transactional method that calls other #Transactional annotated methods, the outer most annotation should override the inner annotaions and create a larger transaction, thus annotations also override eachother.
(3): In DBs like Oracal/Mysql a read only transaction can be translated READ_ONLY level which provides no dirty reads, no unrepeatable reads but doesn’t allow any updates. That means the flush mode will be set as FlushMode.NEVER in the current Hibernate Session preventing the session from commiting the transaction. Even setReadOnly(true) will be called on the JDBC Connection which ensure that you cannot call session.flsuh() even to flush session manually.
since Spring doesn't do persistence itself, it cannot specify what readOnly should exactly mean. This attribute is only a hint to the provider, the behavior depends on, in this case, Hibernate.
In regards of (1):
Suppose, you own a company and have 1000 employees. You maintain a database table to track if an employee was paid at the end of the month already or not.
So, here we are, the end of the month and pay day. Payroll sends you an excel file
with 600 names who has just received their compensation for the last month. So you log on to your computer and start your java app and select the excel to save all 600 records in your database. It usually takes 2 minutes, but now it fails at 1 minute and 23 second. What is your expectation now? Do you expect a partially uploaded file with gosh'knows how many records, or nothing at all? Your autocommit will drive it: If autocommit=false then you can give a go to upload the whole file again and again, but if autocommit=true you might need to tweak around your input data first to remove some records to prevent duplicates in your database.
I hope, my simplified example helps you to better understand it, but really the purpose is to ensure either everything from a batch is saved (it includes every write operation, like insert/update/delete) in the database or nothing at all in case an error occurs at anytime during the process. In the real life in most of the cases you and your organisation will expect complete data sets rather than partial data set in the database. Understanding it is key and anybody who advises to 'use autocommit=true because it is safe' should be avoided by miles. This is a key concept and one of the foundation of data management.
I am attempting to do a select on a row and update the value. While I do this I need exclusive access to the row. In other words, no other process (inside or outside the VM), should be able to read the row until after I update the row. The current value should not be "selectable". I have tried the following transaction annotation.
#Transactional(isolation = Isolation.SERIALIZABLE, readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class, timeout=960)
This definitely works within the Spring context, but while putting a sleep statement in the middle of the transaction, I'm still able to select the current row value using a database tool.
Is there a way to get a XLOCK/ROWLOCK (whichever is the appropriate) using Spring/Hibernate?
Versions:
Spring: 3.0.5.RELEASE
Hibernate: 3.6.3.Final
JTDS: 1.2.4
If I can't use Spring/Hibernate, a link to a JTDS example would be much appreciated.
Thank you.
SERIALIZABLE isolation level allows other transactions to read data, but not to modify. So you need to explicitly SELECT ... FOR UPDATE (in Hibernate: Query#setLockMode(LockMode.UPGRADE)).
Use explicit locking with the Hibernate. There is more information here.
However, I think you have to think once more time - do you really need the pessimistic lock? In most cases optimistic lock works better, and hibernate supports versioning very well.
We are using Hibernate Spring MVC with OpenSessionInView filter.
Here is a problem we are running into (pseudo code)
transaction 1
load object foo
transaction 1 end
update foo's properties (not calling session.save or session.update but only foo's setters)
validate foo (using hibernate validator)
if validation fails ?
go back to edit screen
transaction 2 (read only)
load form backing objects from db
transaction 2 end
go to view
else
transaction 3
session.update(foo)
transaction 3 end
the problem we have is if the validation fails
foo is marked "dirty" in the hibernate session (since we use OpenSessionInView we only have one session throughout the http request), when we load the form backing objects (like a list of some entities using an HQL query), hibernate before performing the query checks if there are dirty objects in the session, it sees that foo is and flushes it, when transaction 2 is committed the updates are written to the database.
The problem is that even though it is a read only transaction and even though foo wasn't updated in transaction 2 hibernate doesn't have knowledge of which object was updated in which transaction and doesn't flush only objects from that transaction.
Any suggestions? did somebody ran into similar problem before
Update: this post sheds some more light on the problem: http://brian.pontarelli.com/2007/04/03/hibernate-pitfalls-part-2/
You can run a get on foo to put it into the hibernate session, and then replace it with the object you created elsewhere. But for this to work, you have to know all the ids for your objects so that the ids will look correct to Hibernate.
There are a couple of options here. First is that you don't actually need transaction 2 since the session is open you could just load the backing objects from the db, thus avoiding the dirty check on the session. The other option is to evict foo from the session after it is retrieved and later use session.merge() to reattach it when you what your changes to be stored.
With hibernate it is important to understand what exactly is going on under the covers. At every commit boundary it will attempt to flush all changes to objects in the current session regardless of whether or not the changes where made in the current transaction or any transaction at all for that matter. This is way you don't actually need to call session.update() for any object that is already in the session.
Hope this helps
There is a design issue here. Do you think an ORM is a transparent abstraction of your datastore, or do you think it's a set of data manipulation libraries? I would say that Hibernate is the former. Its whole reason for existing is to remove the distinction between your in-memory object state and your database state. It does provide low-level mechanisms to allow you to pry the two apart and deal with them separately, but by doing so you're removing a lot of Hibernate's value.
So very simply - Hibernate = your database. If you don't want something persisted, don't change your persistent objects.
Validate your data before you update your domain objects. By all means validate domain objects as well, but that's a last line of defense. If you do get a validation error on a persistent object, don't swallow the exception. Unless you prevent it, Hibernate will do the right thing, which is to close the session there and then.
What about using Session.clear() and/or Session.evict()?
What about setting singleSession=false on the filter? That might put your operations into separate sessions so you don't have to deal with the 1st level cache issues. Otherwise you will probably want to detach/attach your objects manually as the user above suggests. You could also change the FlushMode on your Session if you don't want things being flushed automatically (FlushMode.MANUAL).
Implement a service layer, take a look at spring's #Transactional annotation, and mark your methods as #Transactional(readOnly=true) where applicable.
Your flush mode is probably set to auto, which means you don't really have control of when a DB commit happens.
You could also set your flush mode to manual, and your services/repos will only try to synchronize the db with your app when you tell them to.