Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meaning of mappedBy attribute in an annotation?

Tags:

hibernate

jpa

I'm a novice in hibernate. I was trying to create a one-to-one mapping between Person and PersonDetail entity in the following code:

@Entity
public class Person {

    private int personId;
    private String personName;
    private PersonDetail personDetail;

    @OneToOne(mappedBy="person")
    public PersonDetail getPersonDetail() {
        return personDetail;
    }

    public void setPersonDetail(PersonDetail personDetail) {
        this.personDetail = personDetail;
    }

    @Id
    @GeneratedValue
    public int getPersonId() {
        return personId;
    }

    public void setPersonId(int personId) {
        this.personId = personId;
    }

    public String getPersonName() {
        return personName;
    }

    public void setPersonName(String personName) {
        this.personName = personName;
    }
}


@Entity
public class PersonDetail {

    private int personDetailId;
    private String zipCode;
    private String job;
    private double income;

    private Person person;

    @OneToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    @Id
    @GeneratedValue
    public int getPersonDetailId() {
        return personDetailId;
    }

    public void setPersonDetailId(int personDetailId) {
        this.personDetailId = personDetailId;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public double getIncome() {
        return income;
    }

    public void setIncome(double income) {
        this.income = income;
    }

}

I want that PersonDetail should be the owning entity (I know that vice versa is correct, but I just want to test). What I do not understand is the use of the mappedBy attribute which I've put in the Person entity. If I remove it, I get the following error: "Could not determine type for: com.hibernate.onetoonemapping.PersonDetail, at table: Person, for columns: [org.hibernate.mapping.Column(personDetail)]"

What does this mappedBy attribute do? I've read that mappedBy attribute is placed on the non-owning side. But what does it exactly do?

like image 650
n_g Avatar asked Jan 22 '12 17:01

n_g


3 Answers

It means that that relation between entites has already been mapped, so you don't do that twice. You just say "Hey it's done over there" by using mappedBy attribute.

like image 114
Xorty Avatar answered Nov 09 '22 16:11

Xorty


When you want to make a relationship bi-directional (accessible from either side of the entity) you used mappedBy on the non-owning side because you can only define the relationship once.

mappedBy tells Hibernate how to create instances of your entities and load the data into them. It should refer to the field name in the class that you are annotating, PersonDetail in this instance, where the relationship is defined.

like image 29
Jeff Avatar answered Nov 09 '22 17:11

Jeff


In your scenario the mappedBy attribute should not have any impact.

I was confused about the name of the primary key (it is different from "id"), but using a non-standard name as identifier (somehow as expected) does not cause the problem you are facing.

I have experimented with the mappedBy parameter in the context of OneToMany and OneToOne associations and would like to share my findings. I have a similar scenario for testing purposes and to illustrate the impact.

Person.class:

@Entity
public class Person {

  @Id
  @GeneratedValue
  private Long id;

  @Column(name = "name")
  private String name;

  @OneToOne
  private Address address;
}

Address.class (for OneToOne tests):

@Entity
public class Address {
  @Id
  @GeneratedValue
  private Long id;

  @Column(name = "address")
  private String address;


  @OneToOne(mappedBy="address")
  private Person person;

  @OneToMany(cascade = CascadeType.ALL)
  private Set<Phone> phone = new HashSet<Phone>();
}

Phone.class (for OneToMany tests):

@Entity
public class Phone {

  @Id
  @GeneratedValue
  private Long id;

  @ManyToOne(optional = false)
  private Person person;

  @Column(name = "number")
  private String number;
}

By moving around the "mappedBy" parameter for the OneToOne association, in any case the sql statements executed by hibernate remained the same:

Hibernate: 
/* insert domain.Address
    */ insert 
    into
        Address
        (id, address) 
    values
        (default, ?)
Hibernate: 
/* insert domain.Person
    */ insert 
    into
        Person
        (id, address_id, name) 
    values
        (default, ?, ?)

For OneToMany associations I found that if you do not specify "mappedBy" hibernate automatically uses a join table, whereas with the "mappedBy" parameter the association is mapped as a seperate column in the entity table.

If I use...

 @OneToMany(cascade = CascadeType.ALL)
 private Set<Phone> phone = new HashSet<Phone>();

...persising a person with one phone entry results in the following statements to be executed:

Hibernate: 
/* insert domain.Phone
    */ insert 
    into
        Phone
        (id, number, person_id) 
    values
        (default, ?, ?)
Hibernate: 
/* insert collection
    row domain.Person.phone */ insert 
    into
        Person_Phone
        (Person_id, phone_id) 
    values
        (?, ?)

Whereas using...

 @OneToMany(mappedBy="id", cascade = CascadeType.ALL)
 private Set<Phone> phone = new HashSet<Phone>();

results in a slightly different relational model that is closer to what would one possibly expect in this case:

Hibernate: 
/* insert domain.Phone
    */ insert 
    into
        Phone
        (id, number, person_id) 
    values
        (default, ?, ?)
like image 21
Vgt Avatar answered Nov 09 '22 18:11

Vgt