前面的相关文章中,我们已经介绍了使用XML配置文件映射实体类及其各种类型的属性的相关知识。然而不论是时代的潮流还是臃肿繁杂的配置代码告诉我们,注解配置才是更人性化的设计,于是学习了基本的映射实体类的基本注解,此处做一点总结,后续文章将陆续更新使用注解的方式管理配置各种映射关联关系。本篇主要涉及以下内容:

  • 使用最基本的注解映射一个实体类
  • 使用注解映射属性
  • 使用注解映射主键
  • 其他特殊类型的属性映射

一、使用最基本的注解映射一个实体类

@Entity
@Table(name = "userInfo")
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
//省略getter和setter方法
}
//在hibernate.cfg.xml中添加实体类
//这样hibernate就会根据配置文件去查找该实体类并做映射操作
<mapping class="User_Annotation.UserInfo"/>

这就是映射一个最简单的实体类所用到的最基本的注解。其中,

  • @Entity:指定当前被修饰的类是一个实体类,用于映射到数据库中的表。
  • @Table(name = "userInfo"):详细指定了该类映射到数据库中的哪张表,这里映射到userInfo表。
  • @Id:指定被修饰的属性将映射到数据表的主键列。
  • @GeneratedValue(strategy = GenerationType.IDENTITY):该注解指定了主键的生成策略,一般不单独出现,这里指定了主键自增的策略。

二、使用注解映射普通属性

对于实体类中属性的映射,一般我们使用@Column进行修饰。该注解有很多属性:

  • name:指定该属性映射到数据表中对应的名称
  • nullable:指定该属性映射的数据表中列是否可以为null,默认为true
  • unique:指定该属性映射到数据表中的列是否具有唯一约束
  • length:指定该属性映射到数据表中的列所能保存数据的最大长度,默认是255

默认情况下,我们不使用@Column修饰属性的时候,hibernate会自动以该属性的名称映射到数据表中的列。

我们也可以使用注解@Transient修饰属性,它指明了该属性不会被映射到数据表中某一列,而只是作为一个属性被定义在实体类中。例如:

@Entity
@Table(name = "userInfo")
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@Transient
private int age;
//省略getter,setter方法
}

看看hibernate为我们生成的sql语句:

显然,我们age属性并没有被映射到userinfo表中。

对于枚举类型的属性,我们可以使用@Enumerated注解进行修饰。

在某些特殊情况下,有时我们的实体类属性会被定义为枚举类型,那么对于这种数据库中并无法对应的Java类型,该如何映射呢?Hibernate中提供@Enumerated注解来用于我们映射枚举类型,该注解提供一个value属性,该属性可以取两个值:

  • EnumType.STRING:该枚举类型的属性映射到数据表的字段的类型是字符串型
  • EnumType.ORDINAL:该枚举类型的属性映射到数据表的字段的类型是整数类型

例如:

//定义一个枚举类型
public enum Season {
春季, 夏季, 秋季, 冬季
}
@Entity
@Table(name = "userInfo")
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name; @Enumerated(EnumType.STRING)
private Season season;
//省略getter,setter方法
}

看看我们的userinfo表:

而当我们@Enumerated(EnumType.ORDINAL)修饰属性的时候,那么Hibernate为我们生成的sql语句是:

这两种情况下,数据表中的season字段一种保存的是枚举类型的具体值,一种保存的是枚举值对应的序号。

使用@Temporal注解映射日期时间类型

对于Java来说,表示时间的两个类库,Java.util.Date和java.util.Calendar。而对于数据库而言,表示时间的类型就有很多,例如:date,time,datetime,timestamp等。如何准确的指定最终的映射情况就是我们的@Temporal注解的作用。@Temporal有一个value属性,可以取以下的一些值:

  • TemporalType.DATE:对应于数据库中的date类型
  • TemporalType.TIME:对应于数据库中的time类型
  • TemporalType.TIMESTAMP:对应于数据库中的timestamp类型

例如:

@Temporal(TemporalType.DATE)
private Date date;

上述代码指定了Java.util.Date类型属性映射到数据库中的date类型字段。

三、使用注解映射主键属性

最简单的情况下,我们使用注解@Id标识实体类中的某个属性,那么该属性将会被hibernate映射到数据库主键字段,并且无需指定任何属性值。使用使用@GeneratedValue指定主键的生成策略,通过它的strategy属性来指定具体的主键生成方案,该属性可以取如下几个值:

  • GenerationType.AUTO:hibernate默认为该值,它指明了hibernate自动根据底层数据库选择适当的生成策略
  • GenerationType.IDENTITY:适用于MySQL,SQLserver的主键自增长策略
  • GenerationType.SEQUENCE:适用于Oracle的子串策略
  • GenerationType.TABLE:基于辅助表的生成主键策略

如果不是使用Oracle做数据库的话,一般我们会使用IDENTITY作为默认的主键生成策略。

联合主键的映射可以通过多个@Id进行修饰即可,但要求该实体类必须继承 java.io.Serializable并尽可能的重写Object的两个方法,hashCode和equals,因为多个属性唯一确定一条记录,自然需要比较属性的值。例如:

@Entity
@Table(name = "userInfo")
public class UserInfo implements Serializable {
@Id
private int id;
@Id
private String name;
//省略getter,setter方法
}

看看hibernate为我们创建的表结构:

四、特殊属性的映射

这里的特殊属性指的是实体类中属性类型非常规的基本类型、包装类型、引用类型,而是类似于集合类型、自定义类型等。我们首先看对于集合类型的属性映射情况。

1、映射集合类型的属性

在hibernate中,所有的集合类型属性都会被单独映射到一张表中,无论是List,Set或者Map都会对应于一张新表。首先我们看List的映射,在详细介绍之前,我们先完整的看看list的映射情况。

@Entity
@Table(name = "userInfo")
public class UserInfo implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name; @ElementCollection(targetClass = String.class)
@CollectionTable(name = "address",joinColumns = @JoinColumn(name = "user_id"))
@OrderColumn(name = "list_id")
@Column(name = "address")
private List address;
//省略getter,setter方法
}
//通过实体类实例向数据表中插入数据
UserInfo userInfo = new UserInfo();
userInfo.setName("single");
List<String> list = new ArrayList<String>();
list.add("NanJin");
list.add("XinJiang");
list.add("SiChuan");
list.add("ZheJiang");
list.add("NanTong");
userInfo.setAddress(list); session.save(userInfo);

看看两张表:

现在,我们再来看看所用到的几个注解。@ElementCollection注解用于修饰一个集合类型的属性,targetClass 指定了该集合类型的对应的泛型类型,我们这里指定了String类型,那么hibernate底层会默认构建一个ArrayList来存放所有的集合元素并且每个元素都限定为String类型。

@CollectionTable注解用于配置为集合属性生成的那张新表的基本信息,name 指定新表的表名,joinColumns的值是一个注解@JoinColumn,该注解专门用于配置外键列,这里我们给他命名为user_id,该字段是address表的值依赖于userinfo表的id主键列的值。

@OrderColumn注解用于配置有序集合的序号,由于list是有序的集合,通过该注解将会在address表中增加一个字段保存各个元素在集合中的序号。

@Column注解则指向我们集合元素所在的列,可以配置他们列名等。

总的来说,一旦hibernate发现实体类中有集合类型的属性需要映射,那么就会为集合属性单独映射出一张表,该表至少有两个字段,一个字段依赖于主表的id字段值,在新表中相同该字段值的记录共同组合成为实体类中的集合属性的值,一个字段保存具体的集合元素的值信息。而对于有序集合来说,还应该包含一个字段用于保存每个集合元素在集合中的序号,该序号字段和第一个外键依赖字段组合成新表的联合主键,唯一标识一条记录。

在hibernate的管理下,当有数据添加进userinfo表的时候,hibernate将拿到该实体类实例的集合属性的值,并连带该实例的id一起插入到新表中。当然,当我们想要获取一个userinfo实例的时候,hibernate也会为我们查询address表,并注入到userinfo实例的集合属性中,默认的注入模式是懒加载。

接着,我们看Set集合的映射情况。Set是一种无序并不重复的集合。具体的配置如下:

@Entity
@Table(name = "userInfo")
public class UserInfo implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@ElementCollection(targetClass = String.class)
@CollectionTable(name = "address",joinColumns = @JoinColumn(name = "user_id",nullable = false))
@Column(name = "value",nullable = false)
private Set address;
//省略getter,setter方法
}

相比List,Set由于是无序的,那么自然是没有索引序列,所以无需配置@OrderColumn,但是它要求所有元素必须不可重复,那么通过制定nullable为false即可。

看看表的生成情况:

对于像set一样的无序集合,新表的主键有user_id和value列联合作为主键,可以保证唯一确定一条数据记录。

最后,我们看看一下Map的映射情况,先看代码:

@Entity
@Table(name = "userInfo")
public class UserInfo implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@ElementCollection(targetClass = String.class)
@MapKeyClass(Integer.class)
@CollectionTable(name = "address",joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "value")
private Map map;
//省略getter,setter方法
}

具体的表生成情况:

对于map这种键值对集合,targetClass 用于指定value值的类型,而@MapKeyClass则用于指定key值的类型,其他的几乎没什么变化,对于map集合映射出来的表,user_id和map的key字段将联合组成此表的主键,唯一确定一条记录。

对于性能的要求,hibernate不推荐实体类属性使用数组类型,建议优先使用集合类型。

2、组件属性映射

所谓的组件类型就是指我们自定义的类类型,在某些情况下,实体类中包含自定类型也是很常见的,那么对于我们自定义的类型该如何来映射到数据表呢?Hibernate的映射策略很简单,对于组件中的每个属性都映射出一个列,也就是相当于把组件给拆解了。例如:

//首先定义一个组件类
@Embeddable
public class Disposition {
private String mood;
private String hobby;
//省略getter,setter方法
}

我们定义了一个类,Disposition并使用@Embeddable注解修改该类。当Hibernate对整个类路径进行扫描的时候,就会注册该类为一个组件类型,那么当我们在实体类中引用该类型的时候,hibernate就能找到相应的组件类型。

@Entity
@Table(name = "userInfo")
public class UserInfo implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name; private Disposition disposition;
//省略getter,setter方法
}

最后生成的数据表结构如下:

组件类的每个属性都被映射到userinfo表中了。当我们通过实体类实例向数据表中插入数据的时候,hibernate会将组件类实例拆分出来的各个属性插入到对应的表字段。当我们通过数据表获取userinfo实例的时候,hibernate判断userinfo中有一个组件类属性,于是创建组件类实例并装载相应的数据表中的数值赋值给userinfo的组件类型属性。

3、集合属性为组件类型的表级映射

集合中的元素除了可以是基本类型,包装类型以外,还可以是组件类型,也就是复合类型。那么对于他们的映射却稍显不同,例如:

//定义一个复合类型
@Embeddable
public class Person {
private String name;
private int age;
private String address;
//省略getter,setter方法
}
@Entity
@Table(name = "userInfo")
public class UserInfo implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name; @ElementCollection(targetClass = Person.class)
@CollectionTable(name = "persons",joinColumns = @JoinColumn(name = "user_id"))
@OrderColumn(name = "list_index")
private List list;
//省略getter,setter方法
}

显然,在实体类中的集合类型属性的映射,大体上是一样的。首先我们通过targetClass 属性指定集合中的元素类型,通过CollectionTable配置为集合生成的新表的基本信息,通过OrderColumn指定索引列。当然,这里我们不需要使用Column注解配置集合元素本身在数据表中的字段名,因为数据库中没有相对应的类型存储。Hibernate选择将集合中的复合类型拆分成多个字段,其他的和普通的集合属性映射并没有太大变化。

只不过对于普通的集合类型映射来说,图中红色框中内容仅仅是一个字段,而对于复合类型,由于数据库中并没有相对应的类型来存储,所以就需要拆分成基本的字段类型。

至此,使用注解方法来配置实体类的基本内容已经简单介绍完了,还有很多相对而言并不常用的基于Hibernate自身的注解并没有做介绍,待作者深入使用后再做相关补充,总结不到之处,望指出!

Hibernate框架学习之注解映射实体类的更多相关文章

  1. Hibernate框架学习之注解配置关系映射

         上篇文章我们通过注解对映射了单个实体类,但是具体项目中往往实体类之间又是相互关联的,本篇文章就是从实体类之间存在的不同关联角度,具体学习下如何映射他们之间的关联,主要涉及内容如下: 单向的一 ...

  2. [ SSH框架 ] Hibernate框架学习之四(JPA)

    一.JPA概述以及它和Hibernate之间的关系 1.1.Hibernate 概述 JPA Java Persistence API,是EJB3规范中负责对象持久化的应用程序编程接口(ORM接口), ...

  3. hibernate框架学习笔记5:缓存

    缓存不止存在与程序中,电脑硬件乃至于生活中都存在缓存 目的:提高效率 比如IO流读写字节,如果没有缓存,读一字节写一字节,效率低下 hibernate中的一级缓存:提高操作数据库的效率 示例: 抽取的 ...

  4. Hibernate用注解实现实体类和表的映射

    数据库mysql: 1.一对一 person50表password50表是一对一的关系: password50表中有外键 person_id person实体类: package com.c50.en ...

  5. hibernate用注解配置实体类的映射

    一.注解类 1. @Table 声明了该实体bean映射指定的表(table),目录(catalog)和schema名字 2. @Id 声明了该实体bean的标识属性(对应表中的主键). 3. @Co ...

  6. Eclipse中通过Hibernate Tools插件实现从数据库逆向生成Hibernate带注解的实体类

    一.安装hibernate tools插件 1.在线安装 通过Eclipse的Help->Install New Software 在线安装插件,插件连接为: eclipse helios(3. ...

  7. Myeclipse 10使用hibernate生成注解(annotation)实体类

    以MySQL数据库为例,请在数据库里面建好对应的表. 1.配置数据库链接 打开Myelipse Database Explorer视图 Window-->Open Perspective--&g ...

  8. hibernate -- 注解映射实体和表

    表名的映射 //代表此类参与ORM映射,此注解必须要有 @Entity //代表user这个类映射了一个表user50,如果表名和类名一样,此注解可以省略 @Table(name="user ...

  9. hibernate映射实体类查询时数据库空字段赋值给实体类报错的问题

    因为一直报实体类空异常,去网上查了资料只查到了并没有查到数据库空值时不给实体类赋值的属性 异常 org.hibernate.InvalidMappingException: Could not par ...

随机推荐

  1. An Introduction to Variational Methods (5.2)

    我们现在已经得到了关于潜在变量Z的优化分布的表达形式: ‍ 其中: ‍ 所以现在我们可以得到Z的期望: ‍ 另外对于Z还值得一提的是,我们从其优化分布的表达式中可以看出,各个Z的组成部分之间还是相互耦 ...

  2. python 的日志logging模块学习

    1.简单的将日志打印到屏幕 import logging logging.debug('This is debug message') logging.info('This is info messa ...

  3. 我从.net转别的语言的遭遇,现在貌似又要回头(一)

    从2016年开始,作为公司最后的一个.net部门,被迫转向了php. php里面装得一手什么逼呢? 首先,你要来几本入门的书,linux,mysql,php基础语法,nginx或apache.哥那时候 ...

  4. zoj 1108 FatMouse's Speed 基础dp

    FatMouse's Speed Time Limit: 2 Seconds      Memory Limit:65536 KB     Special Judge FatMouse believe ...

  5. 让asp.net网站支持多语言,使用资源文件

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="test.aspx.cs&quo ...

  6. Echarts数据可视化polar极坐标系,开发全解+完美注释

    全栈工程师开发手册 (作者:栾鹏) Echarts数据可视化开发代码注释全解 Echarts数据可视化开发参数配置全解 6大公共组件详解(点击进入): title详解. tooltip详解.toolb ...

  7. windows 本地构建hadoop-spark运行环境(hadoop-2.6, spark2.0)

    下载hadoop http://hadoop.apache.org/releases.html --> http://mirrors.tuna.tsinghua.edu.cn/apache/ha ...

  8. UWP 分享用那个图标

    有两个图标,如果让你选,你会用哪个图标做分享图标? 这就算有意义的图标和通用图标的选择. 可以看到 左边的图标比较有意义,但是右边的图标是通用的. 是需要选有意义的?还是通用的 在 UWP ,选的是第 ...

  9. 微信开发-微信JSSDK错误:invalid url domain

    错误类型:invalid url domain 调试返回参数: { "errMsg": "config:invalid url domain" } 截图: 环境 ...

  10. Python装饰器,json,pickle

    装饰器 定义:本质是函数,装饰其它函数是为了给其添加新功能: 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 实现装饰器知识储备: 1.函数即变量: 2.高阶函数 3.嵌套 ...