My Hibernate HQL query seems to be returning stale data.
I have a simple java class called Account, instances of which map onto a single database table with two varchar columns, username and surname.
If I run a HQL query such as:
List<?> accountList = session.createQuery("from Account where surname is null").list();
I get back a List of Account objects, as expected (some of the rows in the table indeed have null surname fields).
I then set the surname on the returned objects to some non-null value:
Iterator<?> accountIter = accountList.iterator();
while (accountIter.hasNext()) {
Account account = (Account) accountIter.next();
log("Adding surname of Jones to : " + account.getUsername());
account.setSurname("Jones");
}
At this point, if I ran the HQL query again, I would expect to get back an empty List (as all surnames should be non-null), but instead I get back the same objects as when I ran the query the first time. This is not what I expected.
Quoting from the Hibernate docs:
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/
"there are absolutely no guarantees about when the Session executes the JDBC calls, only the order in which they are executed. However, Hibernate does guarantee that the Query.list(..) will never return stale or incorrect data."
This seems contrary to the behaviour of my code. Looking at the program output in Listing 4 below, the SQL Update statement happens after all the select statements, so the last select returns incorrect data. Can anyone shed light on what is going on, or what I am doing wrong?
If I surround the setting of the surnames with a transaction, and perform a session.saveOrUpdate(account)
it all works, but I thought that this was not required.
I would like my code to only deal with the domain classes if possible, and be free of persistence code as much as possible.
I am using Hibernate 4.1.8.Final, with Java 1.6
My full code listing is below:
Listing 1: Main.java:
package uk.ac.york.cserv.hibernatetest;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class Main {
private static SessionFactory sf;
Session session;
public static void main(String[] args) {
Main main = new Main();
main.doExample();
}
public Main() {
sf = new Configuration()
.configure("hibernate-ora.cfg.xml")
.buildSessionFactory();
session = sf.openSession();
}
public void closeSession() {
session.flush();
session.close();
}
public List<?> getAccountList() {
return session.createQuery("from Account where surname is null").list();
}
public void printAccountList(List<?> accountList) {
Iterator<?> accountIter = accountList.iterator();
while (accountIter.hasNext()) {
System.out.println(accountIter.next());
}
}
public void log(String msg) {
System.out.println(msg);
}
public void doExample() {
log("Print all accounts with null surnames...");
printAccountList(getAccountList());
log("Adding surnames to accounts that have null surnames...");
//session.beginTransaction();
Iterator<?> accountIter = getAccountList().iterator();
while (accountIter.hasNext()) {
Account account = (Account) accountIter.next();
log("Adding surname of Jones to : " + account.getUsername());
account.setSurname("Jones");
//session.saveOrUpdate(account);
}
//session.getTransaction().commit();
log("Again print all accounts that have null surnames (should be none)...");
printAccountList(getAccountList());
closeSession();
}
}
Listing 2: Account.java:
package uk.ac.york.cserv.hibernatetest;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="ACCOUNTS")
public class Account {
@Id
@Column(name = "USERNAME", unique = true, nullable = false)
private String username;
@Column(name = "SURNAME")
private String surname;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
@Override
public String toString() {
return "Account [username=" + username + ", surname=" + surname + "]";
}
}
Listing 3: Hibernate-ora.cfg.xml:
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@testhost:1521:test</property>
<property name="connection.username">testschema</property>
<property name="connection.password">testpassword</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Names of the annotated classes -->
<mapping class="uk.ac.york.cserv.hibernatetest.Account"/>
</session-factory>
</hibernate-configuration>
Listing 4: Output of the program:
Print all accounts with null surnames...
Hibernate: select account0_.USERNAME as USERNAME0_, account0_.SURNAME as SURNAME0_ from ACCOUNTS account0_ where account0_.SURNAME is null
Account [username=user2, surname=null]
Adding surnames to accounts that have null surnames...
Hibernate: select account0_.USERNAME as USERNAME0_, account0_.SURNAME as SURNAME0_ from ACCOUNTS account0_ where account0_.SURNAME is null
Adding surname of Jones to : user2
Again print all accounts that have null surnames (should be none)...
Hibernate: select account0_.USERNAME as USERNAME0_, account0_.SURNAME as SURNAME0_ from ACCOUNTS account0_ where account0_.SURNAME is null
Account [username=user2, surname=Jones]
Hibernate: update ACCOUNTS set SURNAME=? where USERNAME=?