问答

如何动态改变返回json的映射关系

作者:admin 2021-04-18 我要评论

比如说权限控制时,角色role1只能访问数据表table1的部分字段。我的想法是将建立一个role-数据表-字段的表table2,然后在查询table1后会根据orm返回一个对象,然...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

比如说权限控制时,角色role1只能访问数据表table1的部分字段。我的想法是将建立一个role-数据表-字段的表table2,然后在查询table1后会根据orm返回一个对象,然后再查询table2, 返回json时只对允许访问的字段进行映射。
但是我只知道怎么建立静态的映射以及过滤,不知道如何动态地进行映射?
除了这种方式,还能如何控制字段的访问权限?

###

其实呢,控制字段权限有没有啥好东西可用,之前我没遇到过类似需求,没太关注,但是根据跟题主的评论来看,其实就是动态的序列化。其他不说Jackson我记得就是可以做到动态序列化的

例如如下,这样就只有id被序列化出来了

@Data
@AllArgsConstructor
@JsonFilter("user")
public class User {
    private Long id;
    private String name;
    
    public static void main(String[] args) throws JsonProcessingException {
        User user = new User(1l, "haha");
        
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider();
        simpleFilterProvider.addFilter("user", SimpleBeanPropertyFilter.filterOutAllExcept("id"));
        objectMapper.setFilterProvider(simpleFilterProvider);
        
        String s = objectMapper.writeValueAsString(user);
        System.out.println(s);
    }
}

如果仅仅只是为了达到这样的效果,其实也就够了叭。仅供参考嗷


2020-03-27更新

其实吧,之前的那种通过查询table1出来数据后再根据table2的记录选择字段序列化的方式感觉还是不够好,原因显而易见,那就是table1的数据是完整查出来了啊

个人观点,正确的模式理应来说应该先是根据table2的记录查询出要使用哪些字段,然后再根据这些字段去查询table1,对于数据库来说就是select colum1,colum2 ...

但是由于运用了orm的一些工具,所以本质来说我们是排斥直接面向数据库编程的,面向JPA编程才是关键,虽说大多数orm肯定也是支持native query,咱们也还是要回归面向对象去处理数据库的。

所以说结合我之前的经验,这里的问题我感觉就转变为:如何根据某些特定的字段通过JPA api做出select colum1,colum2 ...的效果。

回到JPA以及现在Spring的生态,肯定不得不使用到Spring Data JPA对于JPA的再封装。所以上面的问题又再变为:如何根据某些特定的字段通过Spring Data JPA api做出select colum1,colum2 ...的效果。

而我们平常使用Spring Data JPA都会使用起Respository,但是普通咱们的Respository还是满足不了我们的需求的,所以我们需要定制Respository

比如我们有个EntityStudent,里面3个字段,还有个对应的StudentRepository

@Data
@Entity
@Table(name = "student")
public class Student {
    @Id
    @Column 
    private Long id;
    
    @Column
    private String name;
    
    @Column(name = "nick_name")
    private String nickName;
}

public interface StudentRepository extends JpaRepository<Student, Long> {
    
}

由于StudentRepository满足不了我们的需求,所以我们需要定制一下,或者按照官方说法叫扩展一些StudentRepository的功能,参考Spring Data JPA文档

我们创建一个新接口CustomStudentRepository,我们就是要根据一些字段名然后查询出来后直接转换为JSON字符串(不转为一个POJO,主要就是因为本身需要动态,POJO里的字段是不固定的,干脆就直接到最后的JSON字符串)

public interface CustomStudentRepository {
    String listToJSONString(List<String> fieldNames);
}

然后创建新接口的实现CustomStudentRepositoryImpl去完成逻辑,大多数逻辑其实就是用JPA api去操作数据库,最后结果用fastjson直接转成字符串

@RequiredArgsConstructor
public class CustomStudentRepositoryImpl implements CustomStudentRepository {
    @Autowired
    @NonNull 
    private final EntityManager em;
    
    @Override
    public String listToJSONString(List<String> fieldNames) {
        CriteriaQuery<Tuple> criteriaQuery = em.getCriteriaBuilder().createTupleQuery();
        Root<Student> root = criteriaQuery.from(Student.class);
        List paths = fieldNames.stream().map(fieldName -> root.get(fieldName))
                                        .collect(Collectors.toList());
        criteriaQuery.multiselect(paths);
        
        List<Tuple> tupleResult = em.createQuery(criteriaQuery).getResultList();
        
        List<Map<String, Object>> list = new ArrayList<>();
        for (Tuple tuple : tupleResult) {
            Map<String, Object> map = new HashMap<>();
            for (int i = 0; i < fieldNames.size(); i++) {
                map.put(fieldNames.get(i), tuple.get(i));
            }
            list.add(map);
        }
        return JSONObject.toJSONString(list);
    }
}

当然最后再修改一下我们的StudentRepository,毕竟CustomStudentRepositoryStudentRepository的扩展,因此StudentRepository是要继承CustomStudentRepository

public interface StudentRepository extends JpaRepository<Student, Long>, CustomStudentRepository {
    
}

这样使用StudentRepository就可以调用listToJSONString方法达到效果了

String students = studentRepository.listToJSONString(Arrays.asList("id", "nickName"));
System.out.println(students);

image.png

当然我这里listToJSONString的方法入参和出参,仅仅是为了解决我最开始提到的问题,实际题主自己的情况还可以再做调整,比如去掉入参List<String> fieldNames,这里fieldNames的来源直接在listToJSONString查询table2获取到,或者自己搞一个StudentServicefieldNames的来源在Service中读到,Service对外暴露的方法可以再业务化一点,这些都是看题主实际项目设计了,最后如果在listToJSONString中还有更多的查询条件的需求,简单了解一下JPA的语法,其实也不算太难。

就酱~拜(??????)??

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • nginx响应速度很慢

    nginx响应速度很慢

  • 点击选中的多选框,会在已选那一栏显示

    点击选中的多选框,会在已选那一栏显示

  • PHP 多态的理解

    PHP 多态的理解

  • 关于C语言中static的问题

    关于C语言中static的问题

腾讯云代理商
海外云服务器