<2021SC@SDUSC>山东大学软件工程应用与实践JPress代码分析(十二)

2021SC@SDUSC

本篇文章着重围绕代码模块jpress-service,jpress-service-provider,分析RoleService、RoleServiceProvider两个类。本次分析的两个类针对于使用网站后台的用户进行角色权限操作的控制,包含角色的设置、角色的权限类型等等。报告中会针对代码中对数据库的操作进行底层的深入补充。

一、RoleService

1.1 数据库表

用户有对应的角色,并且一个用户可以有多个角色,由此有user_role_mapping表。
在这里插入图片描述
对于登入后台的角色,首先涉及的是角色role表。
在这里插入图片描述
通过role_permission_mapping表,将role与permission表联系在一起,表示角色及对应的权限。
在这里插入图片描述
permission表标明权限对应的菜单(或说是功能选项)。
在这里插入图片描述

1.2 RoleService

该类为接口类,定义了JPress操作以上数据库表的接口,对角色以及权限进行操作,在provider里会给予详细实现,在这里简述接口的用途。

Role findById(Object id);

根据主键id查找角色表的model

Object saveOrUpdate(Role model);

新增或者更新Role表的Model 数据(主键值为 null 就新增,不为 null 则更新)

boolean isSupperAdmin(long userId);

用户是否是超级管理员的判定,常用在PermissionServiceProvider中

boolean hasRole(long userId, String... roles);

判断在role表中,该用户是否拥有这个角色

boolean hasAnyRole(long userId);

判断该用书是否有任何一个后台角色

boolean addPermission(long roleId, long permissionId);

给角色添加相应特权

boolean doResetUserRoles(long userId, Long... RoleIds);

通过一系列角色的id,重置该用户的每项角色

List<Role> findRoleListByUserId(long userId);

找到该用户的所有角色返回角色列表

二、RoleServiceProvider

本类中的方法多为操作底层dao层对数据库进行增删改查操作,在下方选出两个用法较多、类中比较特殊的方法进行解析。

2.1 deleteById

@Override
@CacheEvict(name = "user_role", key = "*")
public boolean deleteById(Object id) {
    return Db.tx(() -> {
        Db.update("delete from user_role_mapping where role_id = ? ", id);
        Db.update("delete from role_permission_mapping where role_id = ? ", id);
        return RoleServiceProvider.super.deleteById(id);
    });
}
  • 方法的目的为根据id删除用户角色映射表与角色权限映射表中的对应Model。
  • 使用@CacheEvict注解,每次调用都会清除缓存。
  • Db.tx(() -> {})为JFinal的事务运用。在要往数据库操作多条数据时,就需要用到事务,JFinal中有封装好的事务应用DB.tx(),返回布尔值,一但在语句中的一条操作失败则发生回滚事件。
  • db操作中,将用户角色映射表与角色权限映射表中信息删除。
  • 因继承JbootServiceBase<Role>,调用父类方法,底层调用dao层进行数据库操作,并更新缓存。

2.2 doResetUserRoles

@Override
@CachesEvict({
        @CacheEvict(name = "user_role", key = "*"),
        @CacheEvict(name = "user_permission", key = "*")
})
public boolean doResetUserRoles(long userId, Long... RoleIds) {
    if (RoleIds == null || RoleIds.length == 0) {
        return Db.delete("delete from user_role_mapping where user_id = ? ", userId) > 0;
    }

    return Db.tx(() -> {
        Db.delete("delete from user_role_mapping where user_id = ? ", userId);

        List<Record> records = new ArrayList<>();
        for (Long roleId : RoleIds) {
            Record record = new Record();
            record.set("user_id", userId);
            record.set("role_id", roleId);
            records.add(record);
        }

        Db.batchSave("user_role_mapping", records, records.size());

        return true;
    });
}
  • 方法的目的为根据用户的id以及多个角色的id进行对某个用户的角色的重新设置。
  • 使用JFinal的事务Db.tx()首先删除用于的所有角色。
  • 根据输入的角色列表,创建user_id对应role_id的记录。
  • 通过Db.batchSave()进行user_role_mapping表中记录的保存。batchSave为JFinal提供的DB+Record下独特的批处理保存模式。

三、补充:JFinal数据库操作

在上文中,我们可以看到DB+Record的数据库操作模式,也能看到基本的调用Dao层对数据库增删改查,还有JFinal使用SqlUtils.buildInSqlPara(ids)进行参数构建等等。接下来会补充JFinal操作数据的更多种方式。

3.1 Dialect多数据库支持

  • Dialect:方言。指的是数据库的方言。MySQL 是一种方言,Oracle 也是一种方言,MSSQL 也是一种方言,他们之间在遵循 SQL 规范的前提下,都有各自的扩展特性。
  • 拿分页来说,MySQL 的分页是用关键字 limit, 而 Oracle 用的是 ROWNUM,MSSQL 可能又是另一种分页方式。
  • 目前ActiveRecordPlugin提供了MysqlDialect、OracleDialect、PostgresqlDialect、SqlServerDialect、Sqlite3Dialect、AnsiSqlDialect实现类。MysqlDialect与OracleDialect分别实现对Mysql与Oracle的支持,AnsiSqlDialect实现对遵守ANSI SQL数据库的支持。以下是数据库Dialect的配置代码:
public class DemoConfig extends JFinalConfig {
  public void configPlugin(Plugins me) {
    ActiveRecordPlugin arp = new ActiveRecordPlugin();
    me.add(arp);
    // 配置Postgresql方言
    arp.setDialect(new PostgresqlDialect());
  }
}

3.2 表关联操作

  • JFinal ActiveRecord 天然支持表关联操作,并不需要学习新的东西,此为无招胜有招。表关联操作主要有两种方式:一是直接使用sql得到关联数据;二是在Model中添加获取关联数据的方法。
  • 假定现有两张数据库表:user、blog,并且user到blog是一对多关系,blog表中使用user_id关联到user表。如下代码演示使用第一种方式得到user_name:
public void relation() {
  String sql = "select b.*, u.user_name from blog b inner join user u on b.user_id=u.id where b.id=?";
  Blog blog = Blog.dao.findFirst(sql, 123);
  String name = blog.getStr("user_name");
}
  • 以下代码演示第二种方式在Blog中获取相关联的User以及在User中获取相关联的Blog:
public class Blog extends Model<Blog>{
    public static final Blog dao = new Blog().dao();
    
    public User getUser() {
       return User.dao.findById(get("user_id"));
    }
}
 
public class User extends Model<User>{
    public static final User dao = new User().dao();
    
    public List<Blog> getBlogs() {
       return Blog.dao.find("select * from blog where user_id=?", get("id"));
    }
}
  • 上面代码在具体的 Model 中 new 了一个 dao 对象出来,这种用法仅用于表关联操作,其它情况的 dao 对象应该让 Service 层持有。

3.3 复合主键

  • JFinal ActiveRecord 从 2.0 版本开始,采用极简设计支持复合主键,对于 Model 来说需要在映射时指定复合主键名称,以下是具体例子:
ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
// 多数据源的配置仅仅是如下第二个参数指定一次复合主键名称
arp.addMapping("user_role", "userId, roleId", UserRole.class);
 
//同时指定复合主键值即可查找记录
UserRole.dao.findByIds(123, 456);
 
//同时指定复合主键值即可删除记录
  • 如上代码所示,对于Model来说,只需要在添加Model映射时指定复合主键名称即可开始使用复合主键,在后续的操作中JFinal会对复合主键支持的个数进行检测,当复合主键数量不正确时会报异常,尤其是复合主键数量不够时能够确保数据安全。复合主键不限定只能有两个,可以是数据库支持下的任意多个。
  • 对于 Db + Record 模式来说,复合主键的使用不需要配置,直接用即可:
Db.findByIds("user_role", "roleId, userId", 123, 456);
Db.deleteByIds("user_role", "roleId, userId", 123, 456);

这正是在上文中给我们对user_role_mapping中复合主键的删除时使用的操作模式。

四、总结

本篇文章详细分析了JPress管理后台对用户、角色、权限的操作接口与实现方法。在代码分期的前期文章中,我们使用的是简单的数据库操作方式,只是重点介绍了DB+Record的操作模式,但是在本篇文章中通过实例分析总结了更加深入且更复杂的JFinal数据库操作,比如多数据库支持、表关联操作、复合主键等等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值