-1

Background: I am using Hibernate in DAOs, which are used by a business layer class. Everything is autowired in Spring. I wire the sessionManager into the DAO, then wire the DAO into the business logic class.

I am doing annotation-based transaction management using @Transactional.

The error that I am seeing is as follows:

Could not create JDBC savepoint; 
nested exception is     
java.sql.SQLException: Connection is read-only. 
Queries leading to data modification are not allowed.

The method at which the exception is thrown is a read-only method, containing two read-only reads, in sequence.

I've tried to distill the code as much as possible into this demo:

@Transactional(propagation = Propagation.REQUIRES_NEW,value="transactionManager",readOnly=true)
public Typ2 validateAndIfSoGet(String param){
    Typ blah = dao1.getTyp(param);
    if(blah!=null){
        return dao2.getTyp2(blah);
    }
    else return null;
}

You can see that there are two reads happening from different DAO objects in this one transaction. Why is Spring complaining about a save point? Must there be some save between the reads? Would the correct fix then be to make it readOnly=false? Or would that be a band-aid cover up fix?

Thanks in advance.


Edit:

getTyp() and getType2() are DAO methods that make calls to the respective underlying databases to retrieve rows as objects. They use simple Hibernate mappings (1 DAO: 1 table, 1 row: 1 entity object.)

The full getTyp method looks like this:

@Override
@Transactional(value="transactionManager",propagation = Propagation.NESTED,readOnly=true)
public Typ getTyp(String param){
           Typ typ = (Typ) currentSession().get(Typ.class, param);
            return typ;
}

(using Hibernate's Session.get method).

The getCurrentSession method is not annotated. I'm not sure if this has anything to do with the problem.

@Autowired
@Qualifier("sessionFactory")
private SessionFactory sessFactory;

private Session currentSession(){
    return sessFactory.getCurrentSession();
}
PrgrMerr
  • 49
  • 1
  • 8
  • What does `getTyp()` and `getTyp2()` do? They must be doing something that tries to create a savepoint *(see error message)*, so look there for your problem. – Andreas Apr 22 '16 at 22:45
  • Those are DAO methods that make calls to the respective underlying databases to retrieve rows as objects. They use simple Hibernate mappings (1 DAO: 1 table, 1 row: 1 entity object.) The full getTyp method looks like this: ` @Override @Transactional(value="transactionManager",propagation = Propagation.NESTED,readOnly=true) public Typ getTyp(String param){ Typ typ = (Typ) currentSession().get(Typ.class, param); return typ; }` (using Hibernate's Session.get method). getTyp2 uses the same format. – PrgrMerr Apr 22 '16 at 22:54
  • Don't post code in a comment. Edit the question. – Andreas Apr 22 '16 at 22:58
  • Why are you creating a `NESTED` transaction for reading a value from the database? Why do you even have transaction control at the DAO layer? Transactions should be managed by the business logic, not the DAO. Anyway, change `NESTED` to `REQUIRED` or `MANDATORY`. Do you even know what nested means? – Andreas Apr 22 '16 at 23:00
  • On changing NESTED to REQUIRED, the new error becomes "HTTP Status 500 - Request processing failed; nested exception is org.hibernate.HibernateException: get is not valid without active transaction". Might it have anything to do with that getCurrentSession method? Or does this new error mean that something isn't right with session management at the business layer? The business layer class is itself wired into a Spring MVC dispatcher servlet, which does not use transaction management. – PrgrMerr Apr 22 '16 at 23:09
  • are you sure this is the method which orginates the savepoint ( - if you set it to readonly false does the problem go away ) . The only other thing I got is REQUIRES_NEW will suspend any existing transaction and maybe this is where the savepoint is occurring . ( try it with REQUIRES and readonly = true to check ) http://docs.oracle.com/javaee/5/tutorial/doc/bncij.html – diarmuid Apr 22 '16 at 23:09
  • @diarmuid `REQUIRES_NEW` means that it needs a *different* transaction, that if committed will stay committed even if outer transaction is rolled back. This means that it has to be done using a separate connection, because JDBC doesn't support multiple independent transactions on a single connection. --- `NESTED` means that it can be rolled back without affecting the outer transaction. It is a sub-transaction, not an independent transaction. This is what SavePoints do. – Andreas Apr 22 '16 at 23:14
  • @merRef `REQUIRED` means that a transaction will be established if not already done, so the error is likely coming from somewhere else. Check your stacktrace. – Andreas Apr 22 '16 at 23:17

1 Answers1

1

After a ton of looking around, I finally found the problem.

It was not at all what I'd expected.

In my Hibernate config, I had set a property in the SessionFactory of

<prop key="hibernate.current_session_context_class">thread</prop>

Apparently, this screws up Spring's transaction management. I removed this, and suddenly all of my Spring-based transaction management worked.

Here is a relevant thread that helped me. Spring Source Discussion Thread

Thanks to all for the help in troubleshooting.

PrgrMerr
  • 49
  • 1
  • 8
  • On further investigation as to why this was causing problems, I also came across this related StackOverflow thread. http://stackoverflow.com/questions/19875485/using-current-session-context-class-property-hibernate-3-hibernate-4 – PrgrMerr Apr 23 '16 at 11:22