написать впереди
Внутренние программисты Java должны столкнуться с соответствующими требованиями, такими как чтение информации Excel в БД, и могут внезапно подумать о POI Apache как о техническом решении, но когда объем данных Excel очень велик, вы можете обнаружить, что POI — это POI. Excel считываются и помещаются в память, поэтому потребление памяти очень серьезное.Если операция чтения Excel, содержащая большой объем данных, выполняется одновременно, легко вызвать проблему переполнения памяти.
Однако появление EasyExcel очень хорошо решило проблемы, связанные с POI.Первоначально 3M Excel требовалось около 100M памяти для POI, но EasyExcel может уменьшить ее до нескольких M, и в то же время, независимо от того, насколько большой Excel , переполнения памяти не будет, т.к. читать содержимое Excel построчно (старое правило, не слишком беспокойтесь о картинке ниже, просто имейте впечатление в уме, после прочтения следующего варианта использования а потом снова посмотрите на эту картинку, она очень простая)
Кроме того, EasyExcel инкапсулирует преобразование модели на верхнем уровне и не требует ячеек и других связанных операций, что делает его проще и удобнее для пользователей.
простое чтение
Предположим, у нас есть следующее в excel:
Нам нужно создать новую сущность пользователя и добавить к ней переменные-члены.
@Data
public class User {
/**
* 姓名
*/
@ExcelProperty(index = 0)
private String name;
/**
* 年龄
*/
@ExcelProperty(index = 1)
private Integer age;
}
вы могли заметить@ExcelProperty
В аннотации также используется атрибут индекса (0 представляет первый столбец и т. д.), аннотация также поддерживает сопоставление по имени «имя столбца», например:
@ExcelProperty("姓名")
private String name;
Согласно документации github:
Не рекомендуется использовать индекс и имя одновременно, либо объект использует только индекс, либо объект использует только имя для соответствия
- Если столбец информации о чтении шаблона Excel фиксирован, рекомендуется использовать его в виде индекса, потому что, если имя используется для совпадения, повторяющееся имя приведет к тому, что только одно поле будет считывать данные, поэтому индекс является более безопасным. способ
- Если индекс столбца шаблона Excel часто меняется, лучше выбрать метод имени, а не постоянно изменять значение индекса аннотации объекта.
Таким образом, вы можете выбрать в соответствии с вашей собственной ситуацией
Пишите тестовые случаи
В классе EasyExcel перегружено много методов чтения, я не буду их здесь перечислять, проверьте сами, в то же время метод листа также может указывать номер листа, по умолчанию используется информация первого листа.
код вышеnew UserExcelListener()
Это очень привлекательно, что также является ключом к EasyExcel, читающему содержимое Excel построчно.UserExcelListener
наследоватьAnalysisEventListener
@Slf4j
public class UserExcelListener extends AnalysisEventListener<User> {
/**
* 批处理阈值
*/
private static final int BATCH_COUNT = 2;
List<User> list = new ArrayList<User>(BATCH_COUNT);
@Override
public void invoke(User user, AnalysisContext analysisContext) {
log.info("解析到一条数据:{}", JSON.toJSONString(user));
list.add(user);
if (list.size() >= BATCH_COUNT) {
saveData();
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
saveData();
log.info("所有数据解析完成!");
}
private void saveData(){
log.info("{}条数据,开始存储数据库!", list.size());
log.info("存储数据库成功!");
}
}
Вот, пожалуйста, вернитесь к схеме EasyExcel в начале статьи: метод invoke считывает данные построчно, что соответствует подписчику 1, метод doAfterAllAnalysed соответствует подписчику 2. Понятно?
распечатать результат:
Из этого видно, что хотя данные анализируются построчно, мы можем настроить пороговое значение и выполнить пакетную обработку данных, что демонстрирует гибкость операции EasyExcel.
пользовательский конвертер
Это самое основное чтение и запись данных.Наши бизнес-данные обычно не так просты, и иногда их даже нужно преобразовать в программно-читаемые данные.
преобразование гендерной информации
Например, чтобы добавить столбец «Пол» в Excel, его пол — мужской/женский, нам нужно преобразовать информацию о поле в Excel в информацию о программе: «1: мужской; 2: женский».
Сначала добавьте пол переменной-члена в сущность пользователя:
@ExcelProperty(index = 2)
private Integer gender;
EasyExcel поддерживает наш пользовательский конвертер для преобразования содержимого Excel в информацию, необходимую нашей программе, вот новый GenderConverter для преобразования информации о поле
public class GenderConverter implements Converter<Integer> {
public static final String MALE = "男";
public static final String FEMALE = "女";
@Override
public Class supportJavaTypeKey() {
return Integer.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
String stringValue = cellData.getStringValue();
if (MALE.equals(stringValue)){
return 1;
}else {
return 2;
}
}
@Override
public CellData convertToExcelData(Integer integer, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return null;
}
}
Общий тип интерфейса Converter вышеуказанной программы относится к типу данных Java, который необходимо преобразовать, что согласуется с типом возвращаемого значения в методе supportJavaTypeKey.
Открыть аннотации@ExcelProperty
Просмотр, эта аннотация поддерживает пользовательский конвертер, поэтому мы добавляем сущность пользователя.genderпеременная-член и укажите преобразователь
/**
* 性别 1:男;2:女
*/
@ExcelProperty(index = 2, converter = GenderConverter.class)
private Integer gender;
Посмотрим на результат работы:
Данные были преобразованы, как мы и ожидали, отсюда также видно, что Converter можно определить один раз и использовать везде.
Преобразование информации о дате
Информация о дате также является нашими общими данными преобразования. Например, новый столбец «Дата рождения» в Excel, нам нужно разобрать его наyyyy-MM-ddформат, нам нужно отформатировать его, EasyExcel проходит@DateTimeFormat
Форматирование аннотаций
Добавить переменную-член в сущность пользователяbirth, при подаче@DateTimeFormat
Аннотировать, форматировать по мере необходимости
/**
* 出生日期
*/
@ExcelProperty(index = 3)
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
private String birth;
Посмотрим на результат работы:
Если вы укажете здесь тип рождения как Дата, попробуйте, что вы получите?
Пока программный код написан в тестовом режиме Как Java-веб-разработчику, особенно в рамках текущей основной архитектуры Spring Boot, как читать информацию Excel в веб-режиме?
веб-чтение
Простой Интернет
Все очень просто, достаточно переместить ключевой код тесткейса в Контроллер, создаем новыйUserController
, в котором добавитьupload
метод
@RestController
@RequestMapping("/users")
@Slf4j
public class UserController {
@PostMapping("/upload")
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), User.class, new UserExcelListener()).sheet().doRead();
return "success";
}
}
На самом деле, при написании тест-кейсов вы могли обнаружить, что слушатель передается в метод EasyExcel.read в виде new в качестве параметра.Это не соответствует правилам Spring IoC.Мы обычно читаем данные Excel для Прочитанные данные записывают некоторую бизнес-логику, а бизнес-логика обычно записывается на уровне службы.Как мы вызываем наш код службы в слушателе?
** Пока не смотри вниз, какие у тебя планы? **
анонимный внутренний класс
Анонимный внутренний класс — это самый простой способ, нам нужно сначала создать новую информацию о сервисном слое: Создайте новый интерфейс IUser:
public interface IUser {
public boolean saveData(List<User> users);
}
Создайте новый класс реализации интерфейса IUser UserServiceImpl:
@Service
@Slf4j
public class UserServiceImpl implements IUser {
@Override
public boolean saveData(List<User> users) {
log.info("UserService {}条数据,开始存储数据库!", users.size());
log.info(JSON.toJSONString(users));
log.info("UserService 存储数据库成功!");
return true;
}
}
Затем введите IUser в контроллер:
@Autowired
private IUser iUser;
Измените метод загрузки и реализуйте его в виде анонимного внутреннего класса, переопределяющего метод слушателя:
@PostMapping("/uploadWithAnonyInnerClass")
public String uploadWithAnonyInnerClass(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), User.class, new AnalysisEventListener<User>(){
/**
* 批处理阈值
*/
private static final int BATCH_COUNT = 2;
List<User> list = new ArrayList<User>();
@Override
public void invoke(User user, AnalysisContext analysisContext) {
log.info("解析到一条数据:{}", JSON.toJSONString(user));
list.add(user);
if (list.size() >= BATCH_COUNT) {
saveData();
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
saveData();
log.info("所有数据解析完成!");
}
private void saveData(){
iUser.saveData(list);
}
}).sheet().doRead();
return "success";
}
Посмотреть Результаты:
Такой способ реализации, по сути, просто переписывает весь контент в слушателе и выводит его в контроллере, очень неудобно смотреть на такой раздутый контроллер? Очевидно, что это не лучшая реализация кода.
параметр конструктора
Когда я проанализировал исходный код унифицированного возврата SpringBoot, мне интересно, обнаружили ли вы, что большая часть базового исходного кода Spring передает параметры в форме конструкторов, поэтому мы можем добавить параметризованный конструктор к слушателю и внедрить IUser с зависимостями в контроллер. как форма конструктора передается слушателю:
@Slf4j
public class UserExcelListener extends AnalysisEventListener<User> {
private IUser iUser;
public UserExcelListener(IUser iUser){
this.iUser = iUser;
}
// 省略相应代码...
private void saveData(){
iUser.saveData(list); //调用 userService 中的 saveData 方法
}
Измените метод контроллера:
@PostMapping("/uploadWithConstructor")
public String uploadWithConstructor(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), User.class, new UserExcelListener(iUser)).sheet().doRead();
return "success";
}
Результат выполнения: такой же, как указано выше
После этого изменения код контроллера выглядит очень понятным, но если есть другие сервисы, которые необходимо внедрить в последующем бизнесе, всегда ли нужно добавлять конструкторы параметров? Очевидно, что этот подход также не очень гибкий.
На самом деле, при использовании анонимных внутренних классов вы можете подумать, что мы можем решить эту проблему с помощью лямбда-выражения Java8.
Лямбда-параметры
Чтобы решить проблему передачи параметров в конструктор, и мы также надеемся, что прослушиватель будет более универсальным, нет необходимости создавать новый прослушиватель для каждого бизнеса Excel, потому что слушатель считывает данные Excel построчно и нам нужно только изменить наш код бизнес-логики.Просто передать его слушателю, поэтому нам нужно использоватьConsumer<T>
, который используется в качестве параметра для создания прослушивателя.
Создайте новый класс инструментов ExcelDemoUtils для создания слушателя:
Мы видим, что метод getListener получаетConsumer<List<T>>
параметры, так что при вызове следующего кода наша бизнес-логика будет выполняться соответствующим образом:
consumer.accept(linkedList);
Продолжайте преобразовывать метод Controller:
Результат выполнения: такой же, как указано выше
На данный момент нам нужно только настроить бизнес-логику вbatchInsert
В методе:
- Познакомьтесь с простотой контроллера RESTful API
- Слушатель более общий и гибкий, он играет роль абстрактного класса, а конкретная логика передается реализации абстрактного метода.
- Бизнес-логика более масштабируема и логика понятнее
Суммировать
Здесь было представлено основное использование EasyExcel для чтения информации Excel, и есть еще много нераскрытых деталей, вы можете проверить это самостоятельно.EasyExcel GithubДокументация, чтобы узнать больше. Гибкое использование функционального интерфейса Java 8 упрощает улучшение возможности повторного использования кода и делает его более лаконичным и стандартизированным.
В дополнение к чтению Excel, есть также запись Excel.Если вам нужно записать его в указанное место, очень удобно использовать инструмент FileWriter HuTool.Для использования EasyExcel, если у вас есть какие-либо вопросы, вы также можете Добро пожаловать в обсуждение под блогом
Для получения полного кода, пожалуйста, ответьте на «демо» в официальном аккаунте, нажмите на ссылку и просмотрите содержимое папки «easy-excel-demo». Кроме того, домашняя страница личного блога временно закрыта в связи с особых причин. Доступ к другим каталогам является нормальным. Дополнительные статьи можно найти наdayarch.top/archivesВходной вид
благодарный
Большое спасибо автору EasyExcel 🌹🌹, который делает чтение и запись Excel более удобными
вопрос души
- Помимо Consumer, если вам нужна бизнес-логика, которая возвращает значения, какой функциональный интерфейс вы должны использовать?
- Как работать со сложными заголовками?
- Как записать данные БД в Excel и загрузить их?
- Чему вы научились из дизайна EasyExcel? Добро пожаловать, чтобы оставить сообщение под блогом, чтобы обсудить
инструменты повышения производительности
Рекомендуемое чтение
- На этот раз в мир параллелизма, пожалуйста, не пропустите
- Чтобы научиться параллельному программированию, необходимо глубокое понимание этих трех основных элементов.
- Есть три источника одновременных ошибок, пожалуйста, следите за ними.
- Наглядность и упорядоченность, Бывает-прежде чем это сделать
- Решить проблему атомарности? Что вам нужно в первую очередь, так это понимание макросов
- О чем мы должны говорить при опросе одновременных изменчивых ключевых слов?
Добро пожаловать, чтобы продолжать обращать внимание на публичный номер: «Сун Гун И Бин».
- Передовая технология Java для обмена галантереей
- Резюме эффективных инструментов | Ответ на «Инструменты»
- Анализ вопроса интервью и ответ
- Сбор технических данных | Ответ на «данные»
Узнайте о стеке технологий Java легко и весело, думая о чтении детективных романов, и постепенно разлагайте технические проблемы на основе принципов упрощения сложных проблем, превращения абстрактных проблем в конкретные и графические, а технология постоянно обновляется, пожалуйста, продолжайте платить внимание...
2019.10.24 Дополнительные инструкции
Увидев, что в области комментариев есть детская обувь, возникает некоторая путаница с методом Lambda, поэтому я добавляю здесь отдельное пояснение.
Откровенно говоря, я сначала не знала, как писать так, благодаря просьбе моего друга, заранее спасибо.
Рождение Lambda значительно облегчает обработку наших коллекций.Мы имеем дело с коллекциями каждый день, что позволяет нам легко игнорировать функциональный интерфейс Lambda.Начнем с решения проблемы раздутого кода в анонимных внутренних классах, таких как () -> Система, которая часто упоминается в туториалах .out.println("hello world");
public void testRunnable(Runnable runnable){
runnable.run();
}
@Test
public void callTestRunnable1(){
testRunnable(new Runnable() {
@Override
public void run() {
System.out.println("hello Runnable");
}
});
}
@Test
public void callTestRunnable(){
testRunnable(() -> System.out.println("hello Runnable"));
}
Некоторые друзья здесь могут сказать, какAnalysisEventListener
Как насчет преобразования анонимных внутренних классов в Lambda? Ответ — нет, это нарушает определение функционального интерфейса (есть только один абстрактный метод, остальные — по умолчанию).Оглядываясь на приведенный выше код, я просто поместил анонимный метод внутреннего класса в класс инструмента ExcelDemoUtils, который более традиционен. Способ улучшился, мы используем дженерики Java, мы можем обрабатывать любую сущность, кроме сущности пользователя, см. код:
public static <T> AnalysisEventListener<T> getListener() {
return new AnalysisEventListener<T>() {
private static final int BATCH_COUNT = 2;
List<T> list = new ArrayList<T>();
@Override
public void invoke(T user, AnalysisContext analysisContext) {
log.info("解析到一条数据:{}", JSON.toJSONString(user));
list.add(user);
if (list.size() >= BATCH_COUNT) {
saveData();
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
saveData();
log.info("所有数据解析完成!");
}
private void saveData(){
log.info("{}条数据,开始存储数据库!", list.size());
log.info("存储数据库成功!");
}
};
}
Класс инструментов, написанный здесь, не имеет возможности бизнес-посадки.Обычно нам нужно выполнить определенную бизнес-логику прочитанного содержимого Excel (списка) и, наконец, сохранить его в БД.Это предложение переводится в соответствии с Lambda как(list) -> 特定业务逻辑的执行
Это просто соответствует методу accept(T t) функционального интерфейса Consumer, поэтому мы просто добавляем параметр типа Consumer в метод getListener выше, поэтому для вызова метода getListener нам нужно передать параметр типа Consumer. выполняя метод Consumer.accept, будет выполняться бизнес-логика в написанном нами методе batchInsert.
Поэтому, когда основная логика выполнения фиксирована, и только некоторые части нуждаются в конкретной бизнес-обработке, мы можем использовать функциональные интерфейсы для передачи параметров, просто запомните этот образ мышления.
Добро пожаловать, чтобы обратить внимание на приведенный выше публичный аккаунт с QR-кодом, применить технологию на практике, интересно поговорить о кодировании и, наконец, поздравить с праздником 1024 года.