Bashu Bashu - 5 months ago 85
MySQL Question

org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update

I get below mentioned stack trace though the data gets inserted successfully.

Hibernate: select attendee_.attendeeId, attendee_.attendeeName as attendee2_1_ from attendee attendee_ where attendee_.attendeeId=?
Hibernate: select attendee_.attendeeId, attendee_.attendeeName as attendee2_1_ from attendee attendee_ where attendee_.attendeeId=?
Hibernate: insert into event (eventName, startDate, eventId) values (?, ?, ?)
Hibernate: insert into attendee (attendeeName, attendeeId) values (?, ?)
Hibernate: insert into attendee (attendeeName, attendeeId) values (?, ?)
Hibernate: update attendee set attendeeId=? where attendeeId=?
Hibernate: update attendee set attendeeId=? where attendeeId=?
Aug 29, 2010 7:39:10 PM org.hibernate.util.JDBCExceptionReporter logExceptions
WARNING: SQL Error: 1062, SQLState: 23000
Aug 29, 2010 7:39:10 PM org.hibernate.util.JDBCExceptionReporter logExceptions
SEVERE: Duplicate entry '11' for key 'PRIMARY'
Aug 29, 2010 7:39:10 PM org.hibernate.event.def.AbstractFlushingEventListener performExecutions
SEVERE: Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:69)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:202)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:230)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:296)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:980)
at com.practice.hibernate.basic.BasicOperations.main(BasicOperations.java:51)
Caused by: java.sql.BatchUpdateException: Duplicate entry '11' for key 'PRIMARY'
at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:665)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195)
... 6 more
Exception in thread "main" org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:69)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:202)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:230)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:296)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:980)
at com.practice.hibernate.basic.BasicOperations.main(BasicOperations.java:51)
Caused by: java.sql.BatchUpdateException: Duplicate entry '11' for key 'PRIMARY'
at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:665)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195)
... 6 more


Please note:

a) My db has no records currently
b) Data gets inserted into DB successfully.

Here I am trying to persist an Event object which contains two Attendee Objects. That is all.

My Test Class:

public static void main(String[] args) {
Session session = HibernateRuntime.getSession();

try {
Set<Attendee> attendees = new HashSet<Attendee>(2);

Attendee attendee = new Attendee();
attendee.setAttendeeId(3);
attendee.setAttendeeName("Baswanth Rao");

Attendee attendee1 = new Attendee();
attendee1.setAttendeeId(4);
attendee1.setAttendeeName("Razi Ahmed");

attendees.add(attendee);
attendees.add(attendee1);

Event event = new Event();
event.setEventId(11);
event.setEventName("Initiatives Workshop 3");
event.setStartDate(new Date());
event.setAttendees(attendees);

session.save(event);
session.flush();

} finally {
session.close();
}
}


Event.hbm.xml:

<hibernate-mapping package="com.practice.hibernate.vo">
<class name="Event" table="event">
<id name="eventId" column="eventId" type="long">
<generator class="assigned" />
</id>

<property name="eventName" type="string" length="100" />
<property name="startDate" type="date" />

<set name="attendees" cascade="all">
<key column="attendeeId" />
<one-to-many class="Attendee" />
</set>
</class>
</hibernate-mapping>


hibernate.cfg.xml

<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/test</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<property name="connection.autocommit">false</property>

<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>

<mapping resource="com/practice/hibernate/vo/Event.hbm.xml"></mapping>
<mapping resource="com/practice/hibernate/vo/Attendee.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration>

Answer

Your Event.hbm.xml says:

<set name="attendees" cascade="all">
    <key column="attendeeId" />
    <one-to-many class="Attendee" />
</set>

In plain english, this means that the column Attendee.attendeeId is the foreign key for the association attendees and points to the primary key of Event.

When you add those Attendees to the event, hibernate updates the foreign key to express the changed association. Since that same column is also the primary key of Attendee, this violates the primary key constraint.

Since an Attendee's identity and event participation are independent, you should use separate columns for the primary and foreign key.

Edit: The selects might be because you don't appear to have a version property configured, making it impossible for hibernate to know whether the attendees already exists in the database (they might have been loaded in a previous session), so hibernate emits selects to check. As for the update statements, it was probably easier to implement that way. If you want to get rid of these separate updates, I recommend mapping the association from both ends, and declare the Event-end as inverse.