0

I am having trouble inserting records with a @OneToMany relationship using Hibernate and Spring MVC. I can successfully insert records without adding anything to the @OneToMany collection. However, upon adding a collection record, it fails stating that there is a SQLIntegrityConstraintViolationException.

My current code for the mapping (annotation-style) is as follows:

Contact.java

package mil.navy.navsupbsc.entity;

import java.util.Collection;
import java.util.LinkedHashSet;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

@Entity
@Table(name = "CONTACT2")
public class Contact extends Auditable {

public Contact() {

}

// Create with mandatory fields
public Contact(long id, Salutation salutation, String firstName,
        String middleInitial, String lastName,
        MilitaryCivilianInformation milCivInfo) {
    this.setContactId(id);
    this.setSalutation(salutation);
    this.setFirstName(firstName);
    this.setMiddleInitial(middleInitial);
    this.setLastName(lastName);
    this.setMilitaryCivilianInformation(milCivInfo);
}

/**
 * 
 */
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue
@SequenceGenerator(name = "CONTACT_SEQ")
@Column(name = "CONTACT_ID")
private Long contactId;

@Fetch(FetchMode.SUBSELECT)
@OneToMany(cascade = { CascadeType.ALL, CascadeType.REMOVE }, mappedBy = "contact", orphanRemoval = true)
private Collection<Email> emails = new LinkedHashSet<Email>();

/**
 * @return the emails
 */
public Collection<Email> getEmails() {

    for (Email email : emails) {
        email.getEmailType();
    }

    return emails;
}

/**
 * @param emails
 *            the emails to set
 */
public void setEmails(Collection<Email> emails) {

    this.emails.clear();

    for (Email email : emails) {
        this.addEmail(email);
    }

}


public void addEmail(Email email) {

    email.setContact(this);
    this.getEmails().add(email);

}

[...more Getters / Setters and fields]

}

Email.java

package mil.navy.navsupbsc.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import org.codehaus.jackson.annotate.JsonBackReference;

/**
 * Implements Auditing Properties
 */

@Entity
@Table(name = "EMAIL")
public class Email extends Auditable {

private static final long serialVersionUID = -4833322552325183301L;

@Id
@GeneratedValue
@SequenceGenerator(name = "EMAIL_SEQ")
@Column(name = "EMAIL_ID")
private long emailId;

@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CONTACT_FK")
private Contact contact;

[More fields]

public Contact getContact() {
    return contact;
}

public void setContact(Contact contact) {
    this.contact = contact;
}

public long getEmailId() {
    return emailId;
}

public void setEmailId(long emailId) {
    this.emailId = emailId;
}

[more getters / setters]

}

ContactDAOImpl (I've tried many variations of this with no success)

public void saveContact(Contact contact) {
    Session session = sessionFactory.getCurrentSession();
    Collection<Email> emailCollection = new LinkedHashSet<Email>();
    emailCollection = contact.getEmails();
    Contact contactToSave;
    long contactId;
    if (contact.getContactId() == 0 || contact.getContactId() == null) {
        contactToSave = new Contact((long) 0, contact.getSalutation(),
                contact.getFirstName(), contact.getMiddleInitial(),
                contact.getLastName(),
                contact.getMilitaryCivilianInformation());

        session.save(contactToSave);
        session.flush();

        for (Email email : emailCollection) {
            // email.setContact(contactToSave);
            contactToSave.addEmail(email);
        }

        session.saveOrUpdate(contactToSave);
        session.flush();
        session.clear();
    }

Any help with this is much appreciated. I had a previous version of this that updated records correctly, but can't seem to work out the Save new records. I also originally used the contact that I passed in from the web service, but I attempted to create a new record in the DAO to eliminate potential problems in the latest variation of my code.

Also, I know that there are many similar questions, but I have tried many of the answers with no success (hence the new question).

Thank you for your help!

UPDATE

I checked to see what ID the data layer returns after the initial save and verified (unsuccessfully) that the same ID was saved in the database. The returned ID is different than the saved ID. For example, the latest save showed the Contact ID as '1129' with the returned contact after the initial save. I did a retrieve from the database with contact ID '1129' - and it successfully returned the contact. After closing the transaction, I viewed the data directly in the database. The database showed '193' as the Contact ID instead of '1129'. Any ideas??

Ashot Karakhanyan
  • 2,804
  • 3
  • 23
  • 28
Laura Ritchey
  • 711
  • 8
  • 20
  • Can you also post the stacktrace? – Nivas Mar 25 '14 at 18:21
  • @Nivas - I figured out the problem...turns out it was because of using a trigger directly in the database rather than using the value generated through Hibernate prior to the insert. I also had incorrect syntax for directing Hibernate to use my custom sequence generator rather than the default Hibernate sequence generator. Since it's working now, I don't have the stack trace. Thanks for looking at the issue though! If you have any further suggestions based on the solution I posted, feel free to post them - I'm still new to Hibernate and JAVA - so I really appreciate the help I've received! – Laura Ritchey Mar 25 '14 at 18:32

1 Answers1

0

I figured out the issue I was having.

Prior to creating the Hibernate entity definition, I created a sequence and trigger in the database directly. I also specified the same sequence in the entity definition within the JAVA hibernate code. However, my syntax for generating the sequence was not complete.

Since the syntax for generating the sequence was incomplete, the JAVA code was using the default HIBERNATE_SEQUENCE generator, rather than the sequence I created. Hibernate returned the sequence value from HIBERNATE_SEQUENCE (creating the ID prior to the database insert). However, immediately prior to inserting the value into the database, Oracle ran my custom sequence and assigned the Contact ID to the newly generated value.

Consequently, when I tried to add a value to the collection, the Insert statement attempted to use the sequence value generated by Hibernate as the foreign key, rather than the sequence value generated by the database.

In order to ensure that the Hibernate code and database were in sync, I removed the ContactID trigger from the database. Based on another question in StackOverflow (Hibernate and Oracle Sequence), I updated the JAVA Hibernate code for the Contact.java ContactID declaration to:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CONTACT_SEQ")
@SequenceGenerator(name = "CONTACT_SEQ", sequenceName = "contact_seq", allocationSize = 1)
@Column(name = "CONTACT_ID")
private Long contactId;

Thankfully this fixed the problem. The JAVA Hibernate code now creates the ID through the sequence I specified. The database then successfully inserts those values into the database!

I was also able to simplify my save to:

public void saveContact(Contact contact) {

    Session session = sessionFactory.getCurrentSession();

    if (contact.getContactId() == 0 || contact.getContactId() == null) {

        session.save(contact);
        session.flush();

    }

}

I'm sure that I'll need to change it again though to account for updating the contact - probably using saveOrUpdate() instead of save().

Community
  • 1
  • 1
Laura Ritchey
  • 711
  • 8
  • 20