Hibernate映射关系及CRUD操作

Stella981
• 阅读 1011

Hibernate对象的三种状态的概念及互相转化:

  1. 瞬时状态或临时状态(Transient):用new创建的对象,它没有持久化,没有处于Session中;
  2. 持久状态(Persistent):已经持久化,加入到了Session缓存中。如通过hibernate语句保存的对象;
  3. 三脱管状态或游离状态(Detached):持久化对象脱离了Session的对象。如Session缓存被清空的对象。特点:已经持久化,但不在Session缓存中;

 

临时状态(Transient)

持久化状态(Persistent)

游离状态(Detached)

是否处于Session缓存中

×

×

数据库中是否有对应记录

×

Hibernate映射关系及CRUD操作

游离对象和临时对象异同:

两者都不会被Session关联,对象属性和数据库可能不一致;

游离对象由持久化对象关闭Session而转化而来,在内存中还有对象所以此时就变成游离状态了;

深入理解Hibernate和SQL的关系:

在操作了hibernate的方法如save()等后,并没有直接生成sql语句,去操作数据库,而是把这些更新存入Session中,只有Session缓存要被更新时,底层的sql语句才能执行,数据存入数据库。下面举例说明:

一、Session.save(user)运行机理。

  1. 把User对象加入缓存中,使它变成持久化对象;
  2. 选用映射文件指定的标识生成ID;
  3. 在Session清理缓存时候执行:在底层生成一个insert sql语句,把对象存入数据库;

PS:在你执行Session.save(user)后,在Session清理缓存前,如果你修改user对象属性值,那么最终存入数据库的值将是最后修改的值;此过程中ID不能被修改;

二、Session.delete(user)运行过程。

  • 如果user是持久化对象,则执行删除操作,同样底层数据库的执行条件是:在Session清理缓存时候;
  • 如果user是游离对象则先将user对象和Session关联,使之成为持久化对象,然后按照user是持久化对象的过程执行;

load()和get()方法的区别:

  • load():hibernate对于load()方法认为该数据在数据库中一定存在,由于session中的缓存对于hibernate来说是个相当廉价的资源,所以在load时会先查一下session缓存看看该id对应的对象是否存在,若不存在就创建代理来延迟加载,如果在使用过程中发现了问题,只能抛异常,实际使用数据时才查询二级缓存和数据库;
  • get():而对于get()方法,hibernate会首先在session缓存中查找,然后在二级缓存中查找,还没有就查数据库,数据库中没有就返回null。

创建SessionFactory的三种方式:

在以前的版本中,通常我们创建SessionFactory的方式是:

configure.buildSessionFactory();

Hibernate4.0之后引入新特性——Service Register机制,创建方式为:

StandardServiceRegistryBuilder serviceRegistryBuilder = new StandardServiceRegistryBuilder(); 
ServiceRegistry serviceRegistry = serviceRegistryBuilder.build(); 
SessionFactory sf = configuration.buildSessionFactory(serviceRegistry); 

但是紧接着在4.1之后的版本中,StandardServiceRegistryBuilder又被取消了,取而带之的做法是:

Configuration configiguration = new Configuration().configure();
ServiceRegistryBuilder builder = new ServiceRegistryBuilder().applySettings(configiguration.getProperties());
ServiceRegistry registry = builder.buildServiceRegistry(); 
factory = configiguration.buildSessionFactory(registry);

创建 Configuration类的对象时执行 configure() 方法:

Configuration cfg = new Configuration().configure();等同于Configuration cfg = new Configuration();

configure()方法默认会在classpath下面寻找hibernate.cfg.xml文件,如果没有找到该文件,系统会打印如下信息并抛出HibernateException异常。

关联关系映射(关联关系映射在数据库里没有任何区别,区别在于类):

1、一对一外键单向关联

主表类引用副表类,并在主表类的引用字段的get()方法上加@OneToOne

get()方法上加 @JoinColumn(name="该属性在数据库映射的字段名") 注解,在副表添加映射字段(缺省默认为.......)

2、一对一外键双向关联

主表类与副表类互相引用,并在各自引用字段的get()方法上加@OneToOne 

建议在副表类设置mappedBy属性 @OneToOne(mappedBy="主控端引用的属性名") 注解,表示被控端不用额外添加外键关联,由对方设置(mappedBy的值是属性的get()方法逆向生成,但是通常与属性对应,可理解为等同,通常双向关联必设)

3、联合主键关联

被控端为有联合主键的类,主控端要在引用字段的get()方法上加 @OneToOne 

get()方法上加上如下注解:

@JoinColumns(value={
            @JoinColumn(name="主键1映射字段",referencedColumnName="所参考被控端主键1属性"),

            @JoinColumn(name="主键2映射字段",referencedColumnName="所参考被控端主键2属性"),

})

4、组件映射

被嵌入类不要写 @Entity 注解,因为不作为单独表在数据库映射

主类要有被嵌入类的引用,在其get()方法上添加 @Embedded 注解

5、多对一外键单向关联

多对一在“多”的一方添加“一”的一方的引用,并用 @ManyToOne 注解

用 @JoinColumn(name="多的一方添加的字段名") 指定添加映射字段名。(缺省默认为.......)

6、一对多外键单向关联

一对多在“一”的一方添加“多”的一方的集合的引用(通常用Set,但是当集合里的对象需要排序时可以使用List,并用@OrderBy()注解——较少使用。最好将成员变量初始化,如:

private Set<User> users = new HashSet<User>();

),并用 @OneToMany 注解

一对多若缺省@JoinColumn注解会被默认当做多对多的特殊情况处理,即生成一张中间表

避免此情况应加一个注解 @JoinColumn(name="多的一方添加的字段名") 

数据库添加的映射字段总是加在多的一方,因为数据库没有集合的概念

7、一对多&多对一双向外键关联(等同)

通常把“多”的一方作为主控端,因为以数据库的角度,关联字段在“多”的一方

主控端和被控端互相引用,即在“一”的一方添加“多”的一方的集合的引用,在“多”的一方添加“一”的一方的引用

主控端在引用字段的get()方法上加 @ManyToOne 注解

被控端在引用字段的get()方法上加 @OneToMany 注解,并设置mappedBy属性为主控端引用的属性名

8、多对多中间表单向关联

主控端引用被控端,并在引用字段的get()方法上加 @ManyToMany

要手动定制中间表的内容,可添加如下注解:

@JoinTable(name="role_user",
        joinColumns={@JoinColumn(name="userID")},          //joinColumns是主操作表的中间表列
        inverseJoinColumns={@JoinColumn(name="roleID")}    //inverseJoinColumns是副操作表的中间表列

)

用joinColumns而不是joinColumn是因为可能会有联合主键

8、多对多中间表双向关联

“一”的一方和“多”的一方互相引用,即在“一”的一方添加“多”的一方的集合的引用,在“多”的一方添加“一”的一方的引用,并在各自的引用字段的get()方法上加 @ManyToMany 注解,其中一方设置mappedBy属性

关联关系的CRUD操作——以一对多&多对一双向外键关联为例

cascade属性体现在CUD操作(Create Update Delete):

1、在“多”的一方加该注解 @ManyToOne(cascade=CascadeType.ALL)

说明级联类型为所有操作都进行关联,即当sava()“多”的一方的对象时,同时sava()关联的对象(即“一”的一方)

若需要sava()“一”的一方的对象时同时sava()关联的对象,可用注解@OneToMany(mappedBy="role",cascade=CascadeType.ALL)

但是这时需要注意必须设定双向关联(即set关联对象),否则“多”的一方在数据库里映射的外键关联字段可能为空。

2、当删除时如果被删除对象的cascade=CascadeType.ALL的话,会先级联删除关联的对象,然后删除自己,若关联得对象
cascade的值也是CascadeType.ALL,则继续级联删除。

要在删除“多”的一方时避免此现象,可以在删以前先手动去掉关联关系,即把关联属性设为null,或自己拼hql语句,再删除对应记录,如果不删记录,该记录就变成垃圾数据;

删除“一”的一方时得必须自己拼hql语句。

总之两点规律:

  • a.双向关系在程序中最好一定要要设定双向关联,这样可以从任何一方做增删改查
  • b.双向一定要设定mappedBy注解

fetch属性体现在R操作(Read):

ManyToOne的fetch默认值是FetchType.EAGER  ——  @ManyToOne(fetch=FetchType.EAGER),如果手动改成FetchType.LAZY,则会在用到关联字段属性时发送SQL语句

OneToMany的fetch默认值是FetchType.LAZY  ——  @ManyToOne(fetch=FetchType.LAZY)

PS:双向关系不要两边都设fetch=FetchType.EAGER,否则可能会发出多余的SQL语句,一般用FetchType.LAZY足矣

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
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
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这