风流的鞭炮 · 一对多关系 - EF Core | ...· 1 月前 · |
玩命的牛肉面 · 二十六: 数据库的设计规范 - 哈哈丶丶 ...· 5 月前 · |
旅行中的打火机 · 掌握 Core Data 中的关系:基础 ...· 6 月前 · |
火星上的单杠 · Hibernate Annotations· 11 月前 · |
细心的苹果 · 数据库er图设计_数据库er图_mysql数 ...· 1 年前 · |
旅行中的包子 · 杨秀萍秘书长出席中国—中南半岛国际经济走廊( ...· 2 天前 · |
胆小的青椒 · -上海交通大学中国城市治理研究院· 1 月前 · |
发怒的洋葱 · 深圳技术大学附属学校(光明)怎么样(简介+课 ...· 2 月前 · |
体贴的水煮肉 · [squid-users] Squid ...· 2 月前 · |
勤奋的毛衣 · C++对类中字符串成员进行初始化的两种方法以 ...· 5 月前 · |
hibernate 对象关系映射 passport 实体关系模型 |
https://docs.jboss.org/hibernate/annotations/3.4/reference/zh_cn/html_single/ |
火星上的单杠
11 月前 |
Translator(s): RedSaga Translate Team 满江红翻译团队 <[email protected]>
表 1. Hibernate Annotation v3翻译团队
序号 | 标题 | 中文标题 | 翻译 | 1审 | 2审 |
---|---|---|---|---|---|
-- | Contents | 目录 | Liu Chang | ||
#1 | Setting up an annotations projec | 创建一个注解项目 | melthaw | Zheng Shuai | superq |
#2 | Entity Beans-Introduction | 实体Bean-简介 | melthaw | Zheng Shuai | superq |
#3 | Entity Beans-Mapping with EJB3 Annotations | 实体Bean-用EJB3注解进行映射 | melthaw | Zheng Shuai | superq, Liu Chang, Sean Chan |
#4 | Entity Beans-Mapping Queries | 实体Bean-映射查询 | melthaw | Zheng Shuai | superq, Liu Chang, Sean Chan |
#5 | Entity Beans-Hibernate Annotation Extensions | 实体Bean-Hibernate独有的注解扩展 | Sean Chan | morning | melthaw |
#6 | Overriding metadata through XML | 通过XML覆写元数据 | icess | melthaw | Sean Chan |
#7 | Hibernate Validator | Hibernate验证器 | DigitalSonic | morning | melthaw |
#8 | Hibernate Lucene Integration | Hibernate与Lucene集成 | mochow | morning | melthaw |
#9 | Appendix:Glossary | 附录:术语表 | mochow | Liu Chang | 曹晓钢 |
首先就是设置classpath(当然是在IDE中创建了一个新项目之后)。
我们推荐在一个包装器(wrapper)类 HibernateUtil 的静态初始化代码块中启动Hibernate。或许你在Hibernate文档的其他很多地方看到过这个类, 但是要在你的项目中使用注解,还需要对这个辅助(helper)类进行扩展。扩展如下:
package hello;
import org.hibernate.*;
import org.hibernate.cfg.*;
import test.*;
import test.animals.Dog;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new AnnotationConfiguration().buildSessionFactory();
} catch (Throwable ex) {
// Log exception!
throw new ExceptionInInitializerError(ex);
public static Session getSession()
throws HibernateException {
return sessionFactory.openSession();
这里比较有意思的是使用到了 AnnotationConfiguration 类。 在XML配置文件(通常是 hibernate.cfg.xml )中则定义了包和经过注解的类。下面的xml和前面的声明等价:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <mapping package="test.animals"/> <mapping class="test.Flight"/> <mapping class="test.Sky"/> <mapping class="test.Person"/> <mapping class="test.animals.Dog"/> <mapping resource="test/animals/orm.xml"/> </session-factory> </hibernate-configuration>
注意现在你可以混合使用hbm.xml和注解。资源元素(resource element)可以是hbm文件也可以是EJB3 XML发布描述符,此差别对于配置过程是透明的。
除了上面的方式,你还可以通过编程的方式定义包括注解的类和包
sessionFactory = new AnnotationConfiguration()
.addPackage("test.animals") //the fully qualified package name
.addAnnotatedClass(Flight.class)
.addAnnotatedClass(Sky.class)
.addAnnotatedClass(Person.class)
.addAnnotatedClass(Dog.class)
.buildSessionFactory();
你也可以使用Hibernate Entity Manager来完成以上功能。Hibernate Entity Manager有自己的一套配置机制,详情请参考相关文档。
除了启动方式和配置文件有所改变之外,结合注解来使用Hibernate API和以前没有什么区别, 在其他方面你还是可以继续保持以前的习惯和喜好( hibernate.properties , hibernate.cfg.xml , programmatic APIs等等)。 甚至对于同一个 SessionFactory ,你都可以混合带注解的持久类以及传统的bm.cfg.xml声明方式。 然而你不能多次声明同一个类(要么通过注解要么通过hbm.xml配置文件), 而且在一个映射实体的类继承层次中,这两个配置策略不能同时使用.
为了简化从hbm文件到注解的迁移过程, 配置机制将自动检测在注解和hbm文件中重复的映射。 默认情况下hbm文件中的声明比类中的注解元数据具有更高的优先级。 这种优先级的设定是以类为单位的。 你也可以通过 hibernate.mapping.precedence 修改这种优先级。 默认的值是 hbm, class , 如果改为 class,hbm ,当发生冲突的时候,类中的注解将比hbm文件具有更高的优先级。
每一个持久化POJO类都是一个实体bean,这可以通过在类的定义中使用 @Entity 注解来进行声明:
@Entity public class Flight implements Serializable { Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; }
@Entity @Table(name="tbl_sky") public class Sky implements Serializable {
@Table(name="tbl_sky", uniqueConstraints = {@UniqueConstraint(columnNames={"month", "day"})} )
上面这个例子中,在month和day这两个字段上定义唯一约束. 注意 columnNames 数组中的值指的是逻辑列名.
Hibernate在NamingStrategy的实现中定义了逻辑列名. 默认的EJB3命名策略将物理字段名当作逻辑字段名来使用. 注意该字段名和它对应的属性名可能不同(如果字段名是显式指定的话). 除非你重写了NamingStrategy,否则不用担心这些区别..你可以在实体bean中使用 @Version 注解,通过这种方式可添加对乐观锁定的支持:
@Entity public class Flight implements Serializable { @Version @Column(name="OPTLOCK") public Integer getVersion() { ... } }
上面这个例子中,version属性将映射到 OPTLOCK 列, entity manager使用该字段来检测更新冲突(防止更新丢失,请参考last-commit-wins策略).
根据EJB3规范,version列可以是numeric类型(推荐方式)也可以是timestamp类型. Hibernate支持任何自定义类型,只要该类型实现了 UserVersionType .
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
推荐的替代方案是使用EJB-QL或者Criteria查询的投影(projection)功能.
public String getFullText() { return fullText; public byte[] getFullCode() { return fullCode;
如果某个属性实现了 java.io.Serializable 同时也不是基本类型, 并且没有在该属性上使用 @Lob 注解, 那么Hibernate将使用自带的 serializable 类型.
使用 @Column 注解可将属性映射到列. 使用该注解来覆盖默认值(关于默认值请参考EJB3规范). 在属性级使用该注解的方式如下:
@Entity public class Flight implements Serializable { @Column(updatable = false, name = "flight_name", nullable = false, length=50) public String getName() { ... }
在上面这个例子中, name 属性映射到 flight_name 列. 该字段不允许为空,长度为50,并且是不可更新的(也就是属性值是不变的).
上面这些注解可以被应用到正规属性上例如 @Id 或 @Version 属性.
@Column( name="columnName"; (1) boolean unique() default false; (2) boolean nullable() default true; (3) boolean insertable() default true; (4) boolean updatable() default true; (5) String columnDefinition() default ""; (6) String table() default ""; (7) int length() default 255; (8) int precision() default 0; // decimal precision (9) int scale() default 0; // decimal scale
(1) |
name 可选,列名(默认值是属性名) |
(2) |
unique 可选,是否在该列上设置唯一约束(默认值false) |
(3) |
nullable 可选,是否设置该列的值可以为空(默认值false) |
(4) |
insertable 可选,该列是否作为生成的insert语句中的一个列(默认值true) |
(5) |
updatable 可选,该列是否作为生成的update语句中的一个列(默认值true) |
(6) |
columnDefinition 可选: 为这个特定列覆盖SQL DDL片段 (这可能导致无法在不同数据库间移植) |
(7) |
table 可选,定义对应的表(默认为主表) |
(8) |
length 可选,列长度(默认值255) |
(8) |
precision 可选,列十进制精度(decimal precision)(默认值0) |
(10) |
scale 可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0) |
@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; }
嵌入式对象继承其所属实体中定义的访问类型 (注意:这可以通过使用Hibernate提供的 @AccessType 注解来覆盖原有值)(请参考 Hibernate Annotation Extensions ).
在上面的例子中,实体bean Person 有两个组件属性, 分别是 homeAddress 和 bornIn . 我们可以看到 homeAddress 属性并没有注解. 但是Hibernate自动检测其对应的Address类中的 @Embeddable 注解, 并将其看作一个持久化组件.对于Country中已映射的属性, 则使用 @Embedded 和 @AttributeOverride 注解来覆盖原来映射的列名. 正如你所看到的, Address 对象中还内嵌了 Country 对象, 这里和 homeAddress 一样使用了Hibernate和EJB3自动检测机制. 目前EJB3规范还不支持覆盖多层嵌套(即嵌入式对象中还包括其他嵌入式对象)的列映射. 不过Hibernate通过在表达式中使用"."符号表达式提供了对此特征的支持.
@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注解支持很多EJB3规范中没有明确定义的特性. 例如,可以在嵌入式对象上添加 @MappedSuperclass 注解, 这样可以将其父类的属性持久(详情请查阅 @MappedSuperclass ).
Hibernate现在支持在嵌入式对象中使用关联注解(如 @*ToOne 和 @*ToMany ). 而EJB3规范尚不支持这样的用法。你可以使用 @AssociationOverride 注解来覆写关联列.
在同一个实体中使用两个同类型的嵌入对象, 其默认列名是无效的:至少要对其中一个进行明确声明. Hibernate在这方面走在了EJB3规范的前面, Hibernate提供了 NamingStrategy , 在使用Hibernate时, 通过 NamingStrategy 你可以对默认的机制进行扩展. DefaultComponentSafeNamingStrategy 在默认的EJB3NamingStrategy上进行了小小的提升, 允许在同一实体中使用两个同类型的嵌入对象而无须额外的声明.
和EJB3规范相比,Hibernate提供了更多的id生成器.详情请查阅 Hibernate Annotation Extensions .
下面的例子展示了使用SEQ_STORE配置的sequence生成器
@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE") public Integer getId() { ... }
下面这个例子使用的是identity生成器
@Id @GeneratedValue(strategy=GenerationType.IDENTITY) public Long getId() { ... }
AUTO 生成器适用于可移植的应用(在多个DB间切换). 多个 @Id 可以共享同一个identifier生成器,只要把generator属性设成相同的值就可以了. 通过 @SequenceGenerator 和 @TableGenerator ,你可以配置不同的identifier生成器. 每一个identifier生成器都有自己的适用范围,可以是应用级(application level)和类一级(class level). 类一级的生成器在外部是不可见的, 而且类一级的生成器可以覆盖应用级的生成器. 应用级的生成器则定义在包一级(package level)(如 package-info.java ):
@javax.persistence.TableGenerator( name="EMP_GEN", table="GENERATOR_TABLE", pkColumnName = "key", valueColumnName = "hi" pkColumnValue="EMP", allocationSize=20 @javax.persistence.SequenceGenerator( name="SEQ_GEN", sequenceName="my_sequence" package org.hibernate.test.metadata; 如果在org.hibernate.test.metadata包下面的 package-info.java文件用于初始化EJB配置, 那么该文件中定义的 EMP_GEN 和SEQ_GEN都是应用级的生成器. EMP_GEN定义了一个使用hilo算法 (max_lo为20)的id生成器(该生成器将id的信息存在数据库的某个表中.). id的hi值保存在GENERATOR_TABLE中. 在该表中 pkColumnName"key"等价于 pkColumnValue "EMP", 而valueColumnName "hi"中存储的是下一个要使用的最大值.SEQ_GEN则定义了一个sequence 生成器, 其对应的sequence名为 my_sequence. 注意目前Hibernate Annotations还不支持sequence 生成器中的 initialValue和 allocationSize参数.
下面这个例子展示了定义在类范围(class scope)的sequence生成器:
@Entity @javax.persistence.SequenceGenerator( name="SEQ_STORE", sequenceName="my_sequence" public class Store implements Serializable { private Long id; @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE") public Long getId() { return id; }在这个例子中,Store类使用名为my_sequence的sequence,并且SEQ_STORE 生成器对于其他类是不可见的. 注意在org.hibernate.test.metadata.id包下的测试代码有更多演示Hibernate Annotations用法的例子..
下面是定义组合主键的几种语法:
- 将组件类注解为@Embeddable,并将组件的属性注解为@Id 将组件的属性注解为@EmbeddedId 将类注解为@IdClass,并将该实体中所有属于主键的属性都注解为@Id
对于EJB2的开发人员来说 @IdClass是很常见的, 但是对于Hibernate的用户来说就是一个崭新的用法. 组合主键类对应了一个实体类中的多个字段或属性, 而且主键类中用于定义主键的字段或属性和 实体类中对应的字段或属性在类型上必须一致.下面我们看一个例子:
@Entity @IdClass(FootballerPk.class) public class Footballer { //part of the id key @Id public String getFirstname() { return firstname; public void setFirstname(String firstname) { this.firstname = firstname; //part of the id key @Id public String getLastname() { return lastname; public void setLastname(String lastname) { this.lastname = lastname; public String getClub() { return club; public void setClub(String club) { this.club = club; //appropriate equals() and hashCode() implementation @Embeddable public class FootballerPk implements Serializable { //same name and type as in Footballer public String getFirstname() { return firstname; public void setFirstname(String firstname) { this.firstname = firstname; //same name and type as in Footballer public String getLastname() { return lastname; public void setLastname(String lastname) { this.lastname = lastname; //appropriate equals() and hashCode() implementation如上, @IdClass指向对应的主键类.
Hibernate支持在组合标识符中定义关联(就像使用普通的注解一样),而EJB3规范并不支持此类用法.
@Entity @AssociationOverride( name="id.channel", joinColumns = @JoinColumn(name="chan_id") ) public class TvMagazin { @EmbeddedId public TvMagazinPk id; @Temporal(TemporalType.TIME) Date time; @Embeddable public class TvMagazinPk implements Serializable { @ManyToOne public Channel channel; public String name; @ManyToOne public Presenter presenter;
目前还不支持在接口上进行注解.
@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class Flight implements Serializable { 这种策略支持双向的一对多关联. 这里不支持IDENTITY生成器策略,因为id必须在多个表间共享. 当然,一旦使用这种策略就意味着你不能使用 AUTO 生成器和IDENTITY生成器.
整个继承层次结构中的父类和子类的所有属性都映射到同一个表中, 他们的实例通过一个辨别符(discriminator)列来区分.:
@Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn( name="planetype", discriminatorType=DiscriminatorType.STRING @DiscriminatorValue("Plane") public class Plane { ... } @Entity @DiscriminatorValue("A320") public class A320 extends Plane { ... }
当每个子类映射到一个表时, @PrimaryKeyJoinColumn 和 @PrimaryKeyJoinColumns 注解定义了每个子类表关联到父类表的主键:
@Entity @Inheritance(strategy=InheritanceType.JOINED) public class Boat implements Serializable { ... } @Entity public class Ferry extends Boat { ... } @Entity @PrimaryKeyJoinColumn(name="BOAT_ID") public class AmericaCupClass extends Boat { ... }
@MappedSuperclass public class BaseEntity { @Basic @Temporal(TemporalType.TIMESTAMP) public Date getLastUpdate() { ... } public String getLastUpdater() { ... } @Entity class Order extends BaseEntity { @Id public Integer getId() { ... } }
你可以通过 @AttributeOverride 注解覆盖实体父类中的定义的列. 这个注解只能在继承层次结构的顶端使用.
@MappedSuperclass public class FlyingObject implements Serializable { public int getAltitude() { return altitude; @Transient public int getMetricAltitude() { return metricAltitude; @ManyToOne public PropulsionType getPropulsion() { return metricAltitude; @Entity @AttributeOverride( name="altitude", column = @Column(name="fld_altitude") ) @AssociationOverride( name="propulsion", joinColumns = @JoinColumn(name="fld_propulsion_fk") ) public class Plane extends FlyingObject { }
在上面这个例子中, altitude 属性的值最终将持久化到 Plane 表的 fld_altitude 列.而名为propulsion的关联则保存在 fld_propulsion_fk 外间列.
@Entity public class Body { public Long getId() { return id; } @OneToOne(cascade = CascadeType.ALL) @PrimaryKeyJoinColumn public Heart getHeart() { return heart; @Entity public class Heart { public Long getId() { ...}
上面的例子通过使用注解 @PrimaryKeyJoinColumn 定义了一对一关联.
@Entity public class Customer implements Serializable { @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name="passport_fk") public Passport getPassport() { @Entity public class Passport implements Serializable { @OneToOne(mappedBy = "passport") public Customer getOwner() {
The third possibility (using an association table) is very exotic.
@Entity public class Customer implements Serializable { @OneToOne(cascade = CascadeType.ALL) @JoinTable(name = "CustomerPassports", joinColumns = @JoinColumn(name="customer_fk"), inverseJoinColumns = @JoinColumn(name="passport_fk") public Passport getPassport() { @Entity public class Passport implements Serializable { @OneToOne(mappedBy = "passport") public Customer getOwner() {
在实体属性一级使用 @ManyToOne 注解来定义多对一关联:
@Entity() public class Flight implements Serializable { @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} ) @JoinColumn(name="COMP_ID") public Company getCompany() { return company;
@Entity() public class Flight implements Serializable { @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=CompanyImpl.class ) @JoinColumn(name="COMP_ID") public Company getCompany() { return company; public interface Company {
@Entity() public class Flight implements Serializable { @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} ) @JoinTable(name="Flight_Company", joinColumns = @JoinColumn(name="FLIGHT_ID"), inverseJoinColumns = @JoinColumn(name="COMP_ID") public Company getCompany() { return company;
你可以对 Collection , List (指有序列表, 而不是索引列表), Map 和 Set 这几种类型进行映射. EJB3规范定义了怎么样使用 @javax.persistence.OrderBy 注解来对有序列表进行映射: 该注解接受的参数格式:用逗号隔开的(目标实体)属性名及排序指令, 如 firstname asc, age desc ,如果该参数为空,则默认以id对该集合进行排序. 如果某个集合在数据库中对应一个关联表(association table)的话,你不能在这个集合属性上面使用@OrderBy注解. 对于这种情况的处理方法,请参考 Hibernate Annotation Extensions . EJB3 允许你利用目标实体的一个属性作为Map的key, 这个属性可以用 @MapKey(name="myProperty") 来声明. 如果使用 @MapKey 注解的时候不提供属性名, 系统默认使用目标实体的主键. map的key使用和属性相同的列:不需要为map key定义专用的列,因为map key实际上就表达了一个目标属性。 注意一旦加载,key不再和属性保持同步, 也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新 (请参考 Hibernate Annotation Extensions ). 很多人被 <map> 和 @MapKey 弄糊涂了。 其他它们有两点区别. @MapKey 目前还有一些限制,详情请查看论坛或者 我们的JIRA缺陷系统。 注意一旦加载,key不再和属性保持同步, 也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新. (Hibernate 3中Map支持的方式在当前的发布版中还未得到支持).
Hibernate将集合分以下几类.
表 2.1. 集合语义
语义 | Java实现类 | 注解 |
---|---|---|
Bag 语义 | java.util.List, java.util.Collection | @org.hibernate.annotations.CollectionOfElements 或 @OneToMany 或 @ManyToMany |
List 语义 | java.util.List | (@org.hibernate.annotations.CollectionOfElements 或 @OneToMany 或 @ManyToMany) @org.hibernate.annotations.IndexColumn |
Set 语义 | java.util.Set | @org.hibernate.annotations.CollectionOfElements 或 @OneToMany 或 @ManyToMany |
Map 语义 | java.util.Map | (@org.hibernate.annotations.CollectionOfElements 或 @OneToMany 或 @ManyToMany) @org.hibernate.annotations.MapKey/MapKeyManyToMany(支持真正的map), @javax.persistence.MapKey |
EJB3规范不支持原始类型,核心类型,嵌入式对象的集合.但是Hibernate对此提供了支持 (详情参考 Hibernate Annotation Extensions ).
@Entity public class City { @OneToMany(mappedBy="city") @OrderBy("streetName") public List<Street> getStreets() { return streets; @Entity public class Street { public String getStreetName() { return streetName; @ManyToOne public City getCity() { return city; @Entity public class Software { @OneToMany(mappedBy="software") @MapKey(name="codeName") public Map<String, Version> getVersions() { return versions; @Entity @Table(name="tbl_version") public class Version { public String getCodeName() {...} @ManyToOne public Software getSoftware() { ... } }
上面这个例子中, City 中包括了以 streetName 排序的 Street 的集合. 而 Software 中包括了以 codeName 作为 key和以 Version 作为值的Map.
除非集合为generic类型,否则你需要指定 targetEntity . 这个注解属性接受的参数为目标实体的class.
在属性级使用 @OneToMany 注解可定义一对多关联.一对多关联可以是双向关联.
在EJB3规范中多对一这端几乎总是双向关联中的主体(owner)端, 而一对多这端的关联注解为 @OneToMany( mappedBy=... )
@Entity public class Troop { @OneToMany(mappedBy="troop") public Set<Soldier> getSoldiers() { @Entity public class Soldier { @ManyToOne @JoinColumn(name="troop_fk") public Troop getTroop() { }
Troop 通过 troop 属性和 Soldier 建立了一对多的双向关联. 在 mappedBy 端不必也不能再定义任何物理映射
@Entity public class Troop { @OneToMany @JoinColumn(name="troop_fk") //we need to duplicate the physical information public Set<Soldier> getSoldiers() { @Entity public class Soldier { @ManyToOne @JoinColumn(name="troop_fk", insertable=false, updatable=false) public Troop getTroop() { }
通过联接表处理单向一对多关联是首选方式.这种关联通过 @JoinTable 注解来进行描述.
@Entity public class Trainer { @OneToMany @JoinTable( name="TrainedMonkeys", joinColumns = @JoinColumn( name="trainer_id"), inverseJoinColumns = @JoinColumn( name="monkey_id") public Set<Monkey> getTrainedMonkeys() { @Entity public class Monkey { ... //no bidir
@Entity public class Employer implements Serializable { @ManyToMany( targetEntity=org.hibernate.test.metadata.manytomany.Employee.class, cascade={CascadeType.PERSIST, CascadeType.MERGE} @JoinTable( name="EMPLOYER_EMPLOYEE", joinColumns=@JoinColumn(name="EMPER_ID"), inverseJoinColumns=@JoinColumn(name="EMPEE_ID") public Collection getEmployees() { return employees; @Entity public class Employee implements Serializable { @ManyToMany( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "employees", targetEntity = Employer.class public Collection getEmployers() { return employers;
正如前面所示,被关联端不必也不能描述物理映射: 只需要一个简单的 mappedBy 参数,该参数包含了主体端的属性名,这样就绑定双方的关系.
@Entity public class Store { @ManyToMany(cascade = CascadeType.PERSIST) public Set<City> getImplantedIn() { @Entity public class City { ... //no bidirectional relationship
上面这个例子中, Store_Table 作为联接表. Store_id 列是联接到 Store 表的外键. 而 implantedIn_id 列则联接到 City 表.
@Entity public class Store { @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) public Set<Customer> getCustomers() { @Entity public class Customer { @ManyToMany(mappedBy="customers") public Set<Store> getStores() {
在上面这个例子中, Store_Customer 作为联接表. stores_id 列是联接到 Store 表的外键, 而 customers_id 列联接到 City 表.
组合主键使用一个可嵌入的类作为主键表示,因此你需要使用 @Id 和 @Embeddable 两个注解. 还有一种方式是使用 @EmbeddedId 注解.注意所依赖的类必须实现 serializable以及实现 equals() / hashCode() 方法. 你也可以如 Mapping identifier properties 一章中描述的办法使用 @IdClass 注解.
@Entity public class RegionalArticle implements Serializable { public RegionalArticlePk getPk() { ... } @Embeddable public class RegionalArticlePk implements Serializable { ... } @Entity public class RegionalArticle implements Serializable { @EmbeddedId public RegionalArticlePk getPk() { ... } public class RegionalArticlePk implements Serializable { ... }
@Embeddable 注解默认继承了其所属实体的访问类型, 除非显式使用了Hibernate的 @AccessType 注解(这个注解不是EJB3标准的一部分). 而 @JoinColumns ,即 @JoinColumn 数组, 定义了关联的组合外键(如果不使用缺省值的话). 显式指明 referencedColumnNames 是一个好的实践方式, 否则,Hibernate认为你使用的列顺序和主键声明的顺序一致.
@Entity public class Parent implements Serializable { public ParentPk id; public int age; @OneToMany(cascade=CascadeType.ALL) @JoinColumns ({ @JoinColumn(name="parentCivility", referencedColumnName = "isMale"), @JoinColumn(name="parentLastName", referencedColumnName = "lastName"), @JoinColumn(name="parentFirstName", referencedColumnName = "firstName") public Set<Child> children; //unidirectional @Entity public class Child implements Serializable { @Id @GeneratedValue public Integer id; @ManyToOne @JoinColumns ({ @JoinColumn(name="parentCivility", referencedColumnName = "isMale"), @JoinColumn(name="parentLastName", referencedColumnName = "lastName"), @JoinColumn(name="parentFirstName", referencedColumnName = "firstName") public Parent parent; //unidirectional @Embeddable public class ParentPk implements Serializable { String firstName; String lastName;
注意上面的 referencedColumnName 显式使用方式.
javax.persistence.NamedQueries( @javax.persistence.NamedQuery(name="plane.getAll", query="select p from Plane p") package org.hibernate.test.annotations.query; @Entity @NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date") public class Night { public class MyDao { doStuff() { Query q = s.getNamedQuery("night.moreRecentThan"); q.setDate( "date", aMonthAgo ); List results = q.list();
还可以通过定义 QueryHint 数组的 hints 属性为查询提供一些hint信息.
表 2.2. Query hints
hint | description |
---|---|
org.hibernate.cacheable | 查询是否与二级缓存交互(默认值为false) |
org.hibernate.cacheRegion | 设置缓存区名称 (默认为otherwise) |
org.hibernate.timeout | 查询超时设定 |
org.hibernate.fetchSize | 所获取的结果集(resultset)大小 |
org.hibernate.flushMode | 本次查询所用的刷新模式 |
org.hibernate.cacheMode | 本次查询所用的缓存模式 |
org.hibernate.readOnly | 是否将本次查询所加载的实体设为只读(默认为false) |
org.hibernate.comment | 将查询注释添加入所生成的SQL |
@NamedNativeQuery(name="night&area", query="select night.id nid, night.night_duration, " + " night.night_date, area.id aid, night.area_id, area.name " + "from Night night, Area area where night.area_id = area.id", resultSetMapping="joinMapping") @SqlResultSetMapping(name="joinMapping", entities={ @EntityResult(entityClass=org.hibernate.test.annotations.query.Night.class, fields = { @FieldResult(name="id", column="nid"), @FieldResult(name="duration", column="night_duration"), @FieldResult(name="date", column="night_date"), @FieldResult(name="area", column="area_id"), discriminatorColumn="disc" @EntityResult(entityClass=org.hibernate.test.annotations.query.Area.class, fields = { @FieldResult(name="id", column="aid"), @FieldResult(name="name", column="name") )
@Entity @SqlResultSetMapping(name="implicit", entities=@EntityResult(entityClass=org.hibernate.test.annotations.query.SpaceShip.class)) @NamedNativeQuery(name="implicitSample", query="select * from SpaceShip", resultSetMapping="implicit") public class SpaceShip { private String name; private String model; private double speed; public String getName() { return name; public void setName(String name) { this.name = name; @Column(name="model_txt") public String getModel() { return model; public void setModel(String model) { this.model = model; public double getSpeed() { return speed; public void setSpeed(double speed) { this.speed = speed; }
@Entity @SqlResultSetMapping(name="compositekey", entities=@EntityResult(entityClass=org.hibernate.test.annotations.query.SpaceShip.class, fields = { @FieldResult(name="name", column = "name"), @FieldResult(name="model", column = "model"), @FieldResult(name="speed", column = "speed"), @FieldResult(name="captain.firstname", column = "firstn"), @FieldResult(name="captain.lastname", column = "lastn"), @FieldResult(name="dimensions.length", column = "length"), @FieldResult(name="dimensions.width", column = "width") columns = { @ColumnResult(name = "surface"), @ColumnResult(name = "volume") } ) @NamedNativeQuery(name="compositekey", query="select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as surface from SpaceShip", resultSetMapping="compositekey") public class SpaceShip { private String name; private String model; private double speed; private Captain captain; private Dimensions dimensions; public String getName() { return name; public void setName(String name) { this.name = name; @ManyToOne(fetch= FetchType.LAZY) @JoinColumns( { @JoinColumn(name="fname", referencedColumnName = "firstname"), @JoinColumn(name="lname", referencedColumnName = "lastname") public Captain getCaptain() { return captain; public void setCaptain(Captain captain) { this.captain = captain; public String getModel() { return model; public void setModel(String model) { this.model = model; public double getSpeed() { return speed; public void setSpeed(double speed) { this.speed = speed; public Dimensions getDimensions() { return dimensions; public void setDimensions(Dimensions dimensions) { this.dimensions = dimensions; @Entity @IdClass(Identity.class) public class Captain implements Serializable { private String firstname; private String lastname; public String getFirstname() { return firstname; public void setFirstname(String firstname) { this.firstname = firstname; public String getLastname() { return lastname; public void setLastname(String lastname) { this.lastname = lastname;
如果查询返回的是单个实体,或者你打算使用系统默认的映射, 这种情况下可以不使用 resultSetMapping 而是使用 resultClass 属性:
@NamedNativeQuery(name="implicitSample", query="select * from SpaceShip", resultClass=SpaceShip.class) public class SpaceShip {
@SqlResultSetMapping(name="scalar", columns=@ColumnResult(name="dimension")) @NamedNativeQuery(name="scalar", query="select length*width as dimension from SpaceShip", resultSetMapping="scalar")
本地查询中还有另外一个hint属性: org.hibernate.callable . 这个属性的布尔变量值表明这个查询是否是一个存储过程.
Hibernate 3.1 提供了多种附加的注解,这些注解可以与EJB3的实体混合/匹配使用. 他们被设计成EJB3注解的自然扩展.
为了强化EJB3的能力,Hibernate提供了与其自身特性相吻合的特殊注解. org.hibernate.annotations 包已包含了所有的这些注解扩展.
你可以在EJB3规范所能提供的能力之外,就Hibernate对实体所做的一些操作进行优化.
@org.hibernate.annotations.Where 定义了当获取类实例时所用的SQL WHERE子句(该SQL WHERE子句为可选).
@org.hibernate.annotations.Check 定义了在DDL语句中定义的合法性检查约束(该约束为可选).
@Entity @BatchSize(size=5) @org.hibernate.annotations.Entity( selectBeforeUpdate = true, dynamicInsert = true, dynamicUpdate = true, optimisticLock = OptimisticLockType.ALL, polymorphism = PolymorphismType.EXPLICIT) @Where(clause="1=1") @org.hibernate.annotations.Table(name="Forest", indexes = { @Index(name="idx", columnNames = { "name", "length" } ) } ) public class Forest { ... }
@Entity @Inheritance( strategy=InheritanceType.JOINED public class Vegetable { ... } @Entity @OnDelete(action=OnDeleteAction.CASCADE) public class Carrot extends Vegetable { ... }
@org.hibernate.annotations.GenericGenerator 允许你定义一个Hibernate特定的id生成器.
@Id @GeneratedValue(generator="system-uuid") @GenericGenerator(name="system-uuid", strategy = "uuid") public String getId() { @Id @GeneratedValue(generator="hibseq") @GenericGenerator(name="hibseq", strategy = "seqhilo", parameters = { @Parameter(name="max_lo", value = "5"), @Parameter(name="sequence", value="heybabyhey") public Integer getId() {
为支持这种行为,Hibernate引入了@AccessType注解.你可以对以下元素定义访问类型:
被注解元素的访问类型会被覆盖,若覆盖是在类一级上,则所有的属性继承访问类型. 对于根实体,其访问类型会被认为是整个继承层次中的缺省设置(可在类或属性一级覆盖).
若访问类型被标以"property",则Hibernate会扫描getter方法的注解,若访问类型被标以"field", 则扫描字段的注解.否则,扫描标为@Id或@embeddedId的元素.
@Entity public class Person implements Serializable { @Id @GeneratedValue //access type field Integer id; @Embedded @AttributeOverrides({ @AttributeOverride(name = "iso2", column = @Column(name = "bornIso2")), @AttributeOverride(name = "name", column = @Column(name = "bornCountryName")) Country bornIn; @Embeddable @AccessType("property") //override access type for all properties in Country public class Country implements Serializable { private String iso2; private String name; public String getIso2() { return iso2; public void setIso2(String iso2) { this.iso2 = iso2; @Column(name = "countryName") public String getName() { return name; public void setName(String name) { this.name = name;
有时候,你想让数据库,而非JVM,来替你完成一些计算,也可能想创建某种虚拟列. 你可以使用SQL片段(亦称为公式),而不是将属性映射到(物理)列. 这种属性是只读的(属性值由公求得).
@Formula("obj_length * obj_height * obj_width") public long getObjectVolume()
@TypeDefs( @TypeDef( name="caster", typeClass = CasterStringType.class, parameters = { @Parameter(name="cast", value="lower") package org.hibernate.test.annotations.entity; public class Forest { @Type(type="caster") public String getSmallText() {
当使用组合的用户自定义类型时,你必须自己表示列的定义. @Columns 就是为了此目的而引入的.
@Type(type="org.hibernate.test.annotations.entity.MonetaryAmountUserType") @Columns(columns = { @Column(name="r_amount"), @Column(name="r_currency") public MonetaryAmount getAmount() { return amount; public class MonetaryAmount implements Serializable { private BigDecimal amount; private Currency currency; }
通过在列属性(property)上使用 @Index 注解, 可以在特定列上定义索引,columnNames属性(attribute)将随之被忽略.
@Column(secondaryTable="Cat1") @Index(name="story1index") public String getStoryPart1() { return storyPart1; }
在嵌入式对象内部,你可以在那些指向该嵌入式对象所属元素的属性上定义该注解.
@Entity public class Person { @Embeddable public Address address; @Embeddable public class Address { @Parent public Person owner; person == person.address.owner
某些属性可以在对数据库做插入或更新操作的时候生成. Hibernate能够处理这样的属性,并触发一个后续的查询来读取这些属性.
@Entity public class Antenna { @Id public Integer id; @Generated(GenerationTime.ALWAYS) @Column(insertable = false, updatable = false) public String longitude; @Generated(GenerationTime.INSERT) @Column(insertable = false) public String latitude; 你可以将属性注解为@Generated. 但是你要注意insertability和updatability不要和你选择的生成策略冲突. 如果选择了GenerationTime.INSERT,该属性不能包含insertable列, 如果选择了GenerationTime.ALWAYS,该属性不能包含insertable和updatable列.@Version属性不可以为 @Generated(INSERT)(设计时), 只能是 NEVER或ALWAYS.
@Entity public class Child { @ManyToOne @NotFound(action=NotFoundAction.IGNORE) public Parent getParent() { ... } 有时候删除某实体的时候需要触发数据库的级联删除.@Entity public class Child { @ManyToOne @OnDelete(action=OnDeleteAction.CASCADE) public Parent getParent() { ... } }上面这个例子中,Hibernate将生成一个数据库级的级联删除约束.
EJB3为延迟加载和获取模式提供了fetch选项,而Hibernate 这方面提供了更丰富的选项集.为了更好的调整延迟加载和获取策略,Hibernate引入了 一些附加的注解:
The Hibernate annotations overrides the EJB3 fetching options.
Hibernate注解覆写了EJB3提供的获取(fetch)选项.
表 2.3. 延迟和获取选项的等效注解
Annotations Lazy Fetch @[One|Many]ToOne](fetch=FetchType.LAZY) @LazyToOne(PROXY) @Fetch(SELECT) @[One|Many]ToOne](fetch=FetchType.EAGER) @LazyToOne(FALSE) @Fetch(JOIN) @ManyTo[One|Many](fetch=FetchType.LAZY) @LazyCollection(TRUE) @Fetch(SELECT) @ManyTo[One|Many](fetch=FetchType.EAGER) @LazyCollection(FALSE) @Fetch(JOIN) @OneToMany(cascade = CascadeType.ALL) @IndexColumn(name = "drawer_position", base=1) public List<Drawer> getDrawers() { return drawers; }Hibernate Annotations还支持核心类型集合(Integer, String, Enums, ......)、 可内嵌对象的集合,甚至基本类型数组.这就是所谓的元素集合.
@Entity public class Boy { private Integer id; private Set<String> nickNames = new HashSet<String>(); private int[] favoriteNumbers; private Set<Toy> favoriteToys = new HashSet<Toy>(); private Set<Character> characters = new HashSet<Character>(); @Id @GeneratedValue public Integer getId() { return id; @CollectionOfElements public Set<String> getNickNames() { return nickNames; @CollectionOfElements @JoinTable( table=@Table(name="BoyFavoriteNumbers"), joinColumns = @JoinColumn(name="BoyId") @Column(name="favoriteNumber", nullable=false) @IndexColumn(name="nbr_index") public int[] getFavoriteNumbers() { return favoriteNumbers; @CollectionOfElements @AttributeOverride( name="element.serial", column=@Column(name="serial_nbr") ) public Set<Toy> getFavoriteToys() { return favoriteToys; @CollectionOfElements public Set<Character> getCharacters() { return characters; public enum Character { GENTLE, NORMAL, AGGRESSIVE, ATTENTIVE, VIOLENT, CRAFTY @Embeddable public class Toy { public String name; public String serial; public Boy owner; public String getName() { return name; public void setName(String name) { this.name = name; public String getSerial() { return serial; public void setSerial(String serial) { this.serial = serial; @Parent public Boy getOwner() { return owner; public void setOwner(Boy owner) { this.owner = owner; public boolean equals(Object o) { if ( this == o ) return true; if ( o == null || getClass() != o.getClass() ) return false; final Toy toy = (Toy) o; if ( !name.equals( toy.name ) ) return false; if ( !serial.equals( toy.serial ) ) return false; return true; public int hashCode() { int result; result = name.hashCode(); result = 29 * result + serial.hashCode(); return result; 在嵌入式对象的集合中,可以使用 @Parent注解嵌入式对象的某属性. 该属性指向该嵌入式对象所属的集合实体.注意
旧版的Hibernate Annotations用@OneToMany来标识集合元素. 由于语义矛盾,我们引入了@CollectionOfElements注解. 用@OneToMany来标识集合元素的这种旧有方式目前尚有效, 但是不推荐使用,而且在以后的发布版本中不再支持这种方式.
为了优化数据库访问,你可以激活所谓的Hibernate二级缓存.该缓存是可以按每个实体和集合进行配置的.
@org.hibernate.annotations.Cache定义了缓存策略及给定的二级缓存的范围. 此注解适用于根实体(非子实体),还有集合.
@Entity @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) public class Forest { ... }@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER) @JoinColumn(name="CUST_ID") @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) public SortedSet<Ticket> getTickets() { return tickets; }@Cache( CacheConcurrencyStrategy usage(); (1) String region() default ""; (2) String include() default "all"; (3) )
(1) usage: 给定缓存的并发策略(NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL)
(2) region (可选的):缓存范围(默认为类的全限定类名或是集合的全限定角色名)
(3) include (可选的):值为all时包括了所有的属性(proterty), 为non-lazy时仅含非延迟属性(默认值为all)
Hibernate具有数据过滤器的概念,可在运行期应用于一个给定的session.过滤器需要事先定义好.
现在我们来定义应用于实体或集合加载时的SQL过滤器子句.我们使用@Filter,并将其置于实体或集合元素上.
@Entity @FilterDef(name="minLength", parameters=@ParamDef( name="minLength", type="integer" ) ) @Filters( { @Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"), @Filter(name="minLength", condition=":minLength <= length") public class Forest { ... }
XML部署文件结构被设计为直接映射注释结构,所以如果你知道注释的结构,那么使用XML语法是很简单的.
你可以定义一个或者多个XML文件来描述你的元数据,这些文件会被覆写引擎合并(merged).
你可以使用XML文件来定义全局元数据,对每一个部署文件你不能定义多于一个的元数据.
<?xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd" version="1.0"> <persistence-unit-metadata> <xml-mapping-metadata-complete/> <persistence-unit-defaults> <schema>myschema</schema> <catalog>mycatalog</catalog> <cascade-persist/> </persistence-unit-defaults> </persistence-unit-metadata>xml-mapping-metadata-complete 意味着所有的实体,mapped-superclasses和嵌套的元数据应该从XML文件中启用(忽略注释).
schema / catalog 将覆写所有在元数据中默认定义的schema 和 catalog(包括XML和注释).
cascade-persist 意味着所有注释作为一个 cascade type 都是PERSIST的. 我们推荐你不要使用该特性.
<?xml version="1.0" encoding="UTF-8"?> <entity-mappings (1) xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd" version="1.0"> <package>org.hibernate.test.annotations.reflection</package> (2) <entity class="Administration" access="PROPERTY" metadata-complete="true"> (3) <table name="tbl_admin"> (4) <unique-constraint> <column-name>firstname</column-name> <column-name>lastname</column-name> </unique-constraint> </table> <secondary-table name="admin2"> (5) <primary-key-join-column name="admin_id" referenced-column-name="id"/> <unique-constraint> <column-name>address</column-name> </unique-constraint> </secondary-table> <id-class class="SocialSecurityNumber"/> (6) <inheritance strategy="JOINED"/> (7) <sequence-generator name="seqhilo" sequence-name="seqhilo"/> (8) <table-generator name="table" table="tablehilo"/> (9) </entity> <entity class="PostalAdministration"> <primary-key-join-column name="id"/> (10) </entity> </entity-mappings>
(1) entity-mappings:entity-mappings 是所有XML文件的根元素.你必须定义XML Schema, 该文件包含在hibernate-annotations.jar中,使用Hibernate Annotations 不需要访问网络.
(2) package (可选的): 作为默认的package用于在一个给定的部署描述文件中所有没有限定的类.
(3) entity: 描述一个实体.
metadata-complete 定义对于该元素是否全部使用元数据(换句话来说就是,如果注释出现在类级别应该考虑或者忽略).
一个实体不得不有一个 class 属性来引用 元数据所应用的类.
通过name属性你可以覆写实体的名字, 如果没有定义并且@Entity.name出现了的话,那么就使用该注释(假如metadata complete 没有被设置).
对于metadata complete (参考下面)元素, 你可以定义一个 access(FIELD 或者 PROPERTY(默认值)), 对于非metadata complete 元素,使用注释的access type.
(4) table: 你可以声明table 属性(name, schema, catalog), 如果没有定义, 将使用Java注释.
就象例子中所示的那样你可以定义一个或者多个unique constraints
(5) secondary-table: 定义一个secondary-table,除了你可以通过primary-key-join-column 元素定义 primary key / foreign key 列以外是和一般的table一样的. 在非metadata complete下, annotation secondary tables 仅仅在没有secondary-table 定义的情况下使用, 否则 注释将被忽略.
(6) id-class: 和@IdClass一样定义一个id class.
(7) inheritance: 定义继承策略(JOINED, TABLE_PER_CLASS, SINGLE_TABLE), 仅仅在根实体级别可以使用.
(8) sequence-generator: 定义一个序列产生器.
(9) table-generator: 定义一个table generator
(10) primary-key-join-column: 当 JOINED 继承策略使用时,为sub entities定义一个 primary key join column.
<?xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd" version="1.0"> <package>org.hibernate.test.annotations.reflection</package> <entity class="Music" access="PROPERTY" metadata-complete="true"> <discriminator-value>Generic</discriminator-value> (1) <discriminator-column length="34"/> </entity> <entity class="PostalAdministration"> <primary-key-join-column name="id"/> <named-query name="adminById"> (2) <query>select m from Administration m where m.id = :id</query> <hint name="org.hibernate.timeout" value="200"/> </named-query> <named-native-query name="allAdmin" result-set-mapping="adminrs"> (3) <query>select *, count(taxpayer_id) as taxPayerNumber from Administration, TaxPayer where taxpayer_admin_id = admin_id group by ...</query> <hint name="org.hibernate.timeout" value="200"/> </named-native-query> <sql-result-set-mapping name="adminrs"> (4) <entity-result entity-class="Administration"> <field-result name="name" column="fld_name"/> </entity-result> <column-result name="taxPayerNumber"/> </sql-result-set-mapping> <attribute-override name="ground"> (5) <column name="fld_ground" unique="true" scale="2"/> </attribute-override> <association-override name="referer"> <join-column name="referer_id" referenced-column-name="id"/> </association-override> </entity> </entity-mappings>
(1) discriminator-value / discriminator-column: 当SINGLE_TABLE继承策略使用时,定义鉴别器值 和 保存该值的列.
(2) named-query: 定义命名查询和一些相关的可能的线索. 该定义附加在注释的定义中,如果两个都定义了相同的名字,那么XML将优先考虑.
(3) named-native-query: 定义一个命名本地查询 和他的 sql result set 映射. 作为另外一种选择,你可以定义result-class. 这些定义附加在注释的定义中.如果两个定义了同样的名字,XML文件优先考虑.
(4) sql-result-set-mapping: 描述了 result set mapping 的结构. 你可以定义 实体和列映射. 这些定义附加在注释的定义中,如果定义了同样的名字,XML文件优先考虑.
(5) attribute-override / association-override: 定义一列或者join column overriding. 该overriding 附加在注释的定义中.
Hibernate验证器工作在两个层次上。第一层,它能检查内存中一个类的实例是否违反约束。 第二层,它能将约束应用于Hibernate元模型上,并将它们融入生成的数据库schema中。
Hibernate验证器有些内建约束,这些约束覆盖了大多数的基本数据检查。随后我们会看到, 你不必受制于这些内置约束,因为一分钟内就可以写出你自己的约束。
表 4.1. 内建约束
注解 应用目标 运行时检查 Hibernate元数据影响 @Length(min=, max=) 属性(String) 检查字符串长度是否符合范围 列长度会被设到最大值 @Max(value=) 属性 (以numeric或者string类型来表示一个数字) 检查值是否小于或等于最大值 对列增加一个检查约束 @Min(value=) 属性(以numeric或者string类型来表示一个数字) 检查值是否大于或等于最小值 对列增加一个检查约束 @NotNull 属性 检查值是否非空(not null) 列不为空 @Past 属性(date或calendar) 检查日期是否是过去时 对列增加一个检查约束 @Future 属性 (date 或 calendar) 检查日期是否是将来时 无 @Pattern(regex="regexp", flag=) 属性 (string) 检查属性是否与给定匹配标志的正则表达式相匹配(见 java.util.regex.Pattern ) 无 @Range(min=, max=) 属性(以numeric或者string类型来表示一个数字) 检查值是否在最小和最大值之间(包括临界值) 对列增加一个检查约束 @Size(min=, max=) 属性 (array, collection, map) 检查元素大小是否在最小和最大值之间(包括临界值) 无 @AssertFalse 属性 检查方法的演算结果是否为false(对以代码方式而不是注解表示的约束很有用) 无 @AssertTrue 属性 检查方法的演算结果是否为true(对以代码方式而不是注解表示的约束很有用) 无 @Valid 属性 (object) 对关联对象递归的进行验证。如果对象是集合或数组,就递归地验证其元素。如果对象是Map,则递归验证其值元素。 无 属性(String) 检查字符串是否符合有效的email地址规范。 无 扩展内建约束集是极其方便的。任何约束都包括两部分:约束描述符(注解) 和约束验证器(实现类)。下面是一个简单的用户定义描述符:
@ValidatorClass(CapitalizedValidator.class) @Target(METHOD) @Retention(RUNTIME) @Documented public @interface Capitalized { CapitalizeType type() default Capitalize.FIRST; String message() default "has incorrect capitalization"; }type参数描述属性应该如何被大写。这是一个完全依赖于注解业务(逻辑)的用户 参数。
message是用于描述约束违规的默认字符串,它是强制要求的。你可以采取硬编码的方式, 或者通过Java ResourceBundle机制将message的部分/全部内容提取至外部文件。一旦发现message中{parameter}字符串, 就会在{parameter}这个位置注入相应的参数值(在我们的例子里Capitalization is not {type}会生成 Capitalization is not FIRST), 可以将message对应的整个字符串提取至外部文件ValidatorMessages.properties,这也是一种良好实践。 见Error messages。
@ValidatorClass(CapitalizedValidator.class) @Target(METHOD) @Retention(RUNTIME) @Documented public @interface Capitalized { CapitalizeType type() default Capitalize.FIRST; String message() default "{validator.capitalized}"; #in ValidatorMessages.properties validator.capitalized=Capitalization is not {type}如你所见{}符号是递归的。
为了将一个描述符连接到它的验证器实现,我们使用@ValidatorClass 元注解。验证器类参数必须指定一个实现了Validator<ConstraintAnnotation> 的类。
我们现在要实现验证器(也就是实现规则检查)。一个验证器实现能检查一个属性的值 (实现PropertyConstraint),并且/或者可以修改hibernate映射元数据 (实现PersistentClassConstraint),籍此表示数据库级的约束。
public class CapitalizedValidator implements Validator<Capitalized>, PropertyConstraint { private CapitalizeType type; //part of the Validator<Annotation> contract, //allows to get and use the annotation values public void initialize(Capitalized parameters) { type = parameters.type(); //part of the property constraint contract public boolean isValid(Object value) { if (value==null) return true; if ( !(value instanceof String) ) return false; String string = (String) value; if (type == CapitalizeType.ALL) { return string.equals( string.toUpperCase() ); else { String first = string.substring(0,1); return first.equals( first.toUpperCase(); }如果违反约束,isValid()方法将返回false。更多例子请参考内建验证器实现。
至此我们只看到属性级的验证,你还可以写一个Bean级别的验证注解。Bean自身会被传递给验证器, 而不是bean的属性实例。只要对bean自身进行注解即可激活验证检查。在单元测试套件中还可以找到一个小例子。
public class Address { private String line1; private String line2; private String zip; private String state; private String country; private long id; // a not null string of 20 characters maximum @Length(max=20) @NotNull public String getCountry() { return country; // a non null string @NotNull public String getLine1() { return line1; //no constraint public String getLine2() { return line2; // a not null string of 3 characters maximum @Length(max=3) @NotNull public String getState() { return state; // a not null numeric string of 5 characters maximum // if the string is longer, the message will //be searched in the resource bundle at key 'long' @Length(max=5, message="{long}") @Pattern(regex="[0-9]+") @NotNull public String getZip() { return zip; // should always be true @AssertTrue public boolean isValid() { return true; // a numeric between 1 and 2000 @Id @Min(1) @Range(max=2000) public long getId() { return id; }上面的例子只展示了公共属性验证,你还可以对任何可见度的字段(field)进行注解。
@MyBeanConstraint(max=45) public class Dog { @AssertTrue private boolean isMale; @NotNull protected String getName() { ... }; }你可以对接口进行注解。Hibernate验证器会检查给定bean所扩展或实现的所有父类和接口, 籍以读取相应的验证器注解(信息)。
public interface Named { @NotNull String getName(); public class Dog implements Named { @AssertTrue private boolean isMale; public String getName() { ... }; }Hibernate验证器旨在实现多层数据验证,我们在一处表示约束(带注解的域模型),然后将其运用于 应用程序的不同层。
Hibernate验证器能应用于你应用程序代码中的任何地方。
ClassValidator personValidator = new ClassValidator( Person.class ); ClassValidator addressValidator = new ClassValidator( Address.class, ResourceBundle.getBundle("messages", Locale.ENGLISH) ); InvalidValue[] validationMessages = addressValidator.getInvalidValues(address);头两行为执行类检查而准备Hibernate验证器。第一行依赖于嵌入在Hibernate验证器内的错误 消息(见Error messages),第二行为这些消息准备资源包。这些代码只执行一次, 并将验证器进行缓存处理,这种方式是一种良好实践。
第三行真正验证了Address实例并返回一个InvalidValue数组。 你的应用程序逻辑随后可以对错误做出响应。
除了针对整个bean你还可以对某个特定属性进行检查。这对于一个属性一个属性的用户交互情形或许是有用的。
ClassValidator addressValidator = new ClassValidator( Address.class, ResourceBundle.getBundle("messages", Locale.ENGLISH) ); //only get city property invalid values InvalidValue[] validationMessages = addressValidator.getInvalidValues(address, "city"); //only get potential city property invalid values InvalidValue[] validationMessages = addressValidator.getPotentialInvalidValues("city", "Paris")@Entity @Indexed(index="indexes/essays") public class Essay { }Lucene索引包括四种字段:keyword 字段,text 字段,unstored字段和unindexed字段。 Hibernate注解提供了将实体属性标记为前三种被索引字段的注解。
@Entity @Indexed(index="indexes/essays") public class Essay { @Keyword(id=true) public Long getId() { return id; } @Text(name="Abstract") public String getSummary() { return summary; } @Unstored public String getText() { return text; } }这些注解定义了一个带有三个字段的索引: Id, Abstract 和 Text.
玩命的牛肉面 · 二十六: 数据库的设计规范 - 哈哈丶丶 - 博客园 5 月前 |
火星上的单杠 · Hibernate Annotations 11 月前 |
胆小的青椒 · -上海交通大学中国城市治理研究院 1 月前 |