Hibernate, отображающий проблему «один ко многим»

Я не очень разбираюсь в Hibernate, и я пытаюсь создать сопоставление «один ко многим».

Вот соответствующие таблицы: alt text

И вот мои файлы сопоставления:

<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>

Проще говоря, в одном письме больше вложений. Когда я пытаюсь выполнить CRUD для почты без вложений, все работает нормально. Когда я добавляю какое-либо вложение в почту, я не могу выполнить какую-либо операцию CRUD.

Я получаю следующую трассировку:

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

Спасибо

EDIT: В предложении hvgotcodes:

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;
    }
}

Вложение:

// $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 фактически выполняет вставку:

insert into Attachment (name, file, mail_idmail, idAttachment) values (?, ?, ?, ?)

И значения установлены правильно. Он не терпит неудачу ни в session.save, ни в session.get (даже получает правильную копию). Сбой при совершении транзакции.

Единственное, что я могу получить, это: Cannot add or update a child row: a foreign key constraint fails (maildb.attachment, CONSTRAINTfk_Attachment_mail1FOREIGN KEY (mail_idmail) REFERENCESmail(idmail) ON DELETE NO ACTION ON UPDATE NO ACTION)


person Xorty    schedule 16.01.2011    source источник
comment
Хотя это не имеет прямого отношения к вашему вопросу, вы действительно хотите использовать List для сопоставления этой связи, а не Bag? Кажется, что порядок вложений в сообщении, вероятно, не имеет значения, пока они все там.   -  person R0MANARMY    schedule 16.01.2011
comment
И я изменил его на Bag, но безрезультатно (та же ошибка, что и со списком).   -  person Xorty    schedule 16.01.2011


Ответы (2)


вы получаете исключение нарушения ограничений. Я думаю, что это по внешнему ключу от приложения к почте, о чем свидетельствует

foreign key constraint fails (`maildb`.`attachment`, CONSTRAINT `fk_Attachment_mail1` FOREIGN KEY (`mail_idmail`)

Я заметил, что вы не указываете генератор для своего идентификатора. См. эту документацию

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping-declaration-id

поскольку вы не указали генератор, значение по умолчанию «назначено», что означает, что вы должны программно назначать идентификаторы объектам перед их сохранением, что может быть или не быть тем, что вы хотите.

Вы не показали нам код того, как ваша почтовая сущность связана с сущностью вложения, или как вы создаете свои сущности, что помогло бы мне больше помочь... в зависимости от того, как вы это делаете, гибернация может или не может быть в состоянии для назначения внешнего ключа в классе вложения.

РЕДАКТИРОВАТЬ - из ваших комментариев вам не хватает нескольких вещей. Попробуйте присвоить идентификатор почтового объекта вложению в методе addAttachment в почте. Если вы хотите, чтобы db назначал идентификатор, вам нужно посмотреть, как автоматически увеличивать столбец идентификаторов таблиц для любой системы СУРБД, которую вы используете, а затем вы добавляете генератор к сопоставлениям идентификаторов объектов. Генератор, вероятно, будет «идентичностью» или «приращением» в зависимости от СУБД.

person hvgotcodes    schedule 16.01.2011
comment
@xorty, а твой класс вложений? - person hvgotcodes; 16.01.2011
comment
@xorty, вы хотите, чтобы ваша БД присваивала значения для идентификатора, или вы хотите сделать это сами? - person hvgotcodes; 16.01.2011
comment
@hvgotcodes Теперь я вставил полную реализацию. Я использую hashCode в качестве идентификатора прямо сейчас, хотя я не знаю, хорошая ли это идея. На самом деле я бы не возражал, если бы этим занимался БД. - person Xorty; 16.01.2011
comment
к вашему сведению - почта DTO и вложение одинаковы, за исключением того, что вложение использует пакет java.sql, который нельзя использовать на стороне клиента, поэтому он должен быть таким - person Xorty; 16.01.2011
comment
@hvgtcodes - для вашего редактирования, если вы посмотрите на конструктор вложения, ему действительно назначен почтовый идентификатор: mailId = dto.getMailId(); Таким образом, метод addAttachment уже работает с правильным идентификатором. - person Xorty; 16.01.2011
comment
@xorty, да, я только что заметил. Хм. Я не уверен - ошибка из базы данных определенно указывает на то, что когда действительно происходит сохранение, внешний ключ не устанавливается. Попробуйте посмотреть значения сущностей в отладчике прямо перед их сохранением. также настройте спящий режим для регистрации sql и убедитесь, что он правильный. - person hvgotcodes; 16.01.2011
comment
@hvgtcodes см. редактирование 2 ... Я полагаю, что это что-то с сопоставлением, это область, в которой мне не хватает знаний ... Или, может быть, что-то с сопоставлением SerialBlob с большим двоичным объектом MySQL? - person Xorty; 16.01.2011
comment
@xorty, эта ошибка в ограничении внешнего ключа означает, что FK из вложения в почту неверен. У вас есть модульный тест для этого? Если это так, попробуйте вручную установить идентификаторы. Если у вас нет модульного теста, напишите его;) Возможно, ваши хэш-коды генерируются неправильно. - person hvgotcodes; 16.01.2011
comment
Да, я на самом деле пишу модульные тесты и устанавливаю все вручную :( Хэш-коды были другими, поэтому я изменил методы hashCode. Теперь они такие же, но это не помогло. - person Xorty; 16.01.2011
comment
@xorty, вы уверены, что исключение осталось прежним после того, как вы изменили хэш-код? - person hvgotcodes; 16.01.2011
comment
В любом случае спасибо, может это просто проклятие ;D - person Xorty; 16.01.2011
comment
я думаю, что это что-то относительно простое, но не имея возможности получить доступ и поиграть с кодом, я не могу знать. Возможно, вам захочется отойти на некоторое время и вернуться позже — иногда это помогает. - person hvgotcodes; 16.01.2011
comment
Да, здесь 04:15 ... так что я пойду спать и увидимся завтра :/ - person Xorty; 16.01.2011
comment
@xorty У меня было еще несколько идей: 1) попробуйте сначала сохранить почту, а затем сохранить вложения. 2) попробуйте не сохранять блоб, если можете. Просто сохраните почту и метаданные вложения. - person hvgotcodes; 16.01.2011
comment
@hvgotcodes спасибо за свежие идеи. Итак - 1) работает, 2) невозможно ... Так что я могу сохранить почту, а затем просмотреть ее вложения и сохранить их. Но я боюсь, что именно поэтому они изобрели отображение, поэтому мне не нужно делать это вот так :D - person Xorty; 16.01.2011
comment
@xorty, да, я полностью согласен. Но, по крайней мере, у вас есть запасной вариант, чтобы вы могли двигаться дальше, а позже вы могли вернуться и исправить это. Теперь попробуйте это. Сохраните почту без вложений, затем добавьте вложения, затем обновите почту. - person hvgotcodes; 16.01.2011
comment
@hvgtcodes - так оно и работает ... но я сделал обновление, как предложил Джоэл - теперь я использую полный экземпляр Mail, а не только его идентификатор в классе вложений. Все равно не повезло с отображением. - person Xorty; 16.01.2011

Поскольку вы хотите установить двунаправленное отношение, вы должны указать в своем вложении объект Mail, а не только Id:

В классе Attcahment удалите

частный почтовый идентификатор; и заменить его на частную почту Почты;

с правильным сеттером и геттером.

В файле сопоставления XML:

<property name="mailId">
    <column name="mail_idmail"></column>
</property>

следует заменить на:

<many-to-one name="mail"
             column="mail_idmail" not-null="true"/>

Кстати, ваши методы hashCode/equals — дерьмо. Вы должны прочитать это: http://community.jboss.org/wiki/EqualsandHashCode В любом случае, это лучше не переопределять их, чем иметь их неправильно.

person joel1di1    schedule 16.01.2011
comment
Каждое вложение должно иметь почтовое свойство, которое уже имеет список вложений ?? - person Xorty; 16.01.2011