I try to let Hibernate manage the order of items in a list. Whenever I remove an item and add it at the end of the list, Hibernate throws an exception. I tried two different databases (MariaDB and HSQLDB) with the according jdbc drivers, but the problem still remains. I extracted the problem and created a basic test case with still the same problem.
My test case consists of two data object classes and one test class:
Code:
@Entity
public class ParentData {
@Id @GeneratedValue
long id;
@OneToMany @OrderColumn( name = "listOrder" )
List<ChildData> children = new ArrayList<ChildData>();
public List<ChildData> getChildren() {
return children;
}
public String toString() {
StringBuilder sb = new StringBuilder();
for( ChildData child: children ) {
sb.append( child.toString() );
sb.append( " / " );
}
return sb.toString();
}
}
Code:
@Entity
public class ChildData {
@Id @GeneratedValue
long id;
String childId;
public ChildData() {
}
public ChildData( String id ) {
childId = id;
}
@Override
public String toString() {
return childId;
}
}
Code:
public class HibTest {
public static void main(String[] args) {
SessionFactory sessionFactory;
Session session = null;
System.out.println(" SETUP HIBERNATE ");
try {
Configuration config = new Configuration().configure("/resources/hibernate.cfg.xml");
sessionFactory = config.buildSessionFactory();
session = sessionFactory.openSession();
} catch (Throwable t) {
System.err.println("Initial SessionFactory creation failed." + t);
throw new ExceptionInInitializerError(t);
}
System.out.println(" CREATE TEST DATA ");
session.beginTransaction();
ParentData parent = new ParentData();
session.save(parent);
String[] childrenStr = new String[] { "One", "Two", "Three", "Four", "Five" };
for (String str : childrenStr) {
ChildData child = new ChildData(str);
session.save(child);
parent.getChildren().add(child);
}
session.getTransaction().commit();
System.out.println(" MOVE CHILD DATA");
List<ChildData> children = parent.getChildren();
try {
System.out.println(parent.toString());
session.beginTransaction();
ChildData child = children.get(0);
children.remove(0);
session.flush();
children.add(child);
session.getTransaction().commit();
System.out.println(parent.toString());
System.out.println(" !! WORK DONE !!");
} catch (Exception e) {
e.printStackTrace();
System.out.println(" !! ERROR !!");
if( session.getTransaction().isActive() )
if( session.getTransaction().getRollbackOnly() )
session.getTransaction().rollback();
else
session.getTransaction().commit();
}
}
}
The output (with Hibernate statements) looks like this:
Code:
MOVE CHILD DATA
One / Two / Three / Four / Five /
Hibernate: delete from ParentData_ChildData where ParentData_id=? and listOrder=?
Hibernate: update ParentData_ChildData set children_id=? where ParentData_id=? and listOrder=?
Mär 12, 2017 11:13:26 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: -104, SQLState: 23505
Mär 12, 2017 11:13:26 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: integrity constraint violation: unique constraint or index violation; UK_9TWN3CBE7SO94343AOP2QKCTC table: PARENTDATA_CHILDDATA
Mär 12, 2017 11:13:26 PM org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl release
INFO: HHH000010: On release of batch it still contained JDBC statements
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:147)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1413)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1393)
at net.upf.hibtest.HibTest.main(HibTest.java:56)
The third line in the output shows me that hibernate tries to set a new children_id at an already existing listOrder position - and so duplicating the children_id, since it is already in use at the previous listOrder position. Instead it should just set the new listOrder positions for the already existing children_ids.
So I think everything would be fine if the line looked something like
Code:
update ParentData_ChildData set listOrder=? where ParentData_id=? and children_id=?
for each line to be changed. But I have no idea how to tell Hibernate to do it this way.
I read a lot of online documentation and a lot of questions in online forums, but not many people seem to have that problem, so I think I must be doing something fundamentally wrong in the way I define my classes (have seen no problem during my research) or in the way I reorder the items in the list (nearly no examples found on the internet on how to do this in a Hibernate way, so I did what seemed natural to me).
In fact, six years ago someone seemed to have the same problem here in this forum, but unfortunately he did not receive an answer (
viewtopic.php?f=1&t=1012610&p=2448639&hilit=ordercolumn#p2448639).
So maybe in the last years somebody has joined this forum who could provide an answer to this?
Would be grateful for any help. Thanks in advance.