Strictly Programming

Hibernate Annotations and Entity relations

Posted in Hibernate, Java by sqllyw on 05/27/2009

Entity relations

@SecondaryTables

One to Many relationship

@Entity
public class Publisher {

 	private Long id;
 	
	@Id @GeneratedValue
    public Long getId() { return id; }
}

@Entity
public class Book {
	private Long id;
	private Publisher publisher;
	
	@Id @GeneratedValue
	public Long getId() {return id;}
	
	public Publisher getPublisher();
}

Publisher has many books, so publisher is in the ONE side while book in the MANY SIDE.
To have a unidirectional relation we can annotate the Book class as:

	@ManyToOne
	public Publisher getPublisher();

or

	@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="publisher_id")		// this will use publisher_id as attribute in the table, otherwise it will be publisher
	public Publisher getPublisher();

Notice there is no need to have MappedBy as it is optional in the unidirectional ManyToOne relation.

with this approach, we can find the publisher from the Book, but finding books from an instance of Publisher object is not possible unless you use some queries. to be able to do that, we need to make the relationship bidirectional, just add this annotation in the Publisher side(ONE side):

@Entity
public class Publisher {

 	private Long id;
 	private List<Book> books;
	
	@Id @GeneratedValue
    public Long getId() { return id; }

	@OneToMany
	@OneToMany(mappedBy="publiser")
	public List<Book> getBooks() { return books;}
}

It is mandatory to have mappedBy annotation in a bidirectional relationship and it has to be in the ONE side(maybe I’m wrong?), the ‘publisher’ here refer to the ‘publisher’ property in the MANY side. you can specify other attributes in the OneToMany annotation:

	@OneToMany(cascade= CascadeType.ALL, mappedBy="publisher")
    @OrderBy("Area desc")

Note: delete orphan to be added.

One to One relationship

@Entity
public class Door {
    private Long id;
    @Id
    public Long getId() {return id;}
    public void setId(Long id) { this.id = id;}
}

@Entity
public class Key {
    private Long id;
    @Id
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id;}
}

their generated sql:

create table Door (
      id bigint not null,
      primary key (id)
  );

create table Key (
      id bigint not null,
      primary key (id)
);

The only thing required to setup a one to one relationship is: to annotate in the one side of the relationship, let’s do it in the Door:

@Entity
public class Door {

    private Long id;
    private Key key;
    @Id

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id;}

    @OneToOne
    public Key getKey() { return key; }
    public void setKey(Key key) { this.key = key;}
}

This change will trigger a change in the SQL as well:

create table Door (
      id bigint not null,
      key_id bigint,
      primary key (id)
  );

alter table Door 
      add index FK2097CE549DF154 (key_id), 
      add constraint FK2097CE549DF154 
      foreign key (key_id) 
      references Key (id);

Door owns the relationship in this case, and it is unidirectional, you can access Key from the Door, but not vice versa. to make it bi-directional you can annotate the other end:

@Entity
public class Key {
    private Long id;
    private Door door;

    @Id
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @OneToOne
    public Door getDoor() {
        return door;
    }

    public void setDoor(Door door) {
        this.door = door;
    }
}

and you got the new sql:

create table Key (
      id bigint not null,
      door_id bigint,
      primary key (id)
  );

You will notice Key now has door_id as a foreign key, both end owns the relation, this isn’t too good. A preferable way is, one end is the owning entity while the other end is a owned entity, in example above we can make the Key a owned entity while Door is a owning entity, to make this happen, add a mappedBy in the Key class:

@OneToOne(mappedBy = "key")

the “key” field is referring to the key field in the Door class, this will generate a new SQL:

create table Key (
 	id bigint not null,
    primary key (id)
);

We don’t have the door_id in this case as Key is only owned by Door, the relationship is maintained by Door with key_id as foreign key stored in the Door class. this allows us to travel from Key to Class as well.

If we want some operations cascaded from the owning class to the owned class, we can do:

  @OneToOne(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
  public Key getKey() {
      return key;
  }

If you delete the Door, then the related Key object will be deleted as well. however this cascade operation does not happen when you do a batch delete on Door. If I understand correctly, the cascade and fetch type should always be specified in the owning entity side, thus it should be in the class that does not have a ‘mappedBy=’ clause, might not be correct. class with ‘mappedBy=’ annotation does not maintain the foreign key, it is in the other class.

Leave a comment