2 Hibernate映射 属性 主键 集合 组件 联合主键

Wesley13
• 阅读 642

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: 在插入数据的时候主键由程序处理(很常用),这是 元素没有指定时的默认生成策略。等同于JPA中的AUTO。

  • identity: 使用SQL Server 和 MySQL 的自增字段,这个方法不能放到 Oracle 中,Oracle 不支持自增字段,要设定sequence(MySQL 和 SQL Server 中很常用)。

  • select: 使用触发器生成主键(主要用于早期的数据库主键生成机制,少用)。

  • sequence: 调用底层数据库的序列来生成主键,要设定序列名,不然hibernate无法找到。

  • seqhilo: 通过hilo算法实现,但是主键历史保存在Sequence中,适用于支持 Sequence 的数据库,如 Oracle(比较少用)

  • increment: 插入数据的时候hibernate会给主键添加一个自增的主键,但是一个hibernate实例就维护一个计数器,所以在多个实例运行的时候不能使用这个方法。

  • foreign: 使用另外一个相关联的对象的主键。通常和联合起来使用。

  • 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配置文件的方式

<hibernate-mapping>
 <class name="com.songxu.modle.Person" table="person">
  <id column="id" name="id" type="int">
   <generator class="increment"/>
  </id>
  <!-- column同名时可以省略 -->
  <property column="name" generated="never" lazy="false" name="name" type="string"/>
  <property column="age" generated="never" lazy="false" name="age" type="int"/>
  <property column="registertime" generated="never" lazy="false"
   name="time" type="date"/>
 </class>
</hibernate-mapping>



<id column="id" name="id" type="string">
        <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用于保存学校的名称

@Id @Column(name="perosn_id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    // 标识属性
    private Integer id;
    private String name;
    private int age;
    // 集合属性,保留该对象关联的学校
    @ElementCollection(targetClass=String.class)
    // 映射保存集合属性的表
    @CollectionTable(name="school_inf", // 指定表名为school_inf
        joinColumns=@JoinColumn(name="person_id" , nullable=false))
    // 指定保存集合元素的列为 school_name
    @Column(name="school_name")
    // 映射集合元素索引的列
    @OrderColumn(name="list_order")
    private List<String> schools
        = new ArrayList<>();

生成的表后,List集合属性的表总是以外键列和元素索引列作为联合主键

2.2 Set集合属性

Set是无序集合,因此无需使用@OrderColumn注解映射集合元素的索引列。

// 集合属性,保留该对象关联的学校
    @ElementCollection(targetClass=String.class)
    // 映射保存集合属性的表
    @CollectionTable(name="school_inf", // 指定表名为school_inf
        joinColumns=@JoinColumn(name="person_id" , nullable=false))
    // 指定保存集合元素的列为 school_name,nullable=false增加非空约束
    @Column(name="school_name" , nullable=false)
    private Set<String> schools
        = new HashSet<>();

2.3Map属性

Map属性需要使用@MapKeyColumn映射保存Map Key的数据列

// 集合属性,保留该对象关联的考试成绩
    @ElementCollection(targetClass=Float.class)
    // 映射保存集合属性的表
    @CollectionTable(name="score_inf", // 指定表名为score_inf
        joinColumns=@JoinColumn(name="person_id" , nullable=false))
    @MapKeyColumn(name="subject_name")
    // 指定Map key的类型为String类型
    @MapKeyClass(String.class)
    // 映射保存Map value的数据列
    @Column(name="mark")
    private Map<String , Float> scores
        = 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) 定义单独的组件类

@Embeddable
public class Name
{
    // 定义first成员变量
    @Column(name="person_firstname")
    private String first;
    // 定义last成员变量
    @Column(name="person_lastname")
    private String last;
    // 引用拥有该Name的Person对象
    @Parent      // ①
    private Person owner;

    // 无参数的构造器
    public Name()
    {
    }
    // 初始化全部成员变量的构造器
    public Name(String first , String last)
    {
        this.first = first;
        this.last = last;
    }

    // first的setter和getter方法
    public void setFirst(String first)
    {
        this.first = first;
    }
    public String getFirst()
    {
        return this.first;
    }

    // last的setter和getter方法
    public void setLast(String last)
    {
        this.last = last;
    }
    public String getLast()
    {
        return this.last;
    }

    // owner的setter和getter方法
    public void setOwner(Person owner)
    {
        this.owner = owner;
    }
    public Person getOwner()
    {
        return this.owner;
    }

}



@Id @Column(name="person_id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;
    private int age;
    // 组件属性name
    private Name name;

(2)持久化类内部定义组件

这种方式无需使用@Embeddable注解修饰,而是直接在持久化类中使用@Embedded注解修饰组件属性

public class Name
{
    // 定义first成员变量
    private String first;
    // 定义last成员变量
    private String last;
    // 引用拥有该Name的Person对象
    @Parent
    private Person owner;

    // 无参数的构造器
    public Name()
    {
    }
    // 初始化全部成员变量的构造器
    public Name(String first , String last)
    {
        this.first = first;
        this.last = last;
    }

    // first的setter和getter方法
    public void setFirst(String first)
    {
        this.first = first;
    }
    public String getFirst()
    {
        return this.first;
    }

    // last的setter和getter方法
    public void setLast(String last)
    {
        this.last = last;
    }
    public String getLast()
    {
        return this.last;
    }

    // owner的setter和getter方法
    public void setOwner(Person owner)
    {
        this.owner = owner;
    }
    public Person getOwner()
    {
        return this.owner;
    }

}



@Embedded
    @AttributeOverrides({
        @AttributeOverride(name="first", column = @Column(name="person_firstname")),
        @AttributeOverride(name="last", column = @Column(name="person_lastname"))
    })
    private Name name;

2.5 组件属性为集合

如果组件包含了 list map  set集合,可以直接在组件类中使用@ElementCollection修饰集合属性,并使用@CollectionTable指定保存集合属性的属性表。

@Embeddable
public class Name
{
    // 定义first成员变量
    @Column(name="person_firstname")
    private String first;
    // 定义last成员变量
    @Column(name="person_lastname")
    private String last;
    // 引用拥有该Name的Person对象
    @Parent
    private Person owner;
    // 集合属性,保留该对象关联的考试成绩
    @ElementCollection(targetClass=Integer.class)
    @CollectionTable(name="power_inf",
        joinColumns=@JoinColumn(name="person_name_id" , nullable=false))
    @MapKeyColumn(name="name_aspect")
    @Column(name="name_power" , nullable=false)
    @MapKeyClass(String.class)
    private Map<String , Integer> power
        = new HashMap<>();

    // 无参数的构造器
    public Name()
    {
    }
}

2.6 组件作为复合主键

使用组件作为复合主键,也就是使用组件作为持久化类的标识符,则该组件类必须满足以下要求。

  • 有无参数的构造器。

  • 必修实现java.io.Serializabel接口。(在Hibernate4中,不是必须的)

  • 建议正确地重写equals()和hashCode()方法,也就是根据组件类的关键属性来区分组件对象。(因为组件是唯一标识符,必须重写这两个方法。)

    public class Name implements java.io.Serializable { // 定义first成员变量 private String first; // 定义last成员变量 private String last; // 无参数的构造器 public Name() { } // 初始化全部成员变量的构造器 public Name(String first , String last) { this.first = first; this.last = last; } // first的setter和getter方法 public void setFirst(String first) { this.first = first; } public String getFirst() { return this.first; } // last的setter和getter方法 public void setLast(String last) { this.last = last; } public String getLast() { return this.last; } // 重写equals()方法,根据first、last进行判断 public boolean equals(Object obj) { if (this == obj) { return true; } if (obj != null && obj.getClass() == Name.class) { Name target = (Name)obj; return target.getFirst().equals(getFirst()) && target.getLast().equals(getLast()); } return false; } // 重写hashCode()方法,根据first、last计算hashCode值 public int hashCode() { return getFirst().hashCode() * 31 + getLast().hashCode(); } }

    // 以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; // first的setter和getter方法 public void setFirst(String first) { this.first = first; } public String getFirst() { return this.first; } // last的setter和getter方法 public void setLast(String last) { this.last = last; } public String getLast() { return this.last; } // age的setter和getter方法 public void setAge(int age) { this.age = age; } public int getAge() { return this.age; } // 重写equals()方法,根据first、last进行判断 public boolean equals(Object obj) { if (this == obj) { return true; } if (obj != null && obj.getClass() == Person.class) { Person target = (Person)obj; return target.getFirst().equals(getFirst()) && target.getLast().equals(getLast()); } return false; } // 重写hashCode()方法,根据first、last计算hashCode值 public int hashCode() { return getFirst().hashCode() * 31 + getLast().hashCode(); } }

版权声明:本文为博主原创文章,未经博主允许不得转载。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
2年前
spring和JPA
Entity使用@Entity注解使javabean类成为实体类。一般使用@Id注解在成员变量或者其对应的get方法设置实体类的主键。例子:packagecom.hgf.jpa.domain;importjavax.persistence.Entity;importjavax
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
2年前
JPA、Hibernate框架、通用mapper
JPA是描述对象关系表的映射关系,将运行期实体对象持久化到数据库中,提出以面向对象方式操作数据库的思想。Hibernate框架核心思想是ORM实现自动的关系映射。缺点:由于关联操作提出Hql语法。执行CRUD时产生大量冗余的sql,性能较低mybatis继承Hibernate优点,使用通用mapper插件实现JPA的思想操作数据库通用map
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这