使用easyexcel读数据库写入到excel和读excel写入数据库
pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.lacknb</groupId>
<artifactId>easy_excel_use</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<spring.version>5.1.5.RELEASE</spring.version>
<mybatis.version>3.4.5</mybatis.version>
<tomcat.version>9.0.12</tomcat.version>
<mybatis-spring.version>1.3.2</mybatis-spring.version>
<com.alibaba>1.1.10</com.alibaba>
<mysql-connector>5.1.43</mysql-connector>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>LATEST</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework</groupId>-->
<!-- <artifactId>spring-aop</artifactId>-->
<!-- <version>${spring.version}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework</groupId>-->
<!-- <artifactId>spring-tx</artifactId>-->
<!-- <version>${spring.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- spring与mybatis整合包-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<!-- Druid数据源-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${com.alibaba}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
</dependencies>
</project>
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
# allowMultiQueries=true 允许批量插入
jdbc.url=jdbc:mysql:///one?allowMultiQueries=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
log4j.properties
##define an appender named console
log4j.appender.console=org.apache.log4j.ConsoleAppender
#The Target value is System.out or System.err
log4j.appender.console.Target=System.out
#set the layout type of the apperder
log4j.appender.console.layout=org.apache.log4j.PatternLayout
#set the layout format pattern
log4j.appender.console.layout.ConversionPattern=[%-5p] %m%n
##define a logger
#log4j.rootLogger=TRACE,console
log4j.logger.cn.lacknb.listener.OneDataListener=debug,console
log4j.logger.cn.lacknb.dao.OneDao=debug,console
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.lacknb" />
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置mybatis工厂 同时指定数据源 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- <property name="configLocation" value="classpath:mybatis-config.xml"/> mybatis的配置文件-->
<property name="mapperLocations" value="classpath:mapper/OneMapper.xml"/>
</bean>
<!-- 配置一个可以批量执行的sqlSession -->
<!-- <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">-->
<!-- <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />-->
<!-- <constructor-arg name="executorType" value="BATCH"/>-->
<!-- </bean>-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.lacknb.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>
OneMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.lacknb.dao.OneDao">
<resultMap id="oneMap" type="cn.lacknb.beans.OneBo">
<id property="id" column="id"/>
<result column="title" property="title"/>
<result column="words_info" property="wordInfo"/>
<result column="text" property="text"/>
<result column="img_url" property="imgUrl"/>
<result column="pic_info" property="picInfo"/>
<result column="post_date" property="postDate"/>
<result column="share_url" property="shareUrl"/>
</resultMap>
<select id="selectAll" resultMap="oneMap">
select * from t_one
</select>
<insert id="addOne">
insert into t_one (id, title, words_info, text, pic_info, img_url, post_date, share_url) values
<foreach collection="list" separator="," item="item">
(#{item.id}, #{item.title}, #{item.wordInfo}, #{item.text}, #{item.picInfo},
#{item.imgUrl}, #{item.postDate}, #{item.shareUrl})
</foreach>
</insert>
</mapper>
One.java, 这是读excel用到的, 根据名字匹配
package cn.lacknb.beans;
import com.alibaba.excel.annotation.ExcelProperty;
/**
* ClassName: One <br/>
* Description: 读excel文件, 数据封装成这个类 <br/>
* date: 2019年12月19日 16:40 <br/>
*
* @author nianshao <br/>
*/
public class One {
/**
* Description: 指定列的下标, 或列名.
* 这里不建议 name和index同时使用
* 用名字匹配的时候,这里需要注意, 如果名字重复, 会导致只有一个 字段读取到数据
*/
@ExcelProperty(value = "序号")
private Integer id;
@ExcelProperty(value = "标题")
private String title;
@ExcelProperty(value = "内容作者")
private String wordInfo;
@ExcelProperty(value = "内容")
private String text;
@ExcelProperty(value = "图片链接")
private String imgUrl;
@ExcelProperty(value = "图片作者")
public String picInfo;
@ExcelProperty(value = "发布日期")
private String postDate;
@ExcelProperty(value = "分享链接")
private String shareUrl;
public String getShareUrl() {
return shareUrl;
}
public void setShareUrl(String shareUrl) {
this.shareUrl = shareUrl;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getWordInfo() {
return wordInfo;
}
public void setWordInfo(String wordInfo) {
this.wordInfo = wordInfo;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
public String getPicInfo() {
return picInfo;
}
public void setPicInfo(String picInfo) {
this.picInfo = picInfo;
}
public String getPostDate() {
return postDate;
}
public void setPostDate(String postDate) {
this.postDate = postDate;
}
@Override
public String toString() {
return "One{" +
"id=" + id +
", title='" + title + '\'' +
", wordInfo='" + wordInfo + '\'' +
", text='" + text + '\'' +
", imgUrl='" + imgUrl + '\'' +
", picInfo='" + picInfo + '\'' +
", postDate='" + postDate + '\'' +
'}';
}
}
OneBo.java, 从数据库查询之后 封装这个类
package cn.lacknb.beans;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
/**
* ClassName: OneBo <br/>
* Description: 用来接收 从数据库查询的数据, 然后写入excel <br/>
* date: 2019年12月19日 16:28 <br/>
*
* @author nianshao <br/>
*/
public class OneBo extends BaseRowModel {
@ExcelProperty(value = "序号", index = 0)
private Integer id;
@ExcelProperty(value = "标题", index = 1)
private String title;
@ExcelProperty(value = "内容作者", index = 2)
private String wordInfo;
@ExcelProperty(value = "内容", index = 3)
private String text;
@ExcelProperty(value = "图片链接", index = 4)
private String imgUrl;
@ExcelProperty(value = "图片作者", index = 5)
public String picInfo;
@ExcelProperty(value = "发布日期", index = 6)
private String postDate;
@ExcelProperty(value = "分享链接", index = 7)
private String shareUrl;
public String getShareUrl() {
return shareUrl;
}
public void setShareUrl(String shareUrl) {
this.shareUrl = shareUrl;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getWordInfo() {
return wordInfo;
}
public void setWordInfo(String wordInfo) {
this.wordInfo = wordInfo;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
public String getPicInfo() {
return picInfo;
}
public void setPicInfo(String picInfo) {
this.picInfo = picInfo;
}
public String getPostDate() {
return postDate;
}
public void setPostDate(String postDate) {
this.postDate = postDate;
}
}
OneDao.java
package cn.lacknb.dao;
import cn.lacknb.beans.One;
import cn.lacknb.beans.OneBo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* ClassName: OneDao <br/>
* Description: <br/>
* date: 2019年12月19日 16:45 <br/>
*
* @author nianshao <br/>
*/
public interface OneDao {
List<OneBo> selectAll();
int addOne(@Param("list") List<One> list);
}
监听器
package cn.lacknb.listener;
import cn.lacknb.beans.One;
import cn.lacknb.dao.OneDao;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* ClassName: OneDataLisenter <br/>
* Description: <br/>
* date: 2019年12月19日 17:48 <br/>
*
* @author nianshao <br/>
*/
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class OneDataListener extends AnalysisEventListener<One> {
private static final Logger LOGGER = LoggerFactory.getLogger(OneDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<One> list = new ArrayList<One>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private OneDao oneDao;
// public DemoDataListener() {
// // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
// demoDAO = new DemoDAO();
// }
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param oneDao
*/
public OneDataListener(OneDao oneDao) {
this.oneDao = oneDao;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(One data, AnalysisContext context) {
// System.out.println(data);
LOGGER.info("解析到一条数据:{}", data);
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
if (list.size() != 0){
saveData();
}
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
oneDao.addOne(list);
LOGGER.info("存储数据库成功!");
}
}
测试类
package cn.lacknb.test;
import cn.lacknb.beans.One;
import cn.lacknb.beans.OneBo;
import cn.lacknb.dao.OneDao;
import cn.lacknb.listener.OneDataListener;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Sheet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import static com.alibaba.excel.support.ExcelTypeEnum.XLSX;
/**
* ClassName: Example01 <br/>
* Description: <br/>
* date: 2019年12月19日 12:49 <br/>
*
* @author nianshao <br/>
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Example01 {
private static final String PATH = "/media/nianshao/nie475/java项目/easy_excel_use/files";
@Autowired
private OneDao oneDao;
@Before
public void test(){
System.out.println(1);
}
/**
* Description: 从数据库查询数据, 然后将数据写到excel中 <br/>
* @date: 19-12-19 下午5:08 <br/>
* @param: [] <br/>
* @return:void
*/
@Test
public void test01() throws IOException {
OutputStream out = new FileOutputStream(PATH + "/one.xlsx");
long time1 = System.currentTimeMillis();
ExcelWriter writer = new ExcelWriter(out, XLSX);
Sheet sheet = new Sheet(1, 0, OneBo.class, "sheet的名字", null);
sheet.setAutoWidth(Boolean.TRUE);
List<OneBo> collect = oneDao.selectAll();
writer.write(collect, sheet);
writer.finish();
out.close();
long time2 = System.currentTimeMillis();
System.out.println("总耗时: " + (time2 - time1) / 1000);
}
}
测试类
package cn.lacknb.test;
import cn.lacknb.beans.One;
import cn.lacknb.beans.OneBo;
import cn.lacknb.dao.OneDao;
import cn.lacknb.listener.OneDataListener;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Sheet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import static com.alibaba.excel.support.ExcelTypeEnum.XLSX;
/**
* ClassName: Example01 <br/>
* Description: <br/>
* date: 2019年12月19日 12:49 <br/>
*
* @author nianshao <br/>
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Example01 {
private static final String PATH = "/media/nianshao/nie475/java项目/easy_excel_use/files";
@Autowired
private OneDao oneDao;
@Before
public void test(){
System.out.println(1);
}
/**
* Description: 读取excel文件, 封装成对象 <br/>
* @date: 19-12-19 下午5:28 <br/>
* @param: [] <br/>
* @return:void
*/
@Test
public void test02() throws FileNotFoundException {
long time1 = System.currentTimeMillis();
EasyExcel.read(PATH + "/one.xlsx", One.class, new OneDataListener(oneDao)).sheet().doRead();
long time2 = System.currentTimeMillis();
System.out.println("总耗时: " + (time2 - time1) / 1000);
}
}
这里的读和写, 用的两种不同的方法
官网上例子的两种方法
简单的读, 这里的new DemoDataListener() 就是监听器
/**
* 最简单的读
* <p>1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
* <p>3. 直接读即可
*/
@Test
public void simpleRead() {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法1:
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
// 写法2:
fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
excelReader.finish();
}
简单的写
/**
* 最简单的写
* <p>1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>2. 直接写即可
*/
@Test
public void simpleWrite() {
// 写法1
String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
// 写法2
fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
excelWriter.write(data(), writeSheet);
// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
}
监听器的格式
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
demoDAO.save(list);
LOGGER.info("存储数据库成功!");
}
}
2630条数据
从数据库中提取, 写入到excel中
总耗时7秒
从excel文件中获取数据, 存到数据库中
总耗时 14秒
往数据库中存数据的时候, 数据库的链接一定要加utf8, 否则会乱码.
在applicationContext.xml中, 导入mybatis的配置文件的时候, 一定要记住mapperLocation与configLocation的区别
configLocation 是导入的mybatis的配置文件
mapperLocations是导入mybatis的mapper映射文件
<!-- <property name="configLocation" value="classpath:mybatis-config.xml"/> mybatis的配置文件-->
<property name="mapperLocations" value="classpath:mapper/OneMapper.xml"/>