Hibernate 学习笔记03 --ID生成策略

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中的其他常量:DATETIMESTAMP

    @Temporal(TemporalType.TIMESTAMP)

    public Date getBirthday() {

        return birthday;

    }

 

    // EnumType.ORDINAL表示采用枚举的序数(12。。。)

    @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中的其他常量:DATETIMESTAMP

    @Temporal(TemporalType.TIMESTAMP)

    public Date getBirthday() {

        return birthday;

    }

 

    // EnumType.ORDINAL表示采用枚举的序数(12。。。)

    @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继承来的古老方法,也不推荐使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值