I have an Hibernate Dao test case that gives me some high chaparal black magic again..
It is a test case to test the sorting order by id property, the id being the primary key value of the object.
First things first, the mapping, the domain and the dao...
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Apr 18, 2010 1:03:51 PM by Hibernate Tools 3.2.1.GA -->
<hibernate-mapping>
<class name="com.thalasoft.learnintouch.core.domain.Form" table="form" dynamic-insert="true" dynamic-update="true">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<version name="version" type="int">
<column name="version" not-null="true" />
</version>
<property name="name" type="string">
<column name="name" length="50" not-null="true" />
</property>
<property name="description" type="string">
<column name="description" not-null="false" />
</property>
<property name="title" type="string">
<column name="title" not-null="false" />
</property>
<property name="image" type="string">
<column name="image" length="50" not-null="false" />
</property>
<property name="email" type="string">
<column name="email" not-null="false" />
</property>
<property name="instructions" type="text">
<column name="instructions" length="65535" not-null="false" />
</property>
<property name="acknowledge" type="text">
<column name="acknowledge" length="65535" not-null="false" />
</property>
<property name="webpageId" type="string">
<column name="webpage_id" not-null="false" />
</property>
<property name="mailSubject" type="string">
<column name="mail_subject" not-null="false" />
</property>
<property name="mailMessage" type="text">
<column name="mail_message" length="65535" not-null="false" />
</property>
<set name="formItems" inverse="true" cascade="all" order-by="list_order">
<key>
<column name="form_id" not-null="true" />
</key>
<one-to-many class="com.thalasoft.learnintouch.core.domain.FormItem" />
</set>
</class>
</hibernate-mapping>
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Apr 18, 2010 1:03:51 PM by Hibernate Tools 3.2.1.GA -->
<hibernate-mapping>
<class name="com.thalasoft.learnintouch.core.domain.FormItem" table="form_item" dynamic-insert="true" dynamic-update="true">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<version name="version" type="int">
<column name="version" not-null="true" />
</version>
<many-to-one name="mailList" class="com.thalasoft.learnintouch.core.domain.MailList" cascade="all" fetch="select">
<column name="mail_list_id" />
</many-to-one>
<many-to-one name="form" class="com.thalasoft.learnintouch.core.domain.Form" cascade="all" fetch="select">
<column name="form_id" not-null="true" />
</many-to-one>
<property name="type" type="string">
<column name="type" length="50" not-null="true" />
</property>
<property name="name" type="string">
<column name="name" length="50" not-null="false" />
</property>
<property name="text" type="text">
<column name="text" length="65535" not-null="false" />
</property>
<property name="help" type="string">
<column name="help" not-null="false" />
</property>
<property name="defaultValue" type="string">
<column name="default_value" length="50" not-null="false" />
</property>
<property name="size" type="string">
<column name="size" length="3" not-null="false" />
</property>
<property name="maxlength" type="string">
<column name="maxlength" length="4" not-null="false" />
</property>
<property name="listOrder" type="int">
<column name="list_order" not-null="true" />
</property>
<property name="inMailAddress" type="boolean">
<column name="in_mail_address" not-null="false" />
</property>
<set name="formItemValues" inverse="true" cascade="all">
<key>
<column name="form_item_id" not-null="true" />
</key>
<one-to-many class="com.thalasoft.learnintouch.core.domain.FormItemValue" />
</set>
<set name="formValids" inverse="true" cascade="all">
<key>
<column name="form_item_id" not-null="true" />
</key>
<one-to-many class="com.thalasoft.learnintouch.core.domain.FormValid" />
</set>
</class>
</hibernate-mapping>
Code:
package com.thalasoft.learnintouch.core.domain;
import java.util.HashSet;
import java.util.Set;
public class Form implements java.io.Serializable {
private Integer id;
private int version;
private String name;
private String description;
private String title;
private String image;
private String email;
private String instructions;
private String acknowledge;
private String webpageId;
private String mailSubject;
private String mailMessage;
private Set<FormItem> formItems = new HashSet<FormItem>(0);
public Form() {
}
public Form(String name, String description, String title, String image,
String email, String instructions, String acknowledge,
String webpageId, String mailSubject, String mailMessage) {
this.name = name;
this.description = description;
this.title = title;
this.image = image;
this.email = email;
this.instructions = instructions;
this.acknowledge = acknowledge;
this.webpageId = webpageId;
this.mailSubject = mailSubject;
this.mailMessage = mailMessage;
}
public Form(String name, String description, String title, String image,
String email, String instructions, String acknowledge,
String webpageId, String mailSubject, String mailMessage,
Set<FormItem> formItems) {
this.name = name;
this.description = description;
this.title = title;
this.image = image;
this.email = email;
this.instructions = instructions;
this.acknowledge = acknowledge;
this.webpageId = webpageId;
this.mailSubject = mailSubject;
this.mailMessage = mailMessage;
this.formItems = formItems;
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public int getVersion() {
return this.version;
}
public void setVersion(int version) {
this.version = version;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImage() {
return this.image;
}
public void setImage(String image) {
this.image = image;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getInstructions() {
return this.instructions;
}
public void setInstructions(String instructions) {
this.instructions = instructions;
}
public String getAcknowledge() {
return this.acknowledge;
}
public void setAcknowledge(String acknowledge) {
this.acknowledge = acknowledge;
}
public String getWebpageId() {
return this.webpageId;
}
public void setWebpageId(String webpageId) {
this.webpageId = webpageId;
}
public String getMailSubject() {
return this.mailSubject;
}
public void setMailSubject(String mailSubject) {
this.mailSubject = mailSubject;
}
public String getMailMessage() {
return this.mailMessage;
}
public void setMailMessage(String mailMessage) {
this.mailMessage = mailMessage;
}
public Set<FormItem> getFormItems() {
return this.formItems;
}
@SuppressWarnings("unused")
private void setFormItems(Set<FormItem> formItems) {
this.formItems = formItems;
}
public void addFormItem(FormItem formItem) {
Form oldForm = formItem.getForm();
if (oldForm != this) {
if (oldForm != null) {
oldForm.getFormItems().remove(formItem);
}
formItem.setForm(this);
this.formItems.add(formItem);
}
}
}
Code:
package com.thalasoft.learnintouch.core.domain;
import java.util.HashSet;
import java.util.Set;
public class FormItem implements java.io.Serializable {
private Integer id;
private int version;
private MailList mailList;
private Form form;
private String type;
private String name;
private String text;
private String help;
private String defaultValue;
private String size;
private String maxlength;
private int listOrder;
private String inMailAddress;
private Set<FormItemValue> formItemValues = new HashSet<FormItemValue>(0);
private Set<FormValid> formValids = new HashSet<FormValid>(0);
public FormItem() {
}
public FormItem(Form form, String type, String name, String text,
String help, String defaultValue, String size, String maxlength,
int listOrder, String inMailAddress) {
this.form = form;
this.type = type;
this.name = name;
this.text = text;
this.help = help;
this.defaultValue = defaultValue;
this.size = size;
this.maxlength = maxlength;
this.listOrder = listOrder;
this.inMailAddress = inMailAddress;
}
public FormItem(MailList mailList, Form form, String type, String name,
String text, String help, String defaultValue, String size,
String maxlength, int listOrder, String inMailAddress,
Set<FormItemValue> formItemValues, Set<FormValid> formValids) {
this.mailList = mailList;
this.form = form;
this.type = type;
this.name = name;
this.text = text;
this.help = help;
this.defaultValue = defaultValue;
this.size = size;
this.maxlength = maxlength;
this.listOrder = listOrder;
this.inMailAddress = inMailAddress;
this.formItemValues = formItemValues;
this.formValids = formValids;
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public int getVersion() {
return this.version;
}
public void setVersion(int version) {
this.version = version;
}
public MailList getMailList() {
return this.mailList;
}
public void setMailList(MailList mailList) {
this.mailList = mailList;
}
public Form getForm() {
return this.form;
}
public void setForm(Form form) {
this.form = form;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public String getHelp() {
return this.help;
}
public void setHelp(String help) {
this.help = help;
}
public String getDefaultValue() {
return this.defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public String getSize() {
return this.size;
}
public void setSize(String size) {
this.size = size;
}
public String getMaxlength() {
return this.maxlength;
}
public void setMaxlength(String maxlength) {
this.maxlength = maxlength;
}
public int getListOrder() {
return this.listOrder;
}
public void setListOrder(int listOrder) {
this.listOrder = listOrder;
}
public String getInMailAddress() {
return this.inMailAddress;
}
public void setInMailAddress(String inMailAddress) {
this.inMailAddress = inMailAddress;
}
public Set<FormItemValue> getFormItemValues() {
return this.formItemValues;
}
@SuppressWarnings("unused")
private void setFormItemValues(Set<FormItemValue> formItemValues) {
this.formItemValues = formItemValues;
}
public void addFormItemValue(FormItemValue formItemValue) {
FormItem oldFormItem = formItemValue.getFormItem();
if (oldFormItem != this) {
if (oldFormItem != null) {
oldFormItem.getFormItemValues().remove(formItemValue);
}
formItemValue.setFormItem(this);
this.formItemValues.add(formItemValue);
}
}
public Set<FormValid> getFormValids() {
return this.formValids;
}
@SuppressWarnings("unused")
private void setFormValids(Set<FormValid> formValids) {
this.formValids = formValids;
}
public void addFormValid(FormValid formValid) {
FormItem oldFormItem = formValid.getFormItem();
if (oldFormItem != this) {
if (oldFormItem != null) {
oldFormItem.getFormValids().remove(formValid);
}
formValid.setFormItem(this);
this.formValids.add(formValid);
}
}
}
Code:
package com.thalasoft.learnintouch.core.dao.hibernate;
import java.io.Serializable;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.transaction.annotation.Transactional;
import com.thalasoft.learnintouch.core.dao.FormDao;
import com.thalasoft.learnintouch.core.domain.Form;
@Transactional
public class FormHibernateDao extends GenericHibernateDao<Form, Serializable> implements FormDao {
public Form findByName(String name) {
Criteria criteria = getSession().createCriteria(Form.class);
criteria.add(Restrictions.eq("name", name)).setMaxResults(1);
return (Form) criteria.uniqueResult();
}
@SuppressWarnings("unchecked")
public List<Form> findByImage(String image) {
Criteria criteria = getSession().createCriteria(Form.class);
criteria.add(Restrictions.eq("image", image));
return criteria.list();
}
}
Code:
package com.thalasoft.learnintouch.core.dao.hibernate;
import java.io.Serializable;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.transaction.annotation.Transactional;
import com.thalasoft.learnintouch.core.dao.FormItemDao;
import com.thalasoft.learnintouch.core.domain.Form;
import com.thalasoft.learnintouch.core.domain.FormItem;
@Transactional
public class FormItemHibernateDao extends GenericHibernateDao<FormItem, Serializable> implements FormItemDao {
@SuppressWarnings("unchecked")
public List<FormItem> findByForm(Form form) {
Criteria criteria = getSession().createCriteria(FormItem.class);
criteria.add(Restrictions.eq("form", form)).addOrder(Order.asc("listOrder"));
return criteria.list();
}
@SuppressWarnings("unchecked")
public List<FormItem> findByFormOrderById(Form form) {
Criteria criteria = getSession().createCriteria(FormItem.class);
criteria.add(Restrictions.eq("form", form)).addOrder(Order.asc("id"));
return criteria.list();
}
@Override
public FormItem findByListOrder(Form form, int listOrder) {
Criteria criteria = getSession().createCriteria(FormItem.class);
criteria.add(Restrictions.eq("form", form)).add(Restrictions.eq("listOrder", listOrder)).setMaxResults(1);
return (FormItem) criteria.uniqueResult();
}
@Override
public FormItem findNextByListOrder(Form form, int listOrder) {
Criteria criteria = getSession().createCriteria(FormItem.class);
criteria.add(Restrictions.eq("form", form)).add(Restrictions.gt("listOrder", listOrder)).addOrder(Order.asc("listOrder")).setMaxResults(1);
return (FormItem) criteria.uniqueResult();
}
@Override
public FormItem findPreviousByListOrder(Form form, int listOrder) {
Criteria criteria = getSession().createCriteria(FormItem.class);
criteria.add(Restrictions.eq("form", form)).add(Restrictions.lt("listOrder", listOrder)).addOrder(Order.desc("listOrder")).setMaxResults(1);
return (FormItem) criteria.uniqueResult();
}
@Override
public long countListOrderDuplicates(Form form) {
long itemCount = 0;
try {
Query query = getSession().createQuery("select count(fi1.id) as count from FormItem fi1, FormItem fi2 where fi1.id != fi2.id and fi1.listOrder = fi2.listOrder and fi1.form.id = ?");
query.setLong(0, form.getId());
Long count = (Long) query.list().get(0);
itemCount = count.longValue();
} catch (Exception e) {
itemCount = 0;
}
return itemCount;
}
}
And now, the test itself... in two versions, one that passes and one that fails.
First, the test that passes:
Code:
@Test
public void testFindByFormOrderById() {
formItem1 = formItemDao.makePersistent(formItem1);
formItem0 = formItemDao.makePersistent(formItem0);
List<FormItem> formItems = formItemDao.findByFormOrderById(form);
assertEquals(2, formItems.size());
assertEquals(2, formItems.get(0).getListOrder());
assertEquals(formItem0.getName(), formItems.get(0).getName());
assertEquals(1, formItems.get(1).getListOrder());
assertEquals(formItem1.getName(), formItems.get(1).getName());
}
And the test that fails:
Code:
@Test
public void testFindByFormOrderById() {
formItem0 = formItemDao.makePersistent(formItem0);
formItem1 = formItemDao.makePersistent(formItem1);
List<FormItem> formItems = formItemDao.findByFormOrderById(form);
assertEquals(2, formItems.size());
assertEquals(2, formItems.get(0).getListOrder());
assertEquals(formItem0.getName(), formItems.get(0).getName());
assertEquals(1, formItems.get(1).getListOrder());
assertEquals(formItem1.getName(), formItems.get(1).getName());
}
You can see that the only difference, is the order of persisting the formItem0 and formItem1.
And that makes the test pass or fail.
Puzzling...
The exception of the failing test:
Quote:
-------------------------------------------------------------------------------
Test set: com.thalasoft.learnintouch.core.dao.FormItemDaoTest
-------------------------------------------------------------------------------
Tests run: 4, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.088 sec <<< FAILURE!
testFindByFormOrderById(com.thalasoft.learnintouch.core.dao.FormItemDaoTest) Time elapsed: 0.02 sec <<< FAILURE!
java.lang.AssertionError: expected:<2> but was:<1>
at org.junit.Assert.fail(Assert.java:74)
at org.junit.Assert.failNotEquals(Assert.java:448)
at org.junit.Assert.assertEquals(Assert.java:102)
at org.junit.Assert.assertEquals(Assert.java:323)
at org.junit.Assert.assertEquals(Assert.java:319)
at com.thalasoft.learnintouch.core.dao.FormItemDaoTest.testFindByFormOrderById(FormItemDaoTest.java:110)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:160)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
at org.springframework.test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:160)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)
at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)