实体类与数据库之间存在某种映射关系,Hibernate依据这种映射关系完成数据的存取,因此映射关系的配置在Hibernate中是最关键的。Hibernate支持xml配置文件与@注解配置两种方式。xml配置文件是最基础的配置,而@注解是Java的官方JPA(Java Persistence API)提供的。本章分别使用@注解与xml讲解Hibernate的映射配置。


一、实体类映射:

从Java的角度讲,实体类就是普通的Java封装类(有人称为POJO有人称为VO)。仅从实体类中的代码信息,Hibernate并不能得知该实体类对应哪个数据表,因此还需要以某种方式配置一下。常用的方式有*.hbm.xml文件配置与@注解配置两种。

hbm.xml文件就是普通的xml文件,hbm为HibernateMapping的缩写,这样从文件名上就能判断该文件为Hibernate实体类配置文件。在JPA出现之前,Hibernate都使用hbm.xml文件配置。JPA出现后,推荐使用JPA的@注解配置,因为对于所有的ORM框架,@注解都是通用的。

1、@注解配置:

实体类一般有ID、普通属性、集合属性等,分别对应数据库的主键、普通列、外键。在@注解配置中,实体类用@Entity注解,用@Table指定对应的数据表,用@Id配置主键,用@Column配置普通属性,用@OneToMany、@ManyToOne、@OneToOne、@ManyToMany配置是实体间的关系。

2、XML配置:

多个实体类可以配置在一个XML文件中。Hibernate推荐用一个同名的XML文件配置一个实体类,便于阅读和维护。XML文件一般以“*.hbm.xml”结尾,便于辨认,也可以直接使用“*.xml”结尾。

实体类还需要配置到hibernate.cfg.xml中,以便hibernate初始化实体类与数据库表的映射关系。如果只配置了映射关系,而没有配置到hibernate.cfg.xml中,hibernate仍然不会知道哪些类是实体类,因为hibernate无法通过遍历所有的类来决定哪些是实体类。如果实体类是用@注解配置的,需要用<mappingclass=”” />配置,而如果是用XML文件配置的,则需要使用<mappingresource=”” />配置XML配置文件。

二、主键映射:


实体类最好有主键列,并有对应的getter和setter方法,这是hibernate推荐的。逐渐尽量使用可以为null值的类型,例如Integer、Long、String等,而不要使用int、long等。因为如果主键为null,则表示该实体类还没保存到数据库,是一个临时状态(Transient),而int、long等原始类型则不具备该功能。


1、@注解配置主键

Hibernate中用@Id声明该列为主键列,同时用@Column声明该列的列名。当列名与属性名相同时,@Column配置可省略。@GeneratedValue用于指定主键的生成策略。Hibernate支持多种逐渐生成规则,例如自增长、由某个表决定、由Sequence决定等等。如果不配置

@GeneratedValue,则必须手动设置ID值。
@Id
@Column(name = "id")
// 设置主键类型, auto表示主键是自增长类型
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;

2.  XML文件中配置主键


如果使用XML配置,主键用<id />配置,name指定实体类的主键属性,column指定数据表中的主键列名。使用嵌套的<generator />配置主键生成策略,native表示使用数据库自己的策略,在MySQL中就是自增长类型,如果不用自动增长类型,则可以用assigned,例如:

<id name="id" column="id">
<generator class="native" />
</id>

主键生成规则

上面说了主键的配置,在配置主键过程中,配置了主键是否是自动生成的。@Id配置主键的同时,也要用@GeneratedValue配置主键生成规则。主键生成规则也成为主键生成策略,负责维护新实体的主键值。用的最多的策略是自增长策略。Hibernate还支持其他的多种主键生成规则。这些生成规则有些是数据库提供的,有些是Hibernate提供的。

 

1.  使用@注解配置主键生成规则

到目前为止,@注解只支持四种逐渐生成策略:GenerationType.AUTO、GenerationType.TABLE、GenerationType.SEQUENCE、GenerationType.IDENTITY,意义分别为:

qGenerationType.AUTO:自动方式,根据底层数据库自动选择。如果为MySQL等支持自增长类型的数据库,则为自增长类型(auto_increment)。

qGenerationType.TABLE:使用指定的表来决定主键的取值,一般结合@TableGenerator使用,示例代码如下:

@Id
@TableGenerator(name = "tb_cat_gen", allocationSize= 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator= "tb_cat_gen")
private Integer id;

qGenerationType.SEQUENCE:使用Sequence来决定主键的取值,适合Oracle、DB2、PostgreSQL、SAPDB等支持Sequence的数据库,一般结合@SequenceGenerator使用。注意某些数据库如Oracle等没有自增长类型,只能使用Sequence,示例代码如下:

@Id
@SequenceGenerator(name = "seq_cat", allocationSize= 25)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator= "seq_cat")
@Column(name = "id")
private Integer id;

qGenerationType.IDENTITY:支持DB2、MySQL、MSSQLServer、Sybase与HypersonicSQL数据库的identit类型主键。

2.  使用XML文件配置主键生成规则

XML配置中支持的主键生成规则,比使用@注解的配置的主键生成规则要多,XML配置中支持的主键生成规则有以下几种:

qnative:取决于数据库,相当于GenerationType.AUTO。

qidentity:使用identity类型,相当于GenerationType.IDENTITY。

qsequence:使用sequence,相当于GenerationType.SEQUENCE。需要指定sequence的名称,示例代码如下:

<id name="id" type=" java.lang.Integer" column=" id"><generatorclass="sequence"><!--使用sequence主键生成规则 --><paramname="sequence">id_sequence</param></generator>
</id>

qincrement:自增长类型,由Hibernate而不是数据库维护,因此即使Oracle等不支持自增长类型的数据库也可以使用。

qhilo:hi/low算法,使用指定的表给主键赋值,相当于GenerationType.TABLE。需要指定表名、列名等,实例代码如下:

<id name="id" type=" java.lang.Integer" column=" id"><generatorclass="hilo"><paramname="table">users</param><paramname="column">id</param><paramname="max_lo">200</param></generator>
</id>

qseqhilo:基于sequence的hilo算法,例如:

<id name="id" type=" java.lang.Integer" column="id"><generatorclass="seqhilo"><param name="sequence">hi_value</param><paramname="max_lo">200</param></generator>
</id>

quuid:使用128位的UUID算法计算一个唯一的值,会使用IP地址及相关的计算机硬件信息。计算结果为32位的16进制数,对应的主键类型必须为String。

qguid:使用MySQL或者MSSQLServer等数据库提供的GUID值。

qassigned:默认值,不使用任何策略,在保存进数据库之前必须使用setter方法赋值。

qselect:使用数据库触发器赋值。

qforeign:使用外键赋值,在一对一实体关系时,可保证关系双方的Id保持一致。

MySQL数据库与Hibernate都提供自增长策略,但是原理是不太一样的。如果采用MySQL的自增长,插入数据时Hibernate生成的SQL语句中将不包含id主键列数据。该主键的当前值、下一个值由数据库自己维护。如果使用Hibernate的自增长,插入数据时Hibernate生成的SQL语句将包含id主键列,并由Hibernate维护该主键的当前值以及下一个值。

对于普通的应用来说,数据库自增长与Hibernate自增长在使用上没有区别。但是如果某数据库同时被两个Hibernate程序使用,那么此时使用Hibernate自增长将会出现错误。例如如果当前主键值为101,那么Hibernate会认为下个主键值为102,两个Hibernate程序插入数据时都会将主键值设为101,这时会因为主键冲突而导致其中一个写数据失败。

      三、普通属性映射:


普通属性是指除了主键外的、java基本类型的属性,例如Integer(int)、Long(long)、Short(short)、Boolean(boolean)、Double(double)、Float(float)、String(string)、Date等类型属性。注意Integer类型与int类型是不同的,Integer默认为null,在数据库中也表现为null,而int默认为0,在数据库中也表现为0.

1、@配置普通属性

普通属性使用@Column与@Basic配置。二者都可以省略。如果省略,则全部按照默认的规则配置,@Column与@Basic的用法如下:

@Column中可指定nullable(是否允许为null)、unique(是否唯一)、insertable(是否允许插入)、updatable(是否允许更新)、length(列长度)、columnDefinition(列类型)、scale(整数长度)、precision(小数点精度)等。这些属性用于生成DDL建表语句。如果属性对应的列名与属性名一致,@Column可以省略。

q@Basic可为普通属性配置加载方式,默认为即时加载。如果列数据比较大,例如大文本类型或者LOB类型,可配置为延迟加载。optional配置该列是否可为null。如果为true,表示该属性是可选的,可以为null,否则不可以为null。

@Column与@Basic使用的代码示例如下:

@Column(name = "usersName", nullable = true, columnDefinition ="varchar", insertable= true, length = 255, unique = true, updatable= true, precision = 2, scale = 4)
@Basic(fetch = FetchType.LAZY, optional=true)
private String usersName;

2 、使用XML文件配置普通属性映射

XML中使用<property/>标签配置普通属性。type属性指定列类型,相当于@Column中的columnDefinition。例如,如果设置type=”text”可以为String类型属性设置为大文本类型列。不同于@注解中的@Column,如果属性名与列名一致,column属性可省略,xml配置中的<property>必须配置,否则视为不参与持久化的列。配置为:

<property name="salary" precision="2"scale="10" length="255"
column="salary" type="string" update="true"insert="true"
lazy="false" unique="false" not-null="false">
</property>

在使用@注解配置中,如果没有对普通属性进行配置,则默认该属性名与数据表列名相同;而xml文件配置中,如果对普通属性没有配置,则认为该属性没有对应的数据库列,不参与持久化。二者是截然不同的。

四、日期属性配置:


日期属性也属于普通属性,普通属性的配置规则也适用于日期属性。日期属性又包括只有日期没有时间(java.sql.Date)、没有日期只有时间(java.sql.Time)。既有日期又有时间(java.sql.Timestamp)等3种情况,因此要多一些配置。由于java.sql.Date、java.sql.Time、java.sql.Timestamp都是java.util.Date的子类,所以日期属性直接使用父类java.util.Date就可以了,hibernate会根据日期属性决定该类型是java.sql.Date、java.sql.Time还是java.sql.Timestamp。

1、使用@注解配置普通属性映射

日期属性也是普通的属性,需要用@Basic声明加载方式、@Column等指定列名,二者都可省略。另外,如果日期属性是java.util.Date类型的,必须要用@Temporal配置日期类型,取值可以为Date、Time或者Timestemp。否则Hibernate将无法区分该类型是到底是java.sql.Date(只有年月日等日期信息)类型还是java.sql.Time(只有时分秒等时间信息)类型、还是java.sql.TimeStamp(既有日期信息、又有时间信息)类型。例如:

@Temporal(TemporalType.TIMESTAMP)// 日期类型为DATE, TIME或者TIMESTEMP。
@Column(name = " birthday")
private java.util.Date birthday;

在配置日期属性时,如果属性类型是java.util.Date类型,需要用@Temporal声明日期类型。但是如果是java.sql.Time、java.sql.Date或者java.sql.TimeStamp类型的,类型本身就已经很明确了,不再需要@Temporal声明了。

2 、使用XML文件配置日期属性映射

在配置日期类型属性时,type属性中指定日期类型,取值可以为date、time、timestamp等简写方式,也可以为java.sql.Date、java.sql.Time、java.sql.Timestamp等全写方式。

同样的道理,如果Java中属性类型为java.util.Date类型,必须指定是java.sql.Date(只有年月日等日期信息)类型还是java.sql.Time(只有时分秒等时间信息)类型、还是java.sql.TimeStamp(既有日期信息、又有时间信息)类型。示例代码如下:

<property name=" birthday" type="date"></property>

五、临时属性映射:


实体类可能有一些临时属性,在JPA中被称为Transient属性。这些属性用于方便计算等其他用途,而不是保存数据到数据库中。这些属性必须被标记为Transient,以便hibernate把他们区别对待。否则hibernate会试图往数据库写该属性,可能会因对应的列不存在而抛出异常。

1、使用@注解配置临时属性映射

Java标注中,临时属性必须使用@Transient标注,既可以配置在临时属性上,也可以配置在对应的getter、setter方法上。例如:

@Transient
public int getCount() {// 临时属性,用于计算总记录数
return name == null ? 0 :count;
}

如果只有形如getter、setter的方法,但是没有对应的属性,Hibernate仍然会认为该属性存在。因此也需要用@Transient标注。

2 、使用XML文件配置临时属性映射

在XML配置中,所有没有配置到XML文件中的属性都被视为临时属性。如果某属性漏配置了,该属性值将不被保存到数据库中。

六.版本属性配置:


Hibernate中有一种特殊的属性:版本(Version)属性。版本属性不参与业务逻辑,只用来保证不会有两个线程同时对该数据进行写操作。版本属性是乐观锁的一种实现方法。乐观锁是相对于悲观锁而言的。

乐观锁与悲观锁:悲观锁与乐观锁都是保证数据准确性的机制。

为保证数据的准确性,程序必须保证在一个线程修改数据的时候,该数据没有被其他线程修改。在传统的数据库编程中,程序修改数据时先锁定该数据行,使其他程序无法修改该行数据,修改完毕后释放数据锁,以此保证数据准确性。由于该机制需要锁定数据行,被锁定的数据只能被一个线程使用,因此被称为悲观锁。

乐观锁使用完全不同的方式。乐观锁通过Version列保存当前数据的版本,如果程序修改了数据,就将版本加1。反过来,如果版本列有了变化,说明该数据被修改过了。程序保存数据时会检查数据的Version列。如果Version列已经发生了变化,程序会重新读取、修改并保存数据。由于该机制不需要锁定数据行,允许多条线程同时访问同一条数据,因此被称为乐观锁。乐观锁的效率要高于悲观锁,因袭从现代编程中更倾向于乐观锁。

在现代的web编程中,开发者已经不需要关心悲观锁、乐观锁的实现细节,只需要配置一下即可,框架底层会自动实现。

1、悲观锁的使用


Hibernate的悲观锁是使用SQL语句或者HQL语句实现的,下面是一个典型的倚赖数据库的悲观锁调用:

select * from users wherename=”Jack” for update

这条 sql语句锁定了users表中所有符合检索条件(name=”Jack”)的记录。本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。Hibernate的悲观锁,也是基于数据库的锁机制实现。

下面的代码实现了对查询记录的加锁:

String hql ="fromUsers where name='Jack';

Query query = session.createQuery(hql);

query.setLockMode("users",LockMode.UPGRADE); //对users表进行加锁

List list = query.list();// 执行查询,获取数据

query.setLockMode 对查询语句中,特定数据库表的记录进行加锁(这里是users表),这里也就是对返回的所有 users记录进行加锁。

观察运行期 Hibernate生成的SQL语句:

select users0_.id asid, users0_.name as name, users0_.group_id

as group_id, users0_.user_type as user_type, users0_.sex as sex

from users users0_ where users0_.name='Jack' ) for update

这里 Hibernate通过使用数据库的for update子句实现了悲观锁机制。

2、乐观锁的使用:


Hibernate支持乐观锁,保存数据时Hibernate会自动完成检查Version列、修改数据、更新Version列等工作。Hibernate隐藏了所有的Version操作细节,只需要指定实体类的Version列即可。实体类中可用@Version配置版本属性。版本列一般为数字类型属性。例如:

@Version
private int version;
XML中使用<version />配置乐观锁,name属性配置版本列。注意<version/>版本列要配置在<id/>主键后面、<property/>普通属性前面。例如:
<version name="version"></version>
XML配置版本属性要比@配置灵活,版本属性既可以为int、long等数据类型,也可以为Timestamp时间戳等类型,配置时用type配置类型,例如:
<version type="timestamp" column=”version”>
或者直接用<timestemp/>配置日期版本,与上面的配置是等价的:
<timestamp column="version">

【大话Hibernate】Hibernate两种实体关系映射详解相关推荐

  1. Hibernate一对多/多对一关系映射详解及相应的增删查改操作

    以客户与订单的关系为例 1.在实体模型类中绑定两者关系: 客户类: public class Customer {private Integer id;//客户idprivate String nam ...

  2. Hibernate对象关系映射详解之一对多关系映射

    Hibernate对象关系映射详解之"一对多"关系映射 之前学习Hibernate框架的时候,对这七大关系映射一直是云里雾里的,虽然可以仿照写出代码,但是不能独立编写出来.鉴于工作 ...

  3. CCIE理论-第十三篇-IPV6-路由-静态+(EIGRP+OSPF)两种做法+IPV4-ARP代理详解(精髓篇)

    CCIE理论-第十三篇-IPV6-路由-静态+(EIGRP+OSPF)两种做法+IPV4-ARP代理详解(精髓篇) 其实呢,路由协议,静态路由 他还是ipv4那一套,只不过多了点东西 该怎么搞怎么搞, ...

  4. Hibernate之两种一对一关系映射方式

    前言 在前面的Hibernate学习中已经介绍了多对一.一对多.多对多的关系,本文主要介绍一对一关系.比如:一个用户有且仅有一个身份证,一个身份证也只能对应一个用户.而在学习Hibernate框架主要 ...

  5. hibernate 表关系映射详解之多对多

    举例:商品类型表与商品表,每种类型对应多个商品,每个商品对应多种类型 关系图: hirbernate实现多对多映射有两种方法,第一种是通过中间表直接映射,第二种是通过中间表间接映射. 直接映射: 配置 ...

  6. 第二章 关系映射详解

    本章学习目标 generator 主键策略 对象关系映射之一对多映射 cascade 和 inverse 配置详解 对象关系映射之多对多映射 对象关系映射之一多一映射 1. generator主键策略 ...

  7. 深入php-fpm的两种进程管理模式详解

    php-fpm的两种进程管理模式 php-fpm的进程数也是可以根据设置分为动态和静态的. 一种是直接开启指定数量的php-fpm进程,不再增加或者减少: 另一种则是开始的时候开启一定数量的php-f ...

  8. iOS 两种预览使用详解(配Demo下载)( Quicklook UIDocumentInteractionController)

    文章目录 二. 使用 1. 首先,引入框架 2. 将QLPreviewController设置为属性 3. 初始化 4. 设置代理和数据源 5. 编码问题 个人小结 QLPreviewControll ...

  9. linux下如何解压iso文件怎么打开方式,ISO文件如何打开 ISO文件的两种打开方式【详解】...

    ISO文件如何打开? 我们下载的大部分LINUX安装文件都是iso格式,下载到本地后双击会被WINRAR打开,许多人把它当成了压缩包,这是不正确的,ISO文件通过一些手段不需要解压就可以直接硬盘安装. ...

最新文章

  1. 禅修笔记——硅谷最受欢迎的情商课
  2. 【Linux】10_存储管理EXT4文件系统详解
  3. 工程师文化播客: 从工程转向管理,访谈Github公司的Phil Haack
  4. Koa -- 基于 Node.js 平台的下一代 web 开发框架
  5. 扫地机器人电路原理图_扫地机有这一台就够了:石头扫地机器人T6 首拆
  6. 如何安装php5.5,源码安装php5.5
  7. 数据可视化系列(一):Matplotlib初相识
  8. 【Flask】Flask常用信号
  9. 搜狐-新闻页 粗略整理-自我学习
  10. Android Sensor Driver(四)——IIC总线和驱动
  11. MPLS ××× Carrier Supporting Carrier Option AB(一)
  12. 【渝粤教育】国家开放大学2018年春季 4990T电子商务概论(农) 参考试题
  13. Java集合对象详解
  14. php cunstruct,php读取二进制流(C语言结构体struct数据文件)
  15. 利用python画简单图形示例代码_使用python画社交网络图实例代码
  16. 对php课程的建议,万紫千红总是春——对新课程语文教学、复习的建议与采饶措施a href=http://www.ruiwen.com/friend/list.php(教师中心专稿)/a...
  17. 电力变电站三维可视化
  18. 《论语》原文及其全文翻译 学而篇12
  19. 清空hive表 姿势大全
  20. 站长必懂!什么是CPM、CPC、CPA、CPR?

热门文章

  1. 读:[你必须知道的.NET] 第五回:深入浅出关键字---把new说透
  2. Microsoft uaa bus driver for high definition audio
  3. 传感器应用的demo自动录音器
  4. android Drawable.mutate()的使用
  5. Android Studio 生成 ButterKnife 注入的插件
  6. jdbc连接Oracle/MySQL数据库进行批量导入操作,如何提高效率???
  7. Java并发—锁的四种状态
  8. 暂时关闭 windows 病毒防护
  9. 二十一、Hadoop学记笔记————kafka的初识
  10. ROW_NUMBER、RANK()、DENSE_RANK()和OVER的使用