美文网首页
Spring Data JPA | 禁止生成外键约束

Spring Data JPA | 禁止生成外键约束

作者: mrzhqiang | 来源:发表于2021-03-24 19:32 被阅读0次

    Spring Data JPA

    通常看到这篇文章的同学,已经对 JPA 有了较深入的了解,因此我们跳过不必要的介绍,直接进入主题。

    禁止生成外键

    经过实践,可以得到两个结论:

    • 结论 1 @OneToOne@OneToMany
      使用 @JoinColumn(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) 可以禁止生成外键。
    • 结论 2 @ManyToOne@ManyToMany
      使用 @JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) 可以禁止生成外键,其中 foreignKey 禁止生成当前实体在关联表中的外键,inverseForeignKey 禁止生成集合中的元素实体在关联表中的外键。

    结论 2 可能比较抽象,通常来说,有 账号角色 两张表是多对多的关系,即一个账号可以拥有多个角色,一个角色也可以被多个账号拥有。

    在这种场景下,账号表中会有 角色集合字段,即 roleList,我们使用 @ManyToMany 注解来标记这个字段为多对多关系,然后通过 @JoinTable 来禁止使用外键。

    这个时候,我们需要给 @JoinTable 注解加入 foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT) 参数,表示在 账号角色关联表 中,将禁止生成账号 ID (当前实体)的外键,同时加上 inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT) 参数,表示在 账号角色关联表 中,将禁止生成角色 ID(集合中的元素实体) 的外键。

    实践样例

    为了更具体一点,我们来个实践样例。

    首先我们有账号 Account、用户信息 User、角色 Role 这三个实体,AccountUser 是一对一关系 @OneToOneAccountRole 是多对多关系 @ManyToMany,这是最典型的搭配,基本覆盖大部分场景。

    账号实体:

    
    @Data
    @Entity
    @Table(name = "account")
    public class Account {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        /**
         * 用户名,唯一不重复,不能为空。
         */
        @Column(unique = true, nullable = false)
        private String username;
        /**
         * 密码,不能为空,不可逆的加密存储。
         */
        @Column(nullable = false)
        private Password password;
    
        /**
         * 用户信息,注册时需要填写,用来找回密码,确认身份。
         */
        @OneToOne(cascade = CascadeType.ALL, optional = false)
        @JoinColumn(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
        private User user;
    
        /**
         * 角色信息,通常是 USER 角色,也可以被添加为 ADMIN 角色。
         */
        @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        @JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), 
                inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
        private List<Role> authorities;
    
        // 省略其他代码 ...
    }
    
    

    生成的账号表:

    生成的账号角色关联表:

    用户信息实体:

    
    @Data
    @Entity
    @Table(name = "user")
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        /**
         * 真实姓名
         */
        @Column(nullable = false)
        private String realName;
        /**
         * 电话号码
         */
        private String phone;
        /**
         * 手机号码
         */
        private String mobile;
        /**
         * 电子邮箱
         */
        private String email;
    
        @OneToOne
        @JoinColumn(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
        private Account account;
    
        // 省略其他代码...
    }
    
    

    生成的用户信息表:

    角色实体:

    
    @Data
    @Entity
    @Table(name = "role")
    public class Role implements GrantedAuthority {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private String name;
    
        @Enumerated(EnumType.STRING)
        @Column(nullable = false)
        private Authority authority = Authority.ROLE_USER;
    
        @Override
        public String getAuthority() {
            return authority.toString();
        }
    
        public enum Authority {
            ROLE_ADMIN("ROLE_ADMIN"),
            ROLE_USER("ROLE_USER"),
            ;
    
            private final String value;
    
            Authority(String value) {
                this.value = value;
            }
    
            @Override
            public String toString() {
                return value;
            }
        }
    }
    
    

    生成的角色表:

    可以看到,所有的数据库表中,都没有生成外键,说明禁止成功了。

    简单剖析

    样例中的 Spring Data JPA 使用的是 hibernate-core:5.4.27.Final 库。

    org.hibernate.cfg.AnnotationBinder.java 中,我们可以看到这样的一段代码:

    通过 Find Usages,我们在 org.hibernate.mapping.ManyToOne.java 中发现这样的判断:

    还有 org.hibernate.mapping.SimpleValue.java 中也有类似的判断:

    我们暂不进行更深入的解析,通过上面的判断,我们知道只要外键名称是 none 就不会自动创建外键。

    而使用结论 1 和结论 2 的方式,可以在绑定外键时自动设置外键名称为 none,进而禁止自动创建外键。

    总结

    网上的教程都是表面,有的时候,自己去看源码才能收获更多的知识和乐趣。

    相关文章

      网友评论

          本文标题:Spring Data JPA | 禁止生成外键约束

          本文链接:https://www.haomeiwen.com/subject/qnmhhltx.html