发一个本人写的通用EXCEL导入功能,看了一下其他人发过的类似功能的导入组件,相比起来,我写的这个优点在于,能实现数据的一对一、一对多关联;直接使用SQL语句插入数据,不用通过实体类,执行效率会高一些。缺点在于,需要进行XML文件的配置,会有点麻烦。
首先举例说下使用方法。要导入以下两个EXCEL文件
--------student.xls--------------
学号 | 姓名 | 性别 |
2010001 | 张三 | 男 |
2010002 | 李四 | 女 |
------------sex.xls---------------
代码 标题
1 男
2 女
其中学生信息表中的性别字段为外键,XML配置如下
EXCEL文件与数据表映射关系excel-table-mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings>
<mapping><!-- 性别代码 -->
<import-name>sex</import-name><!-- 导入名称 -->
<first-row>1</first-row> <!-- 导入数据的开始行-->
<last-row>0</last-row> <!-- 结束行,为0表示到最后一行-->
<table-name>t_sex</table-name><!-- 要导入的数据库表名-->
<xml-name>com/ronghz/excel/t_sex.xml</xml-name><!-- EXCEL行与字段的映射文件,与src的相对路径-->
</mapping>
<mapping><!-- 学生资料 -->
<import-name>student</import-name>
<first-row>1</first-row>
<last-row>0</last-row>
<table-name>t_student</table-name>
<xml-name>com/ronghz/excel/t_student.xml</xml-name>
</mapping>
</mappings>
EXCEL文件列与数据库字段的映射关系
t_sex.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings>
<mapping>
<excel-order>0</excel-order> <!-- 数据在EXCEL文件中的列序号-->
<excel-name>代码</excel-name> <!-- 列名,可以为空-->
<column-name>code</column-name> <!-- 该列对应的字段名-->
<column-type>varchar</column-type> <!-- 数据类型,可能的取值为int,double,varchar,date,datetime,foreignkey,其它值都会当作varchar进行处理-->
</mapping>
<mapping>
<excel-order>1</excel-order>
<excel-name>内容</excel-name>
<column-name>title</column-name>
<column-type>varchar</column-type>
</mapping>
</mappings>
t_student.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings>
<mapping>
<excel-order>0</excel-order>
<excel-name>学号</excel-name>
<column-name>code</column-name>
<column-type>varchar</column-type>
</mapping>
<mapping>
<excel-order>1</excel-order>
<excel-name>姓名</excel-name>
<column-name>name</column-name>
<column-type>varchar</column-type>
</mapping>
<mapping>
<excel-order>2</excel-order>
<excel-name>性别</excel-name>
<column-name>sex</column-name>
<column-type>foreignkey</column-type> <!-- 声明该字段为外键-->
<outtable-name>t_sex</outtable-name> <!-- 外表的表名-->
<outtable-key>t_sex_id</outtable-key> <!-- 外表的主键-->
<outtable-value>title</outtable-value> <!-- 主表导入的数据对应于外表的哪个字段-->
</mapping>
</mappings>
调用相关类执行导入
public static void main(String[] args) {
ImportExcel ie = new ImportExcel();
try {
ie.execute("sex","D:/sex.xls");
ie.execute("student","D:/student.xls");
} catch (SQLException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
建立数据库
CREATE DATABASE `xls`;
CREATE TABLE `xls`.`t_sex` (
`t_sex_id` int(10) unsigned NOT NULL auto_increment,
`code` varchar(45) NOT NULL,
`title` varchar(45) NOT NULL,
PRIMARY KEY (`t_sex_id`)
);
CREATE TABLE `xls`.`t_student` (
`t_student_id` int(10) unsigned NOT NULL auto_increment,
`code` varchar(45) NOT NULL,
`name` varchar(45) default NULL,
`sex` int(10) unsigned default NULL,
PRIMARY KEY (`t_student_id`);
主要代码解释:
一共包含5个类:
Conn.java 数据库连接类
FileMapping.java
ImportExcel.java
TableMapping.java
TableMappingArray.java
ImportExcel.java的代码package com.ronghz.excel;
import java.io.*;
import java.sql.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import jxl.*;
import jxl.read.biff.BiffException;
/**
* Email:[email protected]
* Blog:http://blog.csdn.net/dreamdiary
* 欢迎转载;转载时请注明出处
*
*/
public class ImportExcel {
/**
* 执行导入操作
*
* @param importName导入的名称
* @param excelPath文件路径
* @return
* @throws SQLException
* @throws ParseException
*/
public boolean execute(String importName, String excelPath) throws SQLException, ParseException {
FileMapping fileMapping = new FileMapping(importName);// 通过导入类型名,找到对应的表名、字段映射配置文件
String xmlName = fileMapping.getXmlName();
TableMappingArray mappingArray = new TableMappingArray(xmlName);// 把以上代码找到的XML文件读取到数组中
TableMapping[] array = mappingArray.getArray();
Conn conn = new Conn();
try {
conn.beginTrans();
int haveFK = 0;// 外键关联的数目
String addTempColumn = "ALTER TABLE " + fileMapping.getTableName();
String delTempColumn = "ALTER TABLE " + fileMapping.getTableName();
String[] updateFK = new String[array.length];
String preSql = "UPDATE $tableName$ t1 SET $tableKey$=(SELECT $outtablKey$ FROM $outtableName$ t2 WHERE t1.$tableKeyTmp$ = t2.$outtableValue$)"
+ " WHERE EXISTS ( SELECT 1 FROM $outtableName$ t3 WHERE t1.$tableKeyTmp$=t3.$outtableValue$)";
String columns = "";
String values = "";
for (int i = 0; i < array.length; i++) {
if (array[i].getColumnType() == TableMapping.TYPE_FOREIGNKEY) {//如果该列类型属于外键
// 生成插入数据的SQL语句
columns += array[i].getColumnName() + "_tmp,";
// 增加临时字段用于保存导入的数据,导入完成后再将临时字段删除
addTempColumn += " ADD COLUMN " + array[i].getColumnName() + "_tmp VARCHAR(100),";
delTempColumn += " DROP COLUMN " + array[i].getColumnName() + "_tmp,";
// 把临时字段的数据转换成外键
String update = preSql.replace("$tableName$", fileMapping.getTableName());
update = update.replace("$tableKey$", array[i].getColumnName());
update = update.replace("$tableKeyTmp$", array[i].getColumnName() + "_tmp");
update = update.replace("$outtableName$", array[i].getOuttableName());
update = update.replace("$outtablKey$", array[i].getOuttableKey());
update = update.replace("$outtableValue$", array[i].getOuttableValue());
updateFK[haveFK++] = update;
} else {
columns += array[i].getColumnName() + ",";
}
values += "?,";
}
if (haveFK > 0) {//如果有类型为外键的列
conn.executeUpdate(addTempColumn.substring(0, addTempColumn.length() - 1));
}
//读取EXCEL文件的内容
String insertSQL = "INSERT INTO " + fileMapping.getTableName() + " (" + columns.substring(0, columns.length() - 1)+ ") " +
"VALUES (" + values.substring(0, values.length() - 1) + ")";
Workbook book = null;
book = Workbook.getWorkbook(new java.io.File(excelPath));
jxl.Sheet sheet = book.getSheet(0);
int sheetRows = sheet.getRows() > fileMapping.getLastRow() ? sheet.getRows() : fileMapping.getLastRow();
Cell cell;
PreparedStatement ps = conn.prepareStatement(insertSQL);
//
for (int row = fileMapping.getFirstRow(), count = 0; row < sheetRows; row++, count++) {
for (int i = 0; i < array.length; i++) {
cell = sheet.getCell(array[i].getExcelOrder(), row);
//根据数据库类型进行转换
switch (array[i].getColumnType()) {
case TableMapping.TYPE_INT:
ps.setInt(i + 1, cell.getContents().equals("") ? 0 : Integer.parseInt(cell.getContents()));
break;
case TableMapping.TYPE_DOUBLE:
ps.setDouble(i + 1, cell.getContents().equals("") ? 0 : Double.parseDouble(cell.getContents()));
break;
case TableMapping.TYPE_DATE:
ps.setTimestamp(i + 1, new Timestamp(parse(cell.getContents(), array[i].getColumnFormat()).getTime()));
break;
case TableMapping.TYPE_DATETIME:
ps.setTimestamp(i + 1, new Timestamp(parse(cell.getContents(), array[i].getColumnFormat()).getTime()));
break;
case TableMapping.TYPE_FOREIGNKEY:// 外键字段作为字符串类型插入到临时字段中
ps.setString(i + 1, cell.getContents());
break;
case TableMapping.TYPE_STRING:
ps.setString(i + 1, cell.getContents());
break;
}
}
ps.addBatch();
if (count > 500) {
count = 0;
ps.executeBatch();
}
}
ps.executeBatch();
if (haveFK > 0) {// 把临时字段的数据转换成外键
for (int i = 0; i < haveFK; i++) {
conn.executeUpdate(updateFK[i]);
}
//删除临时字段
conn.executeUpdate(delTempColumn.substring(0, delTempColumn.length() - 1));
}
conn.commit();
return true;
} catch (BiffException ex) {
System.out.println("Read Excel file failed!");
System.out.println("File name: " + excelPath);
} catch (IOException ex) {
System.out.println("Read Excel file failed!");
System.out.println("File name: " + excelPath);
} catch (SQLException e) {
conn.rollback();
System.out.println("DateBase ERROR!");
e.printStackTrace();
throw e;
} finally {
if (conn != null) {
conn.close();
conn = null;
}
}
return true;
}
public java.util.Date parse(String time, String format) throws ParseException {
if (time == null) {
return null;
} else {
SimpleDateFormat sdf = new SimpleDateFormat(format);
java.util.Date d = sdf.parse(time);
return d;
}
}
public static void main(String[] args) {
ImportExcel ie = new ImportExcel();
try {
ie.execute("sex","D:/sex.xls");
ie.execute("student","D:/student.xls");
} catch (SQLException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
如果要应用于WEB项目,只需要增加上传文件的操作,并把上传后的文件路径传到execute()函数中就可以了。
代码比较多,不全部贴出来了,需要的请到以下地址下载http://download.csdn.net/source/2464234