First of all, 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.
If you specify readOnly
as true
, the flush mode will be set as FlushMode.NEVER
in the current Hibernate Session preventing the session from committing the transaction.
Furthermore, setReadOnly(true) will be called on the JDBC Connection, which is also a hint to the underlying database. If your database supports it (most likely it does), this has basically the same effect as FlushMode.NEVER
, but it's stronger since you cannot even flush manually.
Now let's see how transaction propagation works.
If you don't explicitly set readOnly
to true
, you will have read/write transactions. Depending on the transaction attributes (like REQUIRES_NEW
), sometimes your transaction is suspended at some point, a new one is started and eventually committed, and after that the first transaction is resumed.
OK, we're almost there. Let's see what brings readOnly
into this scenario.
If a method in a read/write transaction calls a method that requires a readOnly transaction, the first one should be suspended, because otherwise a flush/commit would happen at the end of the second method.
Conversely, if you call a method from within a readOnly transaction that requires read/write, again, the first one will be suspended, since it cannot be flushed/committed, and the second method needs that.
In the readOnly-to-readOnly, and the read/write-to-read/write cases the outer transaction doesn't need to be suspended (unless you specify propagation otherwise, obviously).
Two answers:
a) don't do it. Use @Transactional
in the service layer or the dao layer, but not both (the service layer is the usual choice, as you probably want one transaction per service method)
b) if you do it, what happens depends on the propagation
attribute of the @Transactional
annotation and is described in this section: 10.5.7 Transaction propagation. Basically: PROPAGATION_REQUIRED
means the same transaction will be used for both methods, while PROPAGATION_REQUIRES_NEW
starts a new transaction.
About your comments:
Of course I kept reading and realized that, as I'm using proxies, this second method won't be managed by the transactional proxy, thus it's like any other method call.
That's not true in your situation (only if both methods were within the same class).
If a bean has methods a
and b
, and a
calls b
, then b
is called on the actual method, not the proxy, because it is called from within the proxy (a bean doesn't know that it is proxied to the outside world).
proxy bean
a() --> a()
|
V
b() --> b()
In your situation, however, a service would have an injected dao object, which would be a proxy itself, so you'd have a situation like this:
proxy bean
service a() --> a()
|
/---------/
|
V
dao b() --> b()
Best Answer
From javadoc:
So, it means that, for example, multiple invocations of Hibernate's
SessionFactory.getCurrentSession()
insidemethodWithSupportsTx()
would return the same session.