【问题标题】:Can I add check constraints in Room?我可以在 Room 中添加检查约束吗?
【发布时间】:2020-01-02 05:54:49
【问题描述】:

在我的数据库中,我有 3 个表:problemsusersrolesroles 表有 2 个值:clientcoach

数据库规则:

  1. 用户只有一个角色(客户或教练)。
  2. 一个问题有一个教练和一个客户。

problems 实体有两个指向 users 表的外键:coach_idclient_id。我可以使用 Android Room 添加检查约束以确保问题符合此规则吗?

我想避免一个问题有两个客户或两个教练的情况。

数据库概念图

【问题讨论】:

    标签: android sqlite relational-database android-room


    【解决方案1】:

    目前 ROOM 不支持添加 CHECK 约束。

    但是,您可以通过 DROPPING 表然后创建表来引入 CHECK 约束(这可能是不可取的,因为这可能会引入持续的复杂性,例如在迁移时)

    • 但是,CHECK 约束是有限的,不能包含子查询,我认为这会使事情复杂化,甚至可能排除存在合适/可用的 CHECK 约束。

      • 我相信您会看到类似coach INTEGER CHECK((SELECT authority FROM user JOIN role ON role.roleid = user.role) = 0) REFERENCES user(userid) ON DELETE CASCADE ON UPDATE CASCADE, 的内容(假设由于不清楚哪一列表示角色类型,因此教练必须具有权限为0 的角色)。但是,无法使用 CHECK,因为它会导致 CHECK 约束中禁止的子查询错误。

    因此,在插入问题的过程中以编程方式执行此类检查可能会更简单。也就是说,对于每种类型(教练/客户),如果指定了不正确的角色,则从用户那里获取角色并拒绝实际插入数据库。这更类似于 Room 的 OO 立场(即表支持并根据对象创建)。

    示例

    也许根据您的问题考虑以下几点:-

    Role.java(角色表)

    @Entity(tableName = "role")
    public class Role {
    
        public static final int AUTHORITY_COACH = 0;
        public static final int AUTHORITY_CLIENT = 1;
    
        @PrimaryKey
        Long roleid;
        String description;
        int authority;
    
        public Role(String description, int authority) {
            this.description = description;
            if ( authority >=  AUTHORITY_CLIENT) {
                this.authority = AUTHORITY_CLIENT;
            } else {
                this.authority = AUTHORITY_COACH;
            }
        }
    
        public Long getRoleid() {
            return roleid;
        }
    
        public void setRoleid(Long roleid) {
            this.roleid = roleid;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        public int getAuthority() {
            return authority;
        }
    
        public void setAuthority(int authority) {
            this.authority = authority;
        }
    }
    

    User.java(用户表)

    @Entity(
            tableName = "user",
            foreignKeys = {
                    @ForeignKey(
                            entity = Role.class,
                            parentColumns = {"roleid"},
                            childColumns = {"role"},
                            onDelete = ForeignKey.CASCADE,
                            onUpdate = ForeignKey.CASCADE
                            )
            },
            indices = {
                    @Index("role")
            }
    )
    public class User {
    
        @PrimaryKey
        Long userid;
        String name;
        String surname;
        String username;
        String email;
        String password;
        long role;
    
        public User(String name, String surname, String username, String email, String password, long role) {
            this.name = name;
            this.surname = surname;
            this.username = username;
            this.email = email;
            this.password = password;
            this.role = role;
        }
    
        public Long getUserid() {
            return userid;
        }
    
        public void setUserid(Long userid) {
            this.userid = userid;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getSurname() {
            return surname;
        }
    
        public void setSurname(String surname) {
            this.surname = surname;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public long getRole() {
            return role;
        }
    
        public void setRole(long role) {
            this.role = role;
        }
    }
    

    Problem.java(问题表)

    @Entity(
            tableName = "problem",
            foreignKeys = {
                    @ForeignKey(
                            entity = User.class,
                            parentColumns = {"userid"},
                            childColumns = {"coach"},
                            onDelete = ForeignKey.CASCADE,
                            onUpdate = ForeignKey.CASCADE
                    ),
                    @ForeignKey(
                            entity = User.class,
                            parentColumns = {"userid"},
                            childColumns = {"client"},
                            onDelete = ForeignKey.CASCADE,
                            onUpdate = ForeignKey.CASCADE
                    )
            },
            indices = {
                    @Index(value = "coach"),
                    @Index(value = "client")
            }
    )
    public class Problem {
    
        @PrimaryKey
        Long problemid;
        String title;
        String status;
        long coach;
        long client;
    
        public Problem(){}
    
        public Problem(String title, String status, User coach, User client) {
            this.title = title;
            this.status = status;
            this.coach = coach.getUserid();
            this.client = client.getUserid();
        }
    
        public Long getProblemid() {
            return problemid;
        }
    
        public void setProblemid(Long problemid) {
            this.problemid = problemid;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public long getCoach() {
            return coach;
        }
    
        public void setCoach(long coach) {
            this.coach = coach;
        }
    
        public long getClient() {
            return client;
        }
    
        public void setClient(long client) {
            this.client = client;
        }
    }
    

    UserWithRole.java(POJO 将用户与用户角色结合起来)

    public class UserWithRole {
    
        @Embedded
        User user;
        @Relation(entity = Role.class,entityColumn = "roleid",parentColumn = "role")
        Role userrole;
    
        public UserWithRole(){}
    
        public User getUser() {
            return user;
        }
    
        public Role getUserrole() {
            return userrole;
        }
    }
    
    • 未实际使用(但可用于检查)

    AllDao.java(数据访问全部合并)

    @Dao
    public interface AllDao {
    
        @Insert
        long insertRole(Role role);
    
        @Insert
        long insertUser(User user);
    
        @Insert
        long insertProblem(Problem problem);
    
        @Query("SELECT (authority = " + Role.AUTHORITY_COACH + ") FROM user JOIN role ON role.roleid = user.role WHERE userid = :userid ")
        boolean isUserACoach(long userid);
    
        @Query("SELECT * FROM user WHERE userid = :userid")
        User getUserById(long userid);
    
    }
    
    • isUuserACoach 是复制上述假设的 CHECK 约束的重要查询。

    Database.java(@Database 房间)

    @androidx.room.Database(version = 1,entities = {Role.class,User.class,Problem.class})
    public abstract class Database extends RoomDatabase {
    
        abstract AllDao allDao();
    }
    

    MainActivity.java(全部测试)

    public class MainActivity extends AppCompatActivity {
        Database database;
        AllDao allDao;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            database = Room.databaseBuilder(
                    this,
                    Database.class,
                    "mydb"
            )
                    .allowMainThreadQueries()
                    .build();
            allDao = database.allDao();
    
            long coachRole = allDao.insertRole(new Role("Coach",Role.AUTHORITY_COACH));
            long newClientRole = allDao.insertRole(new Role("New Client",Role.AUTHORITY_CLIENT));
            long oldClientRole = allDao.insertRole(new Role("Old Client",Role.AUTHORITY_CLIENT));
    
            long fredBloggs = allDao.insertUser(new User("Fred","Bloggs","fblog","fblog@mail.com","password",coachRole));
            long marySmith = allDao.insertUser(new User("Mary","Smith","msmith","msmith@mail.com","password",newClientRole));
            long anneWalker = allDao.insertUser(new User("Anne","Walker","awalker","awalkjer@mail.com","password",oldClientRole));
    
            //<<<<<<<<<< SHOULD BE OK TO ADD >>>>>>>>>>
            if (verifyAndAddProblem("Problem 1","new",allDao.getUserById(fredBloggs),allDao.getUserById(marySmith)) > 0) {
                Log.d("PROBLEMADDRESULT","Problem Added");
            } else {
                Log.d("PROBLEMADDRESULT","Problem not added");
            }
    
            //<<<<<<<<<< SHOULD NOT BE ADDED (annWalker is NOT a coach) >>>>>>>>>>
            if (verifyAndAddProblem("Problem 2","new",allDao.getUserById(anneWalker),allDao.getUserById(marySmith)) > 0) {
                Log.d("PROBLEMADDRESULT","Problem Added");
            } else {
                Log.d("PROBLEMADDRESULT","Problem not added");
            }
    
            //<<<<<<<<<< SHOULD NOT BE ADDED (fredBloggs is a coach BUT is NOT a client) >>>>>>>>>>
            if (verifyAndAddProblem("Problem 3","new",allDao.getUserById(fredBloggs),allDao.getUserById(fredBloggs)) > 0) {
                Log.d("PROBLEMADDRESULT","Problem Added");
            } else {
                Log.d("PROBLEMADDRESULT","Problem not added");
            }
        }
    
        //ALTERNATIVE TO CHECK CONSTRAINT DONE HERE
        private long verifyAndAddProblem(String title, String status, User coach, User client) {
    
            long rv = -1;
            if (!allDao.isUserACoach(coach.getUserid()) || allDao.isUserACoach(client.getUserid())) return rv;
            return allDao.insertProblem(new Problem(title,status,coach,client));
        }
    }
    

    当运行日志包含:-

    2020-01-02 08:47:33.647 D/PROBLEMADDRESULT: Problem Added
    2020-01-02 08:47:33.658 D/PROBLEMADDRESULT: Problem not added
    2020-01-02 08:47:33.661 D/PROBLEMADDRESULT: Problem not added
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-07
      • 2014-02-14
      • 2019-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-04
      • 1970-01-01
      相关资源
      最近更新 更多