Hibernate mapping one-to-many problem
I am not very experienced with Hibernate and I am trying to create one-to-many mapping.
Here are relevant tables:
And here are my mapping files:
<hibernate-mapping package="com.xorty.mailclient.server.domain">
<class name="Attachment" table="Attachment">
<id name="id">
<column name="idAttachment"></column>
</id>
<property name="filename">
<column name="name"></column>
</property>
<property name="blob">
<column name="file"></column>
<type name="blob"></type>
</property>
<property name="mailId">
<column name="mail_idmail"></column>
</property>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.xorty.mailclient.server.domain.Mail" table="mail">
<id name="id" type="integer" column="idmail"></id>
<property name="content">
<column name="body"></column>
</property>
<property name="ownerAddress">
<column name="account_address"></column>
</property>
<property name="title">
<column name="head"></column>
</property>
<set name="receivers" table="mail_has_contact" cascade="all">
<key column="mail_idmail"></key>
<many-to-many column="contact_address" class="com.xorty.mailclient.client.domain.Contact"></many-to-many>
</set>
<bag name="attachments" cascade="save-update, delete" inverse="true">
<key column="mail_idmail" not-null="true"/>
<one-to-many class="com.xorty.mailclient.server.domain.Attachment"/>
</bag>
</class>
</hibernate-mapping>
In plain english, one mail has more attachments. When I try to do CRUD on mail without attachments, everyting works just fine. When I add some attachment to mail, I cannot perform any CRUD operation.
I end up with following trace:
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:268)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:184)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
at domain.DatabaseTest.testPersistMailWithAttachment(DatabaseTest.java:355)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at junit.framework.TestCase.runTest(TestCase.java:168)
at junit.framework.TestCase.runBare(TestCase.java:134)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at junit.framework.TestSuite.runTest(TestSuite.java:232)
at junit.framework.TestSuite.run(TestSuite.java:227)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.sql.BatchUpdateException: Cannot add or update a child row: a foreign key constraint fails (`maildb`.`attachment`, CONSTRAINT `fk_Attachment_mail1` FOREIGN KEY (`mail_idmail`) REFERENCES `mail` (`idmail`) ON DELETE NO ACTION ON UPDATE NO ACTION)
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1666)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1082)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 27 more
Thank you
EDIT: On hvgotcodes proposal:
package com.xorty.mailclient.server.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.xorty.mailclient.client.domain.Account;
import com.xorty.mailclient.client.domain.AttachmentDTO;
import com.xorty.mailclient.client.domain.Contact;
import com.xorty.mailclient.client.domain.MailDTO;
/**
* Heavy weight Hibernate Mail
* @author MisoV
* @version 0.1
*/
public class Mail implements Serializable {
private List<Attachment> attachments = new ArrayList<Attachment>();
private String content;
private int id;
private boolean isNew;
private Account owner;
private String ownerAddress;
private Set<Contact> receivers = new HashSet<Contact>();
private String sender;
private String title;
/**
* Hibernate purposes
*/
public Mail() { // $codepro.audit.disable
}
/**
* Unwraps light DTO object to heavy Hibernate object.
* @param dto Corresponding DTO class.
*/
public Mail(final MailDTO dto) {
for (final AttachmentDTO attachmentDTO : dto.getAttachments()) {
attachments.add(new Attachment(attachmentDTO));
}
content = dto.getContent();
id = dto.getId();
isNew = dto.isNew();
owner = dto.getOwner();
ownerAddress = dto.getOwnerAddress();
receivers = dto.getReceivers();
sender = dto.getSender();
title = dto.getTitle();
}
/**
* Inserts new attachment
* @param attachment
*/
public void addAttachment(final Attachment attachment) {
attachments.add(attachment);
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Mail)) {
return false;
}
final Mail other = (Mail) obj;
if (attachments == null) {
if (other.attachments != null) {
return false;
}
} else if (!attachments.equals(other.attachments)) {
return false;
}
if (content == null) {
if (other.content != null) {
return false;
}
} else if (!content.equals(other.content)) {
return false;
}
if (id != other.id) {
return false;
}
if (isNew != other.isNew) {
return false;
}
if (owner == null) {
if (other.owner != null) {
return false;
}
} else if (!owner.equals(other.owner)) {
return false;
}
if (ownerAddress == null) {
if (other.ownerAddress != null) {
return false;
}
} else if (!ownerAddress.equals(other.ownerAddress)) {
return false;
}
if (receivers == null) {
if (other.receivers != null) {
return false;
}
} else if (!receivers.equals(other.receivers)) {
return false;
}
if (sender == null) {
if (other.sender != null) {
return false;
}
} else if (!sender.equals(other.sender)) {
return false;
}
if (title == null) {
if (other.title != null) {
return false;
}
} else if (!title.equals(other.title)) {
return false;
}
return true;
}
/**
* @return the attachments
*/
public List<Attachment> getAttachments() {
return attachments;
}
/**
* @return the content
*/
public String getContent() {
return content;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @return the owner
*/
public Account getOwner() {
return owner;
}
/**
* @return the ownerAddress
*/
public String getOwnerAddress() {
return ownerAddress;
}
/**
* @return the receivers
*/
public Set<Contact> getReceivers() {
return receivers;
}
/**
* @return the sender
*/
public String getSender() {
return sender;
}
/**
* @return the title
*/
public String getTitle() {
return title;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((attachments == null) ? 0 : attachments.hashCode());
result = prime * result + ((content == null) ? 0 : content.hashCode());
result = prime * result + id;
result = prime * result + (isNew ? 1231 : 1237);
result = prime * result + ((owner == null) ? 0 : owner.hashCode());
result = prime * result
+ ((ownerAddress == null) ? 0 : ownerAddress.hashCode());
result = prime * result
+ ((receivers == null) ? 0 : receivers.hashCode());
result = prime * result + ((sender == null) ? 0 : sender.hashCode());
result = prime * result + ((title == null) ? 0 : title.hashCode());
return result;
}
/**
* @return the isNew
*/
public boolean isNew() {
return isNew;
}
/**
* @param attachments the attachments to set
*/
public void setAttachments(final List<Attachment> attachments) {
this.attachments = attachments;
}
/**
* @param content the content to set
*/
public void setContent(final String content) {
this.content = content;
}
/**
* @param id the id to set
*/
public void setId(final int id) {
this.id = id;
}
/**
* @param isNew the isNew to set
*/
public void setNew(final boolean isNew) {
this.isNew = isNew;
}
/**
* @param owner the owner to set
*/
public void setOwner(final Account owner) {
this.owner = owner;
}
/**
* @param ownerAddress the ownerAddress to set
*/
public void setOwnerAddress(final String ownerAddress) {
this.ownerAddress = ownerAddress;
}
/**
* @param receivers the receivers to set
*/
public void setReceivers(final Set<Contact> receivers) {
this.receivers = receivers;
}
/**
* @param sender the sender to set
*/
public void setSender(final String sender) {
this.sender = sender;
}
/**
* @param title the title to set
*/
public void setTitle(final String title) {
this.title = title;
}
}
Attachment:
// $codepro.audit.disable com.instantiations.assist.eclipse.analysis.audit.rule.effectivejava.alwaysOverridetoString.alwaysOverrideToString
/**
*
*/
package com.xorty.mailclient.server.domain;
import java.io.Serializable;
import java.sql.SQLException;
import javax.sql.rowset.serial.SerialBlob;
import javax.sql.rowset.serial.SerialException;
import com.xorty.mailclient.client.domain.AttachmentDTO;
/**
* Heavy weight Hibernate Attachment
* @author MisoV
* @version 0.1
*/
public class Attachment implements Serializable {
private static final long serialVersionUID = 2047475939737947104L;
private SerialBlob blob;
private byte[] content;
private String contentid;
private String contenttype;
private String filename;
private int id;
private int mailId;
/**
* Hibernate purposes
*/
public Attachment() { // $codepro.audit.disable emptyMethod
}
/**
* Unwraps DTO to heavy weight hibernate object.
* @param dto
*/
public Attachment(final AttachmentDTO dto) {
content = dto.getContent();
contentid = dto.getContentid();
contenttype = dto.getContenttype();
filename = dto.getFilename();
id = dto.getId();
mailId = dto.getMailId();
try {
blob = new SerialBlob(content);
} catch (final SerialException e) {
e.printStackTrace();
} catch (final SQLException e) {
e.printStackTrace();
}
}
/**
* @return the blob
*/
public SerialBlob getBlob() {
return blob;
}
/**
* @return the content
*/
public byte[] getContent() {
return content;
}
/**
* @return the contentid
*/
public String getContentid() {
return contentid;
}
/**
* @return the contenttype
*/
public String getContenttype() {
return contenttype;
}
/**
* @return the filename
*/
public String getFilename() {
return filename;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @return the mailId
*/
public int getMailId() {
return mailId;
}
/**
* @param blob the blob to set
*/
public void setBlob(final SerialBlob blob) {
this.blob = blob;
}
/**
* @param content the content to set
*/
public void setContent(final byte[] content) {
this.content = content;
}
/**
* @param contentid the contentid to set
*/
public void setContentid(final String contentid) {
this.contentid = contentid;
}
/**
* @param contenttype the contenttype to set
*/
public void setContenttype(final String contenttype) {
this.contenttype = contenttype;
}
/**
* @param filename the filename to set
*/
public void setFilename(final String filename) {
this.filename = filename;
}
开发者_高级运维 /**
* @param id the id to set
*/
public void setId(final int id) {
this.id = id;
}
/**
* @param mailId the mailId to set
*/
public void setMailId(final int mailId) {
this.mailId = mailId;
}
}
EDIT2:
Hibernate actually performs insert:
insert into Attachment (name, file, mail_idmail, idAttachment) values (?, ?, ?, ?)
And values are correctly set. It doesn't fail on session.save nor on session.get (it even gets correct copy). It fails on committing transaction.
Only thing I can get of it is: Cannot add or update a child row: a foreign key constraint fails (
maildb.
attachment, CONSTRAINT
fk_Attachment_mail1FOREIGN KEY (
mail_idmail) REFERENCES
mail(
idmail) ON DELETE NO ACTION ON UPDATE NO ACTION)
you are getting a constraint violation exception. I think it is on the foreign key from attachment to mail, as evidenced by
foreign key constraint fails (`maildb`.`attachment`, CONSTRAINT `fk_Attachment_mail1` FOREIGN KEY (`mail_idmail`)
I noticed you are not specifying a generator for your id. See this documentation
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping-declaration-id
since you did not specify the generator, the default is 'assigned' which means you have to programmatically assign the ids to the objects before you save them, which might or might not be what you want.
You didn't show us the code for how your Mail entity relates to the Attachment entity, or how you create your entities, which would all me to help more...depending on how you do it, hibernate might or might not be able to assign the foreign key in the attachment class.
EDIT -- from your comments, you are missing a few things. Try assigning the id of the mail entity to the attachment in your addAttachment method on mail. If you want the db to assign the id, you need to look up how to have the id column of the tables auto-increment for whatever RDBMS system you are using, and then you change add a generator to the id mappings of the entities. The generator will probably be 'identity' or 'increment' depending on the RDBMS.
As you want to make a bi-directionnal relation, you should indicate to your Attachment the Mail object and not only the Id :
In the Attcahment class, remove
private int mailId; and replace it by private Mail mail;
with the correct setter and getter.
In the Xml mapping file:
<property name="mailId"> <column name="mail_idmail"></column> </property>
should be replaced by :
<many-to-one name="mail" column="mail_idmail" not-null="true"/>
By the way, your hashCode/equals methods are crap. You should read this : http://community.jboss.org/wiki/EqualsandHashCode Anyway, it's better to not override them than have them wrong.
精彩评论