数据源为Excel的解决方法

        上次做项目时要求数据源改为Excel,与项目一起打包执行。查了一下数据源是否可以直接使用Excel的解决方案,虽然有提供可以连接Excel的驱动,但是以Excel直接操作数据实在是不方便,没有数据库的功能强大,因此想到了使用了内嵌式数据库,再编写一段上传Excel把数据更新到数据库的代码,即可同样达到数据源与项目打包的效果。

一、解决思路

要求:数据源为Excel,与项目一起打包

方法:使用内嵌式数据库,后台处理上传Excel数据至数据库

内嵌式数据库就是不需要像Mysql/Oracle一样安装启动服务,而是在项目中直接引用它的jar包即可,它会以文件的形式存在于数据库中。在此项目中我使用了sqlite3。

上传Excel数据至数据库这一块我使用了fileUpload和poi,前者是用于上传文件,后者用于从Excel中读取数据;数据库连接我采用了SpringJdbcTemplate。好啦,以下是详细的步骤。

二、详细步骤

1、安装内置数据库sqlite3

1)首先从官网下载,我是在Windows下开发的,虽然后续是在linux环境下运行,但是sqlite3它可以自己跨平台,所以我直接下载windows版本就好了。

2)解压,然后创建一个sqlite3类型的文件,在命令提示符中使用sqlite3 DatabaseName.db的命令即可在当前目录下创建一个sqlite3类型的数据库

3)你可以用Navicat等可视化工具连接数据库,也可以把数据库直接复制到项目中,然后在applicationContext配置文件中配置连接信息即可。我这里采用了C3P0连接池。如果是放在resources下面,URL可以像我这样写,另外sqlite3的所使用的驱动是org.sqlite.JDBC。用户名和密码可以不使用。

测试一下,可发现数据库能够正常连接

2、Excel读取数据并写入数据库

poi提供了Excel读取的了解决方案,有一点要注意的是,分为HSSF和XSSF,其中HSSF是针对的office2007之前的版本xls,而XSSF针对的是office2007以后的版本xlsx,我这里采用的是XSSF。

思路:上传文件存放至临时地点——读取Excel数据封装成List——清空数据库之前的数据——插入数据到数据库

读取Excel数据封装成List,主要思想就是遍历每个sheet,再在每个sheet中遍历行,取列值,每行封装成一个对象,每个sheet又封装成一个list,批量插入数据库,下面直接上代码。

读取Excel.java

public class UploadExcel {
    public static List<Ammeter> readXlsx(String path) throws Exception {
        List<Ammeter> list = new ArrayList<Ammeter>();
        InputStream is = new FileInputStream(path);
        XSSFWorkbook xssfWorkbook = new XSSFWorkbook(is);
        for (Sheet xssfSheet : xssfWorkbook) {
            if (xssfSheet == null) {
                continue;
            }
            for (int rowNum = 1; rowNum <= xssfSheet.getLastRowNum(); rowNum++) {
                Row xssfRow = xssfSheet.getRow(rowNum);
                if (xssfRow == null) {
                    continue;
                }
                List<Ammeter> rowList = new ArrayList<Ammeter>();
                Ammeter ammeter = new Ammeter();
                int len=0;

                Cell cell = xssfRow.getCell(0);
                if (cell != null) {
                    cell.setCellType(Cell.CELL_TYPE_STRING);
                }
                ammeter.setTabNumber(cell.getStringCellValue());
                cell = xssfRow.getCell(1);
                if (cell != null) {
                    cell.setCellType(Cell.CELL_TYPE_STRING);
                }if((len=cell.getStringCellValue().length())>6){
                    ammeter.setActivePower(cell.getStringCellValue().substring(0,6));
                }else{
                    ammeter.setActivePower(cell.getStringCellValue());
                }

                cell = xssfRow.getCell(2);
                if (cell != null) {
                    cell.setCellType(Cell.CELL_TYPE_STRING);
                }if((len=cell.getStringCellValue().length())>6){
                    ammeter.setReactivePower(cell.getStringCellValue().substring(0,6));
                }else{
                    ammeter.setReactivePower(cell.getStringCellValue());
                }

                cell = xssfRow.getCell(3);
                if (cell != null) {
                    cell.setCellType(Cell.CELL_TYPE_STRING);
                }if((len=cell.getStringCellValue().length())>6){
                    ammeter.setCumulativePower(cell.getStringCellValue().substring(0,6));
                }else{
                    ammeter.setCumulativePower(cell.getStringCellValue());
                }

                cell = xssfRow.getCell(4);
                if (cell != null) {
                    cell.setCellType(Cell.CELL_TYPE_STRING);
                }if((len=cell.getStringCellValue().length())>6){
                    ammeter.setVoltage(cell.getStringCellValue().substring(0,6));
                }else{
                    ammeter.setVoltage(cell.getStringCellValue());
                }

                cell = xssfRow.getCell(5);
                if (cell != null) {
                    cell.setCellType(Cell.CELL_TYPE_STRING);
                }if((len=cell.getStringCellValue().length())>6){
                    ammeter.setElectricity(cell.getStringCellValue().substring(0,6));
                }else{
                    ammeter.setElectricity(cell.getStringCellValue());
                }

                cell = xssfRow.getCell(6);
                if (cell != null) {
                    cell.setCellType(Cell.CELL_TYPE_STRING);
                }
                ammeter.setCurrentTime(cell.getStringCellValue());

                list.add(ammeter);
            }
        }
        return list;


    }
}

这里有几个点要注意一下:

1)我直接跟业务耦合了,但其实可以抽取出来作为一个工具类的;

2)读取的时候我直接将Excel单元格的类型强制转换为字符串类型,使用的cell.setCellType(Cell.CELL_TYPE_STRING),但是这个方法在新版的poi中的已经弃用了的,后续再看抽象出一个方法类的时候再写,现在先过;

3)Excel中使用下拉填充的数字读取出来有时会带若干小数,在这里我因为强制转换为字符类型,所以采用的subString进行截取,可以使用保留小数后再转换为字符串;如果使用我的subString的方法的话要先判断下字符串长度,避免产生index溢出的错误。

清空数据库Dao.java

 //清空数据库
    public void truncate(){
        String sql = "delete from ammeter";
        jdbcTemplate.execute(sql);
    }

这里要注意的几个点:

1)mysql中可以采用truncate table tableName进行清空数据库,但是sqlite3里是不支持这样的命令的,只能使用delete from tabeName命令,但是这个命令是不能删除自增ID的,即假如你有100条数据,虽然清空掉了,但是插入数据的ID是从101开始的;

2)在JdbcTemplate里,execute()方法可用于执行任何sql语句,但是一般用来执行DDL语句。

插入数据库Dao.java

//插入数据到数据库
    public void insert(List<Ammeter> ammeters){
        final List<Ammeter> tempAmmeter=ammeters;
        String sql = "insert into ammeter(tabNumber,activePower,reactivePower,cumulativePower,electricity,voltage,currentTime) values (?,?,?,?,?,?,?)";
        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                String tabNumber=tempAmmeter.get(i).getTabNumber();
                String activePower=tempAmmeter.get(i).getActivePower();
                String reactivePower=tempAmmeter.get(i).getReactivePower();
                String cumulativePower=tempAmmeter.get(i).getCumulativePower();
                String electricity=tempAmmeter.get(i).getElectricity();
                String voltage=tempAmmeter.get(i).getVoltage();
                String currentTime=tempAmmeter.get(i).getCurrentTime();
                ps.setString(1, tabNumber);
                ps.setString(2, activePower);
                ps.setString(3, reactivePower);
                ps.setString(4, cumulativePower);
                ps.setString(5, electricity);
                ps.setString(6, voltage);
                ps.setString(7, currentTime);
            }

            @Override
            public int getBatchSize() {
                return tempAmmeter.size();
            }
        });
    }

这里要注意的几个点:

1)采用了JdbcTemplate的批量插入方法batchUpdate(),一般来说update()方法用来执行增加、修改和删除等语句,batchUpdate()方法用来执行批处理相关的语句;这里有个疑问,虽然我使用了该批量插入方法,但是在执行5500多条数据时扔用了3-5min时间,具体原因暂未知;

2)如果是想要批量更新数据的话,可以采用临时数据库,先上传数据到临时数据库,再根据外键更新正式数据库,这样速度会快点。

3、客户端上传Excel

思路:浏览器上传Excel——存入项目的某个路径下——UploadExcel读取该路径下的Excel——执行操作

由于牵涉到前端页面,所以采用servlet和jsp;servet中用到了spring框架;上传文件的存放位置采用了相对路径,不能写死,避免了平台问题。直接上代码。

上传按钮.jsp

<form action="${pageContext.request.contextPath}/servlet/UploadHandleServlet" method="post" enctype="multipart/form-data" >
    请选择上传文件<input type="file" name="file"><br>
    <input type="submit" value="上传">
</form>

处理上传Servlet.java

@Controller
public class UploadHandleServlet extends HttpServlet {
    @Autowired
    private AmmeterDao ammeterDao;
    public void init() throws ServletException {
        /**
         * 利用init方法来调用Spring容器BeanFactory
         * 看看UserServlet是否能够通过Spring容器获取对象
         */
        WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); //通过Web容器去得到BeanFactory对象
        AutowireCapableBeanFactory autowireCapableBeanFactory = wc.getAutowireCapableBeanFactory();
        autowireCapableBeanFactory.autowireBean(this);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
        File file = new File(savePath);
        if(!file.exists()&&!file.isDirectory()){
            System.out.println(savePath+"目录不存在,需要创建");
            file.mkdir();
        }
        String message = "上传成功";
        try {
            DiskFileItemFactory diskfactory = new DiskFileItemFactory();//创建工厂类对象
            ServletFileUpload fileUpload = new ServletFileUpload(diskfactory);//使用工厂创建解析器对象
            if(!ServletFileUpload.isMultipartContent(request)){
                return;
            }
            List<FileItem> fileItems = fileUpload.parseRequest(request);
            for(FileItem item:fileItems) {
                if(item.isFormField()) {
                    System.out.println(new String(item.getString().getBytes("ISO-8859-1"),"utf-8"));
                } else{
                    item.write(new File(savePath+"\\"+"temp.xlsx"));
                    System.out.println(message);
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        List<Ammeter> temp= null;
        try {
            temp = UploadExcel.readXlsx(savePath+"\\"+"temp.xlsx");
            System.out.println(temp);
        } catch (Exception e) {
            e.printStackTrace();
        }
        ammeterDao.truncate();
        ammeterDao.insert(temp);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

这里要注意的几个点:

1)由于Dao是使用了Spring框架用Autowired自动装配的,所以使用new是不会调用的,要想在servlet里使用自动装配,需要利用init方法来调用Spring容器BeanFactory,然后使用@Autowired自动装配;

2)使用fileupload的jar包。同时要有io

    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.3.1</version>
    </dependency>

3)不要把路径写死,要用相对路径; this.getServletContext().getRealPath("/WEB-INF/upload");这个路径指的是项目运行的真实路径,到时会在运行的Tomcat服务器下的webapps\ROOT\WEB-INF\upload产生文件;而且指定了文件名后覆盖,不用担心存储问题;

以上。

猜你喜欢

转载自blog.csdn.net/aliyacl/article/details/84023771