Foxnic-SQL (12) —— DAO 个性 : 记录与记录集

概述

  默认状况下,JDBC 从数据库获得的是 ResultSet(游标),然而游标关上着是耗费数据库连贯的,所以咱们心愿,关上游标取数完结后立刻敞开游标。Foxnic-SQL 应用 Rcd(记录)和 RcdSet(记录集) 将游标遍历的数据取出寄存。本节将具体介绍 Rcd(记录)和 RcdSet(记录集)的概念和应用办法。
  本文中的示例代码均可在 https://gitee.com/LeeFJ/foxnic-samples 我的项目中找到。

数据结构

  一般来讲,记录就相似一个 Map 的构造,记录集就是由这些 Map 组成的列表。从内存角度思考,这种存储构造并非是最优的,起因是每个记录各自存储列名是不适合的。一组构造统一的记录没有必要各自独自寄存这些构造信息,而是对立寄存。
所以,Foxnic-SQL 设计了元数据(构造数据)独立,且元数据归属于一个记录集,记录集内的记录构造统一,记录也归属于记录集,并不独立存在。上面这个例子展现了记录、记录集以及元数据之间的关系:

/*** 记录集、记录、元数据的关系*/@Testpublic void demo_RcdSetRcdMeta() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    // 查问并取得第一个记录    Rcd r = dao.queryRecord("select * from sys_dict where code like ?", "%o%");    // 输入记录的数据    System.out.println(r.toJSONObject());    // 取得以后记录的所在的记录集    RcdSet rs = r.getOwnerSet();    // 取得记录集元数据    QueryMetaData meta = rs.getMetaData();    // 遍历元数据    for (int i = 0; i < meta.getColumnCount(); i++) {        // 取得列标签        String label = meta.getColumnLabel(i);        // 按标签取数        Object value = r.getValue(label);        // 输入        System.out.println(label + " = " + value);    }}

取得记录集

  取得记录集的路径一般来讲就是通过 DAO 对象的查询方法。尽管,RcdSet 和 Rcd 的构造函数创立,但这样做仿佛没有太大意义。除了 DAO 的查询方法创立 RcdSet 外,还能够通过本身克隆和取子集的形式取得新的记录集。示例如下:

/*** 取得记录集*/@Testpublic void demo_GetRcdSet() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    // 形式1:查问    RcdSet rs = dao.queryPage("select * from sys_dict where code like ?", 50, 1, "%o%");    // 形式2:克隆    RcdSet rs2 = rs.clone();    // 形式3:子集    RcdSet rs3 = rs.subset(1, 3, true);    // 遍历    for (Rcd rcd : rs3) {        System.out.println(rcd.toJSONObject());    }}

遍历记录集

  获得记录集之后,最常见的操作就是遍历,Foxnic-SQL 设计了多种记录集遍历形式。首先 RcdSet 实现了 Iterable 接口,能够通过 for...each 遍历,其次是 通过 RcdSet.getRcd(i) 办法按下标遍历。RcdSet 也反对 Lambda 形式的遍历。以下是 RcdSet 应用各种办法遍历的示例代码:

/*** 记录集遍历*/@Testpublic void demo_IterateOver() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    RcdSet rs = dao.queryPage("select * from sys_dict where code like ?", 50, 1, "%o%");    //遍历形式-1 : iterator    for (Rcd r : rs) {        System.out.println(r.toJSONObject());    }    // 遍历形式-2 : 按行取    for (int i = 0; i < rs.size(); i++) {        Rcd r = rs.getRcd(i);        System.out.println(r.toJSONObject());    }    // 遍历形式-3 : Lambda    rs.stream().forEach(r -> {        System.out.println(r.toJSONObject());    });    // 遍历形式-3 : Lambda    rs.parallelStream().forEach(r -> {        System.out.println(r.toJSONObject());    });}

值类型与存取值

  特地值得一提的是,RcdSet 在减少新列时仅指定了列名,并未指定列的类型。这是因为,RcdSet 存储的数据实际上是动静的。当 DAO 从数据库取数时,会按从游标中取到的值寄存,Rcd 类提供了多种办法获取开发人员想要的数据类型。
  Rcd 存取值能够应用数值下标也能够用列名,列名也有多种匹配模式。尽管 Rcd 反对多种命名形式,但倡议优先选用数据库原始字段名,其次是驼峰命名。如下示例所示:

/*** 取值与设置值* */@Testpublic void demo_Value() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    RcdSet rs = dao.queryPage("select * from sys_dict where code like ? and create_time is not null", 50, 1, "%o%");    rs.parallelStream().forEach(r->{        // 取 Object 类型值,理论值类型和从数据库取出时的原始类型统一        Object idObj=r.getValue("id");        // 指定取字符串类型        String idStr=r.getString("id");        // 指定取 Long 类型,Rcd 会尽可能转换成 Long 类型,如果不能转换,返回 null        Long idLong=r.getLong("id");        // 指定取 Date 类型,Rcd 会尽可能转换成 Date 类型,如果不能转换,返回 null        Date idDate=r.getDate("id");        // 用序号取数取得更高的效率        idObj=r.getValue(0);        // 按序号设置值,无类型校验        r.set(0,"12345");        // 按列名设置值        r.set("id","12345");        // 设置值,同时单个单词的列名不辨别大小写        idObj=r.getValue("id");        idObj=r.getValue("ID");        // 多单词列名按原始数据库命名的匹配形式        Date date1=r.getDate("create_time");        // 多单词列名按驼峰命名的匹配形式        Date date2=r.getDate("createTime");        // 多单词列名按驼峰命名大写或小写的匹配形式        Date date3=r.getDate("createtime");        Date date4=r.getDate("CREATETIME");        System.out.println();    });}

列管制

  通过 DAO 取得的记录集其构造和查问语句相干,查问语句中有哪些列那么记录集中就会有哪些列。但事实上,开发人员可能须要对记录集的列做一些解决,例如减少新列、删除原有列等。

/*** 列管制*/@Testpublic void demo_Column() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    RcdSet rs = dao.queryPage("select * from sys_dict where code like ?", 50, 1, "%o%");    // 输入列数    System.out.println("size after query " + rs.getFields().size());    // 减少列    rs.addColumn("new_column");    System.out.println("size after add " + rs.getFields().size());    for (String field : rs.getFields()) {        System.out.println("a : " + field);    }    // 变更列名    rs.changeColumnLabel("new_column", "new_column_1");    for (String field : rs.getFields()) {        System.out.println("b : " + field);    }    // 按列取数与设置    for (Rcd r : rs) {        r.set("new_column_1", IDGenerator.getNanoId());    }    // 取数    for (Rcd r : rs) {        // 用原始列名取数        String value1 = r.getString("new_column_1");        // 用驼峰命名取数        Object value2 = r.getValue("newColumn1");        System.out.println("value1 = " + value1 + " ;  value2=" + value2);        assertTrue(value2.equals(value1));    }}

数据结构转换

  在某些场合,咱们须要将记录集转换成某些指标构造,如单列提取为Arrray、List 或 Set;或者按列提取为 Map 构造等等,这些 RcdSet 都能很好的反对。示例代码如下:

/*** 数据结构转换*/@Testpublic void demo_Structure() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    RcdSet rs = dao.queryPage("select * from sys_dict where code like ?", 50, 1, "%o%");    // 提取单列数据为数组    String[] ids = rs.getValueArray("id", String.class);    // 提取单列数据为 List    List<Date> dateList = rs.getValueList("createTime", Date.class);    // 提取单列数据为 Set    Set<Date> codeSet = rs.getValueSet("code", Date.class);    // 提取指定列数据为 Map    Map<String, String> map = rs.getValueMap("id", String.class, "name", String.class);    // 将记录集转换成 Map 构造    Map<String, Rcd> rcdMap = rs.getMappedRcds("id", String.class);    // 将记录集进行分组    Map<Object, List<Rcd>> groupedMap = rs.getGroupedMap("id", "code");}

排序、过滤、去重

  RcdSet 同时也提供若干排序、过滤、去重的办法,示例代码如下:

/*** 排序、过滤、去重*/@Testpublic void demo_misc() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    RcdSet rs = dao.queryPage("select * from sys_dict where code like ?", 50, 1, "%o%");    rs.parallelStream().forEach(r -> {        System.out.println(r.toJSONObject());    });    // 按指定字段去重    rs.distinct("id");    // 按指定字段排序    rs.sort("create_time", true, true);    // 查找,准确匹配,返回第一行匹配的记录    Rcd r = rs.find("name", "机柜状态");    System.out.println(r == null ? "未找到" : r.toJSONObject());    // 过滤,并指定匹配操作符    RcdSet result = rs.filter("id", "36", FilterOperator.CONTAINS);    for (Rcd rcd : result) {        System.out.println("filter" + rcd.toJSONObject());    }    // 减少排名列    rs.rank("rank", "id", true);    for (Rcd rcd : rs) {        System.out.println("rank-1 :  id = " + rcd.getString("id") + " , rank = " + rcd.getInteger("rank"));    }    // 用排名值填充一个曾经存在的列    rs.fillRankField("version", "create_time", false);    for (Rcd rcd : rs) {        System.out.println("rank-2 : id = " + rcd.getString("create_time") + " , rank = " + rcd.getInteger("version"));    }}

序列化

  RcdSet 的序列化次要是将本身或 Rcd 转换成 JSON 对象。Foxnic-SQL 依赖了 FastJSON,示例代码如下:

/*** 序列化*/@Testpublic void demo_serialization() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    RcdSet rs = dao.queryPage("select * from sys_dict where code like ?", 50, 1, "%o%");    // 序列化记录集为 JSONArray 每个元素为 JSONObject    JSONArray array1 = rs.toJSONArrayWithJSONObject();    // 序列化记录集为 JSONArray 每个元素为 JSONArray    JSONArray array2 = rs.toJSONArrayWithJSONArray();    // 序列化记录集为 JSONArray 每个元素为 JSONObject , 并指定哪些字段转入到 JSONObject    JSONArray array4 = rs.toJSONArrayWithJSONObject("id", "code", "name");    System.out.println(array4);    // 序列化记录    for (Rcd r : rs) {        // 全字段转换        JSONObject object1 = r.toJSONObject();        System.out.println(object1);        // 指定字段转换        JSONObject object2 = r.toJSONObject("id", "code", "name");        System.out.println(object2);    }    // 指定列名转换规则 , DB_UPPER_CASE 示意齐全和数据库统一,整体转大写    rs.setDataNameFormat(DataNameFormat.DB_UPPER_CASE);    System.out.println(rs.toJSONArrayWithJSONObject());}

实体转换

  RcdSet 除了转 JSON 对象外,也能够转换成 Java 的 Pojo 对象;转换到 Foxnic 生成的实体对象时将取得更高的性能。示例代码如下:

/*** 解决实体数据*/@Testpublic void demo_entity() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    RcdSet rs = dao.queryPage("select * from example_address", 50, 1);    // 转成实体列表    List<Address> addressList = rs.toEntityList(Address.class);    System.out.println(JSON.toJSON(addressList));    // 转成分页的实体列表    PagedList<Address> addressPageList = rs.toPagedEntityList(Address.class);    System.out.println(JSON.toJSON(addressPageList));    // 遍历并一一转换    for (Rcd r : rs) {        Address address = r.toEntity(Address.class);        System.out.println(JSON.toJSON(address));    }}

数据操作

  开发人员能够通过 RcdSet 和 Rcd 实现数据的长久化操作。Rcd 为开发人员提供了 insert、update、save、delete 等办法,间接操纵在库数据。示例代码如下:

/*** 操作数据*/@Testpublic void demo_crud() {    // 创立DAO    DAO dao = DBInstance.DEFAULT.dao();    String id = IDGenerator.getSnowflakeIdString();    Rcd r = dao.queryRecord("select * from example_address where id=?", id);    boolean suc = false;    if (r == null) {        r = dao.createRecord("example_address");        r.set("id", id);        r.set("name", "leefj");        // 如果是 null 则不连入SQL语句        r.set("phone_number", "13444252562");        r.set("address", "宁波");        r.set("region_type", "国内");        r.set("create_time", new Date());        // 设置数据库表达式        r.setExpr("update_time", "now()");        // 执行插入操作        suc=r.insert();        System.out.println("数据插入"+(suc?"胜利":"失败"));    } else {        r.set("update_time", new Date());        r.set("update_by", "leefj");        // 执行更新操作        suc=r.update(SaveMode.DIRTY_FIELDS);        System.out.println("数据更新"+(suc?"胜利":"失败"));    }    suc=r.delete();    System.out.println("数据删除"+(suc?"胜利":"失败"));}

小结

  本节次要介绍了 Foxnic-SQL 中的记录集(RcdSet)和记录(Rcd),它们为开发人员提供了诸多性能,如遍历与存取值、构造转换,过滤排序、数据操作等。记录集(RcdSet)和记录(Rcd)特地在无 Po 对象的场景下对解决数据,它的动态性也特地适宜很多高级场景。

相干我的项目

  https://gitee.com/LeeFJ/foxnic
  https://gitee.com/LeeFJ/foxnic-web
  https://gitee.com/lank/eam
  https://gitee.com/LeeFJ/foxnic-samples

官网文档

  http://foxnicweb.com/docs/doc.html