如何在不使用外键的情况下保证关联数据的完整性

3,283 阅读2分钟

这是我参与 8 月更文挑战的第 13 天,活动详情查看: 8月更文挑战

阿里JAVA规范:【强制】不得使用外键与级联,一切外键概念必须在应用层解决。

原因:

当加入外键后,数据库每插入一条或删除一条数据,它都需要去看与之关联的表中是否有这个数据项,例如teacher、student表,teacher管理的student_id是student表的主键,当插入一条teacher数据时,要检查当前插入数据中的student_id是否存在于student表中,保证一致性,删除一条teacher数据时,也会触发一次级联操作,会同步更新和删除student表中的student_id。

这样就会导致大中型项目的数据库的业务变动,修改数据库的成本变高。外键影响数据库的查询、插入速度,故选择不插入外键,表与表之间的依赖关系要通过应用程序来解决。

如何做到不使用外键,目前采取的方法:

例如数据库中有用户表和角色表,以及用户对应的角色关联表。

用户表UserBean如下:

DROP TABLE "UserBean";
CREATE TABLE "UserBean" (
  "user_id" NUMBER(11) NOT NULL ,
  "username" VARCHAR2(40 BYTE) ,
  "password" VARCHAR2(40 BYTE) 
)

角色表RoleBean如下:

DROP TABLE "RoleBean";
CREATE TABLE "RoleBean" (
  "role_id" NUMBER(11) NOT NULL ,
  "role_name" VARCHAR2(100 BYTE) ,
  "description" VARCHAR2(200 BYTE) , 
)

用户角色关联表UserRole如下:

DROP TABLE "UserRole";
CREATE TABLE "UserRole" (
  "user_id" NUMBER(11) NOT NULL ,
  "role_id" NUMBER(11) NOT NULL 
)

这个时候在数据库表中不添加外键,而是在应用层,写sql语句时关联上。

后端使用Mybatis连接数据库,当你需要查询某个用户的角色时,在roleMapper中,

public interface RoleMapper {
    //通过username查找用户角色信息
    String findRoleByUsername(@Param("username") String username);
}

roleService中,

@Service
public interface RoleService {
    //通过username查找用户角色信息
    String findRoleByUsername(@Param("username") String username);
}

roleServiceImpl

@Service
public class RoleServiceImpl implements RoleService{

    @Autowired
    RoleMapper roleMapper;
    
    @Override
    public String findRoleByUsername(String username) {
        return roleMapper.findRoleByUsername(username);
    }
}

而在roleMapper.xml中findRoleByUsername方法的sql语句就可以写为:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.tjm.mapper.RoleMapper">
    <select id="findRoleByUsername" resultType="String" parameterType="String">
        select
               "RoleBean"."role"
        from
             "UserBean"
        RIGHT JOIN "UserRole" on "UserBean"."user_id"="UserRole"."user_id"
        LEFT JOIN "RoleBean" on "RoleBean"."role_id"="UserRole"."role_id"
        where "username"=#{username}
    </select>

</mapper>

通过right join和left join来关联两张表,查询出我们需要的信息,插入与删除同样这么操作,也就在不使用外键的情况下,保证了数据库中数据的一致性