Can someone provide some guidance on how best to handle this scenario. I
would like the newly inserted row to come back in the SELECT statement. I'm
just using the default caching in the mapper.xml file, i.e., <cache/>.
Thanks,
Brian
I think that this problem can to be a trigger or constraint of database.
Paulo Soares
Sequence:
1. User enters data and clicks "Save".
2. On the server, a SQLSession is opened.
3. The INSERT statement is executed.
4. The SELECT statement is executed, but does not pull the newly inserted row. It uses what is in the cache.
5. Html response is packaged up to send back to user.
6. SQLSession is committed and then closed.
7. Response sent back to user.
Richard
Sent from my iPhone
2. If the first select is in a different session, then you are using
the global cache and will need to configure MyBatis to clear the cache
on an insert:
<insert flushCache="true" ...>
</insert>
#2 is default behavior, so this leads me to believe that #1 is your issue.
Jeff Butler
It was my impression that the standard way a database transaction was
supposed to function was that anything that happened within the transaction,
or "session", was supposed to be visible within the transaction. In other
words, if I INSERT 10 rows, UPDATE 5 rows, and DELETE 2 rows all in the same
transaction, and then perform a SELECT prior to committing the session,
i.e., still in the same session, the rows returned by the SELECT should
reflect the INSERTS, UPDATES and DELETES that just occurred in that still
uncommitted transaction. Is this incorrect thinking?
It was also my impression that if there were a separate transaction
(session), that it would NOT be able to see the results of the still
uncommitted transaction. This functionality appears to work like this. It's
just the part where it all happens within the same transaction that is not
working as I had anticipated.
The only way I could get it to work is to call sqlSession.commit(), as was
suggested by Richard Yee, in a previous response, but I didn't want to have
to manually call commit() like that in these instances. I'm handling the
commit/rollback in a filter class. Didn't want to have to sprinkle commits
throughout the code.
Thanks,
Brian
As to what can and can't be seen in transactions and by other
transactions, this is controlled by the transaction isolation settings
of the transaction - there is not a one size fits all answer here, it
really depends on the behavior of your specific database and how the
transaction isolation level is set. You should do some research about
the transactional behavior of your database and possibly adjust the
isolation level of your MyBatis transactions.
Jeff Butler
Example:
<Resource name="jdbc/myDbConnectionPool" auth="Container"
type="javax.sql.DataSource"
maxActive="20" maxIdle="10" maxWait="10000"
username="XXXX" password="XXXX"
driverClassName="com.mysql.jdbc.Driver"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
url="jdbc:mysql://XXX:1234/xxxx"
defaultTransactionIsolation="READ_UNCOMMITTED"/>
I then removed this and tried specifying the transaction level in the
SqlSesssion itself like this:
public static void open() {
session.set(GlobalStore.getInstance().getSqlSessionFactory().openSession(Tra
nsactionIsolationLevel.READ_UNCOMMITTED));
return;
}
This did not solve the problem either. Not sure what to think at this point.
The READ_UNCOMMITTED value is supposed to let you do "dirty reads" as they
are called. In other words, I should be able to read data that is
uncommitted.
It must be a cache issue. Here is a console output snippet:
2011-04-10 20:32:49,517 DEBUG [java.sql.PreparedStatement:27] ==>
Executing: INSERT INTO discussion_item ( customer_id, meeting_id, notes,
decision ) values ( ?, ?, ?, ? )
2011-04-10 20:32:49,533 DEBUG
[com.wss.ldsminutes.model.service.BaseService:34]
DiscussionItemService.selectDiscussionItemsForMeeting
2011-04-10 20:32:49,533 DEBUG
[org.apache.ibatis.cache.decorators.LoggingCache:27] Cache Hit Ratio
[com.wss.ldsminutes.model.dao.DiscussionItemMapper]: 0.42857142857142855
Notice the insert, then a select,
DiscussionItemService.selectDiscussionItemsForMeeting executes a select
statement, and then the cache hit ratio notification.
I tried it with and without a call to SqlSession.clearCache() before the
select statement. Neither worked.
Is there a bug somewhere? Do I not have something configured properly?
Jeff Butler
Jeff Butler
> on(Tra nsactionIsolationLevel.READ_UNCOMMITTED));
Since the MyBatis cache is a global cache for all sessions, the cache
is not updated until a commit. This makes sense to me because if
something went wrong and the transaction was rolled back, then you
would not want the cache to reflect a different state than the
database. To make it work without the commit(), we would have to
somehow implement a commit/rollback strategy in the cache - and that
seems like overkill for something as simple as MyBatis.
Consider this sequence:
1. select from foo (populates the cache)
2. insert into foo
3. select from foo (reads from cache)
If steps 2 and 3 are in the same session, then you will not see the
newly inserted record in the second select. This can be easily cured
in two ways:
1. commit after the insert
2. configure your session for auto commit and do the second select in
a second sql session
In summary, I think MyBatis is working correctly here. Your use case
is a bit unusual - I think that adding a commit is not too much
trouble.
Jeff Butler
Consider this sequence:
Jeff Butler
>> i on(Tra nsactionIsolationLevel.READ_UNCOMMITTED));
Yes, that was my intent – avoid manual transaction control, but it looks like it is the easiest option.
Other options:
1. Manually add the new row into the grid (java List object) prior to returning html response. How is this any better than just manually committing?
2. Send a subsequent html request (after transaction has committed normally) to get a refreshed List control for the grid.
Do you have another suggestion?
You might know that there is another cache in MyBatis - the SqlSession
cache. That cache is always cleared on an update/insert/delete so it
is not a contributor to this behavior.
Jeff Butler
Wondering if the SqlSession cache is sufficient for me?? Where can I read
about what these two caches provide, how they differ, how to configure them,
etc.?
Thanks,
Brian Barnett
Jeff Butler
>> >> essi on(Tra nsactionIsolationLevel.READ_UNCOMMITTED));
The SqlSession cache cannot be configured - it just IS. That cache
only exists for the duration of a single SqlSession - and each
SqlSession gets a new instance of a session cache. The SqlSession
cache is not about performance. It's purpose is to help MyBatis deal
with queries that have circular references. Generally, you don't have
to think about the SqlSession cache and it doesn't give you the kind
of behavior you would normally expect from a cache - it's really a
MyBatis internals thing.
Jeff Butler
flushCache: Setting this to true will cause the cache to be flushed whenever
this statement is called
But apparently should say this:
flushCache: Setting this to true will cause the cache to be flushed AFTER
THE TRANSACTION IS COMMITTED whenever this statement is called
Is it really an uncommon scenario to INSERT/UPDATE/DELETE and then do a
SELECT within the same HTTP request, i.e., the same SQLSession? I would
think it would be much more efficient than doing a separate HTTP round trip
request to do the SELECT, just so the commit could happen "normally".
But there's more to think about here. Suppose MyBatis worked the way
you are suggesting...
Transaction 1:
- delete record (flush cache, but no commit yet)
Transaction 2:
- select (refills cache, and includes deleted record because of db
transaction isolation)
Transaction 1:
- select (uses cache which includes deleted record, displays wrong data to user)
- close/commit (database delete is committed, but MyBatis cache
includes deleted record)
Now the cache is wrong and there's no way of knowing it. Not acceptable.
Jeff Butler
In that example, unless the Transaction 2 select used a dirty read,
I'd expect a either Timeout error or row-not-found.
In the original scenario outlined by Brian, if the object is not found
in the Cache, I'd expect MyBatis to go to the DB for it (and then
cache it).
??
Hugh
Maybe there need's to be a "cache sensitivity" setting...so that if you are
doing queries of updates within the same transaction, you can cause the
updates to flush immediately.
--
View this message in context: http://mybatis-user.963551.n3.nabble.com/INSERT-followed-by-SELECT-does-not-return-newly-added-row-tp2801268p3684634.html
Sent from the mybatis-user mailing list archive at Nabble.com.
(All this within one transaction)
One part of the code queries to see if a particular row exists. It doesn't,
and so the cache gets populated with an empty result list. Then we do an
insert into the table. Later we go through some complicated logic, and
query again for the row. It just got created, of course, in the
transaction, but the cache hands back an empty list.
This actually causes a NullPointerException in our code...we expect that
since we just added the row, we should be able to get it back again, and we
index the array at [0]...and the NPE happens.
It seems to me that we really need way to indicate that all caches (or
specific caches) should aggressively clear the cache (immediately, on
insert/update/delete) so that it's correct all the time.
I can't defeat my transaction semantics (by adding commits to satisfy this
caching issue) and risk leaving orphaned rows around.
--
View this message in context: http://mybatis-user.963551.n3.nabble.com/INSERT-followed-by-SELECT-does-not-return-newly-added-row-tp2801268p3688916.html