映射的规则:所有的双向关联都需要有一端被设置为reverse,在一对多的关联中,它必须是代表多的那端,在多对多的关联中,它可以任意选取一端,因为两端之间没有差别。
1、Entity
@Entity public class Flight implements Serializable { Long id; @Id public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
实体是一个被hibernate 持久化的java 对象。
@Entity @Table(name="TBL_FLIGHT", schema="AIR_COMMAND", uniqueConstraints= @UniqueConstraint( name="flight_number", columnNames={"comp_prefix", "flight_number"} ) )
@Table是类一级的映射。可以为实体映射指定表(table),目录(catalog)和schema。结合@UniqueConstraint可以指定表的唯一约束。
2、Identifiers
<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>
大家一定要扭转一个观点,在Hibernate中,主键属性定义为基本类型,并不能够比定义为对象型效率来的高,而且也多了很多麻烦,因此建议大家使用对象型的Integer/Long定义主键。
2.1、组合标识符
2.1.1 id作为一个组件类型的属性
@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;public UserId(){};//implements equals and hashcode…}
主键类必需满足下列条件:
(1)必需被序列化
(2)必需有一个公共的无参构造方法
(3)必需实现equals()和hashCode()方法
对应的xml配置为:
<class name="User"> <composite-id name="id" class="UserId"> <key-property name="firstName" column="fld_firstname"/> <key-property name="lastName"/> </composite-id> </class>
2.2 主键生成器
hibernate提供多种主键生成策略,首先先看几种JPA支持的策略:
1)IDENTITY:supports identity columns in DB2, MySQL, MS SQL Server, Sybase and HypersonicSQL。返回的标识符是long,short和int类型。
2)SEQUENCE:需要数据库支持sequence。返回的标识符是long,short和int类型。
3)TABLE:需要特定的数据库支持。返回的标识符是long,short和int类型。
4)AUTO:根据数据库自动选择.如果没有指定,默认是该策略。
@Entity public class Customer { @Id @GeneratedValue Integer getId() { ... }; } @Entity public class Invoice { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) Integer getId() { ... }; }
SEQUENCE
和TABLE需要额外的配置,例如:
@Id @GeneratedValue( strategy=GenerationType.SEQUENCE, generator="SEQ_GEN") @javax.persistence.SequenceGenerator( name="SEQ_GEN", sequenceName="my_sequence", allocationSize=20 ) public Integer getId() { ... }
@Id @GeneratedValue( strategy=GenerationType.TABLE, generator="TABLE_GEN")@javax.persistence.TableGenerator( name="TABLE_GEN", table="GENERATOR_TABLE", pkColumnName = "key", valueColumnName = "hi" pkColumnValue="EMP", allocationSize=20 )
2.3 加强型主键生成器:
1)org.hibernate.id.enhanced.SequenceStyleGenerator
2)org.hibernate.id.enhanced.TableGenerator
加强型主键生成器的优化,不用每次请求都查询数据库。
3、主动锁属性
当使用长事务时,加入版本属性,非常有用。可以保证如果一个实体被两个会话更新,后面一个更新不会覆盖前面的更新。
@Entity public class Flight implements Serializable { ... @Version @Column(name="OPTLOCK") public Integer getVersion() { ... } }
@Entity public class Flight implements Serializable { ... @Version public Date getLastUpdate() { ... } }
4、Property
如果一个property,不是static类型,也没有标注@Transient,那么默认被认为是要持久化的。
4.1
Property注解中用到的比较多的几个是:@Transient,@Basic,@Temporal,@Lob
例子:
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) //Hibernate创造性的支持enum类型,数据库中的字段是序数或者字符串。 Starred getNote() { ... } //enum persisted as String in database@Lob public String getFullText() { return fullText; } @Lob public byte[] getFullCode() { return fullCode; }
@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 scal)
4.2
@Formula
需要在数据库中进行一些运算,可以通过@Formula创建一些虚字段,这类属性是只读的。
@Formula("obj_length * obj_height * obj_width") public long getObjectVolume()
4.3
@Embedded objects(也叫做Component)
Embeddable对象是它的属性映射到与嵌入它的类同一个表中。
可以在实体中定义一个嵌入式组件,甚至可以覆盖该实体原有的列映射。
组件类必须在类一级通过@Embeddable注解声明。在特定的实体中,通过@Embedded和@AttributeOverride注解可以覆盖该实体原有的列映射。
@Entity public class Person implements Serializable { // Persistent component using defaults Address homeAddress; @Embedded @AttributeOverrides( { @AttributeOverride(name="iso2", column = @Column(name="bornIso2") ), @AttributeOverride(name="name", column = @Column(name="bornCountryName") ) } ) Country bornIn; ... }
@Embeddable public class Address implements Serializable { String city; Country nationality; //no overriding here }
@Embeddable public class Country implements Serializable { private String iso2; @Column(name="countryName") private String name; public String getIso2() { return iso2; } public void setIso2(String iso2) { this.iso2 = iso2; } public String getName() { return name; } public void setName(String name) { this.name = name; } ... }
嵌入式组件的访问类型(Access Type)与它的属主实体一致,当然也可以通过@Access注解来改变它。
在Person实体中,虽然homeAddress没有标注@Embedded,但是hibernate会自动检测其对应的Address类的@Embeddable注解,并将其作为一个持久化属性。
4.4 继承策略
Java支持多态,所以持久化类有以下几种继承策略:
1)SINGLE_TABLE策略:所有的类,包括其继承类的实例全部对应到一张表中
2)JOINED策略:父类和子类对应不同的表,子类对应的表中只存在其扩展的特殊的属性(不包含从父类继承过来的属性)
3)TABLE_PER_CLASS策略:父类和子类都对应不同的表,子类对应的表中存在所有的属性(包含
从父类继承下来的所有属性)
4.4.1 SINGLE_TABLE策略:
每个子类都可以有自己的实体化属性及子类。Version和id属性在根类中声明。继承结构中每个子类都必须定义个唯一的discriminator value,如果没有声明,默认为java类的全限定名。
@Inheritance和@DiscrimininatorColumn在根类中声明。
@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 { ... }
对应xml配置文件中:
<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>
4.4.2 JOINED策略:
每个子类都可以映射到自己的表。该映射策略需要一个discriminator column,每个子类都要声明一个column来保存对象标识符。这个表通过@PrimaryKeyJoinColumn
s 或者<key>
元素指定的主键同时也是指向父类表的外键。
@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; } }
对应xml配置文件:
<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>
4.4.3 将一个实体映射到多个表
在新的应用中,不推荐这么做。
5、@OneToOne关联
one to one关联有三种方式:
1)关联实体享有同样的主键
2)其中一个实体通过外键关联到另外一个实体的主键
3)通过关联表来保存两个实体间的关联
相应这三种方式的设置:
1)关联实体享有同样的主键,这种方式个人感觉并不是很好的设计,耦合性太强
@Entity public class Flight implements Serializable { @OneToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} ) @PrimaryKeyJoinColumn public Company getCompany() { return company; } ... }
2)通过主外键关联:
@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 { ... }
3)通过关联表关联:
@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; } ... }