JPA 实体是普通的 POJO。实际上,它们是 Hibernate 持久实体。它们的映射通过 JDK 5.0 注解而不是 hbm.xml 文件来定义。JPA 2 XML 描述符语法也定义了用于覆盖。
JPA 注解位于 javax.persistence.*
包中。你最喜欢的 IDE 可以为你自动完成注解及其属性 (即使没有特定的 "JPA" 模块,因为 JPA 注解是普通的 JDK 5 注解)。
在 Hibernate 注解测试套件本身中可以找到一套完整的、可工作的示例:大多数单元测试旨在代表一个具体示例,并成为你的灵感来源。你可以在分发版中获取测试套件源代码。
每个持久 POJO 类都是一个实体,并且使用 @Entity
注解 (在类级别) 声明。
@Entity
public class Flight implements Serializable {
Long id;
@Id
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
}
@Entity
将类声明为一个实体 (即一个持久 POJO 类),@Id
声明了该实体的标识属性。其他映射声明是隐式的。Flight 类映射到 Flight 表,使用 id 列作为其主键列。
@Table
设置在类级别;它允许你为实体映射定义表名、目录名和模式名。如果没有定义 @Table
,则使用默认值:实体的非限定类名。
@Entity
@Table(name="tbl_sky")
public class Sky implements Serializable {
...
}
@Table(name="tbl_sky",
uniqueConstraints = {@UniqueConstraint(columnNames={"month", "day"})}
)
public transient int counter; //transient property
private String firstname; //persistent property
@Transient
String getLengthInMeter() { ... } //transient property
String getName() {... } // persistent property
@Basic
int getLength() { ... } // persistent property
@Basic(fetch = FetchType.LAZY)
String getDetailedComment() { ... } // persistent property
@Temporal(TemporalType.TIME)
java.util.Date getDepartureTime() { ... } // persistent property
@Enumerated(EnumType.STRING)
Starred getNote() { ... } //enum persisted as String in database
推荐的替代方法是使用 JP-QL (Java 持久性查询语言) 或 Criteria 查询的投影功能。
@Lob
public String getFullText() {
return fullText;
}
@Lob
public byte[] getFullCode() {
return fullCode;
}
如果属性类型实现 java.io.Serializable
并且不是基本类型,并且如果属性未用 @Lob
注解,则使用 Hibernate serializable
类型。
最佳用例是由多个实体使用的嵌入式类,这些实体可能不使用相同的访问类型。在这种情况下,最好在嵌入式类级别强制访问类型。
若要在给定类上强制访问类型,请使用 @Access
注解,如下所示
@Entity
public class Order {
@Id private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Embedded private Address address;
public Address getAddress() { return address; }
public void setAddress() { this.address = address; }
}
@Entity
public class User {
private Long id;
@Id public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
private Address address;
@Embedded public Address getAddress() { return address; }
public void setAddress() { this.address = address; }
}
@Embeddable
@Access(AcessType.PROPERTY)
public class Address {
private String street1;
public String getStreet1() { return street1; }
public void setStreet1() { this.street1 = street1; }
private hashCode; //not persistent
}
@Entity
public class Order {
@Id private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Transient private String userId;
@Transient private String orderId;
@Access(AccessType.PROPERTY)
public String getOrderNumber() { return userId + ":" + orderId; }
public void setOrderNumber() { this.userId = ...; this.orderId = ...; }
}
在此示例中,默认访问类型为 FIELD
,除了 orderNumber
属性。请注意,相应的字段 (如果有) 必须标记为 @Transient
或 transient
。
用于属性映射的列可以使用 @Column
注解来定义。使用它来覆盖默认值 (有关默认值的更多信息,请参阅 EJB3 规范)。你可以将此注解应用于属性级别,适用于以下属性:
@Entity
public class Flight implements Serializable {
...
@Column(updatable = false, name = "flight_name", nullable = false, length=50)
public String getName() { ... }
name
属性映射到 flight_name
列,该列不可为空,长度为 50 且不可更新 (使属性不可变)。
此注解可以应用于普通属性以及 @Id
或 @Version
属性。
@Column( name="columnName"; boolean un
ique() default false; boolean nu
llable() default true; boolean in
sertable() default true; boolean up
datable() default true; String col
umnDefinition() default ""; String tab
le() default ""; int length
() default 255; int precis
ion() default 0; // decimal precision int scale(
) default 0; // decimal scale
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
@Entity
public class Person implements Serializable {
// Persistent component using defaults
Address homeAddress;
@Embedded
@AttributeOverrides( {
@AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),
@AttributeOverride(name="name", column = @Column(name="bornCountryName") )
} )
Country bornIn;
...
}
@Embeddable
public class Address implements Serializable {
String city;
Country nationality; //no overriding here
}
@Embeddable
public class Country implements Serializable {
private String iso2;
@Column(name="countryName") private String name;
public String getIso2() { return iso2; }
public void setIso2(String iso2) { this.iso2 = iso2; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
...
}
嵌入式对象继承其拥有实体的访问类型(请注意,可以使用 @Access
注解覆盖此类型)。
@Embedded
@AttributeOverrides( {
@AttributeOverride(name="city", column = @Column(name="fld_city") ),
@AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2") ),
@AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName") )
//nationality columns in homeAddress are overridden
} )
Address homeAddress;
@Id
注解允许您定义哪个属性是实体的标识符。此属性可以由应用程序本身设置,也可以由 Hibernate 生成(首选)。您可以使用 @GeneratedValue
注解定义标识符生成策略。
Hibernate 提供了比基本的 JPA 生成器更多的 id 生成器。有关更多信息,请参阅 第 2.4 节,“Hibernate Annotation 扩展”。
以下示例显示了使用 SEQ_STORE 配置的序列生成器(见下文)
@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")
public Integer getId() { ... }
下一个示例使用标识生成器
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
public Long getId() { ... }
对于可移植应用程序(跨多个数据库供应商),AUTO
生成器是首选类型。标识符生成配置可以通过 generator 属性在多个 @Id
映射中共享。有几种配置可以通过 @SequenceGenerator
和 @TableGenerator
使用。生成器的范围可以是应用程序或类。类定义的生成器在类外部不可见,并且可以覆盖应用程序级生成器。应用程序级生成器在 XML 级别定义(请参阅 第 3 章,通过 XML 覆盖元数据)
<table-generator name="EMP_GEN"
table="GENERATOR_TABLE"
pk-column-name="key"
value-column-name="hi"
pk-column-value="EMP"
allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.TableGenerator(
name="EMP_GEN",
table="GENERATOR_TABLE",
pkColumnName = "key",
valueColumnName = "hi"
pkColumnValue="EMP",
allocationSize=20
)
<sequence-generator name="SEQ_GEN"
sequence-name="my_sequence"
allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.SequenceGenerator(
name="SEQ_GEN",
sequenceName="my_sequence",
allocationSize=20
)
如果使用 JPA XML(如 META-INF/orm.xml
)定义生成器,则 EMP_GEN
和 SEQ_GEN
是应用程序级生成器。EMP_GEN
使用 hilo 算法定义基于表的 id 生成器,其 max_lo
为 20。hi 值保存在表 "GENERATOR_TABLE
" 中。信息保存在一行中,其中 pkColumnName
"key" 等于 pkColumnValue
"EMP
",而列 valueColumnName
"hi
" 包含使用的下一个高值。
SEQ_GEN
定义一个序列生成器,使用名为 my_sequence
的序列。此基于序列的 hilo 算法使用的分配大小为 20。请注意,此版本的 Hibernate 注解不会处理序列生成器中的 initialValue
。默认分配大小为 50,因此如果您想使用序列并每次都获取值,则必须将分配大小设置为 1。
我们建议所有新项目使用 hibernate.id.new_generator_mappings=true
,因为新的生成器效率更高,更接近 JPA 2 规范语义。但是它们与现有数据库不向后兼容(如果使用序列或表进行 id 生成)。有关如何激活它们的更多信息,请参阅 第 1.3 节,“属性”。
JPA 规范不支持包级别定义。但是,您可以在包级别使用 @GenericGenerator
(请参阅 第 2.4.2 节,“标识符”)。
以下示例显示了在类范围内定义序列生成器的示例
@Entity
@javax.persistence.SequenceGenerator(
name="SEQ_STORE",
sequenceName="my_sequence"
)
public class Store implements Serializable {
private Long id;
@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")
public Long getId() { return id; }
}
此类将使用名为 my_sequence 的序列,并且 SEQ_STORE 生成器在其他类中不可见。请注意,您可以查看 org.hibernate.test.annotations.id 包中的 Hibernate 注解测试,以获取更多示例。
最后,您可以要求 Hibernate 从另一个关联实体复制标识符。在 Hibernate 行话中,这称为外键生成器,但 JPA 映射更易读,因此建议使用。
@Entity
class MedicalHistory implements Serializable {
@Id @OneToOne
@JoinColumn(name = "person_id")
Person patient;
}
@Entity
public class Person implements Serializable {
@Id @GeneratedValue Integer id;
}
或者
@Entity
class MedicalHistory implements Serializable {
@Id Integer id;
@MapsId @OneToOne
@JoinColumn(name = "patient_id")
Person patient;
}
@Entity
class Person {
@Id @GeneratedValue Integer id;
}
如果您有兴趣了解有关“派生标识”的更多示例,JPA 2 规范在第 2.4.1.3 章中提供了一组很好的示例。
但标识符不必是单个属性,它可以由多个属性组成。
如您所见,最后一种情况远非一目了然。它继承自 EJB 2 的黑暗时代,是为了向后兼容而设计的,我们建议您不要使用它(为了简单起见)。
@Entity
class User {
@EmbeddedId
@AttributeOverride(name="firstName", column=@Column(name="fld_firstname")
UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
您可以注意到 UserId
类是可序列化的。若要覆盖列映射,请使用 @AttributeOverride
。
@Entity
class Customer {
@EmbeddedId CustomerId id;
boolean preferredCustomer;
@MapsId("userId")
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
@OneToOne User user;
}
@Embeddable
class CustomerId implements Serializable {
UserId userId;
String customerNumber;
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
实际上,您的代码只设置 Customer.user
属性,而 Hibernate 会将用户 id 值复制到 CustomerId.userId
属性中。
虽然 JPA 不支持,但 Hibernate 允许您将关联属性直接放置在嵌入式 id 组件中(而不是必须使用 @MapsId
注解)。
@Entity
class Customer {
@EmbeddedId CustomerId id;
boolean preferredCustomer;
}
@Embeddable
class CustomerId implements Serializable {
@OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
String customerNumber;
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
另一种方法(可以说是更自然的方法)是将 @Id
放置在我的实体的多个属性上。这种方法只受 Hibernate 支持,但不需要额外的嵌入式组件。
@Entity
class Customer implements Serializable {
@Id @OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
@Id String customerNumber;
boolean preferredCustomer;
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
@Entity
class Customer {
@Id @OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
@Id String customerNumber;
boolean preferredCustomer;
}
class CustomerId implements Serializable {
UserId user;
String customerNumber;
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
Customer
和 CustomerId
具有相同的属性 customerNumber
以及 user
。
虽然不是 JPA 标准,但 Hibernate 允许您在 @IdClass
中声明普通关联属性。
@Entity
class Customer {
@Id @OneToOne
@JoinColumns({
@JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
@JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
@Id String customerNumber;
boolean preferredCustomer;
}
class CustomerId implements Serializable {
@OneToOne User user;
String customerNumber;
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
Hibernate 支持自动生成某些标识符属性。只需在一条或多条 id 属性上使用 @GeneratedValue
注解即可。
@Entity
public class CustomerInventory implements Serializable {
@Id
@TableGenerator(name = "inventory",
table = "U_SEQUENCES",
pkColumnName = "S_ID",
valueColumnName = "S_NEXTNUM",
pkColumnValue = "inventory",
allocationSize = 1000)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "inventory")
Integer id;
@Id @ManyToOne(cascade = CascadeType.MERGE)
Customer customer;
}
@Entity
public class Customer implements Serializable {
@Id
private int id;
}
使用 @Inheritance
注解在层次结构中顶级实体的类级别声明所选策略。
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable { ... }
此策略支持一对多关联,前提是它们是双向的。此策略不支持 IDENTITY
生成器策略:ID 必须在多个表之间共享。因此,在使用此策略时,不应使用 AUTO
或 IDENTITY
。
所有超类和子类的所有属性都映射到同一个表中,实例通过特殊的区分器列来区分。
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name="planetype",
discriminatorType=DiscriminatorType.STRING
)
@DiscriminatorValue("Plane")
public class Plane { ... }
@Entity
@DiscriminatorValue("A320")
public class A320 extends Plane { ... }
@PrimaryKeyJoinColumn
和 @PrimaryKeyJoinColumns
注释定义了连接子类表的主键。
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Boat implements Serializable { ... }
@Entity
public class Ferry extends Boat { ... }
@Entity
@PrimaryKeyJoinColumn(name="BOAT_ID")
public class AmericaCupClass extends Boat { ... }
这在某些情况下很有用,可以将公共属性通过技术或业务超类共享,而无需将其包含为常规映射实体(即,此实体没有特定表)。为此,可以将它们映射为 @MappedSuperclass
。
@MappedSuperclass
public class BaseEntity {
@Basic
@Temporal(TemporalType.TIMESTAMP)
public Date getLastUpdate() { ... }
public String getLastUpdater() { ... }
...
}
@Entity class Order extends BaseEntity {
@Id public Integer getId() { ... }
...
}
可以使用 @AttributeOverride
注释在根实体级别覆盖在实体超类中定义的列。
@MappedSuperclass
public class FlyingObject implements Serializable {
public int getAltitude() {
return altitude;
}
@Transient
public int getMetricAltitude() {
return metricAltitude;
}
@ManyToOne
public PropulsionType getPropulsion() {
return metricAltitude;
}
...
}
@Entity
@AttributeOverride( name="altitude", column = @Column(name="fld_altitude") )
@AssociationOverride(
name="propulsion",
joinColumns = @JoinColumn(name="fld_propulsion_fk")
)
public class Plane extends FlyingObject {
...
}
altitude
属性将持久化到 Plane
表的 fld_altitude
列中,而推进关联将具体化为一个 fld_propulsion_fk
外键列。
可以在 @Entity
类、@MappedSuperclass
类和指向 @Embeddable
对象的属性上定义 @AttributeOverride
和 @AssociationOverride
。
@Entity
public class Body {
@Id
public Long getId() { return id; }
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
public Heart getHeart() {
return heart;
}
...
}
@Entity
public class Heart {
@Id
public Long getId() { ...}
}
@PrimaryKeyJoinColumn
注释表明实体的主键用作关联实体的外键值。
@Entity
public class Customer implements Serializable {
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name="passport_fk")
public Passport getPassport() {
...
}
@Entity
public class Passport implements Serializable {
@OneToOne(mappedBy = "passport")
public Customer getOwner() {
...
}
@Entity
public class Customer implements Serializable {
@OneToOne(cascade = CascadeType.ALL)
@JoinTable(name = "CustomerPassports",
joinColumns = @JoinColumn(name="customer_fk"),
inverseJoinColumns = @JoinColumn(name="passport_fk")
)
public Passport getPassport() {
...
}
@Entity
public class Passport implements Serializable {
@OneToOne(mappedBy = "passport")
public Customer getOwner() {
...
}
@Entity()
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
@JoinColumn(name="COMP_ID")
public Company getCompany() {
return company;
}
...
}
@Entity
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=CompanyImpl.class )
@JoinColumn(name="COMP_ID")
public Company getCompany() {
return company;
}
...
}
public interface Company {
...
}
@Entity
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
@JoinTable(name="Flight_Company",
joinColumns = @JoinColumn(name="FLIGHT_ID"),
inverseJoinColumns = @JoinColumn(name="COMP_ID")
)
public Company getCompany() {
return company;
}
...
}
一对多关联在属性级别使用 @OneToMany
注释声明。一对多关联可能是双向的。
由于多对一 (几乎) 总是 JPA 规范中双向关系的所有者一方,因此一对多关联由 @OneToMany(mappedBy=...)
注释。
@Entity
public class Troop {
@OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
Troop
通过 troop
属性与 Soldier
存在双向一对多关系。在 mappedBy
一方,你无需(也不应该)定义任何物理映射。
要映射双向一对多,并且一对多一方是拥有方,必须删除 mappedBy
元素并将多对一的 @JoinColumn
设置为不可插入和不可更新。此解决方案没有优化,将产生一些额外的 UPDATE 语句。
@Entity
public class Troop {
@OneToMany
@JoinColumn(name="troop_fk") //we need to duplicate the physical information
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk", insertable=false, updatable=false)
public Troop getTroop() {
...
}
使用被拥有实体中的外键列的单向一对多并不常见,也不太推荐。我们强烈建议您为此类关联使用连接表(如下一节所述)。这种关联通过 @JoinColumn
描述。
@Entity
public class Customer implements Serializable {
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="CUST_ID")
public Set<Ticket> getTickets() {
...
}
@Entity
public class Ticket implements Serializable {
... //no bidir
}
使用连接表的单向一对多是首选。这种关联通过 @JoinTable
描述。
@Entity
public class Trainer {
@OneToMany
@JoinTable(
name="TrainedMonkeys",
joinColumns = @JoinColumn( name="trainer_id"),
inverseJoinColumns = @JoinColumn( name="monkey_id")
)
public Set<Monkey> getTrainedMonkeys() {
...
}
@Entity
public class Monkey {
... //no bidir
}
@Entity
public class Employer implements Serializable {
@ManyToMany(
targetEntity=org.hibernate.test.metadata.manytomany.Employee.class,
cascade={CascadeType.PERSIST, CascadeType.MERGE}
)
@JoinTable(
name="EMPLOYER_EMPLOYEE",
joinColumns=@JoinColumn(name="EMPER_ID"),
inverseJoinColumns=@JoinColumn(name="EMPEE_ID")
)
public Collection getEmployees() {
return employees;
}
...
}
@Entity
public class Employee implements Serializable {
@ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
mappedBy = "employees",
targetEntity = Employer.class
)
public Collection getEmployers() {
return employers;
}
}
如前所述,另一侧不必(也不能)描述物理映射:一个简单的 mappedBy
参数,其中包含所有者端属性名称,将两者绑定在一起。
@Entity
public class Store {
@ManyToMany(cascade = CascadeType.PERSIST)
public Set<City> getImplantedIn() {
...
}
}
@Entity
public class City {
... //no bidirectional relationship
}
一个 Store_City
用作连接表。Store_id
列是 Store
表的外键。implantedIn_id
列是 City
表的外键。
@Entity
public class Store {
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
public Set<Customer> getCustomers() {
...
}
}
@Entity
public class Customer {
@ManyToMany(mappedBy="customers")
public Set<Store> getStores() {
...
}
}
一个 Store_Customer
用作连接表。stores_id
列是 Store
表的外键。customers_id
列是 Customer
表的外键。
在某些简单情况下,不需要关联两个实体,而只是创建基本类型或可嵌入对象的集合。在这种情况下使用 @ElementCollection
。
@Entity
public class User {
[...]
public String getLastname() { ...}
@ElementCollection
@CollectionTable(name="Nicknames", joinColumns=@JoinColumn(name="user_id"))
@Column(name="nickname")
public Set<String> getNicknames() { ... }
}
保存集合数据的集合表使用 @CollectionTable
注解设置。如果省略,集合表名将默认为包含实体的名称和集合属性的名称,以一个下划线分隔:在我们的示例中,它将是 User_nicknames
。
保存基本类型的列使用 @Column
注解设置。如果省略,列名默认为属性名称:在我们的示例中,它将是 nicknames
。
但您不限于基本类型,集合类型可以是任何可嵌入对象。要覆盖集合表中可嵌入对象的列,请使用 @AttributeOverride
注解。
@Entity
public class User {
[...]
public String getLastname() { ...}
@ElementCollection
@CollectionTable(name="Addresses", joinColumns=@JoinColumn(name="user_id"))
@AttributeOverrides({
@AttributeOverride(name="street1", column=@Column(name="fld_street"))
})
public Set<Address> getAddresses() { ... }
}
@Embeddable
public class Address {
public String getStreet1() {...}
[...]
}
在 @AttributeOverride
中,必须使用 value.
前缀来覆盖映射值中使用的可嵌入对象的属性,以及使用 key.
前缀来覆盖映射键中使用的可嵌入对象的属性。
@Entity
public class User {
@ElementCollection
@AttributeOverrides({
@AttributeOverride(name="key.street1", column=@Column(name="fld_street")),
@AttributeOverride(name="value.stars", column=@Column(name="fld_note"))
})
public Map<Address,Rating> getFavHomes() { ... }
@Entity
public class Customer {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@OneToMany(mappedBy="customer")
@OrderBy("number")
public List<Order> getOrders() { return orders; }
public void setOrders(List<Order> orders) { this.orders = orders; }
private List<Order> orders;
}
@Entity
public class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
public String getNumber() { return number; }
public void setNumber(String number) { this.number = number; }
private String number;
@ManyToOne
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
private Customer number;
}
-- Table schema
|-------------| |----------|
| Order | | Customer |
|-------------| |----------|
| id | | id |
| number | |----------|
| customer_id |
|-------------|
@Entity
public class Customer {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@OneToMany(mappedBy="customer")
@OrderColumn(name"orders_index")
public List<Order> getOrders() { return orders; }
public void setOrders(List<Order> orders) { this.orders = orders; }
private List<Order> orders;
}
@Entity
public class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
public String getNumber() { return number; }
public void setNumber(String number) { this.number = number; }
private String number;
@ManyToOne
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
private Customer number;
}
-- Table schema
|--------------| |----------|
| Order | | Customer |
|--------------| |----------|
| id | | id |
| number | |----------|
| customer_id |
| orders_index |
|--------------|
同样,映射可以从关联实体属性之一借用其键,也可以拥有专用列来存储显式键。
@Entity
public class Customer {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@OneToMany(mappedBy="customer")
@MapKey(name"number")
public Map<String,Order> getOrders() { return orders; }
public void setOrders(Map<String,Order> order) { this.orders = orders; }
private Map<String,Order> orders;
}
@Entity
public class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
public String getNumber() { return number; }
public void setNumber(String number) { this.number = number; }
private String number;
@ManyToOne
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
private Customer number;
}
-- Table schema
|-------------| |----------|
| Order | | Customer |
|-------------| |----------|
| id | | id |
| number | |----------|
| customer_id |
|-------------|
否则,映射键将映射到一个或多个专用列。要自定义内容,请使用以下注解之一
您还可以使用 @MapKeyClass
来定义键的类型,如果您没有使用泛型(此时,您应该想知道为什么在今天这个时代您不使用泛型)。
@Entity
public class Customer {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@OneToMany @JoinTable(name="Cust_Order")
@MapKeyColumn(name"orders_number")
public Map<String,Order> getOrders() { return orders; }
public void setOrders(Map<String,Order> orders) { this.orders = orders; }
private Map<String,Order> orders;
}
@Entity
public class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
public String getNumber() { return number; }
public void setNumber(String number) { this.number = number; }
private String number;
@ManyToOne
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
private Customer number;
}
-- Table schema
|-------------| |----------| |---------------|
| Order | | Customer | | Cust_Order |
|-------------| |----------| |---------------|
| id | | id | | customer_id |
| number | |----------| | order_id |
| customer_id | | orders_number |
|-------------| |---------------|
我们建议您从 @org.hibernate.annotations.MapKey
/ @org.hibernate.annotation.MapKeyManyToMany
迁移到上面描述的新标准方法。
具体来说,没有 @OrderColumn 或 @IndexColumn 的 java.util.List 集合将被视为包。
有关集合的更多支持,请通过 Hibernate 特定扩展获得(请参阅 第 2.4 节,“Hibernate 注解扩展”)。
您可能已经注意到,cascade
属性接受一个 CascadeType
数组作为值。JPA 中的级联概念与 Hibernate 中的传递持久性和操作级联非常相似,但语义和级联类型略有不同
有关级联和创建/合并语义的更多信息,请参阅 JPA 规范的第 6.3 章。
您还可以启用孤儿删除语义。如果从 @OneToMany
集合中删除了实体,或者从 @OneToOne
关联中取消了关联实体的引用,则如果将 orphanRemoval
设置为 true,则可以将该关联实体标记为删除。从某种意义上说,这意味着关联实体的生命周期与拥有实体绑定在一起,就像可嵌入对象一样。
@Entity class Customer {
@OneToMany(orphanRemoval=true) public Set<Order> getOrders() { return orders; }
public void setOrders(Set<Order> orders) { this.orders = orders; }
private Set<Order> orders;
[...]
}
@Entity class Order { ... }
Customer customer = em.find(Customer.class, 1l);
Order order = em.find(Order.class, 1l);
customer.getOrders().remove(order); //order will be deleted by cascade
您可以选择急切地或延迟地获取关联的实体。 fetch
参数可以设置为 FetchType.LAZY
或 FetchType.EAGER
。 EAGER
将尝试使用外部联接选择来检索关联的对象,而 LAZY
仅在第一次访问关联对象时才会触发选择。 @OneToMany
和 @ManyToMany
关联默认设置为 LAZY
,而 @OneToOne
和 @ManyToOne
默认设置为 EAGER
。有关静态获取的更多信息,请查看 第 2.4.5.1 节,“延迟选项和获取模式”。
建议的方法是在所有静态获取定义中使用 LAZY
,并通过 JP-QL 动态覆盖此选择。 JP-QL 具有 fetch
关键字,允许您在执行特定查询时覆盖延迟性。这对于提高性能非常有用,并且根据具体用例而定。
组合主键使用嵌入类作为主键表示,因此您将使用 @Id
和 @Embeddable
注释。或者,您可以使用 @EmbeddedId
注释。请注意,依赖类必须是可序列化的并实现 equals()
/hashCode()
。您也可以使用 @IdClass
。这些在 第 2.2.3 节,“映射标识符属性” 中有更详细的介绍。
@Entity
public class RegionalArticle implements Serializable {
@Id
public RegionalArticlePk getPk() { ... }
}
@Embeddable
public class RegionalArticlePk implements Serializable { ... }
或者
@Entity
public class RegionalArticle implements Serializable {
@EmbeddedId
public RegionalArticlePk getPk() { ... }
}
public class RegionalArticlePk implements Serializable { ... }
@Embeddable
继承其拥有实体的访问类型,除非使用了 @Access
。组合外键(如果未使用默认敏感值)在使用 @JoinColumns
元素的关联上定义,它基本上是 @JoinColumn
的数组。建议明确表达 referencedColumnNames
。否则,Hibernate 会假定您使用与主键声明中相同的列顺序。
@Entity
public class Parent implements Serializable {
@Id
public ParentPk id;
public int age;
@OneToMany(cascade=CascadeType.ALL)
@JoinColumns ({
@JoinColumn(name="parentCivility", referencedColumnName = "isMale"),
@JoinColumn(name="parentLastName", referencedColumnName = "lastName"),
@JoinColumn(name="parentFirstName", referencedColumnName = "firstName")
})
public Set<Child> children; //unidirectional
...
}
@Entity
public class Child implements Serializable {
@Id @GeneratedValue
public Integer id;
@ManyToOne
@JoinColumns ({
@JoinColumn(name="parentCivility", referencedColumnName = "isMale"),
@JoinColumn(name="parentLastName", referencedColumnName = "lastName"),
@JoinColumn(name="parentFirstName", referencedColumnName = "firstName")
})
public Parent parent; //unidirectional
}
@Embeddable
public class ParentPk implements Serializable {
String firstName;
String lastName;
...
}
请注意 referencedColumnName
的显式用法。
可以使用 hibernate.cache.default_cache_concurrency_strategy
属性设置默认使用的缓存并发策略
@Entity @Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Forest { ... }
Hibernate 还允许您缓存集合的内容或标识符(如果集合包含其他实体)。在集合属性上使用 @Cache
注释。
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="CUST_ID")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public SortedSet<Ticket> getTickets() {
return tickets;
}
@org.hibernate.annotations.Cache
定义给定二级缓存的缓存策略和区域。
@Cache( CacheConcurrencyStrategy usage(); String reg
ion() default ""; String inc
lude() default "all"; )
用法:给定的缓存并发策略(NONE、READ_ONLY、NONSTRICT_READ_WRITE、READ_WRITE、TRANSACTIONAL) | |
区域(可选):缓存区域(默认为类的 fqcn 或集合的 fq 角色名称) | |
|
不幸的是,您将丢失使用 Criteria API 编写的查询的类型安全性。
<entity-mappings>
<named-query name="plane.getAll">
<query>select p from Plane p</query>
</named-query>
...
</entity-mappings>
...
@Entity
@NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")
public class Night {
...
}
public class MyDao {
doStuff() {
Query q = s.getNamedQuery("night.moreRecentThan");
q.setDate( "date", aMonthAgo );
List results = q.list();
...
}
...
}
您还可以通过 hints
属性中的 QueryHint
数组为查询提供一些提示。
您还可以使用 lockMode
属性定义返回的实体应被锁定的锁定模式。这等效于实体管理器查找操作的可选锁定模式。
@NamedNativeQuery(name="night&area", query="select night.id nid, night.night_duration, "
+ " night.night_date, area.id aid, night.area_id, area.name "
+ "from Night night, Area area where night.area_id = area.id",
resultSetMapping="joinMapping")
@SqlResultSetMapping(name="joinMapping", entities={
@EntityResult(entityClass=Night.class, fields = {
@FieldResult(name="id", column="nid"),
@FieldResult(name="duration", column="night_duration"),
@FieldResult(name="date", column="night_date"),
@FieldResult(name="area", column="area_id"),
discriminatorColumn="disc"
}),
@EntityResult(entityClass=org.hibernate.test.annotations.query.Area.class, fields = {
@FieldResult(name="id", column="aid"),
@FieldResult(name="name", column="name")
})
}
)
@Entity
@SqlResultSetMapping(name="implicit",
entities=@EntityResult(entityClass=SpaceShip.class))
@NamedNativeQuery(name="implicitSample",
query="select * from SpaceShip",
resultSetMapping="implicit")
public class SpaceShip {
private String name;
private String model;
private double speed;
@Id
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name="model_txt")
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
}
@Entity
@SqlResultSetMapping(name="compositekey",
entities=@EntityResult(entityClass=SpaceShip.class,
fields = {
@FieldResult(name="name", column = "name"),
@FieldResult(name="model", column = "model"),
@FieldResult(name="speed", column = "speed"),
@FieldResult(name="captain.firstname", column = "firstn"),
@FieldResult(name="captain.lastname", column = "lastn"),
@FieldResult(name="dimensions.length", column = "length"),
@FieldResult(name="dimensions.width", column = "width")
}),
columns = { @ColumnResult(name = "surface"),
@ColumnResult(name = "volume") } )
@NamedNativeQuery(name="compositekey",
query="select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as surface from SpaceShip",
resultSetMapping="compositekey")
} )
public class SpaceShip {
private String name;
private String model;
private double speed;
private Captain captain;
private Dimensions dimensions;
@Id
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumns( {
@JoinColumn(name="fname", referencedColumnName = "firstname"),
@JoinColumn(name="lname", referencedColumnName = "lastname")
} )
public Captain getCaptain() {
return captain;
}
public void setCaptain(Captain captain) {
this.captain = captain;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public Dimensions getDimensions() {
return dimensions;
}
public void setDimensions(Dimensions dimensions) {
this.dimensions = dimensions;
}
}
@Entity
@IdClass(Identity.class)
public class Captain implements Serializable {
private String firstname;
private String lastname;
@Id
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@Id
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
如果您检索单个实体并使用默认映射,则可以使用 resultClass
属性而不是 resultSetMapping
@NamedNativeQuery(name="implicitSample", query="select * from SpaceShip",
resultClass=SpaceShip.class)
public class SpaceShip {
@SqlResultSetMapping(name="scalar", columns=@ColumnResult(name="dimension"))
@NamedNativeQuery(name="scalar", query="select length*width as dimension from SpaceShip", resultSetMapping="scalar")
引入了另一个特定于本机查询的查询提示:org.hibernate.callable
,它可以根据查询是否是存储过程而为 true 或 false。
Hibernate 3.1 提供了各种其他注释,您可以将这些注释与 EJB 3 实体混合使用。它们被设计为 EJB3 注释的自然扩展。
为了增强 EJB3 功能,Hibernate 提供了与 Hibernate 特性匹配的特定注释。 org.hibernate.annotations
包包含所有这些注释扩展。
您可以在实体上微调 Hibernate 执行的一些操作,超出了 EJB3 规范提供的范围。
@org.hibernate.annotations.Entity
添加了可能超出标准 @Entity
中定义的范围所需的额外元数据
@org.hibernate.annotations.Proxy
定义实体的延迟属性。lazy(默认为 true)定义类是否延迟。proxyClassName 是用于生成代理的接口(默认值是类本身)。
@org.hibernate.annotations.Where
定义检索此类实例时使用的可选 SQL WHERE 子句。
@org.hibernate.annotations.Check
定义在 DDL 语句中定义的可选检查约束。
@OnDelete(action=OnDeleteAction.CASCADE)
在联接子类上:在删除时使用 SQL 级联删除,而不是常规的 Hibernate 机制。
@org.hibernate.annotations.Table
也可以用于定义辅助表的以下元素
@Entity
@BatchSize(size=5)
@org.hibernate.annotations.Entity(
selectBeforeUpdate = true,
dynamicInsert = true, dynamicUpdate = true,
optimisticLock = OptimisticLockType.ALL,
polymorphism = PolymorphismType.EXPLICIT)
@Where(clause="1=1")
@org.hibernate.annotations.Table(name="Forest", indexes = { @Index(name="idx", columnNames = { "name", "length" } ) } )
@Persister(impl=MyEntityPersister.class)
public class Forest { ... }
@Entity
@Inheritance(
strategy=InheritanceType.JOINED
)
public class Vegetable { ... }
@Entity
@OnDelete(action=OnDeleteAction.CASCADE)
public class Carrot extends Vegetable { ... }
Hibernate 注释在定义标识符时超越了 Java 持久性规范。
@Id @GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
public String getId() {
@Id @GeneratedValue(generator="hibseq")
@GenericGenerator(name="hibseq", strategy = "seqhilo",
parameters = {
@Parameter(name="max_lo", value = "5"),
@Parameter(name="sequence", value="heybabyhey")
}
)
public Integer getId() {
strategy
是 Hibernate3 生成器策略的简称或 IdentifierGenerator
实现的完全限定类名。 您可以通过 parameters
属性添加一些参数。
与它们的标准对应物相反,@GenericGenerator
和 @GenericGenerators
可用于包级注释,使它们成为应用程序级生成器(就像它们在 JPA XML 文件中一样)。
@GenericGenerators(
{
@GenericGenerator(
name="hibseq",
strategy = "seqhilo",
parameters = {
@Parameter(name="max_lo", value = "5"),
@Parameter(name="sequence", value="heybabyhey")
}
),
@GenericGenerator(...)
}
)
package org.hibernate.test.model
有时,您希望数据库为您执行一些计算,而不是在 JVM 中执行,您也可以创建某种虚拟列。 您可以使用 SQL 片段(又名公式)而不是将属性映射到列。 这种属性是只读的(其值由您的公式片段计算)。
@Formula("obj_length * obj_height * obj_width")
public long getObjectVolume()
@TypeDef(
name = "phoneNumber",
defaultForType = PhoneNumber.class,
typeClass = PhoneNumberType.class
)
@Entity
public class ContactDetails {
[...]
private PhoneNumber localPhoneNumber;
@Type(type="phoneNumber")
private OverseasPhoneNumber overseasPhoneNumber;
[...]
}
以下示例显示了 parameters
属性的使用以自定义 TypeDef。
//in org/hibernate/test/annotations/entity/package-info.java
@TypeDefs(
{
@TypeDef(
name="caster",
typeClass = CasterStringType.class,
parameters = {
@Parameter(name="cast", value="lower")
}
)
}
)
package org.hibernate.test.annotations.entity;
//in org/hibernate/test/annotations/entity/Forest.java
public class Forest {
@Type(type="caster")
public String getSmallText() {
...
}
当使用复合用户类型时,您必须表达列定义。 @Columns
已为此目的引入。
@Type(type="org.hibernate.test.annotations.entity.MonetaryAmountUserType")
@Columns(columns = {
@Column(name="r_amount"),
@Column(name="r_currency")
})
public MonetaryAmount getAmount() {
return amount;
}
public class MonetaryAmount implements Serializable {
private BigDecimal amount;
private Currency currency;
...
}
您可以使用在单列属性上使用 @Index
注释来定义对特定列的索引,然后将忽略 columnNames 属性
@Column(secondaryTable="Cat1")
@Index(name="story1index")
public String getStoryPart1() {
return storyPart1;
}
当位于可嵌入对象中时,您可以将其中一个属性定义为指向所有者元素的指针。
@Entity
public class Person {
@Embeddable public Address address;
...
}
@Embeddable
public class Address {
@Parent public Person owner;
...
}
person == person.address.owner
某些属性是在插入或更新时由您的数据库生成的。 Hibernate 可以处理此类属性并触发后续选择以读取这些属性。
@Entity
public class Antenna {
@Id public Integer id;
@Generated(GenerationTime.ALWAYS)
@Column(insertable = false, updatable = false)
public String longitude;
@Generated(GenerationTime.INSERT) @Column(insertable = false)
public String latitude;
}
@Entity
@DiscriminatorFormula("case when forest_type is null then 0 else forest_type end")
public class Forest { ... }
您可以在 JOINED 继承策略中定义 Hibernate 为子类表生成的外部键名称。
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class File { ... }
@Entity
@ForeignKey(name = "FK_DOCU_FILE")
public class Document extends File {
@Entity
public class Child {
...
@ManyToOne
@NotFound(action=NotFoundAction.IGNORE)
public Parent getParent() { ... }
...
}
@Entity
public class Child {
...
@ManyToOne
@OnDelete(action=OnDeleteAction.CASCADE)
public Parent getParent() { ... }
...
}
在这种情况下,Hibernate 在数据库级别生成级联删除约束。
外部键约束虽然由 Hibernate 生成,但名称难以阅读。 您可以使用 @ForeignKey
覆盖约束名称。
@Entity
public class Child {
...
@ManyToOne
@ForeignKey(name="FK_PARENT")
public Parent getParent() { ... }
...
}
alter table Child add constraint FK_PARENT foreign key (parent_id) references Parent
@Any
注解描述了保存元数据信息的列。要将元数据信息的价值和实际实体类型联系起来,使用 @AnyDef
和 @AnyDefs
注解。
@Any( metaColumn = @Column( name = "property_type" ), fetch=FetchType.EAGER )
@AnyMetaDef(
idType = "integer",
metaType = "string",
metaValues = {
@MetaValue( value = "S", targetEntity = StringProperty.class ),
@MetaValue( value = "I", targetEntity = IntegerProperty.class )
} )
@JoinColumn( name = "property_id" )
public Property getMainProperty() {
return mainProperty;
}
idType
代表目标实体的标识符属性类型,而 metaType
代表元数据类型(通常为 String)。
注意,@AnyDef
可以相互使用并重复使用。建议在这种情况下将其作为包元数据放置。
//on a package
@AnyMetaDef( name="property"
idType = "integer",
metaType = "string",
metaValues = {
@MetaValue( value = "S", targetEntity = StringProperty.class ),
@MetaValue( value = "I", targetEntity = IntegerProperty.class )
} )
package org.hibernate.test.annotations.any;
//in a class
@Any( metaDef="property", metaColumn = @Column( name = "property_type" ), fetch=FetchType.EAGER )
@JoinColumn( name = "property_id" )
public Property getMainProperty() {
return mainProperty;
}
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="CUST_ID")
@Sort(type = SortType.COMPARATOR, comparator = TicketComparator.class)
@Where(clause="1=1")
@OnDelete(action=OnDeleteAction.CASCADE)
public SortedSet<Ticket> getTickets() {
return tickets;
}
外键约束虽然由 Hibernate 生成,但名称却难以理解。您可以使用 @ForeignKey
覆盖约束名称。请注意,此注解必须放在关系的拥有方,inverseName
指向另一方的约束。
@Entity
public class Woman {
...
@ManyToMany(cascade = {CascadeType.ALL})
@ForeignKey(name = "TO_WOMAN_FK", inverseName = "TO_MAN_FK")
public Set<Man> getMens() {
return mens;
}
}
alter table Man_Woman add constraint TO_WOMAN_FK foreign key (woman_id) references Woman
alter table Man_Woman add constraint TO_MAN_FK foreign key (man_id) references Man
在一个端点是索引集合(即表示为 @OrderColumn
或作为 Map)的双向关联需要特别考虑。如果关联类上的属性显式地映射索引值,则允许使用 mappedBy
@Entity
public class Parent {
@OneToMany(mappedBy="parent")
@OrderColumn(name="order")
private List<Child> children;
...
}
@Entity
public class Child {
...
//the index column is mapped as a property in the associated entity
@Column(name="order")
private int order;
@ManyToOne
@JoinColumn(name="parent_id", nullable=false)
private Parent parent;
...
}
但是,如果子类上没有这样的属性,我们就不能将关联视为真正的双向关联(关联的一端存在另一端没有的信息:索引)。在这种情况下,我们不能将集合映射为 mappedBy
。相反,我们可以使用以下映射
@Entity
public class Parent {
@OneToMany
@OrderColumn(name="order")
@JoinColumn(name="parent_id", nullable=false)
private List<Child> children;
...
}
@Entity
public class Child {
...
@ManyToOne
@JoinColumn(name="parent_id", insertable=false, updatable=false, nullable=false)
private Parent parent;
...
}
@ManyToAny(
metaColumn = @Column( name = "property_type" ) )
@AnyMetaDef(
idType = "integer",
metaType = "string",
metaValues = {
@MetaValue( value = "S", targetEntity = StringProperty.class ),
@MetaValue( value = "I", targetEntity = IntegerProperty.class ) } )
@Cascade( { org.hibernate.annotations.CascadeType.ALL } )
@JoinTable( name = "obj_properties", joinColumns = @JoinColumn( name = "obj_id" ),
inverseJoinColumns = @JoinColumn( name = "property_id" ) )
public List<Property> getGeneralProperties() {
与 @Any
类似,@ManyToAny
可以使用命名的 @AnyDef
,有关更多信息,请参阅 第 2.4.5.2 节“@Any”。
Hibernate 提供了比 Java 持久性规范更多的操作。您可以使用 @Cascade
注解将以下操作级联
@OneToMany( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
@Cascade(org.hibernate.annotations.CascadeType.REPLICATE)
public Collection<Employer> getEmployers()
Hibernate 能够对您的数据应用任意过滤器。这些过滤器在运行时应用于给定会话。首先,您需要定义它们。
现在我们需要定义应用于实体加载或集合加载的 SQL 过滤器子句。 @Filter
用于放置在实体或集合元素上
@Entity
@FilterDef(name="minLength", parameters=@ParamDef( name="minLength", type="integer" ) )
@Filters( {
@Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
@Filter(name="minLength", condition=":minLength <= length")
} )
public class Forest { ... }
@OneToMany
@JoinTable
//filter on the target entity table
@Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length")
//filter on the association table
@FilterJoinTable(name="security", condition=":userlevel >= requredLevel")
public Set<Forest> getForests() { ... }
Hibernate 使您能够覆盖生成的每个 SQL 语句。我们已经看到了本机 SQL 查询的使用,但您也可以覆盖用于加载或更改实体状态的 SQL 语句。
@Entity
@Table(name="CHAOS")
@SQLInsert( sql="INSERT INTO CHAOS(size, name, nickname, id) VALUES(?,upper(?),?,?)")
@SQLUpdate( sql="UPDATE CHAOS SET size = ?, name = upper(?), nickname = ? WHERE id = ?")
@SQLDelete( sql="DELETE CHAOS WHERE id = ?")
@SQLDeleteAll( sql="DELETE CHAOS")
@Loader(namedQuery = "chaos")
@NamedNativeQuery(name="chaos", query="select id, size, name, lower( nickname ) as nickname from CHAOS where id= ?", resultClass = Chaos.class)
public class Chaos {
@Id
private Long id;
private Long size;
private String name;
private String nickname;
@SQLInsert
、@SQLUpdate
、@SQLDelete
、@SQLDeleteAll
分别覆盖 INSERT 语句、UPDATE 语句、DELETE 语句、删除所有实体的 DELETE 语句。
如果您希望调用存储过程,请确保将 callable
属性设置为 true (@SQLInsert(callable=true, ...)
)。
要检查执行是否正确,Hibernate 允许您定义以下三种策略之一
要定义结果检查样式,请使用 check
参数 (@SQLUpdate(check=ResultCheckStyle.COUNT, ...)
)。
您还可以使用本机 SQL 查询或 HQL 查询覆盖 SQL 加载语句。您只需使用
注解引用命名查询。@Loader
@OneToMany
@JoinColumn(name="chaos_fk")
@SQLInsert( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = ? where id = ?")
@SQLDelete( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = null where id = ?")
private Set<CasimirParticle> particles = new HashSet<CasimirParticle>();
还可以使用 @org.hibernate.annotations.Table
以及 (或全部) 属性 sqlInsert
、sqlUpdate
、sqlDelete
来覆盖辅助表的 SQL 语句。
@Entity
@SecondaryTables({
@SecondaryTable(name = "`Cat nbr1`"),
@SecondaryTable(name = "Cat2"})
@org.hibernate.annotations.Tables( {
@Table(appliesTo = "Cat", comment = "My cat table" ),
@Table(appliesTo = "Cat2", foreignKey = @ForeignKey(name="FK_CAT2_CAT"), fetch = FetchMode.SELECT,
sqlInsert=@SQLInsert(sql="insert into Cat2(storyPart2, id) values(upper(?), ?)") )
} )
public class Cat implements Serializable {
要在注解中定义 Tuplixer,只需在相应的元素上使用 @Tuplizer
注解
@Entity
@Tuplizer(impl = DynamicEntityTuplizer.class)
public interface Cuisine {
@Id
@GeneratedValue
public Long getId();
public void setId(Long id);
public String getName();
public void setName(String name);
@Tuplizer(impl = DynamicComponentTuplizer.class)
public Country getCountry();
public void setCountry(Country country);
}
在第 2.4.5.1 节,“延迟选项和获取模式”中,我们已经了解了如何使用 @Fetch
注解来影响关联对象的获取策略。另一种方法是所谓的获取配置文件。获取配置文件是与 org.hibernate.SessionFactory
关联的命名配置,它在 org.hibernate.Session.
上启用。一旦在 org.hibernate.Session
上启用,获取配置文件将对该会话生效,直到它被显式禁用。让我们看一个例子
@Entity
@FetchProfile(name = "customer-with-orders", fetchOverrides = {
@FetchProfile.FetchOverride(entity = Customer.class, association = "orders", mode = FetchMode.JOIN)
})
public class Customer {
@Id
@GeneratedValue
private long id;
private String name;
private long customerNumber;
@OneToMany
private Set<Order> orders;
// standard getter/setter
...
}
在正常情况下,订单关联将由 Hibernate 延迟加载,但在用例中,如果将客户及其订单一起加载更有效,您可以执行以下操作
Session session = ...;
session.enableFetchProfile( "customer-with-orders" ); // name matches @FetchProfile name
Customer customer = (Customer) session.get( Customer.class, customerId );
session.disableFetchProfile( "customer-with-orders" ); // or just close the session
...
获取配置文件定义是全局的,它与您将它们放置在哪个类上无关。您可以将 @FetchProfile
注解放置在类或包(package-info.java)上。为了为同一个类或包定义多个获取配置文件,可以使用 @FetchProfiles
。
目前只支持连接样式的获取配置文件,但计划是支持其他样式。有关详细信息,请参阅 HHH-3414。另请参阅 Hibernate Core 文档中关于获取配置文件的讨论。
版权所有 © 2004 Red Hat Inc. 及各种作者