开新坑

开新坑了(笑)....公司项目使用的是Spring Data JPA做持久化框架....学习了一段时间以后发现了一点值得注意的小问题.....与大家分享

主要是针对1:N单向关联产生的一系列问题.

@PrePersistent

@PrePersist和@PreUpdate2个注解是我在公司项目里遇到的...公司是在save对象或者update对象的时候去影子表里同时做一个备份时用到的(公司项目很多地方我现在还是不懂...这里我是觉得他们是这么用的...)...然后公司的实体间的关系大部分是单向1:N.

我在自己测试的时候发现这样做是有一点问题的...

比如Student:Phone是单向1:N的关系...在Student里配置Phone的引用:

     @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "id_student")
List<Phone> phones;

然后我照着公司的用法.写了个保存影子表的方法...

 public class ShadowAspect {

     @PrePersist
public void saveShadow(Object o) {
EntityManager em = SpringUtil.getApplicationContext().getBean(EntityManager.class);
Shadowable entity = (Shadowable) o;
Object saved = entity.getShadowObject();
em.persist(saved);
} @PreUpdate
public void updateShadow(Object o) {
EntityManager em = SpringUtil.getApplicationContext().getBean(EntityManager.class);
Shadowable entity = (Shadowable) o;
Object saved = entity.getShadowObject();
em.merge(saved);
}
}

要保存的Eneity都实现了Shadowable接口..能够得到相应的影子表的Entity..

然后我想保存Student和Phone的时候同时保存相应的影子表...

我在Service里创建了一个Student并关联Phone然后保存Student.....

     public Student saveOne() {
Student s = new Student();
s.setName("NO.99");
Phone p1 = new Phone();
Phone p2 = new Phone();
p1.setPhoneName("NO91");
p2.setPhoneName("NO92");
s.setPhones(Arrays.asList(p1, p2));
return studentRepository.save(s);
}

Hibernate打印出来的SQL如下:

Hibernate:
insert
into
StudentShadow
(name)
values
(?)
Hibernate:
insert
into
Student
(name)
values
(?)
Hibernate:
insert
into
PhoneShadow
(phoneName)
values
(?)
Hibernate:
insert
into
Phone
(phoneName)
values
(?)
Hibernate:
insert
into
PhoneShadow
(phoneName)
values
(?)
Hibernate:
insert
into
Phone
(phoneName)
values
(?)
Hibernate:
update
Phone
set
id_student=?
where
id=?
Hibernate:
update
Phone
set
id_student=?
where
id=?

大家发现没有...前面的插入都很统一.影子表插一条数据,实体表插一条数据.

但是最后2条update语句影子表没有执行..只有实体表被执行了...查看数据库可以发现就是影子表外键没有被update (StudentShadow表的主键)

总结下:

就是1:N单向关联的时候保存1端的时候做了这么几件事情..

1.保存1端对象

2.保存N端对象(没写外键)

3.更新外键

保存影子表的时候...1和2是可以在@PrePresistent里利用EntityManager做掉的.但是最后那个update语句不会触发@PreUpdate..并且在@PrePresistent方法里不能做到update...因为@PrePresistent方法里传进来的实体是没有任何关联的单一实体...在这个例子中传进来的Student是不带Phone关联的..所以你不可能去调用merge方法更新Phone的外键.。有点问题。

这是一个坑爹的地方....

级联删除

级联删除时候遇到的问题与前面那个级联保存的问题是类似的.

在删除1端的时候触发的SQL如下.

 Hibernate:
select
student0_.id as id1_2_,
student0_.name as name2_2_
from
Student student0_
where
student0_.id=?
Hibernate:
select
phones0_.id_student as id_stude3_2_0_,
phones0_.id as id1_0_0_,
phones0_.id as id1_0_1_,
phones0_.phoneName as phoneNam2_0_1_
from
Phone phones0_
where
phones0_.id_student=?
19 Hibernate:
20 update
21 Phone
22 set
23 id_student=null
24 where
25 id_student=?
Hibernate:
delete
from
Phone
where
id=?
Hibernate:
delete
from
Phone
where
id=?
Hibernate:
delete
from
Student
where
id=?

因为删除的是1端..调用的是deleteById方法,所以会先根据ID找到对应的实体..然后因为设置了级联删除.还需要把多端找出来..因此还需要做个关联查询..

然后很关键的一步..update操作..解除外键关联..

再删除多端,最后删除1端...

这么看起来没什么问题..但是当表结构比较特殊的时候会发生一些问题...

在实际项目中多张表是这么设计的:

A与B有关联...但是它们没有用外键约束....而是共享主键....

意思就是A,B的主键是一样的(比如是同一个UUID)...但是从表结构上看他们之间没有任何关联(没有任何外键约束)...这样的设计我以前在其他地方实习的时候也看到过...可能设计的时候觉得不用外键关联会比较简单吧...

然后级联删除的时候就会报错....因为JPA要先update,把关联设置成null以后才会delete..然后因为关联在表中是主键,所以这条语句会报错...这就导致级联删除的操作完成不了..只能手动一个个删除实体...比较麻烦...

这也是很坑的....非常容易错...

总结

总的来说就是单向1:N关联的情况下..不管是save还是delete...都要触发update操作...而这个update语句要十分小心..可能会使关联出现null...导致操作失败....

我觉得原因是因为单向关联的N端并不知道1端的情况(类中没有引用)..所以并不会去操作外键..而这个外键就需要JPA去消耗一条额外的update语句...

这些问题在双向关联中就没有出现(因为双向关联的时候1端有N端的引用,所以会去维护外键..并且需要设置mappedBy属性)

比如删除1端的时候的SQL(级联删除):

 Hibernate:
select
student0_.id as id1_2_,
student0_.name as name2_2_
from
Student student0_
where
student0_.id=?
Hibernate:
select
phones0_.id_student as id_stude3_2_0_,
phones0_.id as id1_0_0_,
phones0_.id as id1_0_1_,
phones0_.phoneName as phoneNam2_0_1_,
phones0_.id_student as id_stude3_0_1_
from
Phone phones0_
where
phones0_.id_student=?
Hibernate:
delete
from
Phone
where
id=?
Hibernate:
delete
from
Phone
where
id=?
Hibernate:
delete
from
Phone
where
id=?
Hibernate:
delete
from
Student
where
id=?

没有出现解除外键关联的update....

在级联保存的时候:

Hibernate:
insert
into
StudentShadow
(name)
values
(?)
Hibernate:
insert
into
Student
(name)
values
(?)
Hibernate:
insert
into
PhoneShadow
(phoneName, id_student)
values
(?, ?)
Hibernate:
insert
into
Phone
(phoneName, id_student)
values
(?, ?)
Hibernate:
insert
into
PhoneShadow
(phoneName, id_student)
values
(?, ?)
Hibernate:
insert
into
Phone
(phoneName, id_student)
values
(?, ?)

也没有出现更新关联的update

Spring Data JPA 学习记录1 -- 单向1:N关联的一些问题的更多相关文章

  1. Spring学习笔记(八)Spring Data JPA学习

    ​ jpa简单的命名规则如下,这个不多做介绍,放在这里也是给自己以后查找起来方便,这篇文章主要介绍之前一直忽略了的几个点,像@NoRepositoryBean这个注解,以及怎么自定义Repositor ...

  2. spring data jpa 学习笔记

    springboot 集成 springData Jpa 1.在pom.xml添加依赖 <!-- SpringData-Jpa依赖--> <dependency <groupI ...

  3. Spring Data JPA学习笔记

    下面先来介绍一下JPA中一些常用的查询操作: //And --- 等价于 SQL 中的 and 关键字,比如 findByHeightAndSex(int height,char sex): publ ...

  4. Spring Data JPA one to one 共享主键关联

    /** * Created by xiezhiyan on 17-9-13. */@Entitypublic class Token { @Id @Column(name = "store_ ...

  5. spring data jpa入门学习

    本文主要介绍下spring data jpa,主要聊聊为何要使用它进行开发以及它的基本使用.本文主要是入门介绍,并在最后会留下完整的demo供读者进行下载,从而了解并且开始使用spring data ...

  6. 学习Spring Data JPA

    简介 Spring Data 是spring的一个子项目,在官网上是这样解释的: Spring Data 是为数据访问提供一种熟悉且一致的基于Spring的编程模型,同时仍然保留底层数据存储的特​​殊 ...

  7. Spring data jpa 使用技巧记录

    软件152 尹以操 最近在用Springboot 以及Spring data jpa  ,使用jpa可以让我更方便的操作数据库,特开此帖记录使用jpa的一些小技巧. 一.使用spring data j ...

  8. Spring Data Jpa 入门学习

    本文主要讲解 springData Jpa 入门相关知识, 了解JPA规范与Jpa的实现,搭建springboot+dpringdata jpa环境实现基础增删改操作,适合新手学习,老鸟绕道~ 1. ...

  9. SpringBoot学习笔记:Spring Data Jpa的使用

    更多请关注公众号 Spring Data Jpa 简介 JPA JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR ...

随机推荐

  1. [Linux 性能检测工具]PIDSTAT

    PIDSTAT NAME pidstat对linux任务的统计 语法 pidstat [ -C comm ] [ -d ] [ -h ] [ -I ] [ -l ] [ -p { pid [,...] ...

  2. Laravel-lumen 配置JWT

    具体步骤参照: [ JWT & Lumen ] 第一步 在项目根目录 执行命令 composer require tymon/jwt-auth第二步 在 bootstrap/app.php 的 ...

  3. spool命令

    最近工作中,需对数据进行比对.在此之前,则需将数据导出.想到以前用过的spool命令,实验一番,分享如下: 需建SQL执行脚本,内容如下: set feedback off   --关掉行数显示set ...

  4. Windows Server 2012 虚拟化实战:网络(二)

    关于Windows Server的虚拟化网络,前文描述了在操作系统层面上的出现的配置变化.其中的一些配置通过Windows Server提供的小工具即可实现,如网卡组的配置,而有些需要安装Window ...

  5. I2C子系统之驱动SSD1306 OLED

    理解I2C设备驱动框架,主要围绕四个结构体去分析就容易了. struct i2c_algorithm:提供I2C协议的实现的操作,如:master_xfer实现数据收发的最基本方法. struct i ...

  6. how2heap分析系列:0

    新学期到了,给学弟们写点东西, https://github.com/shellphish/how2heap 这个how2heap挺不错的,讲述了heap上几种不同的漏洞利用技术,在后面发的几篇中我会 ...

  7. CentOS7中防火墙的一些常用配置

    # 启动 systemctl start firewalld # 查看状态 systemctl status firewalld # 停止关闭 systemctl disable firewalld ...

  8. lydsy 2600(二分+中位数前缀和)米仓

    2600: [Ioi2011]ricehub Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 691  Solved: 359[Submit][Stat ...

  9. Parseval's theorem 帕塞瓦尔定理

    Source: wiki: Parseval's theorem As for signal processing, the power within certain frequency band = ...

  10. 使用iTerm2快捷连接SSH

    iTerm2和Mac自带的Terminal差不多,但是功能更强大,无论透明度.字体.配色.分屏等都可以设置,除了这些花哨的功能外,最近新学了一招,就是可以通过Profiles的设置打开就执行写好的脚本 ...