I get an insanely strange result with Hibernate (4.1.9.Final) and MySQL (14.14 Distrib 5.5.29, InnoDB table):
When I persist something to the database using one thread and try to fetch it using another thread, Hibernate does not always find the entity. (Despite the fact that I'm properly committing the transaction and closing the persist-session before opening the load-session.)
Some observations:
I can not reproduce this using a single-threaded program.
I can see the "missing" entity in the database, and
If I restart the application Hibernate can successfully load the entity.
Here's an SSCCE illustrating the problem. (Imports left out for brevity):
public class StressTest {
static SessionFactory sessionFactory;
public static void main(String[] args) throws InterruptedException,
ExecutionException {
// Configure Hibernate
Configuration conf = new Configuration();
conf.setProperty("hibernate.dialect",
"org.hibernate.dialect.MySQLInnoDBDialect");
conf.configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(conf.getProperties())
.buildServiceRegistry();
sessionFactory = conf.buildSessionFactory(serviceRegistry);
// Set up producer / consumer
BlockingQueue<Long> queue = new LinkedBlockingQueue<Long>();
new Consumer(queue).start();
new Producer(queue).start();
}
}
class DummyEntity {
long id;
public long getId() { return id; }
public void setId(long id) { this.id = id; }
}
Producer class (creates DummyEntities
and persists them).
class Producer extends Thread {
BlockingQueue<Long> sink;
public Producer(BlockingQueue<Long> sink) {
this.sink = sink;
}
@Override
public void run() {
try {
while (true) {
Session session = StressTest.sessionFactory.openSession();
DummyEntity entity = new DummyEntity();
entity.setId(new Random().nextLong());
session.beginTransaction();
session.save(entity);
session.getTransaction().commit();
session.close();
sink.put(entity.getId());
}
} catch (InterruptedException ignore) {
System.exit(-1);
}
}
}
Consumer class (loads DummyEntities
from database):
class Consumer extends Thread {
BlockingQueue<Long> source;
public Consumer(BlockingQueue<Long> source) {
this.source = source;
}
@Override
public void run() {
try {
while (true) {
long entityId = source.take();
Session session = StressTest.sessionFactory.openSession();
Object entity = session.get(DummyEntity.class, entityId);
session.close();
if (entity == null) {
System.err.printf("Entity with id %d NOT FOUND", entityId);
System.exit(-1);
}
}
} catch (InterruptedException ignore) {
System.exit(-1);
}
}
}
Finally, here's the mapping-xml for the DummyEntity
.
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping
default-cascade="all"
default-lazy="false">
<class name="se.stresstest.DummyEntity">
<id name="id" type="long">
<generator class="assigned"/>
</id>
</class>
</hibernate-mapping>
The resulting output always ends with something like:
Entity with id -225971146115345 NOT FOUND
Any ideas why?
(This is a refined version of a previous question.)
This behavior can be consistent with a transaction isolation mode of REPEATABLE READ, which happens to be the default transaction isolation mode for InnoDB:
http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html#isolevel_repeatable-read
If your application logic is dependent upon being able to see data committed in other transactions after the current transaction was started - at the expense of repeatable reads (data can/will change as a result of manipulation in other transactions) - you should set the transaction isolation to READ COMMITTED:
http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html#isolevel_read-committed
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With