Hibernate.org Community Documentation
- 5.1. 映射定义(Mapping declaration)
-
- 5.1.1. Entity
- 5.1.2. Identifiers
- 5.1.3. Optimistic locking properties (optional)
- 5.1.4. Property
- 5.1.5. Embedded objects (aka components)
- 5.1.6. Inheritance strategy
- 5.1.7. Mapping one to one and one to many associations
- 5.1.8. 自然 ID(natural-id)
- 5.1.9. Any
- 5.1.10. 属性(Properties)
- 5.1.11. Some hbm.xml specificities
- 5.2. Hibernate 的类型
- 5.3. 多次映射同一个类
- 5.4. SQL 中引号包围的标识符
- 5.5. 数据库生成属性(Generated Properties)
- 5.6. Column transformers: read and write expressions
- 5.7. 辅助数据库对象(Auxiliary Database Objects)
Object/relational mappings can be defined in three approaches:
-
using Java 5 annotations (via the Java Persistence 2 annotations)
-
using JPA 2 XML deployment descriptors (described in chapter XXX)
-
using the Hibernate legacy XML files approach known as hbm.xml
Annotations are split in two categories, the logical mapping annotations (describing the object model, the association between two entities etc.) and the physical mapping annotations (describing the physical schema, tables, columns, indexes, etc). We will mix annotations from both categories in the following code examples.
JPA annotations are in the javax.persistence.*
package. Hibernate specific extensions are in org.hibernate.annotations.*
. You favorite IDE can auto-complete annotations and their attributes for you (even without a specific "JPA" plugin, since JPA annotations are plain Java 5 annotations).
Here is an example of mapping
package eg; @Entity
@Table(name="cats") @Inheritance(strategy=SINGLE_TABLE)
@DiscriminatorValue("C") @DiscriminatorColumn(name="subclass", discriminatorType=CHAR)
public class Cat {
@Id @GeneratedValue
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
public BigDecimal getWeight() { return weight; }
public void setWeight(BigDecimal weight) { this.weight = weight; }
private BigDecimal weight;
@Temporal(DATE) @NotNull @Column(updatable=false)
public Date getBirthdate() { return birthdate; }
public void setBirthdate(Date birthdate) { this.birthdate = birthdate; }
private Date birthdate;
@org.hibernate.annotations.Type(type="eg.types.ColorUserType")
@NotNull @Column(updatable=false)
public ColorType getColor() { return color; }
public void setColor(ColorType color) { this.color = color; }
private ColorType color;
@NotNull @Column(updatable=false)
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
private String sex;
@NotNull @Column(updatable=false)
public Integer getLitterId() { return litterId; }
public void setLitterId(Integer litterId) { this.litterId = litterId; }
private Integer litterId;
@ManyToOne @JoinColumn(name="mother_id", updatable=false)
public Cat getMother() { return mother; }
public void setMother(Cat mother) { this.mother = mother; }
private Cat mother;
@OneToMany(mappedBy="mother") @OrderBy("litterId")
public Set<Cat> getKittens() { return kittens; }
public void setKittens(Set<Cat> kittens) { this.kittens = kittens; }
private Set<Cat> kittens = new HashSet<Cat>();
}
@Entity @DiscriminatorValue("D")
public class DomesticCat extends Cat {
public String getName() { return name; }
public void setName(String name) { this.name = name }
private String name;
}
@Entity
public class Dog { ... }
The legacy hbm.xml approach uses an XML schema designed to be readable and hand-editable. The mapping language is Java-centric, meaning that mappings are constructed around persistent class declarations and not table declarations.
请注意,虽然很多 Hibernate 用户选择手写 XML 映射文档,但也有一些工具可以用来生成映射文档,包括 XDoclet、Middlegen 和 AndroMDA。
下面是一个映射的例子:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat"
table="cats"
discriminator-value="C">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="subclass"
type="character"/>
<property name="weight"/>
<property name="birthdate"
type="date"
not-null="true"
update="false"/>
<property name="color"
type="eg.types.ColorUserType"
not-null="true"
update="false"/>
<property name="sex"
not-null="true"
update="false"/>
<property name="litterId"
column="litterId"
update="false"/>
<many-to-one name="mother"
column="mother_id"
update="false"/>
<set name="kittens"
inverse="true"
order-by="litter_id">
<key column="mother_id"/>
<one-to-many class="Cat"/>
</set>
<subclass name="DomesticCat"
discriminator-value="D">
<property name="name"
type="string"/>
</subclass>
</class>
<class name="Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>
We will now discuss the concepts of the mapping documents (both annotations and XML). We will only describe, however, the document elements and attributes that are used by Hibernate at runtime. The mapping document also contains some extra optional attributes and elements that affect the database schemas exported by the schema export tool (for example, the not-null
attribute).
An entity is a regular Java object (aka POJO) which will be persisted by Hibernate.
To mark an object as an entity in annotations, use the @Entity
annotation.
@Entity
public class Flight implements Serializable {
Long id;
@Id
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
}
That's pretty much it, the rest is optional. There are however any options to tweak your entity mapping, let's explore them.
@Table
lets you define the table the entity will be persisted into. If undefined, the table name is the unqualified class name of the entity. You can also optionally define the catalog, the schema as well as unique constraints on the table.
@Entity
@Table(name="TBL_FLIGHT",
schema="AIR_COMMAND",
uniqueConstraints=
@UniqueConstraint(
name="flight_number",
columnNames={"comp_prefix", "flight_number"} ) )
public class Flight implements Serializable {
@Column(name="comp_prefix")
public String getCompagnyPrefix() { return companyPrefix; }
@Column(name="flight_number")
public String getNumber() { return number; }
}
The constraint name is optional (generated if left undefined). The column names composing the constraint correspond to the column names as defined before the Hibernate NamingStrategy
is applied.
@Entity.name
lets you define the shortcut name of the entity you can used in JP-QL and HQL queries. It defaults to the unqualified class name of the class.
Hibernate goes beyond the JPA specification and provide additional configurations. Some of them are hosted on @org.hibernate.annotations.Entity
:
-
dynamicInsert
/dynamicUpdate
(defaults to false): specifies thatINSERT
/UPDATE
SQL should be generated at runtime and contain only the columns whose values are not null. Thedynamic-update
anddynamic-insert
settings are not inherited by subclasses. Although these settings can increase performance in some cases, they can actually decrease performance in others. -
selectBeforeUpdate
(defaults to false): specifies that Hibernate should never perform an SQLUPDATE
unless it is certain that an object is actually modified. Only when a transient object has been associated with a new session usingupdate()
, will Hibernate perform an extra SQLSELECT
to determine if anUPDATE
is actually required. Use ofselect-before-update
will usually decrease performance. It is useful to prevent a database update trigger being called unnecessarily if you reattach a graph of detached instances to aSession
. -
polymorphisms
(defaults toIMPLICIT
): determines whether implicit or explicit query polymorphisms is used. Implicit polymorphisms means that instances of the class will be returned by a query that names any superclass or implemented interface or class, and that instances of any subclass of the class will be returned by a query that names the class itself. Explicit polymorphisms means that class instances will be returned only by queries that explicitly name that class. Queries that name the class will return only instances of subclasses mapped. For most purposes, the defaultpolymorphisms=IMPLICIT
is appropriate. Explicit polymorphisms is useful when two different classes are mapped to the same table This allows a "lightweight" class that contains a subset of the table columns. -
persister
: specifies a customClassPersister
. Thepersister
attribute lets you customize the persistence strategy used for the class. You can, for example, specify your own subclass oforg.hibernate.persister.EntityPersister
, or you can even provide a completely new implementation of the interfaceorg.hibernate.persister.ClassPersister
that implements, for example, persistence via stored procedure calls, serialization to flat files or LDAP. Seeorg.hibernate.test.CustomPersister
for a simple example of "persistence" to aHashtable
. -
optimisticLock
(defaults toVERSION
): determines the optimistic locking strategy. If you enabledynamicUpdate
, you will have a choice of optimistic locking strategies:-
version(版本检查)
:检查 version/timestamp 字段 -
all(全部)
:检查全部字段 -
dirty(脏检查)
:只检察修改过的字段,允许某些并行更新 -
none(不检查)
:不使用乐观锁定
我们强烈建议你在 Hibernate 中使用 version/timestamp 字段来进行乐观锁定。这个选择可以优化性能,且能够处理对脱管实例的修改(例如:在使用
Session.merge()
的时候)。 -
提示
Be sure to import @javax.persistence.Entity
to mark a class as an entity. It's a common mistake to import @org.hibernate.annotations.Entity
by accident.
Some entities are not mutable. They cannot be updated or deleted by the application. This allows Hibernate to make some minor performance optimizations.. Use the @Immutable
annotation.
You can also alter how Hibernate deals with lazy initialization for this class. On @Proxy
, use lazy
=false to disable lazy fetching (not recommended). You can also specify an interface to use for lazy initializing proxies (defaults to the class itself): use proxyClass
on @Proxy
. Hibernate will initially return proxies (Javassist or CGLIB) that implement the named interface. The persistent object will load when a method of the proxy is invoked. See "Initializing collections and proxies" below.
@BatchSize
specifies a "batch size" for fetching instances of this class by identifier. Not yet loaded instances are loaded batch-size at a time (default 1).
You can specific an arbitrary SQL WHERE condition to be used when retrieving objects of this class. Use @Where
for that.
In the same vein, @Check
lets you define an SQL expression used to generate a multi-row check constraint for automatic schema generation.
There is no difference between a view and a base table for a Hibernate mapping. This is transparent at the database level, although some DBMS do not support views properly, especially with updates. Sometimes you want to use a view, but you cannot create one in the database (i.e. with a legacy schema). In this case, you can map an immutable and read-only entity to a given SQL subselect expression using @org.hibernate.annotations.Subselect
:
@Entity
@Subselect("select item.name, max(bid.amount), count(*) "
+ "from item "
+ "join bid on bid.item_id = item.id "
+ "group by item.name")
@Synchronize( {"item", "bid"} ) //tables impacted
public class Summary {
@Id
public String getId() { return id; }
...
}
定义这个实体用到的表为同步(synchronize),确保自动刷新(auto-flush)正确执行,并且依赖原实体的查询不会返回过期数据。在属性元素和嵌套映射元素中都可使用 <subselect>
。
We will now explore the same options using the hbm.xml structure. You can declare a persistent class using the class
element. For example:
<class name="ClassName" table="tableName" discriminator-value="discriminator_value" mutable="true|false" schema="owner" catalog="catalog" proxy="ProxyInterface" dynamic-update="true|false" dynamic-insert="true|false" select-before-update="true|false" polymorphism="implicit|explicit" where="arbitrary sql where condition" persister="PersisterClass" batch-size="N" optimistic-lock="none|version|dirty|all" lazy="(16)true|false" entity(17)-name="EntityName" check=(18)"arbitrary sql check condition" rowid=(19)"rowid" subsel(20)ect="SQL expression" abstra(21)ct="true|false" node="element-name" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(16) |
|
(17) |
|
(18) |
|
(19) |
|
(20) |
|
(21) |
|
若指明的持久化类实际上是一个接口,这也是完全可以接受的。之后你可以用元素 <subclass>
来指定该接口的实际实现类。你可以持久化任何 static(静态的)内部类。你应该使用标准的类名格式来指定类名,比如:Foo$Bar
。
Here is how to do a virtual view (subselect) in XML:
<class name="Summary">
<subselect>
select item.name, max(bid.amount), count(*)
from item
join bid on bid.item_id = item.id
group by item.name
</subselect>
<synchronize table="item"/>
<synchronize table="bid"/>
<id name="name"/>
...
</class>
The <subselect>
is available both as an attribute and a nested mapping element.
Mapped classes must declare the primary key column of the database table. Most classes will also have a JavaBeans-style property holding the unique identifier of an instance.
Mark the identifier property with @Id
.
@Entity
public class Person {
@Id Integer getId() { ... }
...
}
In hbm.xml, use the <id>
element which defines the mapping from that property to the primary key column.
<id name="propertyName" type="typename" column="column_name" unsaved-value="null|any|none|undefined|id_value" access="field|property|ClassName"> node="element-name|@attribute-name|element/@attribute|." <generator class="generatorClass"/> </id>
|
|
|
|
|
|
|
|
|
|
如果 name
属性不存在,会认为这个类没有标识属性。
The unsaved-value
attribute is almost never needed in Hibernate3 and indeed has no corresponding element in annotations.
You can also declare the identifier as a composite identifier. This allows access to legacy data with composite keys. Its use is strongly discouraged for anything else.
You can define a composite primary key through several syntaxes:
-
use a component type to represent the identifier and map it as a property in the entity: you then annotated the property as
@EmbeddedId
. The component type has to beSerializable
. -
map multiple properties as
@Id
properties: the identifier type is then the entity class itself and needs to beSerializable
. This approach is unfortunately not standard and only supported by Hibernate. -
map multiple properties as
@Id
properties and declare an external class to be the identifier type. This class, which needs to beSerializable
, is declared on the entity via the@IdClass
annotation. The identifier type must contain the same properties as the identifier properties of the entity: each property name must be the same, its type must be the same as well if the entity property is of a basic type, its type must be the type of the primary key of the associated entity if the entity property is an association (either a@OneToOne
or a@ManyToOne
).
As you can see the last case is far from obvious. It has been inherited from the dark ages of EJB 2 for backward compatibilities and we recommend you not to use it (for simplicity sake).
Let's explore all three cases using examples.
Here is a simple example of @EmbeddedId
.
@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;
}
You can notice that the UserId
class is serializable. To override the column mapping, use @AttributeOverride
.
An embedded id can itself contains the primary key of an associated entity.
@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;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
//implements equals and hashCode
}
In the embedded id object, the association is represented as the identifier of the associated entity. But you can link its value to a regular association in the entity via the @MapsId
annotation. The @MapsId
value correspond to the property name of the embedded id object containing the associated entity's identifier. In the database, it means that the Customer.user
and the CustomerId.userId
properties share the same underlying column (user_fk
in this case).
提示
The component type used as identifier must implement equals()
and hashCode()
.
In practice, your code only sets the Customer.user
property and the user id value is copied by Hibernate into the CustomerId.userId
property.
警告
The id value can be copied as late as flush time, don't rely on it until after flush time.
While not supported in JPA, Hibernate lets you place your association directly in the embedded id component (instead of having to use the @MapsId
annotation).
@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;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
//implements equals and hashCode
}
Let's now rewrite these examples using the hbm.xml syntax.
<composite-id
name="propertyName"
class="ClassName"
mapped="true|false"
access="field|property|ClassName"
node="element-name|.">
<key-property name="propertyName" type="typename" column="column_name"/>
<key-many-to-one name="propertyName" class="ClassName" column="column_name"/>
......
</composite-id>
First a simple example:
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName" column="fld_firstname"/>
<key-property name="lastName"/>
</composite-id>
</class>
Then an example showing how an association can be mapped.
<class name="Customer">
<composite-id name="id" class="CustomerId">
<key-property name="firstName" column="userfirstname_fk"/>
<key-property name="lastName" column="userfirstname_fk"/>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
<many-to-one name="user">
<column name="userfirstname_fk" updatable="false" insertable="false"/>
<column name="userlastname_fk" updatable="false" insertable="false"/>
</many-to-one>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
Notice a few things in the previous example:
-
the order of the properties (and column) matters. It must be the same between the association and the primary key of the associated entity
-
the many to one uses the same columns as the primary key and thus must be marked as read only (
insertable
andupdatable
to false). -
unlike with
@MapsId
, the id value of the associated entity is not transparently copied, check theforeign
id generator for more information.
The last example shows how to map association directly in the embedded id component.
<class name="Customer">
<composite-id name="id" class="CustomerId">
<key-many-to-one name="user">
<column name="userfirstname_fk"/>
<column name="userlastname_fk"/>
</key-many-to-one>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
This is the recommended approach to map composite identifier. The following options should not be considered unless some constraint are present.
Another, arguably more natural, approach is to place @Id
on multiple properties of your entity. This approach is only supported by Hibernate (not JPA compliant) but does not require an extra embeddable component.
@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;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
//implements equals and hashCode
}
In this case Customer
is its own identifier representation: it must implement Serializable
and must implement equals()
and hashCode()
.
In hbm.xml, the same mapping is:
<class name="Customer">
<composite-id>
<key-many-to-one name="user">
<column name="userfirstname_fk"/>
<column name="userlastname_fk"/>
</key-many-to-one>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
@IdClass
on an entity points to the class (component) representing the identifier of the class. The properties marked @Id
on the entity must have their corresponding property on the @IdClass
. The return type of search twin property must be either identical for basic properties or must correspond to the identifier class of the associated entity for an association.
警告
This approach is inherited from the EJB 2 days and we recommend against its use. But, after all it's your application and Hibernate supports it.
@Entity
@IdClass(CustomerId.class)
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;
}
class CustomerId implements Serializable {
UserId user;
String customerNumber;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
//implements equals and hashCode
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
//implements equals and hashCode
}
Customer
and CustomerId
do have the same properties customerNumber
as well as user
. CustomerId
must be Serializable
and implement equals()
and hashCode()
.
While not JPA standard, Hibernate let's you declare the vanilla associated property in the @IdClass
.
@Entity
@IdClass(CustomerId.class)
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;
}
class CustomerId implements Serializable {
@OneToOne User user;
String customerNumber;
//implements equals and hashCode
}
@Entity
class User {
@EmbeddedId UserId id;
Integer age;
//implements equals and hashCode
}
@Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
This feature is of limited interest though as you are likely to have chosen the @IdClass
approach to stay JPA compliant or you have a quite twisted mind.
Here are the equivalent on hbm.xml files:
<class name="Customer">
<composite-id class="CustomerId" mapped="true">
<key-many-to-one name="user">
<column name="userfirstname_fk"/>
<column name="userlastname_fk"/>
</key-many-to-one>
<key-property name="customerNumber"/>
</composite-id>
<property name="preferredCustomer"/>
</class>
<class name="User">
<composite-id name="id" class="UserId">
<key-property name="firstName"/>
<key-property name="lastName"/>
</composite-id>
<property name="age"/>
</class>
Hibernate can generate and populate identifier values for you automatically. This is the recommended approach over "business" or "natural" id (especially composite ids).
Hibernate offers various generation strategies, let's explore the most common ones first that happens to be standardized by JPA:
-
IDENTITY: supports identity columns in DB2, MySQL, MS SQL Server, Sybase and HypersonicSQL. The returned identifier is of type
long
,short
orint
. -
SEQUENCE (called
seqhilo
in Hibernate): uses a hi/lo algorithm to efficiently generate identifiers of typelong
,short
orint
, given a named database sequence. -
TABLE (called
MultipleHiLoPerTableGenerator
in Hibernate) : uses a hi/lo algorithm to efficiently generate identifiers of typelong
,short
orint
, given a table and column as a source of hi values. The hi/lo algorithm generates identifiers that are unique only for a particular database. -
AUTO: selects
IDENTITY
,SEQUENCE
orTABLE
depending upon the capabilities of the underlying database.
重要
We recommend all new projects to use the new enhanced identifier generators. They are deactivated by default for entities using annotations but can be activated using hibernate.id.new_generator_mappings=true
. These new generators are more efficient and closer to the JPA 2 specification semantic.
However they are not backward compatible with existing Hibernate based application (if a sequence or a table is used for id generation). See XXXXXXX ??? for more information on how to activate them.
To mark an id property as generated, use the @GeneratedValue
annotation. You can specify the strategy used (default to AUTO
) by setting strategy
.
@Entity
public class Customer {
@Id @GeneratedValue
Integer getId() { ... };
}
@Entity
public class Invoice {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
Integer getId() { ... };
}
SEQUENCE
and TABLE
require additional configurations that you can set using @SequenceGenerator
and @TableGenerator
:
-
name
: name of the generator -
table
/sequenceName
: name of the table or the sequence (defaulting respectively tohibernate_sequences
andhibernate_sequence
) -
catalog
/schema
: -
initialValue
: the value from which the id is to start generating -
allocationSize
: the amount to increment by when allocating id numbers from the generator
In addition, the TABLE
strategy also let you customize:
-
pkColumnName
: the column name containing the entity identifier -
valueColumnName
: the column name containing the identifier value -
pkColumnValue
: the entity identifier -
uniqueConstraints
: any potential column constraint on the table containing the ids
To link a table or sequence generator definition with an actual generated property, use the same name in both the definition name
and the generator value generator
as shown below.
@Id
@GeneratedValue(
strategy=GenerationType.SEQUENCE,
generator="SEQ_GEN")
@javax.persistence.SequenceGenerator(
name="SEQ_GEN",
sequenceName="my_sequence",
allocationSize=20
)
public Integer getId() { ... }
The scope of a generator definition can be the application or the class. Class-defined generators are not visible outside the class and can override application level generators. Application level generators are defined in JPA's XML deployment descriptors (see XXXXXX ???):
<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
)
If a JPA XML descriptor (like META-INF/orm.xml
) is used to define the generators, EMP_GEN
and SEQ_GEN
are application level generators.
注意
Package level definition is not supported by the JPA specification. However, you can use the @GenericGenerator
at the package level (see ???).
These are the four standard JPA generators. Hibernate goes beyond that and provide additional generators or additional options as we will see below. You can also write your own custom identifier generator by implementing org.hibernate.id.IdentifierGenerator
.
To define a custom generator, use the @GenericGenerator
annotation (and its plural counter part @GenericGenerators
) that describes the class of the identifier generator or its short cut name (as described below) and a list of key/value parameters. When using @GenericGenerator
and assigning it via @GeneratedValue.generator
, the @GeneratedValue.strategy
is ignored: leave it blank.
@Id @GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
public String getId() {
@Id @GeneratedValue(generator="trigger-generated")
@GenericGenerator(
name="trigger-generated",
strategy = "select",
parameters = @Parameter(name="key", value = "socialSecurityNumber")
)
public String getId() {
The hbm.xml approach uses the optional <generator>
child element inside <id>
. If any parameters are required to configure or initialize the generator instance, they are passed using the <param>
element.
<id name="id" type="long" column="cat_id">
<generator class="org.hibernate.id.TableHiLoGenerator">
<param name="table">uid_table</param>
<param name="column">next_hi_value_column</param>
</generator>
</id>
所有的生成器都实现 org.hibernate.id.IdentifierGenerator
接口。这是一个非常简单的接口;某些应用程序可以选择提供他们自己特定的实现。当然,Hibernate 提供了很多内置的实现。下面是一些内置生成器的快捷名字:
-
increment
-
用于为
long
,short
或者int
类型生成 唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。在集群下不要使用。 -
identity
-
对 DB2,MySQL,MS SQL Server,Sybase 和 HypersonicSQL 的内置标识字段提供支持。返回的标识符是
long
,short
或者int
类型的。 -
sequence
-
在 DB2,PostgreSQL,Oracle,SAP DB,McKoi 中使用序列(sequence), 而在 Interbase 中使用生成器(generator)。返回的标识符是
long
,short
或者int
类型的。 -
hilo
-
seqhilo
-
使用一个高/低位算法来高效的生成
long
,short
或者int
类型的标识符,给定一个数据库序列(sequence)的名字。 -
uuid
-
Generates a 128-bit UUID based on a custom algorithm. The value generated is represented as a string of 32 hexidecimal digits. Users can also configure it to use a separator (config parameter "separator") which separates the hexidecimal digits into 8{sep}8{sep}4{sep}8{sep}4. Note specifically that this is different than the IETF RFC 4122 representation of 8-4-4-4-12. If you need RFC 4122 compliant UUIDs, consider using "uuid2" generator discussed below.
-
uuid2
-
Generates a IETF RFC 4122 compliant (variant 2) 128-bit UUID. The exact "version" (the RFC term) generated depends on the pluggable "generation strategy" used (see below). Capable of generating values as
java.util.UUID
,java.lang.String
or as a byte array of length 16 (byte[16]
). The "generation strategy" is defined by the interfaceorg.hibernate.id.UUIDGenerationStrategy
. The generator defines 2 configuration parameters for defining which generation strategy to use:-
uuid_gen_strategy_class
-
Names the UUIDGenerationStrategy class to use
-
uuid_gen_strategy
-
Names the UUIDGenerationStrategy instance to use
Out of the box, comes with the following strategies:
-
org.hibernate.id.uuid.StandardRandomStrategy
(the default) - generates "version 3" (aka, "random") UUID values via therandomUUID
method ofjava.util.UUID
-
org.hibernate.id.uuid.CustomVersionOneStrategy
- generates "version 1" UUID values, using IP address since mac address not available. If you need mac address to be used, consider leveraging one of the existing third party UUID generators which sniff out mac address and integrating it via theorg.hibernate.id.UUIDGenerationStrategy
contract. Two such libraries known at time of this writing include http://johannburkard.de/software/uuid/ and http://commons.apache.org/sandbox/id/uuid.html
-
-
guid
-
在 MS SQL Server 和 MySQL 中使用数据库生成的 GUID 字符串。
-
native
-
根据底层数据库的能力选择
identity
、sequence
或者hilo
中的一个。 -
assigned
-
让应用程序在调用
save()
之前为对象分配一个标识符。这是<generator>
元素没有指定时的默认生成策略。 -
select
-
通过数据库触发器选择一些唯一主键的行并返回主键值来分配一个主键。
-
foreign
-
使用另外一个相关联的对象的标识符。它通常和
<one-to-one>
联合起来使用。 -
sequence-identity
-
一种特别的序列生成策略,它使用数据库序列来生成实际值,但将它和 JDBC3 的 getGeneratedKeys 结合在一起,使得在插入语句执行的时候就返回生成的值。目前为止只有面向 JDK 1.4 的 Oracle 10g 驱动支持这一策略。由于 Oracle 驱动程序的一个 bug,这些插入语句的注释被关闭了。
hilo
和 seqhilo
生成器给出了两种 hi/lo 算法的实现, 这是一种很令人满意的标识符生成算法。第一种实现需要一个“特殊”的数据库表来保存下一个可用的“hi”值。 第二种实现使用一个 Oracle 风格的序列(在被支持的情况下)。
<id name="id" type="long" column="cat_id">
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
</generator>
</id>
<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">100</param>
</generator>
</id>
可惜的是,你在为 Hibernate 自行提供 Connection
时无法使用 hilo
。 当 Hibernate 使用 JTA 获取应用服务器的数据源连接时,你必须正确地配置 hibernate.transaction.manager_lookup_class
。
UUID 包含:IP 地址、JVM 的启动时间(精确到 1/4 秒)、系统时间和一个计数器值(在 JVM 中唯一)。 在 Java 代码中不可能获得 MAC 地址或者内存地址,所以这已经是我们在不使用 JNI 的前提下的能做的最好实现了。
对于内部支持标识字段的数据库(DB2、MySQL、Sybase 和 MS SQL),你可以使用 identity
关键字生成。对于内部支持序列的数据库(DB2、Oracle、PostgreSQL、Interbase、McKoi 和 SAP DB),你可以使用 sequence
风格的关键字生成。这两种方式对于插入一个新的对象都需要两次 SQL 查询。例如:
<id name="id" type="long" column="person_id">
<generator class="sequence">
<param name="sequence">person_id_sequence</param>
</generator>
</id>
<id name="id" type="long" column="person_id" unsaved-value="0">
<generator class="identity"/>
</id>
对于跨平台开发,native
策略会从 identity
、sequence
和 hilo
中进行选择,选择哪一个,这取决于底层数据库的支持能力。
If you want the application to assign identifiers, as opposed to having Hibernate generate them, you can use the assigned
generator. This special generator uses the identifier value already assigned to the object's identifier property. The generator is used when the primary key is a natural key instead of a surrogate key. This is the default behavior if you do not specify @GeneratedValue
nor <generator>
elements.
当选择 assigned
生成器时,除非有一个 version 或 timestamp 属性,或者你定义了 Interceptor.isUnsaved()
,否则需要让 Hiberante 使用 unsaved-value="undefined"
,强制 Hibernatet 查询数据库来确定一个实例是瞬时的(transient) 还是脱管的(detached)。
仅仅用于遗留的 schema 中(Hibernate 不能用触发器生成 DDL)。
<id name="id" type="long" column="person_id">
<generator class="select">
<param name="key">socialSecurityNumber</param>
</generator>
</id>
在上面的例子中,类定义了一个命名为 socialSecurityNumber
的具有唯一值的属性,它是一个自然键(natural key),命名为 person_id
的代理键(surrogate key)的值由触发器生成。
Finally, you can ask Hibernate to copy the identifier from another associated entity. In the Hibernate jargon, it is known as a foreign generator but the JPA mapping reads better and is encouraged.
@Entity
class MedicalHistory implements Serializable {
@Id @OneToOne
@JoinColumn(name = "person_id")
Person patient;
}
@Entity
public class Person implements Serializable {
@Id @GeneratedValue Integer id;
}
Or alternatively
@Entity
class MedicalHistory implements Serializable {
@Id Integer id;
@MapsId @OneToOne
@JoinColumn(name = "patient_id")
Person patient;
}
@Entity
class Person {
@Id @GeneratedValue Integer id;
}
In hbm.xml use the following approach:
<class name="MedicalHistory">
<id name="id">
<generator class="foreign">
<param name="property">patient</param>
</generator>
</id>
<one-to-one name="patient" class="Person" constrained="true"/>
</class>
从 3.2.3 版本开始,有两个代表不同标识符生成概念的新的生成器。第一个概念是数据库移植性;第二个是优化。优化表示你不需对每个新标识符的请求都查询数据库。从 3.3.x 开始,这两个新的生成器都是用来取代上面所述的生成器的。然而,它们也包括在当前版本里且可以由 FQN 进行引用。
这些生成器的第一个是 org.hibernate.id.enhanced.SequenceStyleGenerator
,首先,它是作为 sequence
生成器的替代物,其次,它是比 native
具有更好移植性的生成器。这是因为 native
通常在 identity
和 sequence
之间选择,它有差别很大的 semantic,在移植时会导致潜在的问题。然而,org.hibernate.id.enhanced.SequenceStyleGenerator
以不同的方式实现移植性。它根据所使用的方言的能力,在数据库表或序列之间选择以存储其增量。这和 native
的区别是基于表或序列的存储具有恰好相同的 semantic。实际上,序列就是 Hibernate 试图用基于表的生成器来模拟的。这个生成器有如下的配置参数:
-
sequence_name
(可选 — 默认为hibernate_sequence
):序列或表的名字 -
initial_value
(可选,默认为1
):从序列/表里获取的初始值。按照序列创建的术语,这等同于子句 "STARTS WITH"。 -
increment_size
(可选 - 缺省为1
):对序列/表的调用应该区分的值。按照序列创建的术语,这等同于子句 "INCREMENT BY"。 -
force_table_use
(可选 - 缺省为false
):即使方言可能支持序列,是否也应该强制把表用作后台结构。 -
value_column
(可选 - 缺省为next_val
):只和表结构相关,它是用于保存值的字段的名称。 -
optimizer
(optional - defaults tonone
): See 第 5.1.2.3.1 节 “标识符生成器的优化”
新生成器的第二个是 org.hibernate.id.enhanced.TableGenerator
,它的目的首先是替代 table
生成器,即使它实际上比 org.hibernate.id.MultipleHiLoPerTableGenerator
功能要强得多;其次,作为利用可插拔 optimizer 的 org.hibernate.id.MultipleHiLoPerTableGenerator
的替代品。基本上这个生成器定义了一个可以利用多个不同的键值记录存储大量不同增量值的表。这个生成器有如下的配置参数:
-
table_name
(可选 — 默认是hibernate_sequences
):所用的表的名称。 -
value_column_name
(可选 — 默认为next_val
):用于存储这些值的表的字段的名字。 -
segment_column_name
(可选,默认为sequence_name
):用于保存 "segment key" 的字段的名称。这是标识使用哪个增量值的值。 -
segment_value
(可选,默认为default
):我们为这个生成器获取增量值的 segment 的 "segment key"。 -
segment_value_length
(可选 — 默认为255
):用于 schema 生成;创建 Segment Key 字段的字段大小。 -
initial_value
(可选 — 默认是1
):从表里获取的初始值。 -
increment_size
(可选 — 默认是1
):对表随后的调用应该区分的值。 -
optimizer
(optional - defaults to??
): See 第 5.1.2.3.1 节 “标识符生成器的优化”.
For identifier generators that store values in the database, it is inefficient for them to hit the database on each and every call to generate a new identifier value. Instead, you can group a bunch of them in memory and only hit the database when you have exhausted your in-memory value group. This is the role of the pluggable optimizers. Currently only the two enhanced generators (第 5.1.2.3 节 “增强的标识符生成器” support this operation.
-
none
(如果没有指定 optimizer,通常这是缺省配置):这不会执行任何优化,在每次请求时都访问数据库。 -
hilo
:对从数据库获取的值应用 hi/lo 算法。用于这个 optimizer 的从数据库获取的值应该是有序的。它们表明“组编号”。increment_size
将乘以内存里的值来定义组的“hi 值”。 -
pooled
:和hilo
一样,这个 optimizer 试图最小化对数据库的访问。然而,我们只是简单地把“下一组”的起始值而不是把序列值和分组算法的组合存入到数据库结构里。在这里,increment_size
表示数据库里的值。
Hibernate supports the automatic generation of some of the identifier properties. Simply use the @GeneratedValue
annotation on one or several id properties.
警告
The Hibernate team has always felt such a construct as fundamentally wrong. Try hard to fix your data model before using this feature.
@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;
}
You can also generate properties inside an @EmbeddedId
class.
When using long transactions or conversations that span several database transactions, it is useful to store versioning data to ensure that if the same entity is updated by two conversations, the last to commit changes will be informed and not override the other conversation's work. It guarantees some isolation while still allowing for good scalability and works particularly well in read-often write-sometimes situations.
You can use two approaches: a dedicated version number or a timestamp.
一个脱管(detached)实例的 version 或 timestamp 属性不能为空(null),因为 Hibernate 不管 unsaved-value
被指定为何种策略,它将任何属性为空的 version 或 timestamp 实例看作为瞬时(transient)实例。 避免 Hibernate 中的传递重附(transitive reattachment)问题的一个简单方法是 定义一个不能为空的 version 或 timestamp 属性,特别是在人们使用程序分配的标识符(assigned identifiers) 或复合主键时非常有用。
You can add optimistic locking capability to an entity using the @Version
annotation:
@Entity
public class Flight implements Serializable {
...
@Version
@Column(name="OPTLOCK")
public Integer getVersion() { ... }
}
The version property will be mapped to the OPTLOCK
column, and the entity manager will use it to detect conflicting updates (preventing lost updates you might otherwise see with the last-commit-wins strategy).
The version column may be a numeric. Hibernate supports any kind of type provided that you define and implement the appropriate UserVersionType
.
The application must not alter the version number set up by Hibernate in any way. To artificially increase the version number, check in Hibernate Entity Manager's reference documentation LockModeType.OPTIMISTIC_FORCE_INCREMENT
or LockModeType.PESSIMISTIC_FORCE_INCREMENT
.
If the version number is generated by the database (via a trigger for example), make sure to use @org.hibernate.annotations.Generated(GenerationTime.ALWAYS).
To declare a version property in hbm.xml, use:
<version column="version_column" name="propertyName" type="typename" access="field|property|ClassName" unsaved-value="null|negative|undefined" generated="never|always" insert="true|false" node="element-name|@attribute-name|element/@attribute|." />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Alternatively, you can use a timestamp. Timestamps are a less safe implementation of optimistic locking. However, sometimes an application might use the timestamps in other ways as well.
Simply mark a property of type Date
or Calendar
as @Version
.
@Entity
public class Flight implements Serializable {
...
@Version
public Date getLastUpdate() { ... }
}
When using timestamp versioning you can tell Hibernate where to retrieve the timestamp value from - database or JVM - by optionally adding the @org.hibernate.annotations.Source
annotation to the property. Possible values for the value attribute of the annotation are org.hibernate.annotations.SourceType.VM
and org.hibernate.annotations.SourceType.DB
. The default is SourceType.DB
which is also used in case there is no @Source
annotation at all.
Like in the case of version numbers, the timestamp can also be generated by the database instead of Hibernate. To do that, use @org.hibernate.annotations.Generated(GenerationTime.ALWAYS).
In hbm.xml, use the <timestamp>
element:
<timestamp column="timestamp_column" name="propertyName" access="field|property|ClassName" unsaved-value="null|undefined" source="vm|db" generated="never|always" node="element-name|@attribute-name|element/@attribute|." />
|
|
|
|
|
|
|
|
|
|
|
|
注意
注意,<timestamp>
和 <version type="timestamp">
是等价的。并且 <timestamp source="db">
和 <version type="dbtimestamp">
是等价的。
You need to decide which property needs to be made persistent in a given entity. This differs slightly between the annotation driven metadata and the hbm.xml files.
In the annotations world, every non static non transient property (field or method depending on the access type) of an entity is considered persistent, unless you annotate it as @Transient
. Not having an annotation for your property is equivalent to the appropriate @Basic
annotation.
The @Basic
annotation allows you to declare the fetching strategy for a property. If set to LAZY
, specifies that this property should be fetched lazily when the instance variable is first accessed. It requires build-time bytecode instrumentation, if your classes are not instrumented, property level lazy loading is silently ignored. The default is EAGER
. You can also mark a property as not optional thanks to the @Basic.optional
attribute. This will ensure that the underlying column are not nullable (if possible). Note that a better approach is to use the @NotNull
annotation of the Bean Validation specification.
Let's look at a few examples:
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
counter
, a transient field, and lengthInMeter
, a method annotated as @Transient
, and will be ignored by the Hibernate. name
, length
, and firstname
properties are mapped persistent and eagerly fetched (the default for simple properties). The detailedComment
property value will be lazily fetched from the database once a lazy property of the entity is accessed for the first time. Usually you don't need to lazy simple properties (not to be confused with lazy association fetching). The recommended alternative is to use the projection capability of JP-QL (Java Persistence Query Language) or Criteria queries.
JPA support property mapping of all basic types supported by Hibernate (all basic Java types , their respective wrappers and serializable classes). Hibernate Annotations supports out of the box enum type mapping either into a ordinal column (saving the enum ordinal) or a string based column (saving the enum string representation): the persistence representation, defaulted to ordinal, can be overridden through the @Enumerated
annotation as shown in the note
property example.
In plain Java APIs, the temporal precision of time is not defined. When dealing with temporal data you might want to describe the expected precision in database. Temporal data can have DATE
, TIME
, or TIMESTAMP
precision (ie the actual date, only the time, or both). Use the @Temporal
annotation to fine tune that.
@Lob
indicates that the property should be persisted in a Blob or a Clob depending on the property type: java.sql.Clob
, Character[]
, char[]
and java.lang.String
will be persisted in a Clob. java.sql.Blob
, Byte[]
, byte[]
and Serializable
type will be persisted in a Blob.
@Lob
public String getFullText() {
return fullText;
}
@Lob
public byte[] getFullCode() {
return fullCode;
}
If the property type implements java.io.Serializable
and is not a basic type, and if the property is not annotated with @Lob
, then the Hibernate serializable
type is used.
You can also manually specify a type using the @org.hibernate.annotations.Type
and some parameters if needed. @Type.type
could be:
-
Hibernate 基本类型名(比如:
integer, string, character,date, timestamp, float, binary, serializable, object, blob
)。 -
一个 Java 类的名字,这个类属于一种默认基础类型(比如:
int, float,char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob
)。 -
一个可以序列化的 Java 类的名字。
-
一个自定义类型的类的名字。(比如:
com.illflow.type.MyCustomType
)。
If you do not specify a type, Hibernate will use reflection upon the named property and guess the correct Hibernate type. Hibernate will attempt to interpret the name of the return class of the property getter using, in order, rules 2, 3, and 4.
@org.hibernate.annotations.TypeDef
and @org.hibernate.annotations.TypeDefs
allows you to declare type definitions. These annotations can be placed at the class or package level. Note that these definitions are global for the session factory (even when defined at the class level). If the type is used on a single entity, you can place the definition on the entity itself. Otherwise, it is recommended to place the definition at the package level. In the example below, when Hibernate encounters a property of class PhoneNumer
, it delegates the persistence strategy to the custom mapping type PhoneNumberType
. However, properties belonging to other classes, too, can delegate their persistence strategy to PhoneNumberType
, by explicitly using the @Type
annotation.
注意
Package level annotations are placed in a file named package-info.java
in the appropriate package. Place your annotations before the package declaration.
@TypeDef(
name = "phoneNumber",
defaultForType = PhoneNumber.class,
typeClass = PhoneNumberType.class
)
@Entity
public class ContactDetails {
[...]
private PhoneNumber localPhoneNumber;
@Type(type="phoneNumber")
private OverseasPhoneNumber overseasPhoneNumber;
[...]
}
The following example shows the usage of the parameters
attribute to customize the 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() {
...
}
When using composite user type, you will have to express column definitions. The @Columns
has been introduced for that purpose.
@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;
...
}
By default the access type of a class hierarchy is defined by the position of the @Id
or @EmbeddedId
annotations. If these annotations are on a field, then only fields are considered for persistence and the state is accessed via the field. If there annotations are on a getter, then only the getters are considered for persistence and the state is accessed via the getter/setter. That works well in practice and is the recommended approach.
注意
The placement of annotations within a class hierarchy has to be consistent (either field or on property) to be able to determine the default access type. It is recommended to stick to one single annotation placement strategy throughout your whole application.
However in some situations, you need to:
-
force the access type of the entity hierarchy
-
override the access type of a specific entity in the class hierarchy
-
override the access type of an embeddable type
The best use case is an embeddable class used by several entities that might not use the same access type. In this case it is better to force the access type at the embeddable class level.
To force the access type on a given class, use the @Access
annotation as showed below:
@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
}
You can also override the access type of a single property while keeping the other properties standard.
@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 = ...; }
}
In this example, the default access type is FIELD
except for the orderNumber
property. Note that the corresponding field, if any must be marked as @Transient
or transient
.
@org.hibernate.annotations.AccessType
The annotation @org.hibernate.annotations.AccessType
should be considered deprecated for FIELD and PROPERTY access. It is still useful however if you need to use a custom access type.
It is sometimes useful to avoid increasing the version number even if a given property is dirty (particularly collections). You can do that by annotating the property (or collection) with @OptimisticLock(excluded=true)
.
More formally, specifies that updates to this property do not require acquisition of the optimistic lock.
The column(s) used for a property mapping can be defined using the @Column
annotation. Use it to override default values (see the JPA specification for more information on the defaults). You can use this annotation at the property level for properties that are:
-
not annotated at all
-
annotated with
@Basic
-
annotated with
@Version
-
annotated with
@Lob
-
annotated with
@Temporal
@Entity
public class Flight implements Serializable {
...
@Column(updatable = false, name = "flight_name", nullable = false, length=50)
public String getName() { ... }
The name
property is mapped to the flight_name
column, which is not nullable, has a length of 50 and is not updatable (making the property immutable).
This annotation can be applied to regular properties as well as @Id
or @Version
properties.
@Column( name="columnName"; boolean unique() default false; boolean nullable() default true; boolean insertable() default true; boolean updatable() default true; String columnDefinition() default ""; String table() default ""; int length() default 255; int precision() default 0; // decimal precision int scale() default 0; // decimal scale
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sometimes, you want the Database to do some computation for you rather than in the JVM, you might also create some kind of virtual column. You can use a SQL fragment (aka formula) instead of mapping a property into a column. This kind of property is read only (its value is calculated by your formula fragment).
@Formula("obj_length * obj_height * obj_width")
public long getObjectVolume()
The SQL fragment can be as complex as you want and even include subselects.
If a property is not annotated, the following rules apply:
-
If the property is of a single type, it is mapped as @Basic
-
Otherwise, if the type of the property is annotated as @Embeddable, it is mapped as @Embedded
-
Otherwise, if the type of the property is
Serializable
, it is mapped as@Basic
in a column holding the object in its serialized version -
Otherwise, if the type of the property is
java.sql.Clob
orjava.sql.Blob
, it is mapped as@Lob
with the appropriateLobType
<property>
元素为类定义了一个持久化的、JavaBean 风格的属性。
<property name="propertyName" column="column_name" type="typename" update="true|false" insert="true|false" formula="arbitrary SQL expression" access="field|property|ClassName" lazy="true|false" unique="true|false" not-null="true|false" optimistic-lock="true|false" generated="never|insert|always" node="element-name|@attribute-name|element/@attribute|." index="index_name" unique_key="unique_key_id" length="L" precision="P" scale="S" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typename 可以是如下几种:
-
Hibernate 基本类型名(比如:
integer, string, character,date, timestamp, float, binary, serializable, object, blob
)。 -
一个 Java 类的名字,这个类属于一种默认基础类型(比如:
int, float,char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob
)。 -
一个可以序列化的 Java 类的名字。
-
一个自定义类型的类的名字。(比如:
com.illflow.type.MyCustomType
)。
如果你没有指定类型,Hibernarte 会使用反射来得到这个名字的属性,以此来猜测正确的 Hibernate 类型。Hibernate 会按照规则 2,3,4 的顺序对属性读取器(getter方法)的返回类进行解释。然而,这还不够。 在某些情况下你仍然需要 type
属性。(比如,为了区别Hibernate.DATE
和Hibernate.TIMESTAMP
,或者为了指定一个自定义类型。)
access
属性用来让你控制 Hibernate 如何在运行时访问属性。在默认情况下,Hibernate 会使用属性的 get/set 方法对(pair)。如果你指明 access="field"
,Hibernate 会忽略 get/set 方法对,直接使用反射来访问成员变量。你也可以指定你自己的策略,这就需要你自己实现 org.hibernate.property.PropertyAccessor
接口,再在 access 中设置你自定义策略类的名字。
衍生属性(derive propertie)是一个特别强大的特征。这些属性应该定义为只读,属性值在装载时计算生成。 你用一个 SQL 表达式生成计算的结果,它会在这个实例转载时翻译成一个 SQL 查询的 SELECT
子查询语句。
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>
注意,你可以使用实体自己的表,而不用为这个特别的列定义别名(上面例子中的 customerId
)。同时注意,如果你不喜欢使用属性, 你可以使用嵌套的 <formula>
映射元素。
Embeddable objects (or components) are objects whose properties are mapped to the same table as the owning entity's table. Components can, in turn, declare their own properties, components or collections
It is possible to declare an embedded component inside an entity and even override its column mapping. Component classes have to be annotated at the class level with the @Embeddable
annotation. It is possible to override the column mapping of an embedded object for a particular entity using the @Embedded
and @AttributeOverride
annotation in the associated property:
@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; }
...
}
An embeddable object inherits the access type of its owning entity (note that you can override that using the @Access
annotation).
The Person
entity has two component properties, homeAddress
and bornIn
. homeAddress
property has not been annotated, but Hibernate will guess that it is a persistent component by looking for the @Embeddable
annotation in the Address class. We also override the mapping of a column name (to bornCountryName
) with the @Embedded
and @AttributeOverride
annotations for each mapped attribute of Country
. As you can see, Country
is also a nested component of Address
, again using auto-detection by Hibernate and JPA defaults. Overriding columns of embedded objects of embedded objects is through dotted expressions.
@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;
Hibernate Annotations supports something that is not explicitly supported by the JPA specification. You can annotate a embedded object with the @MappedSuperclass
annotation to make the superclass properties persistent (see @MappedSuperclass
for more informations).
You can also use association annotations in an embeddable object (ie @OneToOne
, @ManyToOne
, @OneToMany
or @ManyToMany
). To override the association columns you can use @AssociationOverride
.
If you want to have the same embeddable object type twice in the same entity, the column name defaulting will not work as several embedded objects would share the same set of columns. In plain JPA, you need to override at least one set of columns. Hibernate, however, allows you to enhance the default naming mechanism through the NamingStrategy
interface. You can write a strategy that prevent name clashing in such a situation. DefaultComponentSafeNamingStrategy
is an example of this.
If a property of the embedded object points back to the owning entity, annotate it with the @Parent
annotation. Hibernate will make sure this property is properly loaded with the entity reference.
In XML, use the <component>
element.
<component name="propertyName" class="className" insert="true|false" update="true|false" access="field|property|ClassName" lazy="true|false" optimistic-lock="true|false" unique="true|false" node="element-name|." > <property ...../> <many-to-one .... /> ........ </component>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
其 <property>
子标签为子类的一些属性与表字段之间建立映射。
<component>
元素允许加入一个 <parent>
子元素,在组件类内部就可以有一个指向其容器的实体的反向引用。
The <dynamic-component>
element allows a Map
to be mapped as a component, where the property names refer to keys of the map. See 第 9.5 节 “动态组件(Dynamic components)” for more information. This feature is not supported in annotations.
Java is a language supporting polymorphism: a class can inherit from another. Several strategies are possible to persist a class hierarchy:
-
Single table per class hierarchy strategy: a single table hosts all the instances of a class hierarchy
-
Joined subclass strategy: one table per class and subclass is present and each table persist the properties specific to a given subclass. The state of the entity is then stored in its corresponding class table and all its superclasses
-
Table per class strategy: one table per concrete class and subclass is present and each table persist the properties of the class and its superclasses. The state of the entity is then stored entirely in the dedicated table for its class.
With this approach the properties of all the subclasses in a given mapped class hierarchy are stored in a single table.
Each subclass declares its own persistent properties and subclasses. Version and id properties are assumed to be inherited from the root class. Each subclass in a hierarchy must define a unique discriminator value. If this is not specified, the fully qualified Java class name is used.
@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 { ... }
In hbm.xml, for the table-per-class-hierarchy mapping strategy, the <subclass>
declaration is used. For example:
<subclass name="ClassName" discriminator-value="discriminator_value" proxy="ProxyInterface" lazy="true|false" dynamic-update="true|false" dynamic-insert="true|false" entity-name="EntityName" node="element-name" extends="SuperclassName"> <property .... /> ..... </subclass>
|
|
|
|
|
|
|
|
For information about inheritance mappings see 第 10 章 继承映射(Inheritance Mapping) .
Discriminators are required for polymorphic persistence using the table-per-class-hierarchy mapping strategy. It declares a discriminator column of the table. The discriminator column contains marker values that tell the persistence layer what subclass to instantiate for a particular row. Hibernate Core supports the follwoing restricted set of types as discriminator column: string
, character
, integer
, byte
, short
, boolean
, yes_no
, true_false
.
Use the @DiscriminatorColumn
to define the discriminator column as well as the discriminator type.
注意
The enum DiscriminatorType
used in javax.persitence.DiscriminatorColumn
only contains the values STRING
, CHAR
and INTEGER
which means that not all Hibernate supported types are available via the @DiscriminatorColumn
annotation.
You can also use @DiscriminatorFormula
to express in SQL a virtual discriminator column. This is particularly useful when the discriminator value can be extracted from one or more columns of the table. Both @DiscriminatorColumn
and @DiscriminatorFormula
are to be set on the root entity (once per persisted hierarchy).
@org.hibernate.annotations.DiscriminatorOptions
allows to optionally specify Hibernate specific discriminator options which are not standardized in JPA. The available options are force
and insert
. The force
attribute is useful if the table contains rows with "extra" discriminator values that are not mapped to a persistent class. This could for example occur when working with a legacy database. If force
is set to true
Hibernate will specify the allowed discriminator values in the SELECT
query, even when retrieving all instances of the root class. The second option - insert
- tells Hibernate whether or not to include the discriminator column in SQL INSERTs
. Usually the column should be part of the INSERT
statement, but if your discriminator column is also part of a mapped composite identifier you have to set this option to false
.
提示
There is also a @org.hibernate.annotations.ForceDiscriminator
annotation which is deprecated since version 3.6. Use @DiscriminatorOptions
instead.
Finally, use @DiscriminatorValue
on each class of the hierarchy to specify the value stored in the discriminator column for a given entity. If you do not set @DiscriminatorValue
on a class, the fully qualified class name is used.
@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 { ... }
In hbm.xml, the <discriminator>
element is used to define the discriminator column or formula:
<discriminator column="discriminator_column" type="discriminator_type" force="true|false" insert="true|false" formula="arbitrary sql expression" />
|
|
|
|
|
|
|
|
|
|
鉴别器字段的实际值是根据 <class>
和 <subclass>
元素中的 discriminator-value
属性得来的。
使用 formula
属性你可以定义一个 SQL 表达式,用来判断一行数据的类型。
<discriminator
formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
type="integer"/>
Each subclass can also be mapped to its own table. This is called the table-per-subclass mapping strategy. An inherited state is retrieved by joining with the table of the superclass. A discriminator column is not required for this mapping strategy. Each subclass must, however, declare a table column holding the object identifier. The primary key of this table is also a foreign key to the superclass table and described by the @PrimaryKeyJoinColumn
s or the <key>
element.
@Entity @Table(name="CATS")
@Inheritance(strategy=InheritanceType.JOINED)
public class Cat implements Serializable {
@Id @GeneratedValue(generator="cat-uuid")
@GenericGenerator(name="cat-uuid", strategy="uuid")
String getId() { return id; }
...
}
@Entity @Table(name="DOMESTIC_CATS")
@PrimaryKeyJoinColumn(name="CAT")
public class DomesticCat extends Cat {
public String getName() { return name; }
}
注意
The table name still defaults to the non qualified class name. Also if @PrimaryKeyJoinColumn
is not set, the primary key / foreign key columns are assumed to have the same names as the primary key columns of the primary table of the superclass.
In hbm.xml, use the <joined-subclass>
element. For example:
<joined-subclass name="ClassName" table="tablename" proxy="ProxyInterface" lazy="true|false" dynamic-update="true|false" dynamic-insert="true|false" schema="schema" catalog="catalog" extends="SuperclassName" persister="ClassName" subselect="SQL expression" entity-name="EntityName" node="element-name"> <key .... > <property .... /> ..... </joined-subclass>
|
|
|
|
|
|
|
|
Use the <key>
element to declare the primary key / foreign key column. The mapping at the start of the chapter would then be re-written as:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat" table="CATS">
<id name="id" column="uid" type="long">
<generator class="hilo"/>
</id>
<property name="birthdate" type="date"/>
<property name="color" not-null="true"/>
<property name="sex" not-null="true"/>
<property name="weight"/>
<many-to-one name="mate"/>
<set name="kittens">
<key column="MOTHER"/>
<one-to-many class="Cat"/>
</set>
<joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
<key column="CAT"/>
<property name="name" type="string"/>
</joined-subclass>
</class>
<class name="eg.Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>
For information about inheritance mappings see 第 10 章 继承映射(Inheritance Mapping) .
A third option is to map only the concrete classes of an inheritance hierarchy to tables. This is called the table-per-concrete-class strategy. Each table defines all persistent states of the class, including the inherited state. In Hibernate, it is not necessary to explicitly map such inheritance hierarchies. You can map each class as a separate entity root. However, if you wish use polymorphic associations (e.g. an association to the superclass of your hierarchy), you need to use the union subclass mapping.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable { ... }
Or in hbm.xml:
<union-subclass name="ClassName" table="tablename" proxy="ProxyInterface" lazy="true|false" dynamic-update="true|false" dynamic-insert="true|false" schema="schema" catalog="catalog" extends="SuperclassName" abstract="true|false" persister="ClassName" subselect="SQL expression" entity-name="EntityName" node="element-name"> <property .... /> ..... </union-subclass>
|
|
|
|
|
|
|
|
这种映射策略不需要指定辨别标志(discriminator)字段。
For information about inheritance mappings see 第 10 章 继承映射(Inheritance Mapping) .
This is sometimes useful to share common properties through a technical or a business superclass without including it as a regular mapped entity (ie no specific table for this entity). For that purpose you can map them as @MappedSuperclass
.
@MappedSuperclass
public class BaseEntity {
@Basic
@Temporal(TemporalType.TIMESTAMP)
public Date getLastUpdate() { ... }
public String getLastUpdater() { ... }
...
}
@Entity class Order extends BaseEntity {
@Id public Integer getId() { ... }
...
}
In database, this hierarchy will be represented as an Order
table having the id
, lastUpdate
and lastUpdater
columns. The embedded superclass property mappings are copied into their entity subclasses. Remember that the embeddable superclass is not the root of the hierarchy though.
注意
Properties from superclasses not mapped as @MappedSuperclass
are ignored.
注意
The default access type (field or methods) is used, unless you use the @Access
annotation.
注意
The same notion can be applied to @Embeddable
objects to persist properties from their superclasses. You also need to use @MappedSuperclass
to do that (this should not be considered as a standard EJB3 feature though)
注意
It is allowed to mark a class as @MappedSuperclass
in the middle of the mapped inheritance hierarchy.
注意
Any class in the hierarchy non annotated with @MappedSuperclass
nor @Entity
will be ignored.
You can override columns defined in entity superclasses at the root entity level using the @AttributeOverride
annotation.
@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 {
...
}
The altitude
property will be persisted in an fld_altitude
column of table Plane
and the propulsion association will be materialized in a fld_propulsion_fk
foreign key column.
You can define @AttributeOverride
(s) and @AssociationOverride
(s) on @Entity
classes, @MappedSuperclass
classes and properties pointing to an @Embeddable
object.
In hbm.xml, simply map the properties of the superclass in the <class>
element of the entity that needs to inherit them.
While not recommended for a fresh schema, some legacy databases force your to map a single entity on several tables.
Using the @SecondaryTable
or @SecondaryTables
class level annotations. To express that a column is in a particular table, use the table
parameter of @Column
or @JoinColumn
.
@Entity
@Table(name="MainCat")
@SecondaryTables({
@SecondaryTable(name="Cat1", pkJoinColumns={
@PrimaryKeyJoinColumn(name="cat_id", referencedColumnName="id")
),
@SecondaryTable(name="Cat2", uniqueConstraints={@UniqueConstraint(columnNames={"storyPart2"})})
})
public class Cat implements Serializable {
private Integer id;
private String name;
private String storyPart1;
private String storyPart2;
@Id @GeneratedValue
public Integer getId() {
return id;
}
public String getName() {
return name;
}
@Column(table="Cat1")
public String getStoryPart1() {
return storyPart1;
}
@Column(table="Cat2")
public String getStoryPart2() {
return storyPart2;
}
}
In this example, name
will be in MainCat
. storyPart1
will be in Cat1
and storyPart2
will be in Cat2
. Cat1
will be joined to MainCat
using the cat_id
as a foreign key, and Cat2
using id
(ie the same column name, the MainCat
id column has). Plus a unique constraint on storyPart2
has been set.
There is also additional tuning accessible via the @org.hibernate.annotations.Table
annotation:
-
fetch
: If set to JOIN, the default, Hibernate will use an inner join to retrieve a secondary table defined by a class or its superclasses and an outer join for a secondary table defined by a subclass. If set toSELECT
then Hibernate will use a sequential select for a secondary table defined on a subclass, which will be issued only if a row turns out to represent an instance of the subclass. Inner joins will still be used to retrieve a secondary defined by the class and its superclasses. -
inverse
: If true, Hibernate will not try to insert or update the properties defined by this join. Default to false. -
optional
: If enabled (the default), Hibernate will insert a row only if the properties defined by this join are non-null and will always use an outer join to retrieve the properties. -
foreignKey
: defines the Foreign Key name of a secondary table pointing back to the primary table.
Make sure to use the secondary table name in the appliesto
property
@Entity
@Table(name="MainCat")
@SecondaryTable(name="Cat1")
@org.hibernate.annotations.Table(
appliesTo="Cat1",
fetch=FetchMode.SELECT,
optional=true)
public class Cat implements Serializable {
private Integer id;
private String name;
private String storyPart1;
private String storyPart2;
@Id @GeneratedValue
public Integer getId() {
return id;
}
public String getName() {
return name;
}
@Column(table="Cat1")
public String getStoryPart1() {
return storyPart1;
}
@Column(table="Cat2")
public String getStoryPart2() {
return storyPart2;
}
}
In hbm.xml, use the <join>
element.
<join table="tablename" schema="owner" catalog="catalog" fetch="join|select" inverse="true|false" optional="true|false"> <key ... /> <property ... /> ... </join>
|
|
|
|
|
|
|
|
|
|
|
|
例如,一个人(person)的地址(address)信息可以被映射到单独的表中(并保留所有属性的值类型语义):
<class name="Person"
table="PERSON">
<id name="id" column="PERSON_ID">...</id>
<join table="ADDRESS">
<key column="ADDRESS_ID"/>
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</join>
...
此特性常常对遗留数据模型有用,我们推荐表个数比类个数少,以及细粒度的领域模型。然而,在单独的继承树上切换继承映射策略是有用的,后面会解释这点。
To link one entity to an other, you need to map the association property as a to one association. In the relational model, you can either use a foreign key or an association table, or (a bit less common) share the same primary key value between the two entities.
To mark an association, use either @ManyToOne
or @OnetoOne
.
@ManyToOne
and @OneToOne
have a parameter named targetEntity
which describes the target entity name. You usually don't need this parameter since the default value (the type of the property that stores the association) is good in almost all cases. However this is useful when you want to use interfaces as the return type instead of the regular entity.
Setting a value of the cascade
attribute to any meaningful value other than nothing will propagate certain operations to the associated object. The meaningful values are divided into three categories.
-
basic operations, which include:
persist, merge, delete, save-update, evict, replicate, lock and refresh
; -
special values:
delete-orphan
orall
; -
comma-separated combinations of operation names:
cascade="persist,merge,evict"
orcascade="all,delete-orphan"
. See 第 11.11 节 “传播性持久化(transitive persistence)” for a full explanation. Note that single valued many-to-one associations do not support orphan delete.
By default, single point associations are eagerly fetched in JPA 2. You can mark it as lazily fetched by using @ManyToOne(fetch=FetchType.LAZY)
in which case Hibernate will proxy the association and load it when the state of the associated entity is reached. You can force Hibernate not to use a proxy by using @LazyToOne(NO_PROXY)
. In this case, the property is fetched lazily when the instance variable is first accessed. This requires build-time bytecode instrumentation. lazy="false" specifies that the association will always be eagerly fetched.
With the default JPA options, single-ended associations are loaded with a subsequent select if set to LAZY
, or a SQL JOIN is used for EAGER
associations. You can however adjust the fetching strategy, ie how data is fetched by using @Fetch
. FetchMode
can be SELECT
(a select is triggered when the association needs to be loaded) or JOIN
(use a SQL JOIN to load the association while loading the owner entity). JOIN
overrides any lazy attribute (an association loaded through a JOIN
strategy cannot be lazy).
An ordinary association to another persistent class is declared using a
-
@ManyToOne
if several entities can point to the the target entity -
@OneToOne
if only a single entity can point to the the target entity
and a foreign key in one table is referencing the primary key column(s) of the target table.
@Entity
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
@JoinColumn(name="COMP_ID")
public Company getCompany() {
return company;
}
...
}
The @JoinColumn
attribute is optional, the default value(s) is the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column in the owned side. In this example company_id
because the property name is company
and the column id of Company is id
.
@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 {
...
}
You can also map a to one association through an association table. This association table described by the @JoinTable
annotation will contains a foreign key referencing back the entity table (through @JoinTable.joinColumns
) and a a foreign key referencing the target entity table (through @JoinTable.inverseJoinColumns
).
@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;
}
...
}
注意
You can use a SQL fragment to simulate a physical join column using the @JoinColumnOrFormula
/ @JoinColumnOrformulas
annotations (just like you can use a SQL fragment to simulate a property column via the @Formula
annotation).
@Entity
public class Ticket implements Serializable {
@ManyToOne
@JoinColumnOrFormula(formula="(firstname + ' ' + lastname)")
public Person getOwner() {
return person;
}
...
}
You can mark an association as mandatory by using the optional=false
attribute. We recommend to use Bean Validation's @NotNull
annotation as a better alternative however. As a consequence, the foreign key column(s) will be marked as not nullable (if possible).
When Hibernate cannot resolve the association because the expected associated element is not in database (wrong id on the association column), an exception is raised. This might be inconvenient for legacy and badly maintained schemas. You can ask Hibernate to ignore such elements instead of raising an exception using the @NotFound
annotation.
例 5.1. @NotFound annotation
@Entity
public class Child {
...
@ManyToOne
@NotFound(action=NotFoundAction.IGNORE)
public Parent getParent() { ... }
...
}
Sometimes you want to delegate to your database the deletion of cascade when a given entity is deleted. In this case Hibernate generates a cascade delete constraint at the database level.
例 5.2. @OnDelete annotation
@Entity
public class Child {
...
@ManyToOne
@OnDelete(action=OnDeleteAction.CASCADE)
public Parent getParent() { ... }
...
}
Foreign key constraints, while generated by Hibernate, have a fairly unreadable name. You can override the constraint name using @ForeignKey
.
例 5.3. @ForeignKey annotation
@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
Sometimes, you want to link one entity to an other not by the target entity primary key but by a different unique key. You can achieve that by referencing the unique key column(s) in @JoinColumn.referenceColumnName
.
@Entity
class Person {
@Id Integer personNumber;
String firstName;
@Column(name="I")
String initial;
String lastName;
}
@Entity
class Home {
@ManyToOne
@JoinColumns({
@JoinColumn(name="first_name", referencedColumnName="firstName"),
@JoinColumn(name="init", referencedColumnName="I"),
@JoinColumn(name="last_name", referencedColumnName="lastName"),
})
Person owner
}
This is not encouraged however and should be reserved to legacy mappings.
In hbm.xml, mapping an association is similar. The main difference is that a @OneToOne
is mapped as <many-to-one unique="true"/>
, let's dive into the subject.
<many-to-one name="propertyName" column="column_name" class="ClassName" cascade="cascade_style" fetch="join|select" update="true|false" insert="true|false" property-ref="propertyNameFromAssociatedClass" access="field|property|ClassName" unique="true|false" not-null="true|false" optimistic-lock="true|false" lazy="proxy|no-proxy|false" not-found="ignore|exception" entity-name="EntityName" formula="arbitrary SQL expression" node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" index="index_name" unique_key="unique_key_id" foreign-key="foreign_key_name" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Setting a value of the cascade
attribute to any meaningful value other than none
will propagate certain operations to the associated object. The meaningful values are divided into three categories. First, basic operations, which include: persist, merge, delete, save-update, evict, replicate, lock and refresh
; second, special values: delete-orphan
; and third,all
comma-separated combinations of operation names: cascade="persist,merge,evict"
or cascade="all,delete-orphan"
. See 第 11.11 节 “传播性持久化(transitive persistence)” for a full explanation. Note that single valued, many-to-one and one-to-one, associations do not support orphan delete.
一个典型的简单 many-to-one
定义例子:
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
property-ref
属性只应该用来对付遗留下来的数据库系统,可能有外键指向对方关联表的是个非主键字段(但是应该是一个惟一关键字)的情况下。这是一种十分丑陋的关系模型。比如说,假设 Product
类有一个惟一的序列号,它并不是主键。(unique
属性控制 Hibernate 通过 SchemaExport 工具进行的 DDL 生成。)
<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>
那么关于 OrderItem
的映射可能是:
<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>
当然,我们决不鼓励这种用法。
如果被引用的唯一主键由关联实体的多个属性组成,你应该在名称为 <properties>
的元素 里面映射所有关联的属性。
假若被引用的唯一主键是组件的属性,你可以指定属性路径:
<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>
The second approach is to ensure an entity and its associated entity share the same primary key. In this case the primary key column is also a foreign key and there is no extra column. These associations are always one to one.
例 5.4. One to One association
@Entity
public class Body {
@Id
public Long getId() { return id; }
@OneToOne(cascade = CascadeType.ALL)
@MapsId
public Heart getHeart() {
return heart;
}
...
}
@Entity
public class Heart {
@Id
public Long getId() { ...}
}
注意
Many people got confused by these primary key based one to one associations. They can only be lazily loaded if Hibernate knows that the other side of the association is always present. To indicate to Hibernate that it is the case, use @OneToOne(optional=false)
.
In hbm.xml, use the following mapping.
<one-to-one name="propertyName" class="ClassName" cascade="cascade_style" constrained="true|false" fetch="join|select" property-ref="propertyNameFromAssociatedClass" access="field|property|ClassName" formula="any SQL expression" lazy="proxy|no-proxy|false" entity-name="EntityName" node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" foreign-key="foreign_key_name" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
主键关联不需要额外的表字段;如果两行是通过这种一对一关系相关联的,那么这两行就共享同样的主关键字值。所以如果你希望两个对象通过主键一对一关联,你必须确认它们被赋予同样的标识值。
比如说,对下面的 Employee
和 Person
进行主键一对一关联:
<one-to-one name="person" class="Person"/>
<one-to-one name="employee" class="Employee" constrained="true"/>
现在我们必须确保 PERSON 和 EMPLOYEE 中相关的字段是相等的。我们使用一个被成为 foreign
的特殊的 hibernate 标识符生成策略:
<class name="person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="foreign">
<param name="property">employee</param>
</generator>
</id>
...
<one-to-one name="employee"
class="Employee"
constrained="true"/>
</class>
一个刚刚保存的 Person
实例被赋予和该 Person
的 employee
属性所指向的 Employee
实例同样的关键字值。
Although we recommend the use of surrogate keys as primary keys, you should try to identify natural keys for all entities. A natural key is a property or combination of properties that is unique and non-null. It is also immutable. Map the properties of the natural key as @NaturalId
or map them inside the <natural-id>
element. Hibernate will generate the necessary unique key and nullability constraints and, as a result, your mapping will be more self-documenting.
@Entity
public class Citizen {
@Id
@GeneratedValue
private Integer id;
private String firstname;
private String lastname;
@NaturalId
@ManyToOne
private State state;
@NaturalId
private String ssn;
...
}
//and later on query
List results = s.createCriteria( Citizen.class )
.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", ste ) )
.list();
Or in XML,
<natural-id mutable="true|false"/>
<property ... />
<many-to-one ... />
......
</natural-id>
我们强烈建议你实现 equals()
和 hashCode()
方法,来比较实体的自然键属性。
这一映射不是为了把自然键作为主键而准备的。
-
mutable
(可选,默认为false
):默认情况下,自然标识属性被假定为不可变的(常量)。
There is one more type of property mapping. The @Any
mapping defines a polymorphic association to classes from multiple tables. This type of mapping requires more than one column. The first column contains the type of the associated entity. The remaining columns contain the identifier. It is impossible to specify a foreign key constraint for this kind of association. This is not the usual way of mapping polymorphic associations and you should use this only in special cases. For example, for audit logs, user session data, etc.
The @Any
annotation describes the column holding the metadata information. To link the value of the metadata information and an actual entity type, The @AnyDef
and @AnyDefs
annotations are used. The metaType
attribute allows the application to specify a custom type that maps database column values to persistent classes that have identifier properties of the type specified by idType
. You must specify the mapping from values of the metaType
to class names.
@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;
}
Note that @AnyDef
can be mutualized and reused. It is recommended to place it as a package metadata in this case.
//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;
}
The hbm.xml equivalent is:
<any name="being" id-type="long" meta-type="string">
<meta-value value="TBL_ANIMAL" class="Animal"/>
<meta-value value="TBL_HUMAN" class="Human"/>
<meta-value value="TBL_ALIEN" class="Alien"/>
<column name="table_name"/>
<column name="id"/>
</any>
注意
You cannot mutualize the metadata in hbm.xml as you can in annotations.
<any name="propertyName" id-type="idtypename" meta-type="metatypename" cascade="cascade_style" access="field|property|ClassName" optimistic-lock="true|false" > <meta-value ... /> <meta-value ... /> ..... <column .... /> <column .... /> ..... </any>
|
|
|
|
|
|
|
|
|
|
|
|
<properties>
元素允许定义一个命名的逻辑分组(grouping)包含一个类中的多个属性。这个元素最重要的用处是允许多个属性的组合作为 property-ref
的目标(target)。这也是定义多字段唯一约束的一种方便途径。例如:
<properties name="logicalName" insert="true|false" update="true|false" optimistic-lock="true|false" unique="true|false" > <property ...../> <many-to-one .... /> ........ </properties>
|
|
|
|
|
|
|
|
|
|
例如,如果我们有如下的 <properties>
映射:
<class name="Person">
<id name="personNumber"/>
...
<properties name="name"
unique="true" update="false">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</properties>
</class>
然后,我们可能有一些遗留的数据关联,引用 Person
表的这个唯一键,而不是主键:
<many-to-one name="owner"
class="Person" property-ref="name">
<column name="firstName"/>
<column name="initial"/>
<column name="lastName"/>
</many-to-one>
注意
When using annotations as a mapping strategy, such construct is not necessary as the binding between a column and its related column on the associated table is done directly
@Entity
class Person {
@Id Integer personNumber;
String firstName;
@Column(name="I")
String initial;
String lastName;
}
@Entity
class Home {
@ManyToOne
@JoinColumns({
@JoinColumn(name="first_name", referencedColumnName="firstName"),
@JoinColumn(name="init", referencedColumnName="I"),
@JoinColumn(name="last_name", referencedColumnName="lastName"),
})
Person owner
}
我们并不推荐这样使用,除非在映射遗留数据的情况下。
The hbm.xml structure has some specificities naturally not present when using annotations, let's describe them briefly.
所有的 XML 映射都需要定义如上所示的 doctype。DTD 可以从上述 URL 中获取,也可以从 hibernate-x.x.x/src/org/hibernate
目录中、或 hibernate.jar
文件中找到。Hibernate 总是会首先在它的 classptah 中搜索 DTD 文件。如果你发现它是通过连接 Internet 查找 DTD 文件,就对照你的 classpath 目录检查 XML 文件里的 DTD 声明。
Hibernate 首先试图在其 classpath 中解析 DTD。这是依靠在系统中注册的 org.xml.sax.EntityResolver
的一个具体实现,SAXReader 依靠它来读取 xml 文件。这个自定义的 EntityResolver
能辨认两种不同的 systenId 命名空间:
-
a
hibernate namespace
is recognized whenever the resolver encounters a systemId starting withhttp://www.hibernate.org/dtd/
. The resolver attempts to resolve these entities via the classloader which loaded the Hibernate classes. -
若 resolver 遇到了一个使用
classpath://
URL 协议的 systemId,它会辨认出这是user namespace
,resolver 试图通过(1) 当前线程上下文的 classloader 和(2) 加载 Hibernate class 的 classloader 来查找这些实体。
下面是一个使用用户命名空间(user namespace)的例子:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
<!ENTITY types SYSTEM "classpath://your/domain/types.xml">
]>
<hibernate-mapping package="your.domain">
<class name="MyEntity">
<id name="id" type="my-custom-id-type">
...
</id>
<class>
&types;
</hibernate-mapping>
这里的 types.xml
是 your.domain
包中的一个资源,它包含了一个自定义的 typedef。
这个元素包括一些可选的属性。schema
和 catalog
属性, 指明了这个映射所连接(refer)的表所在的 schema 和/或 catalog 名称。假若指定了这个属性,表名会加上所指定的 schema 和 catalog 的名字扩展为全限定名。假若没有指定,表名就不会使用全限定名。default-cascade
指定了未明确注明 cascade
属性的 Java 属性和 集合类 Hibernate 会采取什么样的默认级联风格。auto-import
属性默认让我们在查询语言中可以使用非全限定名的类名。
<hibernate-mapping schema="schemaName" catalog="catalogName" default-cascade="cascade_style" default-access="field|property|ClassName" default-lazy="true|false" auto-import="true|false" package="package.name" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
假若你有两个持久化类,它们的非全限定名是一样的(就是两个类的名字一样,所在的包不一样 — 译者注),你应该设置 auto-import="false"
。如果你把一个“导入过”的名字同时对应两个类,Hibernate 会抛出一个异常。
注意 hibernate-mapping
元素允许你嵌套多个如上所示的 <class>
映射。但是最好的做法(也许一些工具需要的)是一个持久化类(或一个类的继承层次)对应一个映射文件,并以持久化的超类名称命名,例如:Cat.hbm.xml
、Dog.hbm.xml
,或者如果使用继承,Animal.hbm.xml
。
The <key>
element is featured a few times within this guide. It appears anywhere the parent mapping element defines a join to a new table that references the primary key of the original table. It also defines the foreign key in the joined table:
<key column="columnname" on-delete="noaction|cascade" property-ref="propertyName" not-null="true|false" update="true|false" unique="true|false" />
|
|
|
|
|
|
|
|
|
|
|
|
对那些看重删除性能的系统,我们推荐所有的键都应该定义为 on-delete="cascade"
,这样 Hibernate 将使用数据库级的 ON CASCADE DELETE
约束,而不是多个 DELETE
语句。注意,这个特性会绕过 Hibernate 通常对版本数据(versioned data)采用的乐观锁策略。
not-null
和 update
属性在映射单向一对多关联的时候有用。如果你映射一个单向一对多关联到非空的(non-nullable)外键,你必须 用 <key not-null="true">
定义此键字段。
假设你的应用程序有两个同样名字的持久化类,但是你不想在 Hibernate 查询中使用他们的全限定名。除了依赖 auto-import="true"
以外,类也可以被显式地“import(引用)”。你甚至可以引用没有被明确映射的类和接口。
<import class="java.lang.Object" rename="Universe"/>
<import class="ClassName" rename="ShortName" />
|
|
|
|
注意
This feature is unique to hbm.xml and is not supported in annotations.
任何接受 column
属性的映射元素都可以选择接受 <column>
子元素。同样的,formula
子元素也可以替换 <formula>
属性。
<column
name="column_name"
length="N"
precision="N"
scale="N"
not-null="true|false"
unique="true|false"
unique-key="multicolumn_unique_key_name"
index="index_name"
sql-type="sql_type_name"
check="SQL expression"
default="SQL expression"
read="SQL expression"
write="SQL expression"/>
<formula>SQL expression</formula>
column
上的大多数属性都提供了在自动模式生成过程中对 DDL 进行裁剪的方法。read
和 write
属性允许你指定 Hibernate 用于访问字段值的自定义的 SQL。关于更多的内容,请参考 column read and write expressions。
column
和 formula
属性甚至可以在同一个属性或关联映射中被合并来表达,例如,一些奇异的连接条件。
<many-to-one name="homeAddress" class="Address"
insert="false" update="false">
<column name="person_id" not-null="true" length="10"/>
<formula>'MAILING'</formula>
</many-to-one>
和持久化服务相比,Java 级别的对象分为两个组别:
实体entity 独立于任何持有实体引用的对象。与通常的 Java 模型相比,不再被引用的对象会被当作垃圾收集掉。实体必须被显式的保存和删除(除非保存和删除是从父实体向子实体引发的级联)。这和 ODMG 模型中关于对象通过可触及保持持久性有一些不同 — 比较起来更加接近应用程序对象通常在一个大系统中的使用方法。实体支持循环引用和交叉引用,它们也可以加上版本信息。
一个实体的持久状态包含指向其他实体和值类型实例的引用。值可以是原始类型,集合(不是集合中的对象),组件或者特定的不可变对象。与实体不同,值(特别是集合和组件)是通过可触及性来进行持久化和删除的。因为值对象(和原始类型数据)是随着包含他们的实体而被持久化和删除的,他们不能被独立的加上版本信息。值没有独立的标识,所以他们不能被两个实体或者集合共享。
直到现在,我们都一直使用术语“持久类”(persistent class)来代表实体。我们仍然会这么做。然而严格说来,不是所有的用户自定义的,带有持久化状态的类都是实体。组件就是用户自定义类,却是值语义的。java.lang.String
类型的 java 属性也是值语义的。给了这个定义以后,我们可以说所有 JDK 提供的类型(类)都是值类型的语义,而用于自定义类型可能被映射为实体类型或值类型语义。采用哪种类型的语义取决于开发人员。在领域模型中,寻找实体类的一个好线索是共享引用指向这个类的单一实例,而组合或聚合通常被转化为值类型。
我们会在本文档中重复碰到这两个概念。
挑战在于将 java 类型系统(和开发者定义的实体和值类型)映射到 SQL/数据库类型系统。Hibernate 提供了连接两个系统之间的桥梁:对于实体类型,我们使用 <class>
,<subclass>
等等。对于值类型,我们使用 <property>
,<component>
及其他,通常跟随着 type
属性。这个属性的值是Hibernate 的映射类型的名字。Hibernate 提供了许多现成的映射(标准的 JDK 值类型)。你也可以编写自己的映射类型并实现自定义的变换策略,随后我们会看到这点。
所有的 Hibernate 内建类型,除了 collections 以外,都支持空(null)语义。
内置的 basic mapping types 可以大致地分类为:
-
integer, long, short, float, double, character, byte, boolean, yes_no, true_false
-
这些类型都对应 Java 的原始类型或者其封装类,来符合(特定厂商的)SQL 字段类型。
boolean, yes_no
和true_false
都是 Java 中boolean
或者java.lang.Boolean
的另外说法。 -
string
-
从
java.lang.String
到VARCHAR
(或者 Oracle 的VARCHAR2
)的映射。 -
date, time, timestamp
-
从
java.util.Date
和其子类到 SQL 类型DATE
,TIME
和TIMESTAMP
(或等价类型)的映射。 -
calendar, calendar_date
-
从
java.util.Calendar
到 SQL 类型TIMESTAMP
和DATE
(或等价类型)的映射。 -
big_decimal, big_integer
-
从
java.math.BigDecimal
和java.math.BigInteger
到NUMERIC
(或者 Oracle 的NUMBER
类型)的映射。 -
locale, timezone, currency
-
从
java.util.Locale
,java.util.TimeZone
和java.util.Currency
到VARCHAR
(或者 Oracle 的VARCHAR2
类型)的映射。Locale
和Currency
的实例被映射为它们的 ISO 代码。TimeZone
的实例被影射为它的ID
。 -
class
-
从
java.lang.Class
到VARCHAR
(或者 Oracle 的VARCHAR2
类型)的映射。Class
被映射为它的全限定名。 -
binary
-
把字节数组(byte arrays)映射为对应的 SQL 二进制类型。
-
text
-
Maps long Java strings to a SQL
LONGVARCHAR
orTEXT
type. -
image
-
Maps long byte arrays to a SQL
LONGVARBINARY
. -
serializable
-
把可序列化的 Java 类型映射到对应的 SQL 二进制类型。你也可以为一个并非默认为基本类型的可序列化 Java 类或者接口指定 Hibernate 类型
serializable
。 -
clob, blob
-
JDBC 类
java.sql.Clob
和java.sql.Blob
的映射。某些程序可能不适合使用这个类型,因为 blob 和 clob 对象可能在一个事务之外是无法重用的。(而且, 驱动程序对这种类型的支持充满着补丁和前后矛盾。) -
materialized_clob
-
Maps long Java strings to a SQL
CLOB
type. When read, theCLOB
value is immediately materialized into a Java string. Some drivers require theCLOB
value to be read within a transaction. Once materialized, the Java string is available outside of the transaction. -
materialized_blob
-
Maps long Java byte arrays to a SQL
BLOB
type. When read, theBLOB
value is immediately materialized into a byte array. Some drivers require theBLOB
value to be read within a transaction. Once materialized, the byte array is available outside of the transaction. -
imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date, imm_serializable, imm_binary
-
一般来说,映射类型被假定为是可变的 Java 类型,只有对不可变 Java 类型,Hibernate 会采取特定的优化措施,应用程序会把这些对象作为不可变对象处理。比如,你不应该对作为
imm_timestamp
映射的 Date 执行Date.setTime()
。要改变属性的值,并且保存这一改变,应用程序必须对这一属性重新设置一个新的(不一样的)对象。
实体及其集合的唯一标识可以是除了 binary
、 blob
和 clob
之外的任何基础类型。(联合标识也是允许的,后面会说到。)
在 org.hibernate.Hibernate
中,定义了基础类型对应的 Type
常量。比如,Hibernate.STRING
代表 string
类型。
开发者创建属于他们自己的值类型也是很容易的。比如说,你可能希望持久化 java.lang.BigInteger
类型的属性,持久化成为 VARCHAR
字段。Hibernate没有内置这样一种类型。自定义类型能够映射一个属性(或集合元素)到不止一个数据库表字段。比如说,你可能有这样的 Java 属性:getName()
/setName()
,这是 java.lang.String
类型的,对应的持久化到三个字段:FIRST_NAME
,INITIAL
,SURNAME
。
要实现一个自定义类型,可以实现 org.hibernate.UserType
或 org.hibernate.CompositeUserType
中的任一个,并且使用类型的 Java 全限定类名来定义属性。请查看 org.hibernate.test.DoubleStringType
这个例子,看看它是怎么做的。
<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
<column name="first_string"/>
<column name="second_string"/>
</property>
注意使用 <column>
标签来把一个属性映射到多个字段的做法。
CompositeUserType
,EnhancedUserType
,UserCollectionType
和 UserVersionType
接口为更特殊的使用方式提供支持。
你甚至可以在一个映射文件中提供参数给一个 UserType
。 为了这样做,你的 UserType
必须实现 org.hibernate.usertype.ParameterizedType
接口。为了给自定义类型提供参数,你可以在映射文件中使用 <type>
元素。
<property name="priority">
<type name="com.mycompany.usertypes.DefaultValueIntegerType">
<param name="default">0</param>
</type>
</property>
现在,UserType
可以从传入的 Properties
对象中得到 default
参数的值。
如果你非常频繁地使用某一 UserType
,可以为他定义一个简称。这可以通过使用 <typedef>
元素来实现。Typedefs 为一自定义类型赋予一个名称,并且如果此类型是参数化的,还可以包含一系列默认的参数值。
<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
<param name="default">0</param>
</typedef>
<property name="priority" type="default_zero"/>
也可以根据具体案例通过属性映射中的类型参数覆盖在 typedef 中提供的参数。
尽管 Hibernate 内建的丰富的类型和对组件的支持意味着你可能很少 需要使用自定义类型。不过,为那些在你的应用中经常出现的(非实体)类使用自定义类型也是一个好方法。例如,一个 MonetaryAmount
类使用 CompositeUserType
来映射是不错的选择,虽然他可以很容易地被映射成组件。这样做的动机之一是抽象。使用自定义类型,以后假若你改变表示金额的方法时,它可以保证映射文件不需要修改。
对特定的持久化类,映射多次是允许的。这种情形下,你必须指定 entity name 来区别不同映射实体的对象实例。(默认情况下,实体名字和类名是相同的。) Hibernate 在操作持久化对象、编写查询条件,或者把关联映射到指定实体时,允许你指定这个 entity name(实体名字)。
<class name="Contract" table="Contracts" entity-name="CurrentContract"> ... <set name="history" inverse="true" order-by="effectiveEndDate desc"> <key column="currentContractId"/> <one-to-many entity-name="HistoricalContract"/> </set> </class> <class name="Contract" table="ContractHistory" entity-name="HistoricalContract"> ... <many-to-one name="currentContract" column="currentContractId" entity-name="CurrentContract"/> </class>
注意这里关联是如何用 entity-name
来代替 class
的。
注意
This feature is not supported in Annotations
你可通过在映射文档中使用反向引号(`)把表名或者字段名包围起来,以强制 Hibernate 在生成的 SQL 中把标识符用引号包围起来。Hibernate 会使用相应的 SQLDialect
(方言)来使用正确的引号风格(通常是双引号,但是在 SQL Server 中是括号,MySQL 中是反向引号)。
@Entity @Table(name="`Line Item`")
class LineItem {
@id @Column(name="`Item Id`") Integer id;
@Column(name="`Item #`") int itemNumber
}
<class name="LineItem" table="`Line Item`">
<id name="id" column="`Item Id`"/><generator class="assigned"/></id>
<property name="itemNumber" column="`Item #`"/>
...
</class>
Generated properties 指的是其值由数据库生成的属性。一般来说,如果对象有任何属性由数据库生成值,Hibernate 应用程序需要进行刷新(refresh)
。但如果把属性标明为 generated,就可以转由 Hibernate 来负责这个动作。实际上。对定义了 generated properties 的实体,每当 Hibernate 执行一条 SQL INSERT 或者 UPDATE 语句,会立刻执行一条 select 来获得生成的值。
被标明为 generated 的属性还必须是 non-insertable 和 non-updateable 的。只有 versions、timestamps 和 simple properties 可以被标明为 generated。
never
(默认)标明此属性值不是从数据库中生成。
insert
— 标明此属性值在 insert 的时候生成,但是不会在随后的 update 时重新生成。比如说创建日期就归属于这类。注意虽然 version 和 timestamp 属性可以被标注为 generated,但是不适用这个选项。
always
— 标明此属性值在 insert 和 update 时都会被生成。
To mark a property as generated, use @Generated
.
Hibernate allows you to customize the SQL it uses to read and write the values of columns mapped to simple properties. For example, if your database provides a set of data encryption functions, you can invoke them for individual columns like this:
@Entity
class CreditCard {
@Column(name="credit_card_num")
@ColumnTransformer(
read="decrypt(credit_card_num)",
write="encrypt(?)")
public String getCreditCardNumber() { return creditCardNumber; }
public void setCreditCardNumber(String number) { this.creditCardNumber = number; }
private String creditCardNumber;
}
or in XML
<property name="creditCardNumber">
<column
name="credit_card_num"
read="decrypt(credit_card_num)"
write="encrypt(?)"/>
</property>
注意
You can use the plural form @ColumnTransformers
if more than one columns need to define either of these rules.
If a property uses more that one column, you must use the forColumn
attribute to specify which column, the expressions are targeting.
@Entity
class User {
@Type(type="com.acme.type.CreditCardType")
@Columns( {
@Column(name="credit_card_num"),
@Column(name="exp_date") } )
@ColumnTransformer(
forColumn="credit_card_num",
read="decrypt(credit_card_num)",
write="encrypt(?)")
public CreditCard getCreditCard() { return creditCard; }
public void setCreditCard(CreditCard card) { this.creditCard = card; }
private CreditCard creditCard;
}
每当属性在查询里被引用时,Hibernate 都自动应用自定义的表达式。这种功能和 derived-property formula
相似,但有两个不同的地方:
-
属性由一个或多个属性组成,它作为自动模式生成的一部分导出。
-
属性是可读写的,非只读的。
如果指定了 write
表达式,它必须只包含一个“?”占位符。
允许 CREATE 和 DROP 任意数据库对象,与 Hibernate 的 schema 交互工具组合起来,可以提供在 Hibernate 映射文件中完全定义用户 schema 的能力。虽然这是为创建和销毁 trigger(触发器)或stored procedure(存储过程)等特别设计的,实际上任何可以在 java.sql.Statement.execute()
方法中执行的 SQL 命令都可以在此使用(比如ALTER, INSERT,等等)。本质上有两种模式来定义辅助数据库对象...
第一种模式是在映射文件中显式声明 CREATE 和 DROP 命令:
<hibernate-mapping>
...
<database-object>
<create>CREATE TRIGGER my_trigger ...</create>
<drop>DROP TRIGGER my_trigger</drop>
</database-object>
</hibernate-mapping>
第二种模式是提供一个类,这个类知道如何组织 CREATE 和 DROP 命令。这个特别类必须实现 org.hibernate.mapping.AuxiliaryDatabaseObject
接口。
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
</database-object>
</hibernate-mapping>
还有,这些数据库对象可以特别指定为仅在特定的方言中才使用。
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
<dialect-scope name="org.hibernate.dialect.Oracle9iDialect"/>
<dialect-scope name="org.hibernate.dialect.Oracle10gDialect"/>
</database-object>
</hibernate-mapping>
注意
This feature is not supported in Annotations
版权 © 2004 Red Hat, Inc.