Hi,
I've experienced some weird behavior with Hibernate recently and I haven't foudn anything which explains it yet.
I have a simple piece of project with 2 classes : Speaker and Talk. A speaker can give multiple talks and a talk can only be given by one speaker.
Here is my domain :
I decided to implement it with a bidirectional relationship, with a Set in my Spealer class.
Code:
public class Speaker extends BaseEntity {
...
@OneToMany(mappedBy = "speaker", fetch = FetchType.EAGER)
private Set<Talk> talks;
...
}
And my Talk which simply holds a reference to one Speaker.
Code:
public class Talk extends BaseEntity {
...
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "SPEAKER_FK", nullable=false)
private Speaker speaker;
...
}
Here is my DAO implementation for the Speaker.
Code:
@Repository
@Transactional
public class SpeakerDaoImpl extends GenericDaoImpl<Speaker, Long> implements SpeakerDao {
@Autowired
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory s) {
this.sessionFactory = s;
}
protected SessionFactory getSessionFactory() {
if (sessionFactory == null) {
throw new IllegalStateException(
"sessionFactory has not been set on DAO before usage");
}
return sessionFactory;
}
protected Session getSession() {
return getSessionFactory().getCurrentSession();
}
@SuppressWarnings("unchecked")
public List<Speaker> findByLastName(String lastName) {
Criteria criteria = getSession().createCriteria(getPersistentClass());
criteria.add(Restrictions.like("lastName", lastName));
return criteria.list();
}
@Transactional
public Speaker makePersistent(Speaker speaker) {
getSession().saveOrUpdate(speaker);
return speaker;
}
}
I then wrote a short unit test case to try out what I was doing.
Code:
package com.mycompany.myproject;
import java.util.List;
import org.joda.time.LocalDateTime;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mycompany.myproject.model.Speaker;
import com.mycompany.myproject.model.Talk;
public class AdapterTest {
protected ApplicationContext applicationContext;
private static final Logger LOGGER = LoggerFactory
.getLogger(AdapterTest.class);
protected SpeakerDao speakerDao;
protected TalkDao talkDao;
@Before
public void setUp() {
applicationContext = new ClassPathXmlApplicationContext(new String[] {
"classpath*:adapter-test-context.xml",
"classpath*:datasource-test-context.xml" });
speakerDao = (SpeakerDao) applicationContext.getBean("speakerDao");
talkDao = (TalkDao) applicationContext.getBean("talkDao");
}
@After
public void tearDown() {
applicationContext = null;
speakerDao = null;
talkDao = null;
}
@Test
public void testDataWritingAndReading() {
// Creates one speaker
Speaker newSpeaker = new Speaker();
newSpeaker.setFirstName("John");
newSpeaker.setLastName("Smith");
newSpeaker.setEmail("john.smith@mycompany.com");
newSpeaker.setOrganization("Mycompany");
newSpeaker.setBirthDate(new LocalDateTime(1979, 1, 1, 0, 0));
newSpeaker.setAge(22);
newSpeaker.setGender("M");
Speaker createdSpeaker = speakerDao.makePersistent(newSpeaker);
// Creates a first talk, assign it to the speaker
Talk talk1 = new Talk();
talk1.setTitle("One title");
talk1.setDescription("About this one title");
talk1.setSpeaker(createdSpeaker);
talkDao.makePersistent(talk1);
//Create second talk with speaker3
Talk talk2 = new Talk();
talk2.setTitle("Another title");
talk2.setDescription("About this other title");
talk2.setSpeaker(createdSpeaker);
talkDao.makePersistent(talk2);
List<Speaker> speakersFound = speakerDao.findAll();
LOGGER.debug("Total number of speakers : " + speakersFound.size());
for(Speaker s : speakersFound)
LOGGER.debug(s.toString());
}
}
And this is giving a pretty strange output, instead of giving me 1 speaker, it prints out 2 speakers, both having the same id.
I tried to play around with the way I annotated the fields but nothing changes.
I know I could implement it as unidirectional mapping and removing the Set from my Speaker class but that would just be a workaround as I guess this behavior is not the expected one.
Do you see anything which explains these results ?
For those who might want to check out the complete code :
http://dl.dropbox.com/u/19788800/adapter.zipMany thanks in advance for your help !