17

I've been having an annoying exception a lot lately, and after some research on Google and this forum I still haven't found an answer that could solve my problem.

Here's the thing - sometimes, I get the following error when trying to update or create a new object with hibernate:

org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
at org.springframework.orm.hibernate3.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1186)
at org.springframework.orm.hibernate3.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:696)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:419)
at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)
at org.springframework.orm.hibernate3.HibernateTemplate.save(HibernateTemplate.java:694)

What is really strange is that, sometimes when updating an object with the method getHibernateTemplate().saveOrUpdate(object); it will work, but sometimes with the same object and by calling the same method it doesn't work, but it seems to depend on how I get the object in the first place.

Example: let's say I have a table with 3 fields: id, type, length. What can happen is that, if I get the object by the id and update the length, then it will work. If I get it by the type and update the length, then it won't work. So what I've been doing so far to avoid the problem is to fetch the object the method that does not cause a problem later, but this is becoming more and more annoying to try and find a way that works.

Also, now I have this exception when trying to create an object (but not all of them, just on one specific table), and can't find a way for a workaround. And I tried to add @Transactional(readOnly = false) in the transaction but it didn't change anything, and displaying the mode was saying that I wasn't in read-only anyway.

Any suggestions?

Edit 26th of July: here's some configuration related to hibernate

<property name="hibernateProperties">
    <props>
        <prop key="jdbc.fetch_size">20</prop>
        <prop key="jdbc.batch_size">25</prop>
        <prop key="cglib.use_reflection_optimizer">true</prop>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
        <prop key="connection.autoReconnect">true</prop>
        <prop key="connection.autoReconnectForPools">true</prop>
        <prop key="connection.is-connection-validation-required">true</prop>
    </props>
</property>

also, if it can help

<property name="transactionAttributes">
    <props>
        <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="execute*">PROPAGATION_REQUIRED</prop>
        <prop key="add*">PROPAGATION_REQUIRED</prop>
        <prop key="create*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="delete*">PROPAGATION_REQUIRED</prop>
    </props>
</property>

Edit 31st of August: The relevant code in my Class that extends HibernateDaoSupport, to save the objects is:

public void createObject(Object persisObj) {
    getHibernateTemplate().save(persisObj);
}
Guillaume
  • 2,912
  • 3
  • 35
  • 59
  • 2
    Are you using `@Cache` with **READ_ONLY** mode? Or have a method/class annotated with `@Transactional(readOnly = true)` somewhere? I have ran into these sort of issues myself and found that these two configurations play a role on this error. Mostly if you load an object, modify it and persist it again. I am particularly not very fond of using `HibernateTemplate`, that also may be hiding some configuration in the background. If you past some snippets of your configuration I might be able to help. – Tarcio Saraiva Jul 25 '11 at 01:21
  • Thanks T Man for taking the time to reply. So I've search for any of @Cache/@Transactional in my whole project, and there was no such thing (and I never put it in my project). I actually didn't configure hibernate or write the methods, but I'll add my configuration in the post now. Also, this error appears when creating some objects, but not all of them (always the same though), so it is not only related to updating an object – Guillaume Jul 25 '11 at 22:48

13 Answers13

17

That error message is typically seen when using the Spring OpenSessionInViewFilter and trying to do persistence operations outside of a Spring-managed transaction. The filter sets the session to FlushMode.NEVER/MANUAL (depending on the versions of Spring and Hibernate you're using--they're roughly equivalent). When the Spring transaction mechanism begins a transaction, it changes the flush mode to "COMMIT". After the transaction completes, it sets it back to NEVER/MANUAL, as appropriate. If you're absolutely sure that this isn't happening, then the next most likely culprit is non-thread-safe use of a Session. The Hibernate Session must be used in only one thread. If it crosses over between threads, all kinds of chaos can happen. Note that an entity loaded from Hibernate can hold a reference to the Session in which it was loaded, and handing the entity across threads can thus cause the Session to be accessed from another thread, too.

Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
  • Thanks for your answer. You seem to know a lot about this, and I'm a bit confused about your answer. So I'm using Eclipse to develop, what do you reckon I should do to either detect/correct the problem? what could I do to try and fix it? – Guillaume Jul 27 '11 at 23:51
  • 2
    Best way to start is probably to add an exception breakpoint on the InvalidDataAccessApiUsageException, and then debug the app and make it happen. Then check the stack to see how you got to the exception. Especially check to see how and where the session was opened and whether a transaction was started. You can tell some of it from the exception stacktrace, but having it live in a debugger is better. – Ryan Stewart Jul 28 '11 at 00:12
  • Well I'm still stuck on this. I tried to debug the thing but really don't see what I can do from it. I don't see any session or whatsoever in there. Anybody else has suggestions? – Guillaume Aug 23 '11 at 04:52
  • Answer is helpful even after 7 years. I was getting this issue in persisting data and method was not annoted with @transaction i.e was outside spring transaction. – Vikas May 11 '18 at 09:03
10

add

@Transactional

above your function

Ahmad R. Nazemi
  • 775
  • 10
  • 26
8

I changed the single session propery from view filter. Problem solved:

  <filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    <init-param>
      <param-name>singleSession</param-name>
      <param-value>false</param-value>
    </init-param>
  </filter>
Fırat Küçük
  • 5,613
  • 2
  • 50
  • 53
  • 4
    I tried `getHibernateTemplate().getSessionFactory().getCurrentSession().setFlushMode(FlushMode.AUTO);` and my problem solved... – prem30488 May 28 '14 at 11:21
6

I just stumbled onto this too. I needed to change the flush mode in Spring's OpenSessionInViewFilter to manual, and suddenly I started getting this exception. I found out that the problems were happening in methods that were not annotated as @Transactional, so I guess that Spring implicitly treats all data access code outside such methods as read only. Annotating the method solved the problem.

Another way is to call the setCheckWriteOperations method on the HibernateTemplate object.

double-beep
  • 5,031
  • 17
  • 33
  • 41
alh84001
  • 1,263
  • 1
  • 15
  • 25
  • so I tried to add `@Transactional` to the method (see my edit post for the code): did not change anything. Then I tried to call `setCheckWriteOperations(false);` before the save, got the exception: `java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed`. Then I tried to remove the `,readOnly` part of the `PROPAGATION_REQUIRED,readOnly` (see my first edit) and it worked! With this, `@Transactional` and the check thing don't matter, but I guess this is not a good fix. Any idea why this is happening? – Guillaume Aug 31 '11 at 17:17
  • I really don't ave a clue. Your get* properties wouldn't by any chance create some new objects? Say, add an entry into a log database table or something similar? – alh84001 Sep 06 '11 at 08:28
5

Use below bean for HibernateTemplate in application context.

<bean id="template" class="org.springframework.orm.hibernate4.HibernateTemplate">  
        <property name="sessionFactory" ref="mysessionFactory"></property>
        <property name="checkWriteOperations" value="false"></property>
        </bean>
Amit Sharma
  • 71
  • 1
  • 2
4

Try to use this

hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);

The error should go away as the template is not checking if you are using a transaction.

cheesemacfly
  • 11,622
  • 11
  • 53
  • 72
  • I tried `getHibernateTemplate().getSessionFactory().getCurrentSession().setFlushMode(FlushMode.AUTO);` and my problem solved. – prem30488 May 28 '14 at 11:21
2

Below piece of code is worked for me.

hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);
Rami
  • 7,879
  • 12
  • 36
  • 66
user3198259
  • 178
  • 3
  • 13
2

You must forgot to add @Transactional annotation to your DAO service class/method, this will specify that your database operation will be handled by spring managed transaction.

wise king
  • 39
  • 1
  • 7
2
  1. Add @Transactional above you database operation methods

  2. Make sure you have annotation driven transation manager add <tx:annotation-driven/> above HibernateTransactionManager configuration in applicationContext.xml file

Ihor Patsian
  • 1,288
  • 2
  • 15
  • 25
1

Add @Transactional annotation on Dao/Repository class

@Repository
@Transactional
public class XxxRepository {
    
    @Autowired
    private HibernateTemplate hibernateTemplate;
    
    .
    .
    .

}
Jimmy
  • 995
  • 9
  • 18
0

Try to use this

this.getHibernateTemplate().setCheckWriteOperations(false);

Still if you are using hibernate4 and not able to update entity/table then you need to create session and need to do flush the session i.e.

this.getHibernateTemplate().setCheckWriteOperations(false);
Session session = this.getHibernateTemplate().getSessionFactory().openSession();
session.saveOrUpdate(entityName,app);
session.flush();
session.close();
0

Kindly paste below in your servlet xml:-

    <tx:annotation-driven />

you are missing it.

  • 1
    Please explain in what point of the .xml file this should go, or even better uploads the whole new .xml explaining what you corrected and why – YourHelper Feb 15 '22 at 21:24
0

I had the same problem and after one day of investigation I observed the following declaration

<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

I removed

mode="aspectj"

and the problem disappeared

Mihai
  • 333
  • 1
  • 14
  • unfortunately I don't have a tx:annotation-driven tag in any of my config files – Guillaume Mar 19 '12 at 17:58
  • Maybe you must put it in your config file because in default way it has some wrong attribute values for your transaction manager. This problem is result of wrong configuration and you must find what's wrong – Mihai Mar 20 '12 at 08:58