Применяем Apache POI, docx4j и springframework.jdbc

от автора

Здравствуйте!
Представляю пример простого консольного приложения на Java, которое считывает данные из БД и из файла *.xslx, а затем создает документ *.docx, заполняя при этом поля слияния (mergefield). В нем используются библиотеки Apache POI, docx4j и springframework.jdbc. В примере собрана воедино реализация нескольких часто возникающих в процессе автоматизации задач. Возможно, что он кому-то будет полезен.

О приложение
Что оно умеет:

  • Считывать данные из файла формата xslx
  • Извлекать информации из БД (В данном случае используется Oracle)
  • Формировать файлы формата docx на основе существующего фала, попутно добавлять значения в поля слияния

Необходимость в данного рода приложении возникла, когда потребовалось вручную формировать некоторое количество однотипных документов для их дальнейшей печати и отправки почтой. Собрав воедино различные куски из других своих мелких наработок, появилось это приложение.
Выбор библиотеки Apache POI не стоял, так как уже реализовывал задачи с ее использованием. А вот docx4j применил из-за того, что в ней была возможность заполнять поля слияния в документах MS Word. Это мне и было нужно.
На входе мы имеем некий файл MS Excel, в котором имеется информация, идентифицирующая клиентов. Информация о клиентах не полная. Для извлечения дополнительных данных мы будем вынуждены обращаться в базу данных Oracle через jdbc. Затем приложение сформирует файл MS Word по каждому клиенту.

Реализация

  1. Приложение создано с использованием 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> 

  2. Рассмотрим класс App — главный класс приложения. В метоже main данного класса просто создается объект класса HelperWord и вызывается его метод createWord()
    Класс App

    public class App  {     public static void main( String[] args )     {         HelperWord helper = new HelperWord();         helper.createWord();     } }
  3. В классе 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. Вот ссылка
  4. 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. Я этого не делал, так как задача была разовая. Все равно надеюсь, что пост будет кому-то полезен!
Спасибо за внимание!

ссылка на оригинал статьи http://habrahabr.ru/post/214435/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *