zxpnet网站 zxpnet网站
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

zxpnet

一个爱学习的java开发攻城狮
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 大后端课程视频归档
  • 南航面试题
  • 并发编程

  • 性能调优

  • java8语法

  • lombok

  • 日志

  • 工具类

  • spring

  • mybatis

  • springboot

  • redis

  • zookeeper

  • springcloud

  • dubbo

  • netty

  • springsecurity

  • mq消息中间件

  • shiro

  • beetle

  • 模板引擎

  • jpa

    • jpa基础
    • 对象关系
      • 对象关系
      • 一对一@OneToOne
        • 1、通过关联表的方式来保存一对一的关系
        • 方式二通过外键的方式
      • 多对多
        • 多对多model写法
        • repo写法
      • 多对一与一对多
    • model相关
    • repository相关
    • 雪花算法snowflake
  • 数据结构与算法

  • 数据库知识与设计

  • gradle

  • maven

  • bus

  • 定时任务

  • docker

  • centos

  • 加解密

  • biz业务

  • pigx项目

  • 开源项目

  • 品达通用权限项目-黑马

  • 货币交易项目coin-尚学堂

  • php

  • backend
  • jpa
shollin
2021-07-04
目录

对象关系

  • 对象关系
  • 一对一@OneToOne
    • 1、通过关联表的方式来保存一对一的关系
    • 方式二通过外键的方式
  • 多对多
    • 多对多model写法
    • repo写法
  • 多对一与一对多

# 对象关系

主要有一对一@OneToOne、多对多、一对多、多对一四种关系, 关系可以单向,也可以双向。

关系的维护方与被维护方(mappedBy)。 中间表@JoinTable的定义(表名称、维护方外键属性名称joinColumns、被维护方外键属性名称inverseJoinColumns)

# 一对一@OneToOne

JPA使用@OneToOne来标注一对一的关系。

实体City:城市。  实体Mayor:市长。  City和Mayor是一对一的关系。

这里用两种方式描述JPA的一对一关系。

  • 一种是通过外键的方式(一个实体通过外键关联到另一个实体的主键);

  • 另外一种是通过一张关联表来保存两个实体一对一的关系,会多出来一张表。

# 1、通过关联表的方式来保存一对一的关系

@Entity(name="city")
public class City {

    @Id
    @GeneratedValue
    private Long id;

    /**城市名称*/
    @Column(length=32)
    private String name;

    /**城市的市长*/
    @OneToOne(cascade=CascadeType.ALL) //City是关系的维护端
    @JoinTable(name="city_mayor",joinColumns=@JoinColumn(name="city_id"),inverseJoinColumns=@JoinColumn(name="mayor_id"))
    //通过关联表来保存一对一的关系。定义了一张叫"city_mayor"的表,
    //joinColumns定义一个外键叫"city_id",指向关系维护端City的主键
    //inverseJoinColumns定义了一个外键叫"mayor_id",指向关系被维护端Mayor的主键
    private Mayor mayor;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
@Table(name="mayor")
public class Mayor {

    @Id
    @GeneratedValue
    private Long id;

    /**市长姓名*/
    @Column(length=32)
    private String name;

    /**管理的城市*/
    @OneToOne(mappedBy="mayor",cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)
    //mappedBy="mayor"表明Mayor是关系被维护端,"mayor"是City实体中的属性
    private City city;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 方式二通过外键的方式

Cityp配置如下, Mayor.java还是原注解不变。

@Entity
@Table(name="city")
public class City {

    @Id
    @GeneratedValue
    private Long id;

    /**城市名称*/
    @Column(length=32)
    private String name;

    /**城市的市长*/
    @OneToOne(cascade=CascadeType.ALL)//City是关系的维护端
    @JoinColumn(name="mayor_id")//指定外键的名称
    @Fetch(FetchMode.JOIN)//会使用left join查询,只产生一条语句
    //@Fetch(FetchMode.JOIN) 会使用left join查询,只产生一条sql语句(默认使用)
    //@Fetch(FetchMode.SELECT)   会产生N+1条sql语句
    //@Fetch(FetchMode.SUBSELECT)  产生两条sql语句 第二条语句使用id in (…..)查询出所有关联的数据
    private Mayor mayor;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

参考文章:JPA的一对一映射 - CN.programmer.Luxh - 博客园 (cnblogs.com) (opens new window)

# 多对多

用户User与角色Role为多对多, 关系由用户去维护中间表数据;

一个老师教许多学生,一个学生被许多老师教,一个学生有好多书,同一种书被许多同学拥有,要查询教拥有书"a"的学生的老师!

Hql语句:SELECT t FROM Teacher t join t.students s join s.books b where b.name = 'a'

解释:t.students s中s并不是集合的意思,而是t的students对象的表别名,join t.students s这个hql,hibernate会翻译成两个表的内连接关系

错误写法:SELECT t FROM teacher t where t.students.books.name = 'a'

其实道理是很显然的,t.students是一个Set,那么这个Set怎么可能有books这样的属性呢?同理books.name也不对,所以使用表间连接并给出别名t.students s,此时的对象s才有books属性,所以可以写s.books b,最后使用b.name限制查询b.name = 'a'.

另外一种写法:SELECT t FROM Teacher t,Student s,Book b where s.id in elements(t.students) and b.id in elements(s.books) 这种方法没有出错!不过这种方式要用子查询!

@Entity(name="t_user")
@Data @AllArgsConstructor  @NoArgsConstructor   @ApiModel("权限用户")
public class User implements UserDetails {

    /**
     * User为多对多关系维护方  默认LAZY懒加载
     */
    @ManyToMany
    @JoinTable(name = "t_user_role",
            joinColumns = {@JoinColumn(name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id")})
    private List<Role> roles;
	
	... ...
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity(name = "t_role")
@Data  @AllArgsConstructor  @NoArgsConstructor
public class Role {

    /**
     * mappedBy表示关系交由User维护, 默认LAZY懒加载
     */
    @ManyToMany(mappedBy = "roles")
    private List<User> users;
    ......
}
1
2
3
4
5
6
7
8
9
10
11

保存: 单表各保存自己的,关系维护方保存的时候,会自动往中间表更新数据

@Test
    void contextLoads() {

        Role r1 = new Role();
        r1.setName("admin");
        r1.setCname("管理员");

        Role r2 = new Role("user", "普通用户");
        Role r3 = new Role("test", "测试用户");

        roleService.save(r1);
        roleService.save(r2);
        roleService.save(r3);

        User u1 = new User();
        u1.setUsername("admin");
        u1.setPassword("123");
        u1.setAccountNonExpired(true);
        u1.setAccountNonLocked(true);
        u1.setCredentialsNonExpired(true);
        u1.setEnabled(true);
        u1.setRoles(Arrays.asList(r1));
        userService.save(u1);

        User u2 = new User();
        u2.setUsername("user");
        u2.setPassword("123");
        u2.setAccountNonExpired(true);
        u2.setAccountNonLocked(true);
        u2.setCredentialsNonExpired(true);
        u2.setEnabled(true);
        u2.setRoles(Arrays.asList(r2));
        userService.save(u2);

        User u3 = new User();
        u3.setUsername("test");
        u3.setPassword("123");
        u3.setAccountNonExpired(true);
        u3.setAccountNonLocked(true);
        u3.setCredentialsNonExpired(true);
        u3.setEnabled(true);
        u3.setRoles(Arrays.asList(r3));
        
        // 关系的维护方会更新中间表的数据
        userService.save(u3);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

image-20210704224311638

# 多对多model写法

可以弄一个中间表对应的model, 也可以不要, 多对多,需要明确关系维护方,

关系维护方

//粉丝标签的多对多, 维护中间表wx_fans_tags, 两表的关联字段wxfans_id wxfans_tag_id
  	@ManyToMany
  	@JoinTable( name="wx_fans_tags"
  		, joinColumns = @JoinColumn(name = "wxfans_id"),   
  		inverseJoinColumns = @JoinColumn(name = "wxfans_tag_id"))
  	@JsonIgnore  	
  	private Set<WxFansTag> wxFansTags=new HashSet<WxFansTag>(0);
1
2
3
4
5
6
7

被维护方

@JsonIgnore
	@ManyToMany(mappedBy="wxFansTags")
	private Set<WxFans> wxFanses=new HashSet<WxFans>(0);//粉丝参与活动时,与推荐人之间的一对多关系 
1
2
3

# repo写法

见WxFansTagRepository, 原生写法不能进行缓存, hql写法可以进行缓存,join默认为内联接(inner join),如果要外联接LEFT join ()

public interface WxFansTagRepository extends ZxpRepository<WxFansTag,Long> {
	
	// 不能用QueryHint,因为不会清理缓存 中间表wx_fans_tags,  标签表:wx_fans_tag  粉丝表 wx_fans
	@Query(value = "SELECT t.* FROM wx_fans_tag t, wx_fans_tags ft, wx_fans f WHERE " +
            "ft.wxfans_id = f.id and ft.wxfans_tag_id = t.id AND f.id = ?1",nativeQuery = true)
	List<WxFansTag> findByWxFansId(Long wxFansId);
	
	// hql方式多对多查询
	@QueryHints({@QueryHint(name = Cache.QHINT, value ="true") })
	@Query(value = "SELECT t FROM WxFansTag t JOIN t.wxFanses f WHERE f.id = :wxFansId ")
	List<WxFansTag> findHqlByWxFansId(@Param(value = "wxFansId") Long wxFansId);
}
1
2
3
4
5
6
7
8
9
10
11
12

# 多对一与一对多

实体Company:公司。  实体Employee:雇员。 Company和Employee是一对多关系。那么在JPA中,如何表示一对多的双向关联呢?

JPA使用@OneToMany和@ManyToOne来标识一对多的双向关联。一端(Company)使用@OneToMany,多端(Employee)使用@ManyToOne。

在JPA规范中,一对多的双向关系由多端(Employee)来维护。就是说多端(Employee)为关系维护端,负责关系的增删改查。一端(Company)则为关系被维护端,不能维护关系。

一端(Company)使用@OneToMany注释的mappedBy="company"属性表明Company是关系被维护端。

多端(Employee)使用@ManyToOne和@JoinColumn来注释属性company,@ManyToOne表明Employee是多端,@JoinColumn设置在employee表中的关联字段(外键)。

@Entity
@Table(name="company")
public class Company {

    @Id
    @GeneratedValue
    private Long id;

    /**公司名称*/
    @Column(name="name",length=32)
    private String name;

    /**拥有的员工*/
    @OneToMany(mappedBy="company",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
    //拥有mappedBy注解的实体类为关系被维护端
    //mappedBy="company"中的company是Employee中的company属性
    private Set<Employee> employees = new HashSet<Employee>();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Entity
@Table(name="employee")
public class Employee {

    @Id
    @GeneratedValue
    private Long id;

    /**雇员姓名*/
    @Column(name="name")
    private String name;

    /**所属公司*/
    @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)//可选属性optional=false,表示company不能为空
    @JoinColumn(name="company_id")//设置在employee表中的关联字段(外键)
    private Company company;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jpa基础
model相关

← jpa基础 model相关→

最近更新
01
国际象棋
09-15
02
成语
09-15
03
自然拼读
09-15
更多文章>
Theme by Vdoing | Copyright © 2019-2023 zxpnet | 粤ICP备14079330号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式