...

понедельник, 3 марта 2014 г.

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


сегодня в 15:44


Здравствуйте!

Представляю пример простого консольного приложения на 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. Я этого не делал, так как задача была разовая. Все равно надеюсь, что пост будет кому-то полезен!

Спасибо за внимание!





Свежий взгляд

на бег


протестируй кроссовки

нового поколения




Стань

первоиспытателем!


Скачай Windows Server 2012 R2

и выиграй почетную футболку!


Скачать




Автоматизированное

продвижение сайтов




  • 50% экономии на ссылках

  • Запуск проекта за 10 минут

  • Вывод и удержание в ТОП 10



Подробнее




Новый 3G-планшет Login 2



2790 р.*


*Условия акции на www.megafon.ru

Подробнее




Разрабатываешь

приложения для бизнеса?


Участвуй в конкурсе



Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий