Здравствуйте!
Представляю пример простого консольного приложения на Java, которое считывает данные из БД и из файла *.xslx, а затем создает документ *.docx, заполняя при этом поля слияния (mergefield). В нем используются библиотеки Apache POI, docx4j и springframework.jdbc. В примере собрана воедино реализация нескольких часто возникающих в процессе автоматизации задач. Возможно, что он кому-то будет полезен.
О приложение
Что оно умеет:
- Считывать данные из файла формата xslx
- Извлекать информации из БД (В данном случае используется Oracle)
- Формировать файлы формата docx на основе существующего фала, попутно добавлять значения в поля слияния
Необходимость в данного рода приложении возникла, когда потребовалось вручную формировать некоторое количество однотипных документов для их дальнейшей печати и отправки почтой. Собрав воедино различные куски из других своих мелких наработок, появилось это приложение.
Выбор библиотеки Apache POI не стоял, так как уже реализовывал задачи с ее использованием. А вот docx4j применил из-за того, что в ней была возможность заполнять поля слияния в документах MS Word. Это мне и было нужно.
На входе мы имеем некий файл MS Excel, в котором имеется информация, идентифицирующая клиентов. Информация о клиентах не полная. Для извлечения дополнительных данных мы будем вынуждены обращаться в базу данных Oracle через jdbc. Затем приложение сформирует файл MS Word по каждому клиенту.
Реализация
- Приложение создано с использованием maven. Для начала разберемся с нужными нам зависимостями. Вот что необходимо добавить в файл pom.xml
Dependencies
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>2.5.6</version> <type>jar</type> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-dao</artifactId> <version>2.0.6</version> <type>jar</type> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>2.5.6</version> <type>jar</type> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>2.5.6</version> <type>jar</type> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>2.5.6</version> <type>jar</type> </dependency> <dependency> <groupId>ojdbc</groupId> <artifactId>ojdbc</artifactId> <version>14</version> </dependency> <dependency> <groupId>org.docx4j</groupId> <artifactId>docx4j</artifactId> <version>2.8.1</version> </dependency>
- Рассмотрим класс App — главный класс приложения. В метоже main данного класса просто создается объект класса HelperWord и вызывается его метод createWord()
Класс App
public class App { public static void main( String[] args ) { HelperWord helper = new HelperWord(); helper.createWord(); } }
- В классе HelperWord мы получаем данные о клиенте, обрабатываем их и создаем файл MS Word.
Класс HelperWord
import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.exceptions.InvalidFormatException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; public class HelperWord { // Объект класса MyDataManager для работы с данными private MyDataManager dmg; // Данные из фала *.xslx private List<HashMap> clientsRows; // Дополнительные данные из БД private List<HashMap> additionalData; // Инициирует создание файлов MS Word public void createDocs() { // Создаем объект класса MyDataManager для работы с данными dmg = new MyDataManager(); try { // Извлекаем данные из файла MS Excel clientsRows = dmg.getDataBlock(); // <editor-fold defaultstate="collapsed" desc="Catch clauses"> } catch (FileNotFoundException ex) { Logger.getLogger(HelperWord.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(HelperWord.class.getName()).log(Level.SEVERE, null, ex); //</editor-fold> } // Создаем файл MS Word и заполняем его addDataBlock(); } // Создает файл MS Word и заполняет его private void addDataBlock() { int num = 0; // Считываем информацию о каждом клменте for (HashMap row : clientsRows) { try { num++; // Извлекаем данные о существующем объекте MS Word WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage .load(new File("template.docx")); // Создаем объект для вставки значений в поля слияния List<Map<DataFieldName, String>> data = new ArrayList<Map<DataFieldName, String>>(); // Получаем дополнительные данные о клиенте из базы additionalData = dmg.getAddress(row.get("NAME").toString(), row.get("DOCDATE").toString()); // Заполняем значения для полей слияния Map<DataFieldName, String> map = new HashMap<DataFieldName, String>(); map.put(new DataFieldName("NAME"), row.get("NAME").toString()); map.put(new DataFieldName("ADDRESS"), additionalData.get(0).get("ADDRESS").toString()); data.add(map); // Создаем новый объект MS Word на основе существующего и // значений полей слияния WordprocessingMLPackage output = MailMerger.getConsolidatedResultCrude( wordMLPackage, data); // Сохраняем объект в файл output.save(new File("T:\VIPISKI_KK\Письма\" + num + ". " + row.get("NAME") + ".docx")); // <editor-fold defaultstate="collapsed" desc="Catch clauses"> } catch (InvalidFormatException ex) { Logger.getLogger(HelperWord.class.getName()).log(Level.SEVERE, null, ex); } catch (Docx4JException ex) { Logger.getLogger(HelperWord.class.getName()).log(Level.SEVERE, null, ex); } catch (SQLException ex) { Logger.getLogger(HelperWord.class.getName()).log(Level.SEVERE, null, ex); } catch (FileNotFoundException ex) { Logger.getLogger(HelperWord.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(HelperWord.class.getName()).log(Level.SEVERE, null, ex); } // </editor-fold> } } }
Именно здесь мы заполняем поля слияния в документе, используя библиотеку docx4j. Пожалуй, внимание стоит обратить лишь на классы DataFieldName и MailMerger. Вроде бы, они оба должны присутствовать в библиотеке docx4j, однако в моей сборке их не оказалось. Поэтому они были добавлены в проект отдельно. Пару слов об этих классах
- В классе DataFieldName есть поле name и переопределен метод equals. Это сделано для того, что мы сравнивали названия полей слияния в верхнем регистре
- Класс MailMerger как раз и осуществляет вставку значений в поля слияния документа. Код класса полностью позаимствован с официального сайта docx4j. Вот ссылка
- MyDataManager — класс для работы с данными. Он использует библиотеки Apache POI для чтения фала MS Excel и классы springframework.jdbc для работы с БД.
Класс MyDataManager
import java.io.*; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import oracle.jdbc.pool.OracleDataSource; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; public class MyDataManager { public NamedParameterJdbcTemplate namedPar; private OracleDataSource getDataSource() throws SQLException { // Создаем объект источника данных и заполняем значения параметров OracleDataSource ods = new OracleDataSource(); ods.setDriverType("thin"); ods.setServerName("192.168.x.x"); ods.setPortNumber(); ods.setDatabaseName("SID"); ods.setUser("user"); ods.setPassword("password"); return ods; } //Получаем данные из MS Excel public List<HashMap> getDataBlock() throws FileNotFoundException, IOException { ArrayList<HashMap> res = new ArrayList<HashMap>(); FileInputStream file = new FileInputStream(new File("clients.xlsx")); XSSFWorkbook workbook = new XSSFWorkbook(file); XSSFSheet sheet = workbook.getSheetAt(0); Iterator<Row> rowIterator = sheet.iterator(); // Пропускаю первую строку. В моем случае в ней только заколовки if(rowIterator.hasNext()) rowIterator.next(); //Пробегаемся по всем строкам while (rowIterator.hasNext()) { Row row = rowIterator.next(); HashMap line = new HashMap(); // В моей структуре файла мне интересны только 1-ая и 4-ая строки Cell cell = row.getCell(0); line.put("NAME", cell.getStringCellValue()); cell = row.getCell(3); line.put("DOCDATE", cell.getStringCellValue()); res.add(line); } file.close(); return res; } //Получаем данные из БД public List<HashMap> getAddress(String name, String date) throws SQLException, FileNotFoundException, IOException { // Получаем источник данных OracleDataSource ds = getDataSource(); // Считываем запрос FileInputStream fins = new FileInputStream("query.txt"); BufferedReader br = new BufferedReader( new InputStreamReader(fins, "UTF8")); String query = ""; String line = ""; while ((line = br.readLine()) != null) { query += "n"; query += line; } // Вставляем значения параметров namedPar = new NamedParameterJdbcTemplate(ds); MapSqlParameterSource namedParameters = new MapSqlParameterSource(); namedParameters.addValue("NAME", name); namedParameters.addValue("DOCDATE", date); // Исполняем запрос и получае результат List<HashMap> res = (List<HashMap>) namedPar.query(query, namedParameters, new DataMapper()); try { return res; } finally { ds.close(); } } }
Заключение
Вот собственно и все приложение. Конечно, многон в нем зашито в код (настройки соединения с базой, пути к файлам) и к тому же оно консольное. Можно добавить различные проверки и создать gui. Я этого не делал, так как задача была разовая. Все равно надеюсь, что пост будет кому-то полезен!
Спасибо за внимание!
Автор: astamur