PlayFramework 2.x 技巧-@ManyToMany关联

1. 简介

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

2. @ManyToMany的常用场景

1)单向@ManyToMany关联

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

@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);
}
@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:

       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为关系的查询端。代码如下:

@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);
}
@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的基础上,测试如下代码,

     //通过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:首先建立关系,

       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:查询关系:

    //输出 users.size()为0
     System.out.println(Role.find.setId(id).fetch("users").findUnique().users.size());

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

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

       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实体的定义:

@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关联中,级联删除只会删除关联关系,而不会删除关联实体。

     //在删除u1时,u1和r1的关联关系会被删除,但是r1实体不会被删除
      User.find.byId(1L).delete();

4. 小结

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

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

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值