Jackcess 海量数据的.mdb文件快速生成

前言

之前有完成过一个需求,根据数据库名称提取对应数据库表的数据(有张表内有很多表的信息包括名称、连接信息等),使用这些信息生成一个.mdb文件,通过流返回给前端页面下载

一、mdb数据是什么

mdb (message driven bean)MDB也是EJB中消息驱动Bean的简称,MessageDrivenBean(MDB)。mdb(Microsoft Database)格式是Microsoft Access软件使用的一种存储格式,因其对数据操作的方便性,常用在一些中小型程序中。

在使用jackcess之前,我是直接加载驱动,获取连接这样一步步做下去,然后根据数据,一条条insert这样生成(后台的mdb文件也是几k几十k的加),数据量小的话还行,数据量一多,就经常造成前台等不及直接超时的问题(哪怕后台在一点点的下载)。

也不是没想过异步,但是由于不好确认文件是否下载完成,这种方法就搁置了。

后来公司大佬又做过类似的,几w条数据也是几秒就提取出来了,就去请教了下,了解了Jackcess之后,去网上查询过后,结合之前的数据,也算完成了任务。

二、Jackcess

Jackcess 是一个Java 类库,用来读写微软的Access 数据库。
使用了Jackcess,生成数据的速度确实是几何倍速的提升!

2.1 直接开始代码吧,首先引入依赖

        <dependency>
            <groupId>com.healthmarketscience.jackcess</groupId>
            <artifactId>jackcess</artifactId>
            <version>2.1.4</version>
        </dependency>

2.2 手动创建、单条插入数值
代码中的Types是java.sql.Types的

	//这里同样支持mdb和accdb
		Database db = DatabaseBuilder.create(Database.FileFormat.V2000, new File("D:\\geocloud\\mdbdownloads\\new.mdb"));
		Table newTable;
		try {
			//刚才是创建文件,这里是在文件里创建表,字段名,字段类型
			newTable = new TableBuilder("Archives")
			.addColumn(new ColumnBuilder("档案号")
			.setSQLType(Types.INTEGER))
			.addColumn(new ColumnBuilder("编制单位")
			.setSQLType(Types.VARCHAR))
			.addColumn(new ColumnBuilder("案卷正题名")
			.setSQLType(Types.VARCHAR))
			.addColumn(new ColumnBuilder("案卷题目长度")
			.setSQLType(Types.INTEGER))
			.addColumn(new ColumnBuilder("档案盒规格")
			.setSQLType(Types.VARCHAR))
			.addColumn(new ColumnBuilder("编制单位长度")
			.setSQLType(Types.INTEGER))
			.toTable(db);
			//插入一条数据测试
			newTable.addRow("12", "foo","212",44,"323",56);
		}
		catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

2.3 手动创建列,批量插入数据

     Database db = DatabaseBuilder.create(Database.FileFormat.V2000, new File("D:\\geocloud\\mdbdownloads\\批量导入.mdb"));
        Table newTable = new TableBuilder("table")
                .addColumn(new ColumnBuilder("id")
                        .setSQLType(Types.VARCHAR)
                        .toColumn())
                .addColumn(new ColumnBuilder("name")
                        .setSQLType(Types.VARCHAR)
                        .toColumn())
                .addColumn(new ColumnBuilder("nickname")
                        .setSQLType(Types.VARCHAR)
                        .toColumn())
                .toTable(db);
        ArrayList<Object[]> list=new ArrayList<Object[]>();
        for (int j = 0; j < 10000; j++){
            Object[] obj={j, "李" + j,"小" + j};
            list.add(obj);
        }
        newTable.addRows(list);

2.4 实际使用

以上,都是网络上的例子,确认可执行、可用的。我大概讲讲我总结的,前面的手动创建列名,类型等都可以通过循环设置,大家应该都知道。

主要在批量插入数据这里,有两种情况,两种情况批量插入数据基本一致,主要是获取属性的方法会有不同。

  1. 正常来说,通过数据库查询出来的数据都是List,都有对应的实体类,这种情况前面创建列名等需要的字段名称等数据可以通过反射或者自定义注解获取。数据的插入的话
  2. 我的情况是第二种,由于数据来自许多不同的数据库、表、甚至数据库种类都不同,无法提前创建实体类,属性列名的值可以通过遍历结果map的key来获得。我这里查询出来的结果,是这样的:
    返回值为List的时候
    可以看出基本就算是List了,里面是各个列名和对应的值,
    插入有两种办法
2.4.1 简单的
 Table newTable = new TableBuilder("table")
 table.addRowsFromMaps(data);

使用table自带的addRowsFromMaps方法,可以自动将List进行插入,通过查看源码可以知道,其实他就通过遍历这里List,最终也是使用addRows进行插入的。
addRowsFromMaps源码

2.4.2 根据2.3的例子,进行模仿

从例子2.3我们可以知道,addrows,通过插入一个List<Object[]>,就可以进行正确写入,又根据源码提示,实际上都是通过addrows进行插入数据,所以我写了一个类似addRowsFromMaps的方法(效果一样,就是觉得好玩写的)

List 可以通过这个方法获得一个List<Object[]> ,可直接方法如addrows执行,当然,如果得到的直接是List<Object[]>或者Object[],那就没必要多次一举了.

/**
 *从List<Map<String, Object>>中获取所有值的集合并转化为Object[]
 *@parammapList
*@return
*/
public List<Object[]> getValueList(List<Map<String, Object>> mapList){
    List<Object[]> result = new ArrayList<>();
    for (Map map: mapList){
        Collection values = map.values();
        Object[] objects = values.toArray();
        result.add(objects);
    }
    return result;
}

三、结论

其实看完2.2 2.3两个例子之后就可以想办法照着改就可以了,改成对应的动态形式,程序嘛,功能正确,保证输入输出一致就可以了。

四、遇到的问题(注意)

如果出现Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number的问题,如果无头绪,可先排除设置的字段类型。

最后的顺利导出是因为忽略了时间类型的问题,我设置字段类型为java.sql.Types.或者DATE的话,就会报错。

com.healthmarketscience.jackcess.BatchUpdateException: Failed adding rows (Db=40a7adee-099c-4115-9698-f8f2abf6315e.mdb;Table=db_mining_apply): java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number
	at com.healthmarketscience.jackcess.impl.TableImpl.addRows(TableImpl.java:1677)

在这里插入图片描述

BatchUpdateException查询资料后好像说是批量更新时统一抛出的错误,主要是下面的类型强化转问题。
有资料说:
如果尝试更新的任何字段是Access中的Date/Time字段, Jackcess能够隐式地将字符串转换为数字(在很多情况下,无论如何),但是当涉及到日期时,它就会转不过去,我尝试过将日期数据转化为毫秒数,转为Integer/int/Number存储,还是会存在这个问题,如果有大佬知道为什么的,请不吝赐教!

五、参考资料

  1. Java程序生成Access文件代码实例
  2. Java 的Access 数据库操作库 Jackcess
  3. 如何使用jackcess将数据从数组传递到数据库
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值