====== javadbf操作dbf ======
===== dbf扫盲 =====
==== 概念 ====
- 是常用的桌面型数据库
- 是dBase,Foxbase和FoxPro所使用的数据库格式
==== 打开方式 ====
- DBF Viewer Pro是一个用于Windows下的DBF数据库文件管理器。
- 用FOXPRO打开
- microsoft access可以导入导出.dbf文件
===== javadbf操作dbf =====
JAVA操作dbf文件的读写,这里使用第三方API:javadbf
==== 官网 ====
https://github.com/albfernandez/javadbf
==== 项目引入javadbf方式 ====
- 将jar加入classpath
- maven项目加入依赖
com.github.albfernandez
javadbf
1.9.2
==== 同时支持读与写的类型 ====
^ [XBase类型] ^ [XBase符号] ^ [JavaDBF中java类型] ^
| Character | C | java.lang.String |
| Numeric | N | java.math.BigDecimal |
| Double | F | java.math.BigDecimal |
| Logical | L | java.lang.Boolean |
| Date | D | java.util.Date |
==== 支持读暂不支持写的类型 ====
这算是javadbf的待完善之处,后续版本有可能会跟进优化
^ [FoxPro类型] ^ [符号] ^ [JavaDBF中java类型] ^
| Currency | Y | java.math.BigDecimal |
| Long | I | java.lang.Integer |
| Date Type | T | java.util.Date |
| Timestamp | @ | java.util.Date |
| AutoIncrement | + | java.lang.Integer |
| Memo | M | java.lang.String or byte[] |
| Binary | B | byte[] or java.lang.Double |
| Blob | W | byte[] |
| General | G | byte[] |
| Picture | P | byte[] |
| VarBinary | Q | byte[] |
| Varchar | V | java.lang.String |
| Double | O | java.lang.Double |
==== 分析常见的场景 ====
表: 对应一个 dbf文件,库与表一个概念
表关联: 表之间没有关联查询的功能
创建表: 创建文件,创建列,参考[testWrite1-见下方]
删除表: 删除文件即可
索引: 没有索引概念
增:
第一次创建文件,或者,一次性将数据覆盖原有文件,参考[testWrite1-见下方]
在文件末尾添加记录,参考[testWrite2-见下方]
删:
在[遍历表]的基础上,将想删的数据过滤,再覆盖文件
改:
在[遍历表]的基础上,将想改的数据修改,再覆盖文件
查:
[遍历表]
逐条记录读取,记录转Object数组,参考[testRead1-见下方]
逐条记录读取,根据列名读取列,参考[testRead2-见下方]
根据条件查
在遍历表的基础上,读取列,判断条件
==== DEMO-testRead1 ====
/**
* dbf文件
*/
public static final String DBF_FILE = "/Users/guanxianghui/Desktop/user.dbf";
/**
* 测试读取文件-逐条记录读取,记录转Object数组
*/
@Test
public void testRead1() {
System.out.println("[测试读取文件-逐条记录读取,记录转Object数组]");
System.out.println("DBF文件路径:[" + DBF_FILE + "]");
DBFReader reader = null;
try {
/**
* 读工具类,指定文件
*/
reader = new DBFReader(new FileInputStream(DBF_FILE), Charset.forName("GBK"));
/**
* 读取表结构
*/
int numberOfFields = reader.getFieldCount();
System.out.println("表结构共:[" + numberOfFields + "]列");
System.out.println("=========");
for (int i = 0; i < numberOfFields; i++) {
DBFField field = reader.getField(i);
System.out.println("列:" + field.getName() + "[" + field.getLength() + "]");
}
System.out.println("=========");
/**
* 逐条记录读取,记录转Object数组
*/
System.out.println("总记录数[" + reader.getRecordCount() + "],逐条记录读取,记录转Object数组");
System.out.println("=========");
Object[] rowObjects;
int index = 0;
while (null != (rowObjects = reader.nextRecord())) {
String content = "";
for (int i = 0; i < rowObjects.length; i++) {
if (StringUtils.isNotBlank(content)) {
content += ",";
}
content += rowObjects[i] + "[" + rowObjects[i].getClass().getName() + "]";
}
System.out.println("记录[" + (++index) + "]:" + content);
}
System.out.println("=========");
} catch (DBFException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
/**
* 关闭资源
*/
if (null != reader) {
DBFUtils.close(reader);
}
}
}
输出
[测试读取文件-逐条记录读取,记录转Object数组]
DBF文件路径:[/Users/guanxianghui/Desktop/user.dbf]
表结构共:[3]列
=========
列:COUNTRY[10]
列:NAME[20]
列:SALARY[12]
=========
总记录数[3],逐条记录读取,记录转Object数组
=========
记录[1]:中国[java.lang.String],关向辉[java.lang.String],5000.01[java.math.BigDecimal]
记录[2]:英国[java.lang.String],关向新[java.lang.String],3400.20[java.math.BigDecimal]
记录[3]:澳门[java.lang.String],关向阳[java.lang.String],7300.03[java.math.BigDecimal]
=========
==== DEMO-testRead2 ====
/**
* dbf文件
*/
public static final String DBF_FILE = "/Users/guanxianghui/Desktop/user.dbf";
/**
* 测试读取文件-逐条记录读取,根据列名读取列
*/
@Test
public void testRead2() {
System.out.println("[测试读取文件-逐条记录读取,根据列名读取列]");
System.out.println("DBF文件路径:[" + DBF_FILE + "]");
DBFReader reader = null;
try {
/**
* 读工具类,指定文件
*/
reader = new DBFReader(new FileInputStream(DBF_FILE), Charset.forName("GBK"));
/**
* 读取表结构
*/
int numberOfFields = reader.getFieldCount();
System.out.println("表结构共[:" + numberOfFields + "]列");
System.out.println("=========");
for (int i = 0; i < numberOfFields; i++) {
DBFField field = reader.getField(i);
System.out.println("列:" + field.getName() + "[" + field.getLength() + "]");
}
System.out.println("=========");
System.out.println("总记录数[" + reader.getRecordCount() + "],逐条记录读取,根据列名读取列");
System.out.println("=========");
/**
* 逐条记录读取,根据列名读取列
*/
DBFRow row;
int index = 0;
while ((row = reader.nextRow()) != null) {
System.out.println("第[" + (++index) + "]记录,COUNTRY:[" + row.getString("COUNTRY") + "],NAME:["
+ row.getString("NAME") + "],SALARY:[" + row.getString("SALARY") + "]");
}
System.out.println("=========");
} catch (DBFException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
/**
* 关闭资源
*/
if (null != reader) {
DBFUtils.close(reader);
}
}
}
输出
[测试读取文件-逐条记录读取,根据列名读取列]
DBF文件路径:[/Users/guanxianghui/Desktop/user.dbf]
表结构共[:3]列
=========
列:COUNTRY[10]
列:NAME[20]
列:SALARY[12]
=========
总记录数[6],逐条记录读取,根据列名读取列
=========
第[1]记录,COUNTRY:[中国],NAME:[关向辉],SALARY:[5000.01]
第[2]记录,COUNTRY:[英国],NAME:[关向新],SALARY:[3400.20]
第[3]记录,COUNTRY:[澳门],NAME:[关向阳],SALARY:[7300.03]
第[4]记录,COUNTRY:[中国],NAME:[关向辉],SALARY:[5000.01]
第[5]记录,COUNTRY:[英国],NAME:[关向新],SALARY:[3400.20]
第[6]记录,COUNTRY:[澳门],NAME:[关向阳],SALARY:[7300.03]
=========
==== DEMO-testWrite1 ====
这种方式是正常(Normal)模式(Mode),即非(Not)同步(Syn)方式(Mode),所有数据储存在内存中,在关闭(Close)资源时一口气输出到dfb文件,覆盖文件列+数据!好处:交互少,性能高,在数据量小的场景适用!隐患:数据量过大,有可能会导致内存溢出!
/**
* dbf文件
*/
public static final String DBF_FILE = "/Users/guanxianghui/Desktop/user.dbf";
/**
* 测试写文件-FileOutputStream-覆盖文件列+数据
*/
@Test
public void testWrite1() {
System.out.println("[测试写文件-FileOutputStream-覆盖文件列+数据]");
System.out.println("DBF文件路径:[" + DBF_FILE + "]");
/**
* 定义列
*/
DBFField[] fields = new DBFField[3];
fields[0] = new DBFField();
fields[0].setName("COUNTRY");
fields[0].setType(DBFDataType.CHARACTER);
fields[0].setLength(10);
fields[1] = new DBFField();
fields[1].setName("NAME");
fields[1].setType(DBFDataType.CHARACTER);
fields[1].setLength(20);
fields[2] = new DBFField();
fields[2].setName("SALARY");
fields[2].setType(DBFDataType.NUMERIC);
fields[2].setLength(12);
fields[2].setDecimalCount(2);
DBFWriter writer = null;
System.out.println("设置列,并输出数据到文件");
System.out.println("=========");
try {
/**
* 输出到文件
*/
writer = new DBFWriter(new FileOutputStream(DBF_FILE), Charset.forName("GBK"));//注意,是FileOutputStream
/**
* 设置列
*/
for (DBFField field : fields) {
System.out.println("列:" + field.getName() + "[" + field.getLength() + "]");
}
writer.setFields(fields);
System.out.println("=========");
/**
* 设置数据,并新增数据
*/
Object rowData[] = new Object[3];
rowData[0] = "中国";
rowData[1] = "关向辉";
rowData[2] = new Double(5000.01);
System.out.println("输出数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
writer.addRecord(rowData);
rowData = new Object[3];
rowData[0] = "英国";
rowData[1] = "关向新";
rowData[2] = new Double(3400.20);
System.out.println("输出数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
writer.addRecord(rowData);
rowData = new Object[3];
rowData[0] = "澳门";
rowData[1] = "关向阳";
rowData[2] = new Double(7300.03);
System.out.println("输出数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
writer.addRecord(rowData);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
/**
* 关闭资源
*/
if (null != writer) {
DBFUtils.close(writer);
}
}
System.out.println("=========");
System.out.println("输出完成");
}
输出
[测试写文件-FileOutputStream-覆盖文件列+数据]
DBF文件路径:[/Users/guanxianghui/Desktop/user.dbf]
设置列,并输出数据到文件
=========
列:COUNTRY[10]
列:NAME[20]
列:SALARY[12]
=========
输出数据:[中国,关向辉,5000.01]
输出数据:[英国,关向新,3400.2]
输出数据:[澳门,关向阳,7300.03]
=========
输出完成
==== DEMO-testWrite2 ====
这种方式是同步(Syn)方式(Mode),添加记录addRecord方法调用时,马上输出到dfb文件!好处:数据量大时候,不会内存溢出!隐患:交互多,数据量小的场景,性能低下!
同步(Syn)方式(Mode),如果dbf文件存在,而且已经有列定义,不能重复操作列定义(重复操作会报错:com.linuxense.javadbf.DBFException: Fields has already been set),如果dbf是新文件,则可以操作一次列定义。
/**
* dbf文件
*/
public static final String DBF_FILE = "/Users/guanxianghui/Desktop/user.dbf";
/**
* 测试写文件-File-文件列不变,append追加数据
*/
@Test
public void testWrite2() {
System.out.println("[测试写文件-File-文件列不变,append追加数据]");
System.out.println("DBF文件路径:[" + DBF_FILE + "]");
DBFWriter writer = null;
System.out.println("=========");
try {
/**
* 输出到文件
*/
writer = new DBFWriter(new File(DBF_FILE), Charset.forName("GBK"));//注意,是File
/**
* 设置数据,并新增数据
*/
Object rowData[] = new Object[3];
rowData[0] = "中国";
rowData[1] = "关向辉";
rowData[2] = new Double(5000.01);
System.out.println("追加数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
writer.addRecord(rowData);
rowData = new Object[3];
rowData[0] = "英国";
rowData[1] = "关向新";
rowData[2] = new Double(3400.20);
System.out.println("追加数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
writer.addRecord(rowData);
rowData = new Object[3];
rowData[0] = "澳门";
rowData[1] = "关向阳";
rowData[2] = new Double(7300.03);
System.out.println("追加数据:[" + rowData[0] + "," + rowData[1] + "," + rowData[2] + "]");
writer.addRecord(rowData);
} finally {
/**
* 关闭资源
*/
if (null != writer) {
DBFUtils.close(writer);
}
}
System.out.println("=========");
System.out.println("输出完成");
}
输出
[测试写文件-File-文件列不变,append追加数据]
DBF文件路径:[/Users/guanxianghui/Desktop/user.dbf]
=========
追加数据:[中国,关向辉,5000.01]
追加数据:[英国,关向新,3400.2]
追加数据:[澳门,关向阳,7300.03]
=========
输出完成
==== dbf示例文件 ====
可以解压zip得到dbf示例文件
{{:分享:技术:dbf:user.dbf.zip|}}