Hibernate 提供了三种方式将POJO变成PO类
- 使用持久化注解(以JPA标准注解为主,如果有一些特殊要求,则依然需要使用Hibernate本身提供的注解)
- 使用JPA2提供的XML配置描述文件,这种方式可以让Hibernate的PO类与JPA实体类兼容
- 使用Hibernate传统的XML映射文件(hbm文件)。
1 Hibernate映射主键、属性
(1)使用注解的方式
hibernate主键生成
通常情况下如果实体类的标识属性是基本数据类型、基本类型的包装类、String、Date等类型,可以简单的使用@ID修饰该实体的属性即可。
如果希望Hibernate为逻辑主键自动生成主键值,可以使用@GeneratedValue来修饰实体的标识属性。
GeneratedValue支持的属性
属性
是否必须
说明
strategy
否
指定Hibernate对该主键列使用怎么样的主键生成策略
- GenerationType.Auto:hibernate自动选择最社和底层数据库的主键生成策略,这是默认值
- GenerationType.IDENTITY:对于MySQL、SQL Sever这样的数据库,选择自增长的主键生成策略
- GenerationType.SEQUENCE:对于Oracle这样的数据库,选择使用基于sequence的主键生成策略。应与@SequenceGenerator一起使用
- GenerationType.TABLE:使用辅助表来生成主键。应与@TableGenerator一起使用
generator
否
当使用GenerationType.SEQUENCE,GenerationType.TABLE主键生成策略时,该属性引用@SequenceGenerator,@TableGenerator所定义的生成器的名称
Hibernate主键生成策略
JPA注解只支持AUTO、Identity、SEQUENCE、TABLE这4中生成策略,如果希望使用Hibernate提供的主键生成策略,就需要使用Hibernate本身的@GenericGenerator注解,该注解用于定义生成器。包括name和strategy两个属性。
stratety属性可指定的值:
-
native: 对于 oracle 采用 Sequence 方式,对于MySQL 和 SQL Server 采用identity(自增主键生成机制),native就是将主键的生成工作交由数据库完成,hibernate不管(很常用)。
-
uuid: 采用128位的uuid算法生成主键,uuid被编码为一个32位16进制数字的字符串。占用空间大(字符串类型)。
-
hilo: 使用hilo生成策略,要在数据库中建立一张额外的表,默认表名为hibernate_unique_key,默认字段为integer类型,名称是next_hi(比较少用)。
-
assigned: 在插入数据的时候主键由程序处理(很常用),这是 <generator>元素没有指定时的默认生成策略。等同于JPA中的AUTO。
-
identity: 使用SQL Server 和 MySQL 的自增字段,这个方法不能放到 Oracle 中,Oracle 不支持自增字段,要设定sequence(MySQL 和 SQL Server 中很常用)。
-
select: 使用触发器生成主键(主要用于早期的数据库主键生成机制,少用)。
-
sequence: 调用底层数据库的序列来生成主键,要设定序列名,不然hibernate无法找到。
-
seqhilo: 通过hilo算法实现,但是主键历史保存在Sequence中,适用于支持 Sequence 的数据库,如 Oracle(比较少用)
-
increment: 插入数据的时候hibernate会给主键添加一个自增的主键,但是一个hibernate实例就维护一个计数器,所以在多个实例运行的时候不能使用这个方法。
-
foreign: 使用另外一个相关联的对象的主键。通常和<one-to-one>联合起来使用。
-
guid: 采用数据库底层的guid算法机制,对应MYSQL的uuid()函数,SQL Server的newid()函数,ORACLE的rawtohex(sys_guid())函数等。
-
uuid.hex: 看uuid,建议用uuid替换。
-
sequence-identity: sequence策略的扩展,采用立即检索策略来获取sequence值,需要JDBC3.0和JDK4以上(含1.4)版本
// 消息类的标识属性 @Id @Column(name="news_id") // 使用@GenericGenerator定义主键生成器。 // 该主键生成器名为fk_hilo,使用Hibernate的hilo策略, @GenericGenerator(name="fk_hilo" , strategy="hilo") // 指定使用fk_hilo主键生成器 @GeneratedValue(generator="fk_hilo") private Integer id;
(2) 使用XML配置文件的方式
1<hibernate-mapping> 2 <class name="com.songxu.modle.Person" table="person"> 3 <id column="id" name="id" type="int"> 4 <generator class="increment"/> 5 </id> 6 <!-- column同名时可以省略 --> 7 <property column="name" generated="never" lazy="false" name="name" type="string"/> 8 <property column="age" generated="never" lazy="false" name="age" type="int"/> 9 <property column="registertime" generated="never" lazy="false" 10 name="time" type="date"/> 11 </class> 12</hibernate-mapping> 13 14 15 16<id column="id" name="id" type="string"> 17 <generator class="uuid"></generator>
2 Hibernate 映射集合属性
=====================
Hibernate映射的集合对应Java中的set list map对象,在这里面仅介绍以注解方式配置。
集合属性大致分为两种:一种是单纯的属性集合,例如List、Set或数组等集合属性;另一种是Map结构的集合属性,每个属性值都有对应的Key映射。
不管哪种类型的集合属性,都统一用@ElementCollection 注解进行映射。
在Java的所有集合中,只有Set集合是无序的,即没有显示的索引值。List、数组使用整数作为集合元素的索引值,而Map则使用key作为集合的索引,因此,如果要映射带索引的集合,即需要为集合袁术所在的数据表指定一个索引列,用于保存数组索引、List索引或者是Map集合的Key索引。
集合类型大致可分为如下几种情况:
- 集合袁术是基本类型及其包装类、字符串类型和日期类型:此时使用@ElementCollection映射集合属性,并使用普通的@Column映射集合元素对应的列。
- 集合元素是组件:此时使用@ElementCollection 映射集合属性,然后使用@Embeddable修饰非持久化实体的复合类。
- 集合元素是关联的持久化实体:此时已经不再是集合属性,应该使用@OneToMany或@ManyToMany进行关联映射
2.1 List集合属性
List是有序集合,因此持久化到数据库时也必须增加一列来表示集合的次序。
以下Person类 添加了集合属性list用于保存学校的名称
1@Id @Column(name="perosn_id") 2 @GeneratedValue(strategy=GenerationType.IDENTITY) 3 // 标识属性 4 private Integer id; 5 private String name; 6 private int age; 7 // 集合属性,保留该对象关联的学校 8 @ElementCollection(targetClass=String.class) 9 // 映射保存集合属性的表 10 @CollectionTable(name="school_inf", // 指定表名为school_inf 11 joinColumns=@JoinColumn(name="person_id" , nullable=false)) 12 // 指定保存集合元素的列为 school_name 13 @Column(name="school_name") 14 // 映射集合元素索引的列 15 @OrderColumn(name="list_order") 16 private List<String> schools 17 = new ArrayList<>();
生成的表后,List集合属性的表总是以外键列和元素索引列作为联合主键
2.2 Set集合属性
Set是无序集合,因此无需使用@OrderColumn注解映射集合元素的索引列。
1// 集合属性,保留该对象关联的学校 2 @ElementCollection(targetClass=String.class) 3 // 映射保存集合属性的表 4 @CollectionTable(name="school_inf", // 指定表名为school_inf 5 joinColumns=@JoinColumn(name="person_id" , nullable=false)) 6 // 指定保存集合元素的列为 school_name,nullable=false增加非空约束 7 @Column(name="school_name" , nullable=false) 8 private Set<String> schools 9 = new HashSet<>();
2.3Map属性
Map属性需要使用@MapKeyColumn映射保存Map Key的数据列
1// 集合属性,保留该对象关联的考试成绩 2 @ElementCollection(targetClass=Float.class) 3 // 映射保存集合属性的表 4 @CollectionTable(name="score_inf", // 指定表名为score_inf 5 joinColumns=@JoinColumn(name="person_id" , nullable=false)) 6 @MapKeyColumn(name="subject_name") 7 // 指定Map key的类型为String类型 8 @MapKeyClass(String.class) 9 // 映射保存Map value的数据列 10 @Column(name="mark") 11 private Map<String , Float> scores 12 = new HashMap<>();
虽然程序定义了Person类使用了泛型来显示Map集合的Key、Value的类型,但程序中依然通过注解强制执行Map Key MapValue 的类型,这样可以避免Hibernate通过反射去获取,从而提升了程序性能。
如果注解与Person类定义的泛型指定的类型不一致时,Hibernate将通过注解类型进行数据库表的生成工作。当插入数据时,会抛出数据类型不匹配的异常。
生成的保存Map数据的数据表将使用外键列和Map中的 key作为联合主键
2.4 组件
组件属性是指持久化类的属性不是基本数据类型,也不是字符串、日期等标量类型,而是一个复合类型的。
下面是一个组件的示例。定义了一个Name类型,使用@Embeddable注解,该注解与@Entity类似。该类包含一个owner属性,该属性指向包含该Name属性的实体。为了告诉Hibernate这个owner属性不是普通属性,而是包含Name组件的Person实体,可使用@Parent注解修饰该属性。
(1) 定义单独的组件类
1@Embeddable 2public class Name 3{ 4 // 定义first成员变量 5 @Column(name="person_firstname") 6 private String first; 7 // 定义last成员变量 8 @Column(name="person_lastname") 9 private String last; 10 // 引用拥有该Name的Person对象 11 @Parent // ① 12 private Person owner; 13 14 // 无参数的构造器 15 public Name() 16 { 17 } 18 // 初始化全部成员变量的构造器 19 public Name(String first , String last) 20 { 21 this.first = first; 22 this.last = last; 23 } 24 25 // first的setter和getter方法 26 public void setFirst(String first) 27 { 28 this.first = first; 29 } 30 public String getFirst() 31 { 32 return this.first; 33 } 34 35 // last的setter和getter方法 36 public void setLast(String last) 37 { 38 this.last = last; 39 } 40 public String getLast() 41 { 42 return this.last; 43 } 44 45 // owner的setter和getter方法 46 public void setOwner(Person owner) 47 { 48 this.owner = owner; 49 } 50 public Person getOwner() 51 { 52 return this.owner; 53 } 54 55} 56 57 58 59@Id @Column(name="person_id") 60 @GeneratedValue(strategy=GenerationType.IDENTITY) 61 private Integer id; 62 private int age; 63 // 组件属性name 64 private Name name;
(2)持久化类内部定义组件
这种方式无需使用@Embeddable注解修饰,而是直接在持久化类中使用@Embedded注解修饰组件属性
1public class Name 2{ 3 // 定义first成员变量 4 private String first; 5 // 定义last成员变量 6 private String last; 7 // 引用拥有该Name的Person对象 8 @Parent 9 private Person owner; 10 11 // 无参数的构造器 12 public Name() 13 { 14 } 15 // 初始化全部成员变量的构造器 16 public Name(String first , String last) 17 { 18 this.first = first; 19 this.last = last; 20 } 21 22 // first的setter和getter方法 23 public void setFirst(String first) 24 { 25 this.first = first; 26 } 27 public String getFirst() 28 { 29 return this.first; 30 } 31 32 // last的setter和getter方法 33 public void setLast(String last) 34 { 35 this.last = last; 36 } 37 public String getLast() 38 { 39 return this.last; 40 } 41 42 // owner的setter和getter方法 43 public void setOwner(Person owner) 44 { 45 this.owner = owner; 46 } 47 public Person getOwner() 48 { 49 return this.owner; 50 } 51 52} 53 54 55 56@Embedded 57 @AttributeOverrides({ 58 @AttributeOverride(name="first", column = @Column(name="person_firstname")), 59 @AttributeOverride(name="last", column = @Column(name="person_lastname")) 60 }) 61 private Name name;
2.5 组件属性为集合
如果组件包含了 list map set集合,可以直接在组件类中使用@ElementCollection修饰集合属性,并使用@CollectionTable指定保存集合属性的属性表。
1@Embeddable 2public class Name 3{ 4 // 定义first成员变量 5 @Column(name="person_firstname") 6 private String first; 7 // 定义last成员变量 8 @Column(name="person_lastname") 9 private String last; 10 // 引用拥有该Name的Person对象 11 @Parent 12 private Person owner; 13 // 集合属性,保留该对象关联的考试成绩 14 @ElementCollection(targetClass=Integer.class) 15 @CollectionTable(name="power_inf", 16 joinColumns=@JoinColumn(name="person_name_id" , nullable=false)) 17 @MapKeyColumn(name="name_aspect") 18 @Column(name="name_power" , nullable=false) 19 @MapKeyClass(String.class) 20 private Map<String , Integer> power 21 = new HashMap<>(); 22 23 // 无参数的构造器 24 public Name() 25 { 26 } 27}
2.6 组件作为复合主键
使用组件作为复合主键,也就是使用组件作为持久化类的标识符,则该组件类必须满足以下要求。
-
有无参数的构造器。
-
必修实现java.io.Serializabel接口。(在Hibernate4中,不是必须的)
-
建议正确地重写equals()和hashCode()方法,也就是根据组件类的关键属性来区分组件对象。(因为组件是唯一标识符,必须重写这两个方法。)
public class Name implements java.io.Serializable { // 定义first成员变量 private String first; // 定义last成员变量 private String last;
1// 无参数的构造器 2public Name() 3{ 4} 5// 初始化全部成员变量的构造器 6public Name(String first , String last) 7{ 8 this.first = first; 9 this.last = last; 10} 11 12// first的setter和getter方法 13public void setFirst(String first) 14{ 15 this.first = first; 16} 17public String getFirst() 18{ 19 return this.first; 20} 21 22// last的setter和getter方法 23public void setLast(String last) 24{ 25 this.last = last; 26} 27public String getLast() 28{ 29 return this.last; 30} 31 32// 重写equals()方法,根据first、last进行判断 33public boolean equals(Object obj) 34{ 35 if (this == obj) 36 { 37 return true; 38 } 39 if (obj != null && obj.getClass() == Name.class) 40 { 41 Name target = (Name)obj; 42 return target.getFirst().equals(getFirst()) 43 && target.getLast().equals(getLast()); 44 } 45 return false; 46} 47// 重写hashCode()方法,根据first、last计算hashCode值 48public int hashCode() 49{ 50 return getFirst().hashCode() * 31 51 + getLast().hashCode(); 52}}
// 以Name组件作为标识属性 @EmbeddedId @AttributeOverrides({ // 指定 @AttributeOverride(name="first", column = @Column(name="person_firstname")), @AttributeOverride(name="last", column = @Column(name="person_lastname")) }) private Name name;
2.7多列作为联合主键
Hibernate 还提供了另一种联合主键支持,如果需要直接将持久化类的多列映射成联合主键,则该持久化类必须满足如下条件。
-
有无参数的构造器。
-
必修实现java.io.Serializabel接口。(在Hibernate4中,不是必须的)
-
建议根据联合主键列所映射的属性重写equals()和hashCode()方法,也就是根据组件类的关键属性来区分组件对象。(因为组件是唯一标识符,必须重写这两个方法。)
@Entity @Table(name="person_inf") public class Person implements java.io.Serializable { // 定义first属性,作为标识属性的成员 @Id private String first; // 定义last属性,作为标识属性的成员 @Id private String last; private int age;
1// first的setter和getter方法 2public void setFirst(String first) 3{ 4 this.first = first; 5} 6public String getFirst() 7{ 8 return this.first; 9} 10 11// last的setter和getter方法 12public void setLast(String last) 13{ 14 this.last = last; 15} 16public String getLast() 17{ 18 return this.last; 19} 20 21// age的setter和getter方法 22public void setAge(int age) 23{ 24 this.age = age; 25} 26public int getAge() 27{ 28 return this.age; 29} 30 31// 重写equals()方法,根据first、last进行判断 32public boolean equals(Object obj) 33{ 34 if (this == obj) 35 { 36 return true; 37 } 38 if (obj != null && obj.getClass() == Person.class) 39 { 40 Person target = (Person)obj; 41 return target.getFirst().equals(getFirst()) 42 && target.getLast().equals(getLast()); 43 } 44 return false; 45} 46 47// 重写hashCode()方法,根据first、last计算hashCode值 48public int hashCode() 49{ 50 return getFirst().hashCode() * 31 51 + getLast().hashCode(); 52}}
版权声明:本文为博主原创文章,未经博主允许不得转载。