开发者

Spring JPA联表查询之OneToOne源码详解

目录
  • 前言
  • 源码
    • 注解属性
  • 单向联表
    • user 实体类
    • car 实体类
    • 查询结果
  • 双向联表
    • user 实体
    • car 实体
    • 查询结果
  • 延迟加载(懒加载)
    • uspythoner 实体
    • 查询结果:
    • 查询完会发现,控制台又打印了一个 JPQL:
  • 最后结论

    前言

    前面几篇我们学习的都是单表查询,就是对一张表中的数据进行查询。而实际项目中,基本都会有多张表联合查询的情况,今天我们就来了解下JPA的联表查询是如做的。

    源码

    @OneToOne 注解实现一对一关系映射。比如用户跟车辆的关系(这里假设一个人只能有一辆车),一个用户只能对应一辆车,而一辆车同样只能对应一个用户。

    老规矩,在实例之前,我们先看看源码:

    public @interface OneToOne {
        Class targetEntity() default void.class;
        CascadeType[] cascade() default {};
        FetchType fetch() default EAGER;
        String mappedBy() default "";
        boolean orphanRemoval() default false;
    }
    

    注解属性

    • targetEntity:(可选)表示默认关联的实体类型,默认为当前标注的实体类
    • cascade:(可选)当前类对象操作后级联对象的操作。默认为不级联任何操作。
    • fetch:(可选)关联是否延迟加载或者立刻加载。立刻加载是立刻获取关联的实体;延迟加android载是表示关系类在被访问时才加载。默认值EAGER,也就是立刻加载。
    • mappedBy:(可选)拥有关联关系的域,如果关系是单向的就不需要;如果是双向关系表,那么拥有关系的这一方有建立、解除和更新与另一方关系的能力,而另一方没有,只能被动管理,这个属性被定义在关系的被拥有方
    • orphanRemoval:(可选)是否将删除操作应用于具有已从关系中删除,并将删除操作级联到这些实体。

    单向联表

    我这里所说的单向联表就是只有一方添加注解;通俗讲就是我可以通过 user 获取到其 car 的信息,而不同通过 car 获取到其 user 的信息。

    user 实体类

    @Entity
    @Data
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private int id;
        private String name;
        private int age;
        @OneToOne
        @JoinColumn(name = "car_id")
        private Car car;
    }
    

    car 实体类

    @Data
    @Entity
    public class Car {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private int id;
        private String name;
    }
    

    执行请求 /user/findById?id=1,控制台打印如下:

    Hibernate: 
        select
            user0_.id as id1_1_0_,
            user0_.age as age2_1_0_,
            user0_.car_id as car_iwww.devze.comd4_1_0_,
            user0_.name as name3_1_0_,
            car1_.id as id1_0_1_,
            car1_.name as name2_0_1_ 
        from
            user user0_ 
        left outer join
            car car1_ 
                on user0_.car_id=car1_.id 
        where
            user0_.id=?
    

    查询结果

    Optional[User(id=1, name=lili, age=11, car=Car(id=1, name=苏A00001))]

    从上面的 JPQL 语句我们可以发现,uer 表car 表 是通过left outer jion进行连接的。这样我们可以通过查询 user 信息来获取其使用的 car 信息。

    双向联表

    我们除了需要通过 user 信息来获取其 car 信息为,有时还需要通过 car 信息来获取其 user 信息。只要再在 car 的实体中添加 user 字段,并添加@OneToOne注解。(这里有一个堆栈溢出的问题需要注意一下,详情请见 JPA 错题集 第二个报错信息)

    user 实体

    @Entity
    @Data
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private int i开发者_C学习d;
        private String name;
        private int age;
        @OneToOne
        @JoinColumn(name = "car_id")
        @jsonIgnore
        private Car car;
    }
    

    car 实体

    @Enti编程客栈ty
    @Data
    public class Car {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private int id;
        private String name;
        @OneToOne
        @JoinColumn(name = "user_id")
        @JsonIgnore
        private User user;
    }
    

    执行请求 /car/findById?id=1,控制台打印如下:

    Hibernate: 
        select
            car0_.id as id1_0_0_,
            car0_.name as name2_0_0_,
            car0_.user_id as user_id3_0_0_,
            user1_.id as id1_2_1_,
            user1_.age as age2_2_1_,
            user1_.car_id as car_id4_2_1_,
            user1_.name as name3_2_1_,
            car2_.id as id1_0_2_,
            car2_.name as name2_0_2_,
            car2_.user_id as user_id3_0_2_ 
        from
            car car0_ 
        left outer join
            user user1_ 
                on car0_.user_id=user1_.id 
        left outer join
            car car2_ 
                on user1_.car_id=car2_.id 
        where
            car0_.id=?
    

    查询结果

    Optional[Car(id=1, name=苏A00001, user=com.example.sbjdemo.pojo.User@1c0775ea)] 用户信息可以通过后面的实体类获取

    延迟加载(懒加载)

    上面是 OneToOne 的情形,所以请求出来的数据也是比较有限的。试想一下,如果是 OneToMany,而我们只想获取 one 的信息,但是 many 的数据却跟着一起请求出来的,无论是从数据上还是性能上来讲,都是一种负担。那么,有没有一种方法,让我们能够只获取 one 的信息,而对于 many 可以动态获取呢?这时,我们想到了源码中一个 fetch 方法,他好像可以使用 LAZY 属性来控制数据懒加载。废话少说,上码。

    user 实体

    User 实体类上的 Car 属性中 @OneToOne 添加属性 fetch = FetchType.LAZY

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "car_id")
    @JsonIgnore
    private Car car;
    

    执行请求 /user/findById?id=1,控制台打印如下:

    Hibernate: 
        select
            user0_.id as id1_2_0_,
            user0_.age as age2_2_0_,
            user0_.car_id as car_id4_2_0_,
            user0_.name as name3_2_0_ 
        from
            user user0_ 
        where
            user0_.id=?
    

    查询结果:

    Optional[com.example.sbjdemo.pojo.User@30c311e6] 通过控制台可以看出,只有一个用户查询的 sql,结果也就是一个 user 的实体类数据。那我们的 car 信息该如何查看呢?其实我们可以使用查询结果 resultget 我们所需要的数据,如下图所示:

    Spring JPA联表查询之OneToOne源码详解

    查询完会发现,控制台又打印了一个 JPQL:

    Hibernate: 
        select
            car0_.id as id1_0_0_,
            car0_.name as name2_0_0_,
            car0_.user_id as user_id3_0_0_,
            user1_.id as id1_2_1_,
            user1_.age as age2_2_1_,
            user1_.car_id as car_id4_2_1_,
            user1_.name as name3_2_1_ 
      IyeLm  from
            car car0_ 
        left outer join
            user user1_ 
                on car0_.user_id=user1_.id 
        where
            car0_.id=?
    

    恍然大悟,原来还是通过 sql 去完成的。

    最后结论

    oneToOne 主要针对一对一的场景,他们双方都可以作为维护端或者被维护端,所以 mappedBy 这个属性可有可无。这也要是与其他三个映射关系不同的地方。

    以上就是Spring JPA联表查询之OneToOne源码详解的详细内容,更多关于Spring JPA联表查询的资料请关注我们其它相关文章!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜