Relationships
七つのパターンとデフォルトのフェッチ方式
@OneToOne 単・双方向 EAGER mappedBy、optional、orphanRemoval
@ManyToOne 単・双方向 EAGER optional
@OneToMany 単 LAZY mappedBy、orphanRemoval
@ManyToMany 単・双方向 LAZY mappedBy
※共用属性:cascade、fetch、targetEntity
★基本
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 {
}