前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PlayFramework 2.x 技巧-@ManyToMany关联

PlayFramework 2.x 技巧-@ManyToMany关联

作者头像
joymufeng
发布2018-05-17 15:11:14
1.2K0
发布2018-05-17 15:11:14
举报

1. 简介

    实体之间的关联关系是刚入门的同学比较头疼的问题,但是在日常开发中又是比较重要的技巧,熟练的使用实体关联,能够使代码清晰易懂,并且节省宝贵的开发时间。通常情况下,对于存在关联关系的两个实体,我们会明确指定其中一个实体为关系的维护端,而另一个实体为关系的查询端(反向端)。对于OneToMany或ManyToOne关系,JPA规范明确指出以Many一端为关系的维护端,One一端为关系的查询端;而对于ManyToMany的关联关系,双方都可以作为关系的维护端,因此在日常的开发工作中会出现一些误用。 本文主要从两个方面谈一下@ManyToMany关联,一方面是@ManyToMany的常用场景,另一方面是@ManyToMany的使用误区。

2. @ManyToMany的常用场景

1)单向@ManyToMany关联

    举例来说,User和Role是ManyToMany关联,假如我们只关心一个User有多少个roles,而不关心一个Role下有多少个users,这种情况就属于单向关联。在这种情况下,@ManyToMany注解只出现一次,并且是在关系的维护方,即User这一边,代码如下:

代码语言:javascript
复制
@Entity
public class User extends Model {
	
	@Id
	public long id;
	public String name;
	@ManyToMany(cascade=CascadeType.ALL)
	public List<Role> roles = new ArrayList<Role>();
	
	public static Finder<Long,User> find = new Finder<Long,User>(Long.class, User.class); 
}
代码语言:javascript
复制
@Entity
public class Role  extends Model {
	@Id
	public long id;
	public String name;
	
	public static Finder<Long,Role> find = new Finder<Long,Role>(Long.class, Role.class); 
}

注意,因为不需要根据Role查询User,所以Role实体没有users属性。

Code-1:

代码语言:javascript
复制
User u1=new User();
		u1.name="u1";
		Role r1=new Role();
		r1.name="r1";
		u1.roles.add(r1);
		//r1,u1和r1之间的关系都会被保存进数据库,插入到数据库后,r1和u1的id都为1
		u1.save();

2)双向@ManyToMany关联

    还是上面的User和Role的例子,这时不仅要从User查询关联的roles,而且要从Role查询关联的users,这种情况属于双向关联。在这种情况下,@ManyToMany注解在实体双方对会出现,但是需要注意的是,关系的查询端需要指定mappedBy属性(该属性在下文会有详细介绍)。理论上来讲,User和Role都可以作为关系的维护端,但是通常情况下,我们指定User为关系的维护端,Role为关系的查询端。代码如下:

代码语言:javascript
复制
@Entity
public class User extends Model {
	
	@Id
	public long id;
	public String name;
	@ManyToMany(cascade=CascadeType.ALL)
	public List<Role> roles = new ArrayList<Role>();
	
	public static Finder<Long,User> find = new Finder<Long,User>(Long.class, User.class); 
}
代码语言:javascript
复制
@Entity
public class Role  extends Model {
	@Id
	public long id;
	public String name;
	@ManyToMany(mappedBy="roles")
	public List<User> users = new ArrayList<User>();
	
	public static Finder<Long,Role> find = new Finder<Long,Role>(Long.class, Role.class); 
}

Code-2:在code-1的基础上,测试如下代码,

代码语言:javascript
复制
//通过Role查询关联的users,users列表中包含上面保存的u1,大小为1
	  Role.find.byId(1L).users.size();

3. @ManyToMany的使用误区

1)双向@ManyToMany关联时,实体双方都未指定mappedBy属性

    如果两边都未指定mappedBy属性,则EBean会认为双方都可以作为关系的维护端,为两个实体各自生成一张关系表,即USER_ROLE为User的关系维护表,ROLE_USER为Role的关系维护表。这样导致的结果是,双方的关系信息分别存在各自的关系表里,导致通过User保存的关系,Role实体无法查询到,反之亦然。测试代码如下:

Code-3:首先建立关系,

代码语言:javascript
复制
User u1=new User();
		u1.name="u1";
		Role r1=new Role();
		r1.name="r1";
		u1.roles.add(r1);
		//r1,u1和r1之间的关系都会被保存进数据库,插入到数据库后,r1和u1的id都为1
		u1.save();

Code-4:查询关系:

代码语言:javascript
复制
//输出 users.size()为0
	 System.out.println(Role.find.setId(id).fetch("users").findUnique().users.size());

2)使用关系的查询端保存关系

你觉得下面的代码可以正确的保存关系吗?答案是否定的,有兴趣的同学可以自己试一下!

代码语言:javascript
复制
User u1=new User();
		u1.name="u1";
		Role r1=new Role();
		r1.name="r1";
		r1.users.add(u1);
		r1.save(); //r1和u1间的关系并没有被保存,因为Role是关系的查询端,而不是维护端

3)mappedBy属性的使用

    mappedBy属性用于明确的指定该实体为关系的查询端,而另一个实体为关系的维护端。属性的值可以理解为另一个实体的外键,我们再来看一下Role实体的定义:

代码语言:javascript
复制
@Entity
public class Role  extends Model {
	@Id
	public long id;
	public String name;
	@ManyToMany(mappedBy="roles")
	public List<User> users = new ArrayList<User>();
	
	public static Finder<Long,Role> find = new Finder<Long,Role>(Long.class, Role.class); 
}

mappedBy的属性值为"roles",表示的意思是:根据关系表中的role.id来查询所有的roles。实际上在@OneToMany的注解中看起来更加直接一点。 

4) cascade注解属性的使用

    cascade注解属性一般用在关系的维护端,在上面的例子里就是User这一端。在ManyToMany关联中,级联删除只会删除关联关系,而不会删除关联实体。

代码语言:javascript
复制
//在删除u1时,u1和r1的关联关系会被删除,但是r1实体不会被删除
	  User.find.byId(1L).delete();

4. 小结

1)在双向关联关系中,明确使用mappedBy属性指定关系的查询端(反向端),另一端为关系的维护端;

2)关系的查询端(反向端)只能查询关系,而不能保存关系;

3)ManyToMany关系中的级联删除,只会删除关联关系,而不会删除关联实体。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档