Hello,
Recently I'm moving my application from Hibernate 3.2.0CR2 to 3.2.0GA, but something's broken.
I have two classes, Document and DocumentHistory.
Document references its history snapshots through a one-to-many Map to DocumentHistory. For performance consideration, the map is lazily loaded.
When I try to add a history to a newly loaded document, it fails:
Code:
Document document = (Document) session.load(Document.class, documentId);
assertNotNull(document);
DocumentHistory history = new DocumentHistory();
history.setVersion(1);
history.setTitle(document.getTitle());
history.setContent(document.getContent());
history.setDocument(document);
// Put the new history into the map.
// !!! The history is not persisted along with the document !!!
Map<Integer, DocumentHistory> histories = document.getHistories();
histories.put(history.getVersion(), history);
// !!! Even histories.size() == 0 at this point !!!
assertEquals(1, histories.size());
document.setTitle("New Title");
document.setContent("New Content");
tx.begin();
session.flush();
tx.commit();
Now I have to add Hibernate.initialize(document.getHistories()) or do some read operation before put() as a work around.
I believe this is a bug of Hibernate 3.2.0GA, because the same test case runs successfully under Hibernate 3.2.0CR2.
Should I report it to JIRA?
Complete test code:
Hibernate version: 3.2.0GA
Hibernate Annotations version: 3.2.0GA
Database: MySQL 5.0.26
DB Script:Code:
CREATE TABLE `Document` (
`uuid` char(32) NOT NULL,
`title` varchar(50) NOT NULL,
`content` mediumtext NOT NULL,
PRIMARY KEY (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `DocumentHistory` (
`uuid` char(32) NOT NULL,
`document_uuid` char(32) NOT NULL,
`version` int(11) NOT NULL,
`title` varchar(50) NOT NULL,
`content` mediumtext NOT NULL,
PRIMARY KEY (`uuid`),
KEY `document_uuid` (`document_uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Annotated classes:Code:
@Entity
public class Document implements Serializable {
private String uuid;
private String title;
private String content;
private Map<Integer, DocumentHistory> histories = new HashMap<Integer, DocumentHistory>();
@Id @GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
@Column(name = "title", length = 50)
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Basic(fetch = FetchType.LAZY)
@Column(name = "content", nullable = false)
@Lob
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@OneToMany(mappedBy = "document", fetch = FetchType.LAZY,
cascade = {CascadeType.ALL})
@MapKey(name = "version")
public Map<Integer, DocumentHistory> getHistories() {
return this.histories;
}
public void setHistories(Map<Integer, DocumentHistory> histories) {
this.histories = histories;
}
}
Code:
@Entity
public class DocumentHistory implements Serializable {
private static final long serialVersionUID = 1L;
private String uuid;
private int version;
private String title;
private String content;
private Document document;
@Id @GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
@Column(name = "version", nullable = false, updatable = false)
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Column(name = "title", length = 50)
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Basic(fetch = FetchType.LAZY)
@Column(name = "content", nullable = false)
@Lob
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "document_uuid", nullable = false, updatable = false)
public Document getDocument() {
return document;
}
public void setDocument(Document document) {
this.document = document;
}
}
JUnit test case:Code:
public class HibernateMapTest extends TestCase {
private Session session;
private String documentId;
@Override
protected void setUp() throws Exception {
AnnotationConfiguration cfg = new AnnotationConfiguration();
cfg.addAnnotatedClass(Document.class);
cfg.addAnnotatedClass(DocumentHistory.class);
cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
cfg.setProperty("hibernate.connection.url", "jdbc:mysql://192.168.11.1/sample");
cfg.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
cfg.setProperty("hibernate.connection.username", "*****");
cfg.setProperty("hibernate.connection.password", "*****");
cfg.setProperty("hibernate.show_sql", "true");
SessionFactory sf = cfg.buildSessionFactory();
session = sf.openSession();
// Create a document
Transaction tx = session.beginTransaction();
Document document = new Document();
document.setTitle("Title");
document.setContent("Content");
documentId = (String) session.save(document);
tx.commit();
// Kicks it out of the session cache
session.evict(document);
}
@Override
protected void tearDown() throws Exception {
if (session != null && session.isOpen()) {
Transaction tx = session.getTransaction();
try {
// DB Clean-up
Document document = (Document) session.get(Document.class, documentId);
if (document != null) {
tx.begin();
session.delete(document);
tx.commit();
}
} catch (HibernateException ex) {
if (tx.isActive())
tx.rollback();
} finally {
try {
session.close();
session = null;
}
catch (Throwable e) {
}
}
}
}
public void testHistory1() throws Exception {
Transaction tx = session.getTransaction();
try {
// Load it...
Document document = (Document) session.load(Document.class, documentId);
assertNotNull(document);
DocumentHistory history = new DocumentHistory();
history.setVersion(1);
history.setTitle(document.getTitle());
history.setContent(document.getContent());
history.setDocument(document);
// Put the new history into the map.
// !!! The history is not persisted along with the document !!!
Map<Integer, DocumentHistory> histories = document.getHistories();
histories.put(history.getVersion(), history);
// !!! Even histories.size() == 0 at this point !!!
// assertEquals(1, histories.size());
document.setTitle("New Title");
document.setContent("New Content");
tx.begin();
session.flush();
tx.commit();
session.evict(document);
// And load it agian...
document = (Document) session.load(Document.class, documentId);
assertNotNull(document);
// Check if the DocumentHistory object persisted correctly...
assertEquals(1, document.getHistories().size());
} catch (HibernateException ex) {
fail(ex.toString());
tx.rollback();
}
}
}