22

I'm using Spring with Hibernate as a JPA provider and are trying to get a @OneToMany (a contact having many phonenumbers) to save the foreign key in the phone numbers table. From my form i get a Contact object that have a list of Phone(numbers) in it. The Contact get persisted properly (Hibernate fetches an PK from the specified sequence). The list of Phone(numbers) also gets persisted with a correct PK, but there's no FK to the Contacts table.

public class Contact implements Serializable {

    @OneToMany(mappedBy = "contactId", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
    private List<Phone> phoneList;

}

public class Phone implements Serializable {

    @JoinColumn(name = "contact_id", referencedColumnName = "contact_id")
    @ManyToOne
    private Contact contactId;

}

@Repository("contactDao")
@Transactional(readOnly = true)
public class ContactDaoImpl implements ContactDao {

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void save(Contact c) {
        em.persist(c);
        em.flush();
    }
}


@Controller
public class ContactController {
    @RequestMapping(value = "/contact/new", method = RequestMethod.POST)
    public ModelAndView newContact(Contact c) {
        ModelAndView mv = new ModelAndView("contactForm");
        contactDao.save(c);
        mv.addObject("contact", c);
        return mv;
    }
}

Hopefully I got all of the relevant bits above, otherwise please let me know.

Wrench
  • 4,070
  • 4
  • 34
  • 46
NA.
  • 6,451
  • 9
  • 36
  • 36

7 Answers7

29

You have to manage the Java relationships yourself. For this kind of thing you need something like:

@Entity
public class Contact {
  @Id
  private Long id;

  @OneToMany(cascade = CascadeType.PERSIST, mappedBy = "contact")
  private List<Phone> phoneNumbers;

  public void addPhone(PhoneNumber phone) {
     if (phone != null) {
        if (phoneNumbers == null) {
            phoneNumbers = new ArrayList<Phone>();          
        }
        phoneNumbers.add(phone);
        phone.setContact(this);
     }
  }

  ...
}

@Entity
public class Phone {
  @Id
  private Long id;

  @ManyToOne
  private Contact contact;

  ...
}
cletus
  • 616,129
  • 168
  • 910
  • 942
  • 5
    Is the `addPhone` method correct? It looks to me like the null check is in the wrong place. – Mark Byers Jan 11 '11 at 13:03
  • phone.setContact(this) - it colud be a memory leak? or not? –  Feb 05 '14 at 11:56
  • 6
    Why do I have to manage the java relationship myself? What if I have a unidirectional relationship? – Fagner Brack Oct 10 '14 at 21:51
  • @FagnerBrack If you do not have `@ManyToOne` on the Phone class, hibernate will create a table to manage the relationship which will gamed like `CONTACT_PHONE` and hibernate will insert the required records to that table. – Pavan Kumar Jun 17 '21 at 12:57
6

In reply to Cletus' answer. I would say that it's important to have the @column annotation on the id fields, as well as all the sequence stuff. An alternative to using the mappedBy parameter of the @OneToMany annotation is to use the @JoinColumn annotation.

As a kinda aside your implementation of addPhone needs looking at. It should probably be something like.

public void addPhone(PhoneNumber phone) {
    if (phone == null) {
        return;
    } else {
        if (phoneNumbers == null) {
            phoneNumbers = new ArrayList<Phone>();
        }
        phoneNumbers.add(phone);
        phone.setContact(this);
    }
}
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
mR_fr0g
  • 8,462
  • 7
  • 39
  • 54
5

If the Contact-Phone relationship is unidirectional, you can also replace mappedBy in @OneToMany annotation with @JoinColumn(name = "contact_id").

@Entity
public class Contact {
  @Id
  private Long id;

  @OneToMany(cascade = CascadeType.PERSIST)
  @JoinColumn(name = "contact_id")
  private List<Phone> phoneNumbers;

  // normal getter/setter
  ...
}

@Entity
public class PhoneNumber {
  @Id
  private Long id;

  ...
}

Similar in JPA @OneToMany -> Parent - Child Reference (Foreign Key)

samsong8610
  • 137
  • 1
  • 7
2

I don't think the addPhone method is necessary, you only have to set the contact in the phone object:

phone.setContact(contact);
SkyWalker
  • 28,384
  • 14
  • 74
  • 132
Camel
  • 21
  • 1
1

If you want your relationship unidirectional i.e. can navigate from Contact to Phone's only, you need to add

@JoinColumn(name = "contact_id", nullable = false)

Under your @OneToMany on your parent entity.

nullable = false IS VITAL if you want hibernate to populate the fk on the child table

Tom Chamberlain
  • 2,955
  • 20
  • 24
  • This one really helped me. Nullable automatically made sure to insert key in child table. Ur a savior!! – vkt Oct 23 '20 at 19:16
0

Try this sample:

@Entity
public class Contact {
    @Id
    private Long id;

    @JoinColumn(name = "contactId")
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Phone> phones;
}

@Entity
public class Phone {
    @Id
    private Long id;
    private Long contactId;
}
Ilya Lysenko
  • 1,772
  • 15
  • 24
0

In JPA this helped me

contact.getPhoneList().forEach(pl -> pl.setContact(contact));
contactRepository.save(contact);