当前版本的 SqlDdlDao 的所有实现类都只支持单表查询, 需要使用 join 功能时,需要额外调用 DatasLeftJoinUtil 才能实现.
这将造成查询代码和join代码的分离, 无法实现 sql 重的 join 语句.
因此, 需要写一个新的 SqlDdlDao 实现类, 将 DatasLeftJoinUtil 中的 join 功能加入进去.
// 可以在静态类里存放每一个dao
SqlDdlDao leftDao = new HbaseDaoImpl("leftDao", "hbase://server_1:2121,server_2:2121/");
SqlDdlDao rightDao = new MongoDaoImpl("rightDao", "mongo://username:password@127.0.0.1:27017");
// left 有三个字段: id,a,b
List<Map<String, Object>> left = leftDao.sqlFindListMap("select id, a, b from left where a > 0");
// right 有三个字段: id,left_id,c
List<Map<String, Object>> right = rightDao.sqlFindListMap("select id, left_id, c from right where c > 0");
// join 后 left 有五个字段: id,a,b,right_id,c
DatasLeftJoinUtil.join(left, right, "join right(id as right_id, c) on left.id = right.left_id");
// 现在只需要一个大的dao, 并将各个小的dao注册进来
Map<String, SqlDdlDao> allSource = new HashMap<>();
allSource.put("left", leftDao); // 注册 left 表使用 leftDao 来操作
allSource.put("right", rightDao); // 注册 right 表使用 rightDao 来操作
SqlDdlDao allSourceDao = new SqlDdlDaoAutoSourceImpl(allSource)
// 以下代码等效于之前的三行代码
String sql = "select left.id, left.a, left.b, rigth.id as right_id, right.c from left as left"
+ " join (select id, left_id, c from right) as right on left.id = right.left_id";
List<Map<String, Object>> left = allSourceDao.sqlFindListMap(sql);
// 1. 入参
String sql = "select left.id, left.a, left.b, rigth.id as right_id, right.c from left as left"
+ " join (select id, left_id, c from right where c > 0) as right on left.id = right.left_id"
+ " where left.a > 0"
// 2. 拆分
String leftSql = "select id, a, b from left where a > 0";
String rightSql = "select id, left_id, c from right where c > 0";
String joinSql = "join right(id as right_id, c) on left.id = right.left_id";
String postSql = ""; // 某些情况下会对 join 后的结果执行二次过滤
// 3. 调用不同的dao执行sql, 可能有二次嵌套
SqlDdlDaoMultipleImpl dao = new SqlDdlDaoMultipleImpl();
List<Map<String, Object>> targetDatas = JsonTools.parseJsonToListMap("[{a:1,b:2},{a:11,b:22},{a:111,b:222}]");
List<Map<String, Object>> sourceDatas = JsonTools.parseJsonToListMap("[{c:1,d:3},{c:11,d:33},{c:111,d:333}]");
dao.addTableSource("table1", targetDatas);
dao.addTableSource("table2", sourceDatas);
String sysql = "select * from table1;select * from table2;join right(d) on a = c";
List<Map<String, Object>> maps = dao.sqlFindListMap(sysql);
// maps = [{"a":1,"b":2,"d":3},{"a":11,"b":22,"d":33},{"a":111,"b":222,"d":333}]
因为本工具包不要求注册数据结构,因此无法自动推断字段属于哪张表,所有的字段必须只用前缀标记所属表名.
SqlDdlDaoStreamAsyncImpl 是 SqlDdlDao 的流式异步查询实现。
支持对一个无界流同时执行一批 sql 语句, 从而大幅降低数据库负载。
// 用一个流构造 dao, stream 中可以是 map 也可使是 java bean
SqlDdlDaoStreamAsyncImpl asyncDao = new SqlDdlDaoStreamAsyncImpl(stream);
// 向流中注册多条 sql 语句, 这些 sql 语句的执行对象相同, 逻辑互不相关
String sql1 = "select count(*) as c from stream group by count";
Callback<List<Map<String, Object>>> callback1 = asyncDao.sqlFindListMap(sql1);
String sql2 = "select type, count(*) as c from stream group by count";
Callback<List<Map<String, Object>>> callback2 = asyncDao.sqlFindListMap(sql2);
String sql3 = "select name, count(*) as c from stream group by name";
Callback<List<Map<String, Object>>> callback3 = asyncDao.sqlFindListMap(sql3);
// 利用 count 消费流, 每一条数据被消费时, 所有 callback 中的数据都会被立刻更新。
sqlDdlDaoStreamAsync.count();
// 从 Callback 中获取返回值, 即使流没有被消费完, callback1 也可以放到其他线程中去 get()。
List<Map<String, Object>> listMap1 = callback1.get();
List<Map<String, Object>> listMap2 = callback2.get();
List<Map<String, Object>> listMap3 = callback3.get();
注意: SqlDdlDaoStreamAsyncImpl 目前的版本不支持子查询和联合查询, 后期考虑优先支持支持子查询。
SqlDdlDaoStreamAsyncImpl 支持传入 parallel 流, 此时 SqlDdlDaoStreamAsyncImpl 将会因为多线程执行而提高速度。
但是, parallel 流会增加 SqlDdlDaoStreamAsyncImpl 的内存占用, 并导致 first, last,list 等顺序敏感的 UDAF 函数返回值的变化。
DatasSqlDqlEngine 是针对内存中 List 数据执行 sql 语句的工具。
用于处理那些非数据库数据,比如 xls / csv 中的数据。
该系列有3个实现类:
sybn util 中提供了读写 excel 的工具类,并支持将 spring mvc 的数据 view 转换为excel.
主要有以下功能:
// 读取 excel, 支持 xls, xlsx 格式
List<Object[]> readExcel = PoiReadObjUtil.readExcelObject(new File("D:/xxx.xls"));
List<Map<String, Object>> maps = PoiReadObjUtil.arraysToMaps(readExcel);
LogUtil.info("\r\n", ListLogUtil.conver(maps, 32)); // 打印日志时最大列宽为32
// 写入 excel
ExcelBuilder builder = ExcelBuilderFactory.getExcelBuilder("文件名.xls", "excel标题", list);
builder.exportToLocalFile(new File("D:/"));
// 从 String 读取 csv
String csv = "a,b\r\n\"1\",2\r\n11,22\r\n111,222\r\n";
List<String[]> converterByStr = converterByStr(csv);
List<Map<String, Object>> maps = PoiReadObjUtil.arraysToMaps(converterByStr);
LogUtil.info("\r\n", ListLogUtil.conver(maps, 32)); // 打印日志时最大列宽为32
// 从文件读取 csv (支持 InputStream 或者 文件名 读取)
List<String[]> converterByStr = converter("D:/xxx.csv", Charsets.UTF_8);
List<Map<String, Object>> maps = PoiReadObjUtil.arraysToMaps(converterByStr);
LogUtil.info("\r\n", ListLogUtil.conver(maps, 32)); // 打印日志时最大列宽为32
// 读取 InputStream 到 Stream
String csv = "a,b\r\n\"1\",2\r\n11,22\r\n111,222\r\n";
InputStream is = new ByteArrayInputStream(csv.getBytes());
Stream<String[]> stream = converterStream(is, Charsets.UTF_8);
Stream<Map<String, Object>> stream2 = PoiReadObjUtil.arraysToMaps(stream);
stream.colse(); // 关闭 InputStream
List<Map<String, Object>> converterByStr2 = stream2.collect(Collectors.toList());
LogUtil.info("\r\n", ListLogUtil.conver(converterByStr2, 32)); // 打印日志时最大列宽为32
sybn counter 是一个多线程计数器, 此接口的两个实现 SybnCounterImpl 和 SybnCounterMapImpl 都是线程安全的. sybn counter 主要有以下功能:
import cn.sybn.bean.counter.SybnCounter;
import cn.sybn.bean.counter.SybnCounterImpl;
SybnCounter count = new SybnCounterImpl();
count.setCount("aaa", 1);
count.incCount("aaa", 1);
long aaa = count.getCount("aaa"); // 2
count.setTotal(100);
count.incCurrent(10);
String log = count.toCountString(); // {progress:10/100( 10.00%), currentTime:1ms, remainTime:9ms, aaa:2}
@Test
public void mainTest() {
// 测试并发 300 个线程同时写入
final SybnCounterImpl c = new SybnCounterImpl();
ExecutorService service = Executors.newFixedThreadPool(300);
for (int i = 0; i < 1000; i++) {
service.execute(new SybnCounterUtil.SybnCounterTestThread(c, i));
}
service.shutdown();
try {
service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
LogUtil.info(c);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
LogUtil.getLogger().error("", e);
}
SybnCounterImpl c2 = new SybnCounterImpl(false);
c2.setCount("a", 1);
c2.incCount("a", 1);
c2.clearCount("a");
c2.incCount("a", 1);
Assert.assertEquals(1, c2.getCount("a"));
}