Relationships

七つのパターンとデフォルトのフェッチ方式

@OneToOne 単・双方向 EAGER mappedBy、optional、orphanRemoval

@ManyToOne 単・双方向 EAGER optional

@OneToMany 単 LAZY mappedBy、orphanRemoval

@ManyToMany 単・双方向 LAZY mappedBy

※共用属性:cascade、fetch、targetEntity

DEMO

★基本

import javax.validation.constraints.Null;

import javax.validation.constraints.NotNull;

import org.hibernate.annotations.Type;

import java.util.Date;

import org.joda.time.DateTime;

@MappedSuperclass

@XmlAccessorType(XmlAccessType.FIELD)

@Setter

@Getter

@EqualsAndHashCode(of = { "id" })

public abstract class BaseEntity implements Serializable {

protected BaseEntity() { }

protected BaseEntity(Long id) {

this.id = id;

}

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

@Null(groups={Create.class, Persist.class})

@NotNull(groups={Edit.class, Merge.class})

private Long id;

@Column(nullable = false)

@Version

@XmlTransient

private Long version;

@Column(nullable = false)

@Temporal(TemporalType.TIMESTAMP) // TemporalType.DATEもある

@XmlJavaTypeAdapter(ISODateAdapter.class)

private Date createdDate;

あるいは

@Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")

@Null(group=Create.class)

@Column

private DateTime createdDate;

}

public interface Create {}

public interface Persist {}

public interface Edit {}

public interface Merge {}

import javax.validation.constraints.Pattern;

import org.hibernate.validator.constraints.Index;

import org.hibernate.validator.constraints.NotBlank;

@Entity

@Table(name="T_BOOK") //nameは省略可

@Setter

@Getter

@NoArgsConstructor

@AllArgsConstructor

@Wither

public class Sample extends BaseEntity {

@Basic(optional=false, fetch=FetchType.LAZY)

@Lob

@Column

private byte[] xxx;


@Column(name="book_title", nullable=false, updatable=false, length=100, unique = true)

@NotNull(groups={Create.class, Persist.class})

@Pattern(regexp="xxx")

@Index(name="idx_title")

@NotBlank(groups={Create.class, Persist.class})

private String title;

@Column(nullable = false)

@XmlTransient

private String password;

@Column(nullable = false)

private String apiKey;


@Transient //マッピングが不要

private boolean locked;


@Enumerated(EnumType.STRING)

@Column

private CreditCardType creditCardType; //デフォルトはIntegerに相当


@ElementCollection(fetch=FetchType.LAZY)

@CollectionTable(name="TAG")

@Column(name="value")

private List<String> tags = new ArrayList<String>();


@ElementCollection

@CollectionTable(name="TAG")

@MapKeyColumn(name="position")

@Column(name="title")

private Map<Integer, String> tags = new HashMap<Integer, String>();

public Sample(String title) {

this.title = title;

this.apiKey = UUID.randomUUID().toString().replaceAll("-", "");

}

}

public enum CreditCardType {

VISA,

...

}

@OneToOne

// Unidirection

@Entity

public class Customer {

...

@OneToOne(fetch=LAZY,

cascade = {PERSIST, REMOVE})

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

private Address address;

或は

@OneToOne(

cascade=ALL,

orphanRemoval=true, //Default: false

fetch=LAZY)

@JoinColumn(name="addressId")

private Address address;

...

}

// Bidirection

@Entity

public class Address {

...

@OneToOne(cascade=ALL, mappedBy="address")

private Customer customer;

...

}

★Entity Join

・Shared Primary Key @PrimaryKeyJoinColumn

・Foreign Key @JoinColumn

・Association Table @JoinTable

Shared Primary Key

@Entity

@Table(name = "company")

public class Company implements Serializable {

@Id

@Column(name = "id")

@GeneratedValue

private Integer id;

@ManyToOne

@JoinColumn(name = "status_id")

private CompanyStatus status;

@OneToMany(mappedBy = "company", fetch = FetchType.EAGER)

@OrderBy("name asc")

private Set<Contact> contacts;

@OneToOne(cascade = CascadeType.MERGE)

@PrimaryKeyJoinColumn

private CompanyDetail companyDetail;

...

}

@Entity

@Table(name = "company_detail")

public class CompanyDetail implements Serializable {

@Id

@Column(name = "id")

private Integer id;

...

}

Foreign Key

@Entity

@Table(name = "contact_detail")

public class ContactDetail implements Serializable {

@Id

@Column(name = "id")

@GeneratedValue

private Integer id;

@OneToOne

@MapsId

@JoinColumn(name = "contact_id")

private Contact contact;

...

}

@Entity

@Table(name = "contact")

public class Contact implements Serializable {

@Id

@Column(name = "id")

@GeneratedValue

private Integer id;

@ManyToOne

@JoinColumn(name = "company_id")

private Company company;

@OneToOne(mappedBy = "contact", cascade = CascadeType.ALL)

private ContactDetail contactDetail;

....

}

★CascadeType.REMOVE vs. orphanRemoval

※CascadeType:PERSIST、REMOVE、MERGE、REFRESH、DETACH、ALL

ある場合に同じ動作

@OneToOne(cascade=CascadeType.REMOVE)

@OneToOne(cascade=CascadeType.ALL)

@OneToOne(orphanRemoval=true)

private Address address;

但し、NULLを設定する場合に違う

employee.setAddress(null); // orphanRemoval=trueを指定すると、削除処理は発生

★@Inheritance & @MappedSuperclass

Single Table Inheritance

Project

@Entity

@Inheritance(strategy=InheritanceType.SINGLE_TABLE) // default

@DiscriminatorColumn(name="PROJ_TYPE")

@Table

public abstract class Project {

@Id

private long id;

private String name;

}

@Entity

@DiscriminatorValue("L")

public class LargeProject extends Project {

private BigDecimal budget;

}

@Entity

@DiscriminatorValue("S")

public class SmallProject extends Project {

}

Joined Multiple Table Inheritance

Project

Small_Project

ID

2

Large_Project

@ManyToOne

従側が多い

// Unidirection

@Entity

public class Employee {

@ManyToOne(optional=false, fetch=LAZY, cascade=ALL) //Default: true

@JoinColumn(name="departmentId")

@NotNull

private Department department;

...

}

@OneToMany

主側が多い

import javax.validation.Valid;

// Bidirection

@Entity

public class Order {

...

//方法1:テーブル結合

@OneToMany

@JoinTable(name="jnd_ord_line",

joinColumns=@JoinColumn(name="orderId"),

inverseJoinColumns=@JoinColumn(name="orderLineId"))

private List<OrderLine> orderLines;


//方法2:カラム結合(外部キー)

@OneToMany(fetch=EAGER)

@JoinColumn(name="orderId")

@OrderBy("xxx DESC, yyy ASC")

//@OrderColumn(name="xxxIndex") //数値型が必要

private List<OrderLine> orderLines;

...

@OneToMany(mappedBy="order", cascade=ALL, orphanRemoval=true, fetch=LAZY)

@Valid

private List<OrderLine> orderLines;

}

@ManyToMany

// Unidirection

@Entity

public class Artist {

...

@ManyToMany(cascade=ALL)

@JoinTable(name="jnd_art_cd",

joinColumns={@JoinColumn(name="artistId")},

inverseJoinColumns={@JoinColumn(name="cdId")})

private List<CD> cds;

...

}

// Bidirection

@Entity

public class CD {

...

@ManyToMany(mappedBy="cds")

private List<Artist> artists;

...

}

ID

1

BUDGET

50000

@Entity

@Inheritance(strategy=InheritanceType.JOINED)

@DiscriminatorColumn(name="PROJ_TYPE")

@Table

public abstract class Project {

@Id

private long id;

private String name;

}

@Entity

@DiscriminatorValue("L")

@Table(name="Large_Project")

public class LargeProject extends Project {

private BigDecimal budget;

}

@Entity

@DiscriminatorValue("S")

@Table(name="Small_Project")

public class SmallProject extends Project {

}

Table Per Class Inheritance

Small_Project

ID

2

NAME

Legal

Large_Project

ID

1

NAME

Accounting

BUDGET

50000

@Entity

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)

public abstract class Project {

@Id

private long id;

private String name;

}

@Entity

@Table(name="Large_Project")

public class LargeProject extends Project {

private BigDecimal budget;

}

@Entity

@Table(name="SmallProject")

public class SmallProject extends Project {

}

Mapped Superclass

Small_Project

ID

2

NAME

Legal

Large_Project

ID

1

PROJECT_NAME

Accounting

BUDGET

50000

@MappedSuperclass

public abstract class Project {

@Id

private long id;

private String name;

}

@Entity

@Table(name="Large_Project")

@AttributeOverride(name="name", column=@Column(name="PROJECT_NAME"))

public class LargeProject extends Project {

private BigDecimal budget;

}

@Entity

@Table("Small_Project")

public class SmallProject extends Project {

}