JPA3|学习笔记

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 快速学习JPA3

开发者学堂课程【高校精品课-上海交通大学 -互联网应用开发技术JPA3】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/76/detail/15756


JPA3


内容介绍:

一、回顾上节知识

二、使用or映射工具的原因

三、实体

四、Servelt存储对象

五、实体-person

六、PERSON_EMAIL

七、网络基本应用接口

 

一、回顾上节知识

上节课讲了关系型数据库的访问,谈了几个问题第一个是为什么需要or映射的工具来帮助映射,谈到了or映射是在对持久化进行操作,强调在数据中有持久化的数据,也有非持久化的数据,在前端操作的时候反复强调 HttpSession 对象,讲到 serverlet 如何在 Http 的request 里面去插入属性或者在 HttpSession 里面插入属性,都属于在内存里的瞬时性对象操作,持久化对象指的是要脱离应用是仍然存在的数据,特指的是在关系型数据库中存储的,因为现在还没有操作非关系型数据库等关系型数据库存储,只能在硬盘存储,受关系型数据库管理系统管理的数据不像内存中的数据, Tomcat 退出之后就不存在了仍然存在。


二、使用or映射工具的原因

Java是完全面向对象编程的一种语言,所以希望以面向对象的方式处理数据库中的数据,or映射实际上相当于中间隔离的一层代码,将访问数据库的操作映射成对象的操作,所以看到的都是对象,不存在关系型数据库中的数据,也就是在操作数据的时候完全是操作对象,完全不用关心对象是如何写到数据库中的。在映射的时候讲到大体是一个类映射一张表,表中的一条记录映射的是类中的一个实例,类里面定义的属性就是表中定义的列,每一个对象在每一个属性的取值就是在表里面每一行记录上每一列的值,

图片135.png

但是二者不是完全一一对应的包括粒度、继承结构,讲的person实际上是粒度的问题,实际上有四张表但是映射出来有两张表。继承后面会讲,ID是数据库里面靠主键区分的每一条记录的差异,在内存里面靠对象的引用也是地址来区分对象之间的差异,所以很可能碰到抽象出来两个对象,地址不一样,但实际表达的会有同样一条记录,这个问题现在可能不会遇到未来随着系统的复杂多键成编程时会遇到。关联也是上节课提到的会有 person 和 event 之间的关联表,在关系型数据库里只能把一个多对多的关系,拆成两个一对多的关系,然后去进行映射,在映射的时候 person_event 表格没有了,没有去映射,其次在关系型数据库里如果是一个一对多关系,永远是多的一方有外键关联 person ,即一个人可以参加多个活动,方向只能是由 person_event 指向 person,在关系型数据库决定的只能是n对一,但是在写的例子中可以看出谁引用谁是看 person 里面是否有set类型的变量存储了一组event,如果有相当于 person 引用event,反过来也是一样 event 里面也可以有 person 类型的变量,表示事件有多少人参加,也就是 person 可以指向 event,event也可以指向 person,也可以是双向,这和在数据库中看到的只能是多对一是不同的,这时上节课看到的东西包括在数据库里面存储数据,在从 person 里面找到关联的实践的时候一定要做表的关联找到,但是在类里面直接访问 set<event> 的属性就可以拿到,所以数据的导航也是类和关系图示简单一一对应的。所以基于考虑大体上是一个类在映射一张表在实际上有很多变化要看实际的设计,比如想要简单将关联表映射成实体类不代表会出错,但是并不好。


三、实体

在概念里面所谓实体是在是在实体中能够识别出来的,但刚才讲的例子中只有人和活动,人和活动之间的关联不是一类实体,所以抽象成一类实体不是一种好的设计方案,定义一个实体,实体中有很多的数据成员,数据成员的取值表示实体的状态,状态是持久化的状态,也就是要写入到数据库里的状态,or映射工具就会将实体的状态写入到数据库中。但是也有例外后面会提到,就是一个实体比如有 person类里面可能会有很多属性,但是某一个属性不想写进关系型数据库中想要写其他的,如果想知道细节在讲 NoSQL 的时候放过一个例子,person 的大多数内容都在 MySQL 里存储,在 MongoDB里面存了 icon 图片,两个数据虽然在两个不同的数据库中存储,但是都在 person 类里面定义,但是icon图片类型的属性是不需要写入到 MySQL 里面,在映射中看到,任何事都可能会有例外。

如果真是这样的 person 属性不需要Hibernate写到关系型数据库中。实体里面看到持久化的域或者属性,也就是类里面的数据成员,对每一个属性都要有相应的 get 方法获取值还有相应的set方法对值进行设置,给出了 event 和 person 的例子,第一个版本的 event 非常简单,直接映射数据源映射数据库中 events 的表,这样表的名字和类的名字之间实现解耦不一定要一致,对于里面的属性可以用column 的 annotation 表示对应的数据库中的字段的名字叫什么,所有实现了类的属性和字段名的解耦。

必须要有无参数的构造器,ID可以用数据库中自增的策略,这样在定义 events 的时候不需要人为的维护主键,将信息合在一起之后告知是一个 Entity。通过这个例子可以说明表的名字和类的名字可以不一样,一个类映射一张表,column的名字可以和域的名字不一样,类里面的域映射 column,所以发现二者都可以不一致,Entity 和表之间是完全被解耦,解耦的意义在于如果 event 类写得非常好,可能写了很多应用,在很多大作业中都要做相应的编码工作,但是可以发现 events 经过编写在所有的大作业中复用代码代码可以复用,在不同的大作业中 table 表可能不太一样,比如表达的含义不同于是表的名字和列的名字会有所区别。

但是因为有了这样的机制,就会发现无论表叫什么列叫什么,只要有 string 类型的列、date 类型的列、long 类型的主键,Entity类可以映射到表中,在新系统作业中叫 party,记录 party 的时间和title,在系统软件里面比如叫 ceremony 叫做 topic,不管叫什么只要有三种类型的列,类就可以进行映射,因此会进行处理。

图片136.png

这时解耦的意义是不会让代码和具体的表的结构绑定,这是or映射里面非常重要的概念。

在上面举的例子中还缺少一个东西说明了表和列,问题是表是哪个库的表,因此需要配置文件,配置文件里面会说清楚库在哪里、用的驱动类是什么、用户名、密码等信息,这些类合起来将 event 唯一的映射到 events 表中,映射到 ormsample 库中,hibernate 不会主动托管类因此会产生

mapping class=org.reins.orm.entity.Event告知 hibernate 帮助托管,整个例子就合在一起。

图片137.png


四、Servelt存储对象

接下来写了助手类,上课谈到在数据库中执行操作的时候必须在session 上处理,session是通过 sessionFactory 获取的,sessionFactory 是非常重量级的对象,在系统中不能有多个,是单例的只能有一个,因此写助手类通过助手类获取 sessionFactory,之后的操作基于 sessionFactory 操作,比如获取 sessionFactory在上面获取 session,所有的操作通过 session 操作全部写到事务中,事务接下来会讲现在不要深究,记住做的所有事情想让hibernate 帮助处理,一定要在 session.getTransaction.commit里面写,中间的操作Event t =new Event();t.setDate(Date); t.setTitle(title);session.save(t);需要hibernate进行托管。仔细看中间部分的操作,确实是在面向对象操作,根本没有数据库,只有event存在数据库里,主键也没有去设写到数据库里能自动设置,在实践上getid 时可以将与它关联的ID取出。

图片138.png

接下来跑The event has been inserted例子,上课时已经跑了可以产生效果,这是只有一个 event 的例子。在 hibernate 中想要获取 SessionFactor,在 SessionFactor 上创建 session,在 Session上创建Transaction,在Transaction里面执行操作,这便是hibernate的结构。

图片139.png

在映射时讲过 EVENTS 为一个类,PERSON 为一个类,剩下的两个PERSON_EVENT和PERSON_EMAIL处理方式不同。PERSON_EVENT是一个关联表将一个多对多关系拆成两个一对多,在两个一对多中可以看到将关系组合实现之后,会有两个集合一个是 person 集合,一个是 event 集合,中间的表格 PERSON_EVENT 没有映射,在用两个1对多的关系模拟一个多对多的关系,在关系型数据库中只能这样做。

在 PERSON_EMAIL 中是一对多,不想要把PERSON_EMAIL映射成实体,因为 email 是依附人的,人没有了email单独没有存在的价值,所以将一对多的关系映射成person类的属性,这是上节课没有提到的。类会变为 events 多了一个集合类型的属性private Set<Person>participants=new HashSet,首先通过ManyToMany告知event与participants的person是多对多的,再通过JoinTable说明多对多是通过数据库的一对多实现的。一对多的表格叫做PERSON_EVENT还需要说清楚,PERSON_EVENT是关联表,关联表的主键是EVENTS_ID和PERSON_ID合成的组合主键,两个分别是外键关联另外两个表格中一对多的主键,所以需要清楚表中的event id用的是自己的id,对方的id是person_id对应的是他本身的id就结束。EAGER之后会讲。

 

五、实体-person

在person中首先看event,event是一个集合类用list或者set都行只要是集合类都可以。其他的都和前面的person相似,在多对多的关系中Entity-person中只需要写是多对多的关系,和event产生关联,所以在 even t中找participants指的是自己,所以两边会关联起来,这是 event 中的 participants,在person.java中event与participants关联,event里面的participants指的是event类二者就会关联,即不会去写向Event.java中复杂的东西。

接下来讲EAGER,首先看 getparticipants,比如有人想要getevent,将event对象取出,站在Hibernate的角度,一边是数据库要与数据库建立关联,另一边是Tomcat,数据库上面是 MySQL,要将event记录取回,id、title、date都是这条记录这时就取出了,在组装event对象时id、title、date都取出,participant 这组人是否需要取出,实际上之前看到的是3张表分别为person、event以及 person 和event的关联表。当取出event记录时是否需要到 person 和 event的关联表中找关联的person再到person表中将 person 的信息取出组装到participants里面,如果在数据库中加载event时同时将person、event 以及 person 和 event 的关联表回传到 Tomcat中一起进行就叫eager。

Eager的好处是在 getparticipants 时因为已经将 person 全部取出,在 Tomcat 映射里将数据发给要访问getevent的用户,比如前端是 servlet 将数据直接给 servelt 就可以,不需要再访问数据库,在一次数据库连接里面将与 getevent 相关的所有的数据全部取出。缺点是 person 比较复杂,即一次取回来的数据是一个event加上n个 person 一起取回,数据传输量会比单取event本身的数据多很多。

好处是未来在获取很多人的时候马上可以获取到,这是 getevent 的优缺点,除此之外还有一种策略即只取前面的部分id、title、date关联的人先不取,这是可能会有人在event对象上,因为 event 是对象所以需要小写,如果 event调getparticipants 方法的时候,因为已经一次性完全取出到Tomcat 中直接返给 servelt 就可以。

图片140.png

只加载了上面部分的id、title、date数据,person还没有加载即不在里面,当get的时候Hibernate为空,之前没有加载过,再发起一次数据库连接到数据库里,将与它关联的人取出,这种方式叫做Lazy,lazy的优势在于加载数据的时候不知道用户访问什么,也许用户的操作都大量体现在id、title、date字段上不关心person,将person取出一方面加重数据库做表连接的负担,另外一方面传出的数据量很多所以先不去加载,直到有人访问调getparticipants才发现没有调用再将它拿出。

Lazy的想法是对于某些数据会通过关联表加载的数据或者是其他数据,对于这些数据在一开始没必要加载,因为对这些数据操作的可能性比较小,所以使用lazy,这样加载的数据比较少,到数据库中读一份数据回来不需要传很多数据,当真正要去读的时候再去抓,但是全部是靠Hibernate处理的,所以用户写的代码只需要调getparticipants,无论是eager还是lazy调的都可以折射到,所以这件事情对用户是透明的只是Hibernate处理的,lazy的好处是在开始的时候传的数据量少,缺点是在真正读没有传过来的数据时需要在到数据库中找一次,再发一次请求,如果遇上这种请求要变一些数据库连接花费的代价大一些。Eager在数据中操作的概率非常高不需要多次发数据库的请求找数据一次性可以全部取回。加载event时相关联的数据全部取出,这便是eager,有eager则证明数据全部在Tomcat里,数据全部加载过。

用eager和lazy都可以需要按照应用的需求来看不能立刻说是eager好还是lazy好,如果有一个领先的话就不会有两种选择要根据应用进行选择。现在讲什么样的属性可以被lazy加载。首先像PERSON_EVENT关联的数据不在表中一定可以被lazy加载,其次在数据库中没有说字段必须不为空,即允许字段为空也会加载。在events表中person不在其中它是关联数据加载,这种类一定可以设置lazy或者eager,在events表中id不能为空,如果允许title为空,date不能为空,在加载时title也可以是lazy加载,在events上面设FetchTYP是lazy,即Hibernate碰到希望是lazy时是在加载event表中所有不为空的字段,为空的字段可以不拿可能没有所以不加载。Eager不会去管上面提到的东西会直接去加载。上节课提到的循环加载,需要eager加载person,person中有event列表,person中的event列表写了eager,剩余的person.java中的event需要加载,加载event又会涉及到event中的person,即Event1有两个参加的人p1和p2,如果是eager在加载Event1的时候需要加载p1和p2,而p2参加了两个活动E2和E1p2参加了两个活动E2和E3,在加载P2的时候需要加载E2、E3还有加载两个E1如此循环往复便是循环加载。在Hibernate中可以打破不再继续进行循环,所以去看是没有问题的只是加载了person,循环加载下只加载了person。上节课提到在string中有些问题需要加上两个annotation遇到之后还会在进行讲解。上面讲的东西都是上节课提到的知识。


六、PERSON_EMAIL

接下来讲解PERSON_EMAIL表格是如何映射的,PERSON_EMAIL表没有映射成一个实体只是person里面的属性,PERSON_EMAIL表是一对多的,即一个人可以有多个邮箱地址,所以仍然是list类型的,会有List<Event>emailAddresses=new ArrayList的属性,在List<Event>emailAddresses=new ArrayList的属性上annotation需要用ElementCollection说明下面是一个集合,而且集合来自一张表,即属性来自一张表,表叫做PERSON_EMAIL,既然可以这样表示则说明是一个1对多的关系,在一对多关联关系里面是PERSON_ID在关联person表格中的id,PERSON_ID是一个外键,又因为是一对多的所以一个人会有多个email,所以id为1的人会有多条email。PERSON_ID和EMAIL_ADDR合起来是主键,唯一的标识是一个email。在PERSON_EMAIL一对多的表里面PERSON_ID在关联person表格中的id,所以在person.java里面的PERSON_ID在映射当前的id,如果去找在person-email里面PERSON_ID等于当前类的person id的记录,Columu在映射EMAIL_ADDRESS。如果将一张关联的表映射成一个属性当前用的是ElementCollection和CollectionTable两个annotation进行描述,而如果有两个表是多对多的关系,通过中间的第三张关联表映射用的是ManyToMany和JoinTable,二者用的annotation不同,所以在映射完会发现不会有一个叫做PERSON_EMAIL的实体类,PERSON_EMAIL表中的内容会被映射成person的属性。这便是四张表变为两张表的映射方法。

图片141.png

这个例子至少在回答前面什么是映射的一个坑。在这个例子中可以看出来的一些问题第一个是表和表肯定不是一对一关联,四个表只有两个类,第二映射的方向person和person_email是一对多的永远有一个外键从多指向一,但是在映射类的时候是一个person持有一组email。

在类中的关联关系和表和表之间的关联关系方向是反的,数据的导航方式是通过person获取person_email,从这里可以看到OR映射讲的大的原则表映射成类,类里面的实例映射的是表里的记录,这只是大的原则,细节会有一些变化,完全是因为一端要用面向对象的方式来考虑问题,而另一端是关系型数据库要以二维表个的方式考虑问题,思路不一样所以会有不匹配的情况,要用OR映射工具完成两种思路之间的转换,所以简单的是四个表映射四个类说明用法是错的,这便是PERSON_EMAIL是如何进行映射的。

全部映射完之后例子已经讲过就不再去说,可以看到在表格里运行,运行的结果是谁和谁参加了什么,可以看到目录的结构,首先hibernate .cfg.xml需要放到resource目录里,应用需要自己运行,将数据库中的东西导出从脚本中导进去之后库的脚本在库里面运行,MySQL的客户端工具运行会在库中建立相应的表,需要强调的是有两个servlet和两个实体类,没有讲更复杂的结构,但是学习了用不同的包组织代码。

Controller一层在写接收前端请求的代码比如servlet,entity在存储之前映射的两个类,通过一个简单的例子可以了解到不能将所有的代码全部放在一个包里甚至没有包直接在Java目录里写类,因此需要学会用包组织代码,组织代码的方式非常的直白非常的直观,按照角色和起的作用不同组成不同的包,写任何一个代码都需要这样考虑,在学习c++程序设计的时候将东西放到不同的命名空间处理实际上是一样的道理。这个例子是直接写了一个应用,然后放到了自己安装的Tomcat上,运行起来就是之前看到的已经跑过,可以看到一些SQL的语句,在里面会有show SQL的选项打开之后会是下图所示的样子。随便写一下,比如exam考试,时间为2022-05-01便会加入,将所有的事件全部取出五月一号考试会被添加在最后,id=68是数据库自动生成的,读回来在前边显示68会显示出来,假设1号人要去参加68考试会显示1号是cao cao,cao cao要去参加这个考试。

图片142.png

在后台输出,Hibernate实际在执行的SQL语句,会将信息写进SQL语句。

在servlet中执行代码时,逻辑上的工作不再去讲,无论是获取实例还是将实例更新全部是在transaction中执行,开启transaction获取用户要求加载的人和事件,将人的事件列表中添加新的事件,把事件的参与人列表中添加进去当前的人,增加一个新的email,之后update,所有的事情都发生在内存中,在commit进行提交的时候,数据写入到数据库中,下面在进行了相应的操作之后在开transaction,无论做什么开transaction获取里面人的信息,会显示开transaction进行commit,即做所有的东西都要在transaction里面执行。

这是需要强调的,原因是只有这样做Hibernate才会知道需要帮忙的事情,才会帮忙去做,所以直接在创建一个对象之后进行操作,所有的事情都只会发生在Tomcat的内存中,没有办法写到数据库,只有在开saction以及transaction,在transaction里面执行最后提交才能写入数据库。

图片143.png


七、网络基本应用接口

通过上面的例子可以看到,首先SessionFactory在整个应用中是单例的只有一个,是一个变量一旦创建出来就不能修改,通过助手类获取SessionFactory的引用开session,在session里面执行持久化的对象或集合的动作。

在Transaction执行之前的操作需要注意的是什么是Transient以及什么是detached的对象,二者有些复杂在后面讲到时进行详细的解释,一点一点的推进。

现在来看之前的例子,实际上所有的Entity即实体类必须在前面加Entity的annotation 进行标记,这样Hibernate才会进行处理,在主键上加annotation是id,注意annotation不是加在属性上是加在对应的get方法上,前面讲的person和event用的是同样的方法。在这里产生的疑问是为什么要产生Serializable的接口,Serializable比较复杂,但是想到知道并不困难,在c++里也有类似的东西,比如创建一个Flight的对象能不能从Tomcat里传递到在运行另外一个应用是还有一个Tomcat,将greet部署在里面,假设存在这样的情况,Tomcat或者其他的程序要发给另外一个Tomcat,如果Flight对象通过这种方式能够发走需要实现接口,接口像一个标记接口没有什么方法要求必须实现,不理解也没有关系。因为所有的应用都在一个Tomcat里面运行,不存在这样的需求不写就可以。如果想要关心就是刚才讲过的在两个进程之间传递数据时想要将一个Java对象传给另外一个进程,从一个Java虚拟机传给另一个Java虚拟机还能用则可以实现。

写的程序不复杂可以忽略,忽略之后可以看到一个普通的实体类最重要的是要有Entity和主键,其他的都可以没有,前面讲的类虽然听起来很复杂,复杂的原因是要用自动生成建,如果什么都不写,默认自己维护主键,想用数据库的自动生成建再去标记就可以。在person.java中属于比较复杂的情况,需要有关联的时候才去写,如果表非常简单只有age、first name、last name上面是没有标记的。所以作为实体类至少需要有Entity和ID两个annotation,一个表示的是实体,一个表示哪一个是id,id要写入主键一列。ID要保证很多东西比如不能为空,必须是唯一的。

所以Hibernate会特别关注ID这样的annotation标注过的属性,其他的都不是必须的,Entity和id是必须的,所以看到前面的总结完成后,再看就是这样的。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7月前
|
数据采集 容器
xpath实战
xpath实战
33 0
|
存储 Java 关系型数据库
JPA1|学习笔记
快速学习JPA1
114 0
JPA1|学习笔记
|
存储 前端开发 NoSQL
JPA 5|学习笔记
快速学习JPA 5
111 0
JPA 5|学习笔记
|
SQL 前端开发 Java
JPA2|学习笔记
快速学习JPA2
79 0
JPA2|学习笔记
|
存储 前端开发 JavaScript
JPA 7|学习笔记
快速学习 JPA 7
93 0
 JPA 7|学习笔记
|
存储 JSON 前端开发
JPA 8|学习笔记
快速学习JPA 8
132 0
JPA 8|学习笔记
|
存储 前端开发 Java
JPA 6|学习笔记
快速学习JPA 6
128 0
JPA 6|学习笔记
|
存储 自然语言处理 开发者
WhitespaceAnalyzer|学习笔记
快速学习 WhitespaceAnalyzer
63 0
WhitespaceAnalyzer|学习笔记
|
机器学习/深度学习 存储 人工智能
GraphLearn on PAI|学习笔记
快速学习 GraphLearn on PAI。
103 0
GraphLearn on PAI|学习笔记
|
物联网 Linux 开发者
Pause|学习笔记
快速学习 Pause
82 0
Pause|学习笔记

热门文章

最新文章