1、知识点:
1、 XML生成id
a) generator
b) 常用的四个:native、identity(SQL Server)、sequence(Oracle)、uuid。
2、 @GeneratedValue
a) 自定义ID
b) AUTO
i. 默认:对MySQL,使用auto_increment
ii. 对Oracle使用hibernate_sequence(名称固定)
c) IDENTITY
d) SEQUENCE
i. @SequenceGenerator
e) TABLE
i. @TableGenerator
2、XML方式配置ID生成策略
修改Student.hbm.xml文件:
<?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="com.wolex.hibernate.model"> <class name="Student" table="students"> <id name="id" column="stu_id"> <generator class="uuid"></generator> </id> <!-- 记得length="",不要忘了加双引号 --> <property name="name" column="sname" length="10"></property> <!-- <property name="password" type="java.math.BigDecimal" length="11" precision="2" scale="3"></property> --> <!-- 上面纠结了很久都改变不了最终生成表的number类型的长度与精度 --> <property name="password"></property> <property name="birthday" type="java.util.Date"></property> <property name="registrationDate" type="time"></property> </class>
</hibernate-mapping> |
由于使用了UUID,其在数据库中以varchar2存储,所以Student类中属性id需要定义为String类型,同时修改getter、setter,此处不演示了。
测试类,与之前相比,只是删除了setId()那一行。
package com.wolex.hibernate.model;
import java.util.Date; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import com.wolex.hibernate.model.Student;
public class StudentTest { public static void main(String[] args) { Student stu = new Student(); // stu.setId(1); stu.setName("Hebe"); stu.setPassword(219341); stu.setBirthday(new Date()); stu.setRegistrationDate(new Date());
Configuration cfg = new Configuration(); cfg.configure(); SessionFactory sf; try { // 主要为了观察错误原因 ServiceRegistry sr = new ServiceRegistryBuilder().applySettings( cfg.getProperties()).buildServiceRegistry(); sf = cfg.buildSessionFactory(sr); Session session = sf.openSession(); session.beginTransaction(); session.save(stu); session.getTransaction().commit(); session.close(); sf.close(); } catch (HibernateException e) { e.printStackTrace(); } } } |
Hibernate: drop table students cascade constraints Hibernate: create table students ( stu_id varchar2(255 char) not null, sname varchar2(10 char), password number(19,0), birthday timestamp, registrationDate date, primary key (stu_id) ) Hibernate: insert into students (sname, password, birthday, registrationDate, stu_id) values (?, ?, ?, ?, ?) |
查询数据库表:
SQL> describe students;
Name Null? Type
------------------------------------------------- ----------------------------
STU_ID NOT NULL VARCHAR2(255 CHAR)
SNAME VARCHAR2(10 CHAR)
PASSWORD NUMBER(19)
BIRTHDAY TIMESTAMP(6)
REGISTRATIONDATE DATE
SQL> SELECT * FROM students;
STU_ID SNAME PASSWORDBIRTHDAY REGISTRAT
----------------------------------- ---------- ---------------------------------------- ---------
402880e73f22178c013f22178e1e0000 Hebe 219341 08-JUN-1312.42.15.127000 PM 08-JUN-13
下面将generator中的“uuid”改为“native”,Student类的id属性改为int类型,同时修改setter、getter方法。
测试:
package com.wolex.hibernate.model;
import java.util.Date; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import com.wolex.hibernate.model.Student;
public class StudentTest { public static void main(String[] args) { Student stu = new Student(); // stu.setId(1); stu.setName("Yumiko"); stu.setPassword(219341); stu.setBirthday(new Date()); stu.setRegistrationDate(new Date());
Configuration cfg = new Configuration(); cfg.configure(); SessionFactory sf; try { // 主要为了观察错误原因 ServiceRegistry sr = new ServiceRegistryBuilder().applySettings( cfg.getProperties()).buildServiceRegistry(); sf = cfg.buildSessionFactory(sr); Session session = sf.openSession(); session.beginTransaction(); session.save(stu); session.getTransaction().commit(); session.close(); sf.close(); } catch (HibernateException e) { e.printStackTrace(); } } } |
Hibernate: drop table students cascade constraints Hibernate: drop sequence hibernate_sequence Hibernate: create table students ( stu_id number(10,0) not null, sname varchar2(10 char), password number(19,0), birthday timestamp, registrationDate date, primary key (stu_id) ) Hibernate: create sequence hibernate_sequence Hibernate: select hibernate_sequence.nextval from dual Hibernate: insert into students (sname, password, birthday, registrationDate, stu_id) values (?, ?, ?, ?, ?) |
可以看到,如果使用的是Oracle数据库,将generator的class定义为native(或sequence),则hibernate会为用户自动创建一个序列:hibernate_sequence。其他数据库类似。
数据库表:
SQL> describe students;
Name Null? Type
------------------------------------------------- ----------------------------
STU_ID NOT NULL NUMBER(10)
SNAME VARCHAR2(10 CHAR)
PASSWORD NUMBER(19)
BIRTHDAY TIMESTAMP(6)
REGISTRATIONDATE DATE
SQL> SELECT * FROM students;
STU_ID SNAME PASSWORD BIRTHDAY REGISTRAT
---------- ---------- ---------- ---------------------------------------
1 Yumiko 219341 08-JUN-13 01.04.13.404000PM 08-JUN-13
3、Annotation方式配置ID生成策略
3.1、@GeneratedValue简单使用
修改Teacher类,只需在id项上添加@GeneratedValue即可,其他不变:
package com.wolex.hibernate.model; //注意导入的包 import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient;
@Entity // 指定数据表名为"teachers": @Table(name = "teachers") public class Teacher { private int id; private String name; private String title; private String spouseName; private Date birthday; private Sex sex;
@Id @GeneratedValue public int getId() { return id; }
@Column(name = "tname", length = 20) public String getName() { return name; }
// 不持久化的属性 @Transient public String getSpouse() { return spouseName; }
@Column(length = 10) public String getTitle() { return title; }
// 但只有一个值的时候,value可写可不写: @Temporal(value = TemporalType.TIME) // 枚举类TemporalType中的其他常量:DATE、TIMESTAMP @Temporal(TemporalType.TIMESTAMP) public Date getBirthday() { return birthday; }
// EnumType.ORDINAL表示采用枚举的序数(1、2。。。) @Enumerated(EnumType.STRING) @Column(length = 10) public Sex getSex() { return sex; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
public void setId(int id) { this.id = id; }
public void setName(String name) { this.name = name; }
public void setSpouse(String spouse) { this.spouseName = spouse; }
public void setTitle(String title) { this.title = title; }
public void setSex(Sex sex) { this.sex = sex; } } |
测试类,只需把setId操作去掉即可:
package com.wolex.hibernate.model; import java.util.Date; import org.hibernate.Session; import org.hibernate.tutorial.util.HibernateUtil; import com.wolex.hibernate.model.Teacher;
public class TeacherTest { public static void main(String[] args) { Teacher tea = new Teacher(); // tea.setId(1); //注释掉,因为自动插入id值 tea.setName("kym"); tea.setTitle("中级"); tea.setBirthday(new Date()); tea.setSex(Sex.FEMALE); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); session.save(tea); session.getTransaction().commit(); session.close(); HibernateUtil.getSessionFactory().close(); } } |
Hibernate: drop table teachers cascade constraints Hibernate: drop sequence hibernate_sequence Hibernate: create table teachers ( id number(10,0) not null, birthday timestamp, tname varchar2(20 char), sex varchar2(10 char), title varchar2(10 char), primary key (id) ) Hibernate: create sequence hibernate_sequence Hibernate: select hibernate_sequence.nextval from dual Hibernate: insert into teachers (birthday, tname, sex, title, id) values (?, ?, ?, ?, ?) |
查询数据库表:
SQL> describe teachers;
Name Null? Type
------------------------------------------------- ----------------------------
ID NOTNULL NUMBER(10)
BIRTHDAY TIMESTAMP(6)
TNAME VARCHAR2(20 CHAR)
SEX VARCHAR2(10 CHAR)
TITLE VARCHAR2(10 CHAR)
SQL> SELECT * FROM teachers;
ID BIRTHDAY TNAME SEX TITLE
---------- ------------------------------ ------------------------------ ----------
1 08-JUN-1301.37.08.693000 PM kym FEMALE 中级
3.2、Oracle数据库序列:@SequenceGenerator
通过以上程序,我们发现,如果在多个类中的ID属性上添加@GeneratedValue,则所有类(数据库中的表)共享一个sequence,即hibernate自动为我们生成的hibernate_sequence。有时候这样并不是我们想要的,例如我们希望每个表都有自己按顺序且有规则的自增sequence,此时就需要使用指定@GeneratedValue的strategy了。
在@GeneratedValue注解中,有两个可选元素(Optional Element),如下:
No. | Optional Element | Description |
1 | public abstract GenerationType strategy | ID生成策略 |
2 | public abstract java.lang.String generator | 生成器名字 |
generator可选元素,如果不指定,则默认由persistenceprovider提供,如Hibernate中默认生成hibernate_sequence。至于strategy的可选项参考GenerationType枚举类:
javax.persistence Enum GenerationType |
java.lang.Object java.lang.Enum<GenerationType> javax.persistence.GenerationType |
可见,GenerationType属于枚举类型,其可选值有:
No. | Enum Constant | Description |
1 | TABLE | 需要同时在类上添加@TableGenerator |
2 | AUTO |
|
3 | SEQUENCE | 需要同时在类上添加@SequenceGenerator |
4 | IDENTITY |
|
除了在属性上(或getter方法上)添加@GeneratedValue之外,还需要在类上(也可以添加到ID属性或其getter方法上)添加@SequenceGenerator注解,其中required element只有一个:
Ÿ public abstractjava.lang.String name:唯一名,用于类中引用。
而optional element summary如下:
No. | Optional Element | Description |
1 | public abstract java.lang.String sequenceName | 数据库中存放的序列名 |
2 | public abstract java.lang.String catalog |
|
3 | public abstract java.lang.String schema | sequence的schema名 |
4 | public abstract int initialValue | 主键初始值,实验未明? |
5 | public abstract int allocationSize | The amount to increment,default 50 |
编写Teacher类,使用ID生成策略指定主键生成方式:
package com.wolex.hibernate.model; //注意导入的包 import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient;
@Entity // 指定数据表名为"teachers": @Table(name = "teachers") @SequenceGenerator(name = "teacher_seq", sequenceName = "teacher_seq_db",allocationSize = 1) public class Teacher { private int id; private String name; private String title; private String spouseName; private Date birthday; private Sex sex;
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "teacher_seq") public int getId() { return id; }
@Column(name = "tname", length = 20) public String getName() { return name; }
// 不持久化的属性 @Transient public String getSpouse() { return spouseName; }
@Column(length = 10) public String getTitle() { return title; }
// 但只有一个值的时候,value可写可不写: @Temporal(value = TemporalType.TIME) // 枚举类TemporalType中的其他常量:DATE、TIMESTAMP @Temporal(TemporalType.TIMESTAMP) public Date getBirthday() { return birthday; }
// EnumType.ORDINAL表示采用枚举的序数(1、2。。。) @Enumerated(EnumType.STRING) @Column(length = 10) public Sex getSex() { return sex; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
public void setId(int id) { this.id = id; }
public void setName(String name) { this.name = name; }
public void setSpouse(String spouse) { this.spouseName = spouse; }
public void setTitle(String title) { this.title = title; }
public void setSex(Sex sex) { this.sex = sex; } } |
需要注意的是,在@SequenceGenerator中如果不指定allocationSize的值,则默认开始值和每次自增值都为50。而@GeneratedValue中generator的值为@SequenceGenerator中的name值,这个name只在程序中使用,不存储到数据库中,但允许name值和sequenceName(数据库中的序列名)值相同。
对于@SequenceGenerator的位置也不绝对,上面是添加到实体类上,其实也可以添加到ID属性或其getter方法上。推荐,一般添加到实体类上。
测试类,多插入几条数据,方便观察自增值:
package com.wolex.hibernate.model;
import java.util.Date; import org.hibernate.Session; import org.hibernate.tutorial.util.HibernateUtil; import com.wolex.hibernate.model.Teacher;
public class TeacherTest { public static void main(String[] args) { Teacher tea = new Teacher(); // tea.setId(1); //注释掉,因为自动插入id值 tea.setName("Jolin"); tea.setTitle("高级"); tea.setBirthday(new Date()); tea.setSex(Sex.FEMALE); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); session.save(tea); session.getTransaction().commit(); session.close(); HibernateUtil.getSessionFactory().close(); } } |
Hibernate: select teacher_seq_db.nextval from dual Hibernate: insert into teachers (birthday, tname, sex, title, id) values (?, ?, ?, ?, ?) |
要注意把hbm2ddl.auto赋值为“update”。插入不同的几条数据后,查询数据库表:
SQL> DESC teachers;
Name Null? Type
------------------------------------------------- ----------------------------
ID NOTNULL NUMBER(10)
BIRTHDAY TIMESTAMP(6)
TNAME VARCHAR2(20 CHAR)
SEX VARCHAR2(10 CHAR)
TITLE VARCHAR2(10 CHAR
SQL> SELECT * FROM teachers;
ID BIRTHDAY TNAME SEX TITLE
---------- ------------------------------ --------------- --------------------
1 08-JUN-1311.43.52.944000 PM Jolin FEMALE 高级
2 08-JUN-1311.49.05.077000 PM kym FEMALE 中级
3 08-JUN-1311.49.28.434000 PM Selina FEMALE 初级
4 08-JUN-1311.49.51.160000 PM Kelly FEMALE 初级
3.3、跨数据库平台序列:@TableGenerator
使用GenerationType指定为SEQUENCE,明显不能跨数据库平台,而如果设置为AUTO,表面上可以跨数据库平台,但实际上并不能共享数据,因为在Oracle中生成hibernate_sequence,在SQL server中会指定列使用identity。所以,想要真正达到跨数据库平台,则需要使用@TableGenerator注解。
create table Principal ( id number(10,0) not null, name varchar2(20 char), primary key (id) ); |
package com.wolex.hibernate.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.TableGenerator;
@Entity @TableGenerator ( name = "principal_gen", table = "generator_table", pkColumnName = "pk_key", valueColumnName = "pk_value", pkColumnValue = "principal", allocationSize = 1 ) public class Principal { private int id; private String name;
@Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "principal_gen") public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } } |
编写测试类,记得在运行之前,到hibernate.cfg.xml文件中添加:
<mapping class="com.wolex.hibernate.model.Principal" /> |
测试类如下:
package com.wolex.hibernate.model; import org.hibernate.Session; import org.hibernate.tutorial.util.HibernateUtil;
public class PrincipalTest { public static void main(String args[]) { Principal pri = new Principal(); pri.setName("Jay"); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); session.save(pri); session.getTransaction().commit(); session.close(); HibernateUtil.getSessionFactory().close(); } } |
Hibernate: drop table generator_table cascade constraints Hibernate: create table generator_table ( pk_key varchar2(255 char), pk_value number(10,0) ) Hibernate: select pk_value from generator_table where pk_key = 'principal' for update
Hibernate: insert into generator_table (pk_key, pk_value) values ('principal', ?) Hibernate: update generator_table set pk_value = ? where pk_value = ? and pk_key = 'principal' Hibernate: select pk_value from generator_table where pk_key = 'principal' for update
Hibernate: update generator_table set pk_value = ? where pk_value = ? and pk_key = 'principal' Hibernate: insert into Principal (name, id) values (?, ?) |
数据表如下:
SQL> DESC generator_table;
Name Null? Type
------------------------------------------------- ----------------------------
PK_KEY VARCHAR2(255 CHAR)
PK_VALUE NUMBER(10)
SQL> DESC principal;
Name Null? Type
------------------------------------------------- ----------------------------
ID NUMBER
NAME VARCHAR2(20)
SQL> SELECT * FROM principal;
ID NAME
----------------------------------------------------------------------
1 Jay
SQL> SELECT * FROMgenerator_table;
PK_KEY PK_VALUE
-------------------- ----------
principal 2
注意观察分析hibernate的操作流程(hbm2ddl.auto中为“create”情况下)。
1、 删除generator_table并创建此表;
2、 锁住(for update)generator_table表中列pk_key=‘principal’所在的行;
3、 向pk_key='principal'所在行的pk_value列插入值(如1),并把此值记录在内存中;
4、 更新generator_table表中pk_key='principal'所在行pk_value的值(如2);
5、 之后再执行2~4步骤,不知道为什么??只存在第一次插入值的时候,以后的不会执行此步骤。
6、 向principal表插入数据,其中ID值来自于步骤2中记录在内存中的值(值1)。
将hbm2ddl.auto的property值改为“update”,继续向principal表中插入数据,hibernate运行结果:
Hibernate: select pk_value from generator_table where pk_key = 'principal' for update
Hibernate: update generator_table set pk_value = ? where pk_value = ? and pk_key = 'principal' Hibernate: insert into Principal (name, id) values (?, ?) |
上面为第二次插入数据,所以不存在步骤5的情况。至于第一次为什么有步骤5,有待探讨。
观察数据库表:
SQL> SELECT * FROMgenerator_table;
PK_KEY PK_VALUE
-------------------- ----------
principal 3
SQL> SELECT * FROM principal;
ID NAME
---------- ------------------------------------------------------------
1 Jay
2 Jackie
下面解释一下@TableGenerator中的每一个Element:
@TableGenerator ( name = "principal_gen", table = "generator_table", pkColumnName = "pk_key", valueColumnName = "pk_value", pkColumnValue = "principal", allocationSize = 1 )
|
引自Hibernate的帮助文档:
@TableGenerator:
l name: name of thegenerator(必须项,程序中需要用到)
l table/ sequenceName: name of the table or the sequence (defaulting respectively tohibernate_sequences and hibernate_sequence)
l catalog/ schema:
l initialValue: the value fromwhich the id is to start generating
l allocationSize: the amount toincrement by when allocating id numbers from the generator
In addition, theTABLE strategy also let you customize:
l pkColumnName: the column name containingthe entity identifier
l valueColumnName: the column namecontaining the identifier value
l pkColumnValue: the entityidentifier
l uniqueConstraints: any potentialcolumn constraint on the table containing the ids
说明一下添加@TableGenerator注解后,hibernate后台做了什么:首先根据“table”元素创建一个名为principal_table的表,在程序中的名则根据元素“name”确定为principal_gen(此名只在Java程序中会用到,相当于@SequenceGenerator中的name元素),其中此表有两个字段,一个为pk_key(根据“pkColumnName”指定),另一个为pk_value(根据“valueColumnName”指定);之后向此表插入数据,pk_key列插入“principal”,pk_value列插入“1”;最后更新刚插入的这一行,将pk_value字段的修改为“2”(根据allocationSize而定)。
其中,pk_key字段(“pkColumnName”这个optional element指定)不一定要和表名(principal)或类名相同,但这样做是为了方便管理,而且一个表只应该仅使用一个pkColumnName,这样可以避免unique constraintviolated错误。
4、联合主键
4.1、XML实现
XML方式此处省略,请自行查阅。
4.2、使用Annotation方式实现
联合主键的@IdClass是需要的,因为写Map时方便。
如果在一个类中,主键不是一个属性(或一个组件类实现多个属性作为主键)而是多个属性,则在写Map时就很不方便了。因为Map的形式是一个主键一个值的。
共有4中方法,其中使用@Id,而不添加@IdClass,实体类(此时不需要组件类)只需实现Serializable即可。
使用Annotation实现联合主键十分简单。so far,方法发现有4个。下面引自Hibernate帮助文档一段关于实现联合主键:
5.1.2.1. Composite identifier
You can define a composite primary key through severalsyntaxes:
- 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 be Serializable.
- map multiple properties as @Id properties: the identifier type is then the entity class itself and needs to be Serializable. 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 be Serializable, 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 hasbeen inherited from the dark ages of EJB 2 for backward compatibilities and werecommend you not to use it (for simplicity sake).
为逐一验证以上方法,首先构造一个Tutorial表,由于记录老师辅导学生的数据,这个表由Hibernate帮我们创建。其中hibernate.cfg.xml修改内容如下:
…… <property name="hbm2ddl.auto">create</property> <mapping class="com.wolex.hibernate.model.Tutorial" /> …… |
对于在Object-Oriented中模拟关系数据库中的联合主键,需要多创建一个组件类(Component class),这个类只存在实体类(Entity class)中作为主键的属性,强烈建议不要做其他任何事。但需要注意的是,Component class必须做到以下两点:
l It must implement java.io.Serializable.
l It must re-implement equals()and hashCode()consistentlywith the database's notion of composite key equality.
Note:In Hibernate,although the second requirement is not an absolutely hard requirement ofHibernate,it is recommended.
方法一:使用@EmbeddedId注解Entity class,Tutorial类:
package com.wolex.hibernate.model; import javax.persistence.EmbeddedId; import javax.persistence.Entity;
@Entity public class Tutorial { @EmbeddedId private TutorialPK tutPK; private String content;
public String getContent() { return this.content; }
public void setContent(String content) { this.content = content; }
public TutorialPK getTutPK() { return tutPK; }
public void setTutPK(TutorialPK tutPK) { this.tutPK = tutPK; } } |
Component class,TutorialPK:
package com.wolex.hibernate.model; import java.io.Serializable;
@SuppressWarnings("serial") public class TutorialPK implements Serializable { private int stu_id; private int tea_id;
public int getStu_id() { return stu_id; }
public void setStu_id(int stu_id) { this.stu_id = stu_id; }
public int getTea_id() { return tea_id; }
public void setTea_id(int tea_id) { this.tea_id = tea_id; }
@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + stu_id; result = prime * result + tea_id; return result; }
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TutorialPK other = (TutorialPK) obj; if (stu_id != other.stu_id) return false; if (tea_id != other.tea_id) return false; return true; } } |
测试类,TutorialTest:
package com.wolex.hibernate.model; import org.hibernate.Session; import org.hibernate.tutorial.util.HibernateUtil;
public class TutorialTest { public static void main(String[] args) { TutorialPK pk = new TutorialPK(); pk.setStu_id(1); pk.setTea_id(2); Tutorial tutorial = new Tutorial(); tutorial.setTutPK(pk); tutorial.setContent("数据库SQL语言"); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); session.save(tutorial); session.getTransaction().commit(); session.close(); HibernateUtil.getSessionFactory().close(); } } |
Hibernate: drop table Tutorial cascade constraints Hibernate: create table Tutorial ( stu_id number(10,0) not null, tea_id number(10,0) not null, content varchar2(255 char), primary key (stu_id, tea_id) ) Hibernate: insert into Tutorial (content, stu_id, tea_id) values (?, ?, ?) |
查询数据库表:
SQL> DESC tutorial
Name Null? Type
------------------------------------------------- ----------------------------
STU_ID NOT NULLNUMBER(10)
TEA_ID NOT NULLNUMBER(10)
CONTENT VARCHAR2(255 CHAR)
SQL> SELECT * FROM tutorial;
STU_ID TEA_ID CONTENT
---------- ---------- ------------------------------
1 2 数据库SQL语言
SQL> SELECT 'Miss/Mr'||t.tname||' tutors '||s.sname||' about '||tut.content TUTORIAL
FROM students s,teachers t,tutorialtut
WHERE s.stu_id = tut.stu_id
AND t.id = tut.tea_id;
TUTORIAL
---------------------------------------------------
Miss/Mr kym tutors Yumiko about 数据库SQL语言
测试完成,此方法非常简单,只需在一处添加@EmbeddedId即可。
方法二:使用@Embeddable注解到Component class,同时在Entity class的composite primarykey添加@Id
package com.wolex.hibernate.model; import javax.persistence.Entity; import javax.persistence.Id;
@Entity public class Tutorial { @Id private TutorialPK tutPK; private String content;
public String getContent() { return this.content; }
public void setContent(String content) { this.content = content; }
public TutorialPK getTutPK() { return tutPK; }
public void setTutPK(TutorialPK tutPK) { this.tutPK = tutPK; } } |
TutorialPK类:
package com.wolex.hibernate.model; import java.io.Serializable; import javax.persistence.Embeddable;
@Embeddable @SuppressWarnings("serial") public class TutorialPK implements Serializable { private int stu_id; private int tea_id;
public int getStu_id() { return stu_id; }
public void setStu_id(int stu_id) { this.stu_id = stu_id; }
public int getTea_id() { return tea_id; }
public void setTea_id(int tea_id) { this.tea_id = tea_id; }
@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + stu_id; result = prime * result + tea_id; return result; }
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TutorialPK other = (TutorialPK) obj; if (stu_id != other.stu_id) return false; if (tea_id != other.tea_id) return false; return true; } } |
测试类:
package com.wolex.hibernate.model; import org.hibernate.Session; import org.hibernate.tutorial.util.HibernateUtil;
public class TutorialTest { public static void main(String[] args) { TutorialPK pk = new TutorialPK(); pk.setStu_id(1); pk.setTea_id(3); Tutorial tutorial = new Tutorial(); tutorial.setTutPK(pk); tutorial.setContent("Java三大特性"); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); session.save(tutorial); session.getTransaction().commit(); session.close(); HibernateUtil.getSessionFactory().close(); } } |
Hibernate: drop table Tutorial cascade constraints Hibernate: create table Tutorial ( stu_id number(10,0) not null, tea_id number(10,0) not null, content varchar2(255 char), primary key (stu_id, tea_id) ) Hibernate: drop table Tutorial cascade constraints Hibernate: create table Tutorial ( stu_id number(10,0) not null, tea_id number(10,0) not null, content varchar2(255 char), primary key (stu_id, tea_id) ) |
数据库表:
SQL> SELECT * FROM tutorial;
STU_ID TEA_ID CONTENT
---------- ---------- ------------------------------
1 3 Java三大特性
方法三:Entity class中不直接引用Component class作为composite primarykey,而使用基础数据类型在类中写出所有的主键,在多个主键上都注解@Id,在类上注解@IdClass(...)。实体类如下:
package com.wolex.hibernate.model; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.IdClass;
@IdClass(value=TutorialPK.class) //因为此处value值只有一个,等价于:@IdClass(TutorialPK.class) @Entity public class Tutorial {
// private TutorialPK tutPK; @Id private int stu_id; @Id private int tea_id; public int getStu_id() { return stu_id; }
public void setStu_id(int stu_id) { this.stu_id = stu_id; }
public int getTea_id() { return tea_id; }
public void setTea_id(int tea_id) { this.tea_id = tea_id; }
private String content;
public String getContent() { return this.content; }
public void setContent(String content) { this.content = content; }
// public TutorialPK getTutPK() { // return tutPK; // } // // public void setTutPK(TutorialPK tutPK) { // this.tutPK = tutPK; // } } |
TutorialPK类,不需要添加额外关于联合主键的注解。
package com.wolex.hibernate.model; import java.io.Serializable;
@SuppressWarnings("serial") public class TutorialPK implements Serializable { private int stu_id; private int tea_id;
public int getStu_id() { return stu_id; }
public void setStu_id(int stu_id) { this.stu_id = stu_id; }
public int getTea_id() { return tea_id; }
public void setTea_id(int tea_id) { this.tea_id = tea_id; }
@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + stu_id; result = prime * result + tea_id; return result; }
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TutorialPK other = (TutorialPK) obj; if (stu_id != other.stu_id) return false; if (tea_id != other.tea_id) return false; return true; } } |
测试类,直接调用实体类的setter方法设置属性值。
package com.wolex.hibernate.model;
import org.hibernate.Session; import org.hibernate.tutorial.util.HibernateUtil;
public class TutorialTest { public static void main(String[] args) { // TutorialPK pk = new TutorialPK(); // pk.setStu_id(1); // pk.setTea_id(3); Tutorial tutorial = new Tutorial(); // tutorial.setTutPK(pk); tutorial.setStu_id(2); tutorial.setTea_id(2); tutorial.setContent("Web技术讲解"); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); session.save(tutorial); session.getTransaction().commit(); session.close(); HibernateUtil.getSessionFactory().close(); } } |
Hibernate: drop table Tutorial cascade constraints Hibernate: create table Tutorial ( stu_id number(10,0) not null, tea_id number(10,0) not null, content varchar2(255 char), primary key (stu_id, tea_id) ) Hibernate: insert into Tutorial (content, stu_id, tea_id) values (?, ?, ?) |
查询数据库表:
SQL> SELECT * FROM tutorial;
STU_ID TEA_ID CONTENT
-------------------- ------------------------------
2 2 Web技术讲解
测试成功!方法三中可以只保留实体类,而去掉组件类,这样一来相当于实体类同时也是组件类。修改方法,实体类不添加@IdClass(value=TutorialPK.class),或者将其改为@IdClass(value=Tutorial.class),此时,依然需要遵循component class的原则实现java.io.Serializable接口和override equal()和hashCode()方法。方法四:修改后的实体类:
package com.wolex.hibernate.model; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.Id;
@SuppressWarnings("serial") @Entity public class Tutorial implements Serializable{
// private TutorialPK tutPK; @Id private int stu_id; @Id private int tea_id; public int getStu_id() { return stu_id; }
public void setStu_id(int stu_id) { this.stu_id = stu_id; }
public int getTea_id() { return tea_id; }
public void setTea_id(int tea_id) { this.tea_id = tea_id; }
private String content;
public String getContent() { return this.content; }
public void setContent(String content) { this.content = content; }
// public TutorialPK getTutPK() { // return tutPK; // } // // public void setTutPK(TutorialPK tutPK) { // this.tutPK = tutPK; // } } |
以上Tutorial类中并没有覆写(override)equal()和hashCode()方法,也可以实现目的。因为在Hibernate中允许不覆写equal()和hashCode(),但还是建议将这两种方法进行覆写。
小结:方法一和方法二为推荐的方法;方法四是非标准方法,只有在Hibernate中被支持;而方法三是EJB 2继承来的古老方法,也不推荐使用。