JPA详解

JPA定义了一系列对象持久化的标准。

(零)配置

Maven导入以下包:

<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

在application.yml进行以下配置:

spring:
  profiles:
    active: dev

  #进行MySQL数据库配置
  datasource:
    #数据库驱动名
    #MySQL8以前版本使用com.mysql.jdbc.Driver
    #MySQL8以后版本使用下列驱动:
    driver-class-name: com.mysql.cj.jdbc.Driver
    #数据库要链接的url
    #serverTimezone不是必选项,但某些时候会报错,详细原因自行百度
    url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
    #用户名与密码
    username: root
    password: s14568460s

  #JPA配置
  jpa:
    hibernate:
      #该项决定项目运行时以何种方式建表。
      #create:启动时删数据库中的表,然后创建,退出时不删除数据表
      #create-drop:启动时删数据库中的表,然后创建,退出时删除数据表。如果表不存在报错
      #update:如果启动时表格式不一致则更新表,原有数据保留
      #validate:项目启动表结构进行校验,如果不一致则报错
      ddl-auto: create
      #JPA更新某个版本后,使用@GeneratedValue时做不到自增,加上这个就好了
      #看不懂但是大为震撼
      #也可以在@GeneratedValue中设置strategy = GenerationType.IDENTITY,就可以忽略下面被注释掉的这句了
      #use-new-id-generator-mappings: false
    #在控制台显示SQL语句
    show-sql: true

(一)JPA的简单使用(CRUD)

可以看下面两个文章理解,比我讲得好多了:springDataJpa的入门操作(基本CRUD)_tangiwang的博客-CSDN博客
Spring Boot 2.x 之 Spring Data JPA, Hibernate 5 - CokeCode - 博客园 (cnblogs.com)
博客园的那篇文章讲各个注解讲的很细,强烈建议阅读。
记得每次在方法内执行完增改后要对JpaRepository类型的对象调用save方法。save的参数为new的表。删直接调用dao层的delete方法就好。

(二)@Query注解

@Query注解可以指定JpaRepository方法对应的查询HSQL。
@Query中的参数HQL默认是面向对象的语句,与真实SQL语句有偏差。在该注解下,应当使用实体类的类名和属性名,而非SQL库中的表名和字段名。
想要在@Query输入原生SQL语句,应将其中参数nativeQuery设置为true。
@Query也可以实现更新与删除:@Query 自定义查询及更新删除_Aimyone的博客-CSDN博客_query 删除,但实现不了insert: springData学习(二)@Query注解详解_java_chegnxuyuan的博客-CSDN博客

(三)对象关联关系

对象之间的关联有一对一、一对多和多对多。

一对一:一个人对应一个ID
一对多:一个班对多个人/一个人对多门课
多对多:多个项目对应多个员工

抽象成数据库中的表,我们不难发现对象关联关系的实用性,如某表中的字段可能对应另一个表整表。
接下来讲讲具体操作。

0、键的链接

见下文:数据库设计(一对一、一对多、多对多)皮一下很开心的猴头-CSDN博客数据库一对一

1、一对多/多对一

假设有以下两表:

Person:id(主键),name,age
Class:classId(主键),className

接下来对二者的classId进行链接。

//在Class类下,建立一个属性personList,使得一个班级对应多人
@OneToMany(mappedBy = "class")  //mappedBy用于创建一对多的映射关系,其值为Person类中对应的属性名称
//mappedBy声明自己不是关系维护端,一切由对方维护
private List<Person> personList;
//在Person类下,建立一个class属性,使得多人对应一个班级
@ManyToOne
@JoinColumn(name = "cid")  //指定外键列,name默认值为变量名
private Class class;

(1)各自的增更,与之前的CRUD操作相同。
(2)主键表查询(查询Class中personList):有下面两种方法。
①设置立即加载:调用classRespository.findAll(),在默认情况下实行懒加载策略,即不查询外部链接的库,只查询自身的字段。针对此,我们可以在@OneToMany中将fetch属性(默认值为FetchType.LAZY)改为FetchType.EAGER
②延长Session生命周期:声明事务@Transactional。
(3)外键表查询(查询Person中的class):与CRUD操作相同,因为其为立即加载,并非上述的懒加载。
(4)删除主键表(删除Class类中某一项):直接删除会报错,因为Person类中的class还在链接。有以下解决方法:
①级联删除:在Class类中的OneToMany中对属性cascade(默认空)设置CascadeType.ALL。关于级联的具体操作可以看Hibernate @OneToMany 及 @Cascade级联操作 - 云+社区 - 腾讯云 (tencent.com)
②断开键的连接,再删除主键表:先用update将外键设置为null,断开主外键连接,再执行删除操作(会报no session,需添事务延长session周期)。
(5)删除外键表(删除Person类中某一项):直接删除即可。
这里有一个问题:@OneToMany中fetch设置为FetchType.EAGER时会导致只输出两条SELECT语句而无法删除Person中的某项,解决方法是删除这句或在@ManyToOne中设置此项为LAZY(摘自JPA关于fetch=FetchType.EAGER级联删除的问题_C.-CSDN博客
)。
(6)添加主键表信息同时添加外键表信息(添加Class同时添加Person):注意双向维护,主键和外键都需要添加。后续更新:若报错,应关注瞬时态对象与持久态对象,下文有提到。

2、多对多

假设有以下两表:

Subject: sId(主键), sName
Person: pId(主键), pName

接下来对两表进行链接。注意的是,只应当指定一个表进行主键的维护,防止主键重复。

//在Subject类下,建立一个属性pList,使得一门课对应多人
@ManyToMany(mappedBy = "sList")  //Subject类放弃外键维护权
private List<Person> pList = new ArrayList<>();
//在Person类下,建立一个属性sList,使得一人对应多门课
@ManyToMany
@JoinTable(name = "sub_person", 
uniqueConstraints = {@UniqueConstraint(columnNames = {"p_id", "s_id"})}, 
joinColumns = {@JoinColumn(name="p_id", referencedColumnName="pId")},
inverseJoinColumns = {@JoinColumn(name="s_id", referencedColumnName="sId")})  
//@JoinTable描述当前实体类与中间表之间的关系
//name值为中间表名
//uniqueConstraints中间两个字段名(非属性名)组合起来组成联合主键,其余一大坨照抄(注解相互嵌套不想解释了)
//joinColumns表示当前对象在中间表中的外键,name为外键名,referencedColumnName为参照主键表的主键名称
//inverseJoinColumns表示对方在中间表的外键,与joinColumns类似,不再赘述。
private List<Subject> sList = new ArrayList<>();

Subject通过mappedBy属性的设置放弃了外键维护权利,以后就由Person表维护两表关系了。
看着复杂,其实就是一对多的补充扩展。
(不过确实好难理解55555)
接下来是CRUD。
(1)添加操作:
首先new几个Subject对象和Person对象。
在Subject类直接添加Person到pList并对subjectDao进行save:能正常运行只能添加Subject,中间表和Person无法添加,因为pList加了mappedBy属性,不负责外键维护。被维护的表不需要向键内添加任何数据。
在Person类直接添加Subject到sList并对personDao进行save:编译错误。现在Subject没有受session管理而Person受session管理,直接save不能成功(具体原因:瞬时态对象异常。仅进行personDao.save(person)时,person为持久态对象,subject仍保留为瞬时态对象)。
有两种方法实现,①若既要添加新学生又要建立学生与课的关系,就把Subject交给session管理,即:进行personDao.save(person)前,进行subjectDao.save(subject)(将subject变为持久态对象);②若表内有学生,仅需建立学生与课的关系,可通过subjectDao.getOne取出对象并交给属性引用,即Subject sub = subjectDao.getOne(1)(Dao从数据库查询出来sub)然后再直接添加。
(2)更新操作:仅能通过有外键维护权利(此文为Person)进行两表关系的更新,各自更新各自操作(与添加极其类似)。
(3)查询操作:Person查询sList,与一对多类似,需要设置立即加载,或延长session生命周期;Subject只能查询自己的属性,不能查询pList。
(4)删除操作:删除Person将从Person表删除学生数据,并从中间表删除学生与课的关系;直接删除Subject会失败(可能在中间表被引用)。

3、一对一

假设有以下两表:

Boy: boyName
Girl: girlName

对两表进行连接:

//在Girl类
@OneToOne(mappedBy = "girl")
private Boy boy;
//在Boy类
@OneToOne
@JoinColumn(name = "girlName", unique = true)
//unique表示外键唯一
private Girl girl;

不翻译了,根据多对多理解就行。
(1)添加操作:正常添加即可。注意应从维护端维护键的关系,且应关注瞬时态对象与持久态对象的关系。
(2)更新操作:正常更新即可。涉及两表关系应从维护端更新。
(3)查询操作:与上文一对多、多对多类似。
(4)删除操作:与上文类似。注意,若被维护端有引用(Girl中boy属性不为空),无法直接删除。

(四)延长Session生命周期

上文在被维护端进行查询和删除过程中都会报no session,我们需要添加一个注解@Transactional开启事务。
以一对多删除Subject为例。

public class SubjectService{
    @AutoWired
    SubjectDao subjectDao;

    @AutoWired
    PersonDao personDao;

    @Transactional  //开启事务,延长session生命周期到service层
    public void deleteSubjectWithStudent(){
        //删除所有课程与学生
        List<Subject> subjects = subjectDao.findAll;
        for(Subject Subject : subjects){
            List<Person> persons = subject.getPersonList();
            for(Person person : persons){
                personDao.delete(person);
            }

            subject.setPersonList(null);
            subjectDao.deleteById(subject);
        }
    }
}

这样可以做到将session的生命周期延长到service层。但要延长到view层,我们需要使用过滤器OpenSessionInViewFIlter(SpringBoot配置好了)。只需导入spring-boot-starter-web即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容