JPA的一对一、一对多、多对多查询

JPA的一对一、一对多、多对多查询

上一节直通车

SpringBoot整合SpringData JPA

级联类型

参考文章:
JPA概念解析:CascadeType(各种级联操作)详解

Lombok注解引发的问题

@Data在JPA中要慎用,因为重写的toString、equals、hashCode都会将关联的实体类对象包含进去,导致执行查询时循环调用到栈溢出
建议:

  1. 面对一对多的情况(即类中包含其他类的集合结果集的),不使用 @Data@EqualsAndHashCode@ToString ,重写toString、equals和hashCode
  2. 遇到equals和hashCode的问题时,依然使用@EqualsAndHashCode,但要通过属性exclude去掉集合类型的属性,例如:@EqualsAndHashCode(exclude = {"blogs","roles"})

User——用户实体(对应多个Blog、多个Role)

/**
 * @Description 用户表
 * @author Evad.Wu
 * @date 2022-06-16
 */
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(exclude = {"blogs","roles"})
@Accessors(chain = true)
@Entity
@Table(name = "user")
public class User implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
	@GenericGenerator(name = "idGenerator", strategy = "uuid")
	@GeneratedValue(generator = "idGenerator")
	@Column(name = "id")
	private Long id;
	@Column(name = "username")
	private String username;
	@Column(name = "avatar")
	private String avatar;
	@Column(name = "email")
	private String email;
	@Column(name = "password")
	private String password;
	@Column(name = "status")
	private Integer status;
	@Column(name = "created")
	private Date created;
	@Column(name = "last_login")
	private Date lastLogin;
	@Column(name = "admin")
	private boolean admin;
	@Column(name = "telephone")
	private String telephone;
	@Column(name = "identification")
	private String identification;
	@Column(name = "introduction")
	private String introduction;

	@OneToMany(mappedBy = "user", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
	private Set<Blog> blogs;

	@ManyToMany(targetEntity = Role.class, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
	@JoinTable(name = "tb_user_role",
			joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
			inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
	private Set<Role> roles;

	@Override
	public String toString() {
		return "User{" +
				"id=" + id +
				", username='" + username + '\'' +
				", avatar='" + avatar + '\'' +
				", email='" + email + '\'' +
				", password='" + password + '\'' +
				", status=" + status +
				", created=" + created +
				", lastLogin=" + lastLogin +
				", admin=" + admin +
				", telephone='" + telephone + '\'' +
				", identification='" + identification + '\'' +
				", introduction='" + introduction + '\'' +
				'}';
	}
}

Blog——博客实体(对应一个User)

/**
 * @Description 博客表
 * @author Evad.Wu
 * @date 2022-06-17
 */
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(exclude = {"reviewSet","user"})
@Accessors(chain = true)
@Entity
@Table(name = "blog")
public class Blog implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private Long id;
	@Column(name = "user_id")
	private Long userId;
	@Column(name = "examine_id")
	private String examineId;
	@Column(name = "title")
	private String title;
	@Column(name = "description")
	private String description;
	@Column(name = "content")
	private String content;
	@Column(name = "created")
	private Date created;
	@Column(name = "status")
	private Integer status;
	@Column(name = "index_img")
	private String indexImg;
	@Column(name = "reading")
	private Long reading;

	@ManyToOne(targetEntity = User.class,cascade = CascadeType.ALL,fetch = FetchType.LAZY)
	@JoinColumn(name = "user_id",insertable = false,updatable = false)
	private User user;

	@Override
	public String toString() {
		return "Blog{" +
				"id=" + id +
				", userId=" + userId +
				", examineId='" + examineId + '\'' +
				", title='" + title + '\'' +
				", description='" + description + '\'' +
				", content='" + content + '\'' +
				", created=" + created +
				", status=" + status +
				", indexImg='" + indexImg + '\'' +
				", reading=" + reading +
				'}';
	}
}

Role——角色实体(对应多个User)

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Builder

@Entity
@Table(name = "tb_role")
public class Role extends BaseModel implements Serializable {
	private static final long serialVersionUID = 2480455193854374245L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

	@Column(name = "role_code")
	private String roleCode;

	@Column(name = "role_name")
	private String roleName;
}

BaseModel——基本审计属性

@Data
@MappedSuperclass
@DynamicInsert
@EntityListeners(AuditingEntityListener.class)
public class BaseModel implements Serializable {
    private static final long serialVersionUID = -1152053885220920998L;

    /**
     * 创建人
     */
    @CreatedBy
    @Column(name = "creator", updatable = false, length = 64)
    private String creator;
    /**
     * 创建时间
     */
    @CreatedDate
    @Column(name = "create_time", updatable = false)
    private Date createTime;
    /**
     * 修改人
     */
    @LastModifiedBy
    @Column(name = "modifier", length = 64)
    private String modifier;
    /**
     * 修改时间
     */
    @LastModifiedDate
    @Column(name = "modify_time")
    private Date modifyTime;
    /**
     * 数据可见
     */
    @Column(name = "visible",columnDefinition = "TINYINT DEFAULT 1")
    @ColumnDefault("1")
    private Boolean visible;
    /**
     * 版本
     */
    @Version
    @Column(name = "rec_ver")
    private Long recVer;
}

一对一、一对多(多对一)

区别只是类型是单个对象还是集合对象

User类

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
private Set<Blog> blogs;

Blog类:

@ManyToOne(targetEntity = User.class,cascade = CascadeType.ALL,fetch = FetchType.LAZY)
@JoinColumn(name = "user_id",insertable = false,updatable = false)
private User user;

一对多要相互依赖,首先有多对一,这里Blog类中需要定义一个User,并添加注解@JoinColumn,指定name关联的字段。
然后User类中需要定义Blog集合,接收多个Blog对象
注意:insertableupdatable都置为false,因为Blog类中的userId和User类中的id都指向同一个数据库字段,这样就会导致级联更新时重复修改而报错。

测试代码:一对一

/**
 * 一对多 1: User N: Blog
 */
@Test
public void oneMore() {
	List<Long> userIds = new ArrayList<>();
	userIds.add(2L);
	userIds.add(4L);
	List<User> userList = userRepository.findAllById(userIds);
	for (User user : userList) {
		System.out.println("user: " + user);
		Set<Blog> blogs = user.getBlogs();
		for (Blog blog : blogs) {
			System.out.println("blog: " + blog);
		}
	}
}

在这里插入图片描述

测试代码:多对一

/**
 * 多对一 N: Blog 1: User
 */
@Test
public void moreOne() {
	List<Long> blogIds = new ArrayList<>();
	blogIds.add(9L);
	blogIds.add(10L);
	List<Blog> blogList = blogRepository.findAllById(blogIds);
	for (Blog blog : blogList) {
		System.out.println("blogId: " + blog.getId());
		System.out.println("user: " + blog.getUser());
	}
}

在这里插入图片描述

多对多

Role

/**
 * @Description 角色
 * @author Evad.Wu
 * @date 2022-06-21
 */
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(exclude = {"reviewSet","user"})
@Entity
@Table(name = "tb_role")
public class Role {
	@Id
	@GenericGenerator(name = "idGenerator", strategy = "uuid")
	@GeneratedValue(generator = "idGenerator")
	private String id;

	@Column(name = "user_id")
	private Long userId;

	@Column(name = "role_name")
	private String roleName;

	@Override
	public String toString() {
		return "Role{" +
				"id='" + id + '\'' +
				", userId=" + userId +
				", roleName='" + roleName + '\'' +
				'}';
	}
}

User

@ManyToMany(targetEntity = Role.class, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
@JoinTable(name = "tb_user_role",
		joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
		inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
private Set<Role> roles;
  1. 多对多不需要互相依赖,只要在需要使用的一方添加即可
  2. 多对多是需要中间表来连接两端的,需要添加@JoinTable注解,name为中间表的名字,如果数据库中没有则会创建
  3. joinColumnsinverseJoinColumns@JoinColumnname属性对应中间表中两端的连接字段,referencedColumnName则是每张表中用于连接的字段
    在这里插入图片描述

测试代码:多对多

/**
 * 多对多:N: User N: Role / N: Role N: User
 */
@Test
public void userRoleM2M() {
	Optional<User> userOptional = userRepository.findById(51L);
	User user = userOptional.get();
	System.out.println("userId: " + user.getId());
	Set<Role> roles = user.getRoles();
	for (Role role : roles) {
		System.out.println("role: " + role);
	}
}

在这里插入图片描述

参考网址

JPA概念解析:CascadeType(各种级联操作)详解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加把劲骑士RideOn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值