Формирование объектов PL/SQL для выгрузки отчётов в формате XLS из Formspider с помощью Java

Kate

Administrator
Команда форума
Часто приходится делать различные выгрузки отчётов в формате XLS из Formspider, используя PL/SQL developer (Oracle 11g2). Так часто, что решил автоматизировать этот процесс на Java.
Язык Java изучаю сам по книгам и видеокурсам уже четвёртый год, поэтому если будут какие-то дополнения или замечания, то можете предложить их здесь через pull request.
Сама выгрузка из PL/SQL делается при помощи пакета as_xlsx.
И так, приступим! Я сделал простенький интерфейс для ввода имён объектов, параметров эксель файла и столбцов:
f7252cdcd30bcaad36b65766d391169c.png

Интерфейс описан в классе ниже. Для удобства все параметры заполнены по умолчанию. Остаётся добавить информацию для формирования столбцов.
package QueriesCreater;

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;

public class Gui extends JFrame {
static ImageIcon logo = new ImageIcon(Toolkit.getDefaultToolkit().createImage(Gui.class.getResource("/logo.png")));
static JTable table;
static DefaultTableModel model;
static JTable objectTable;
static DefaultTableModel objectModel;
static int guiWindowHeight = 620;
static int guiWindowWidth = 370;
static int guiWindowX = 650;
static int guiWindowY = 190;

public Gui() {
this.setResizable(false);
setIconImage(logo.getImage());
this.setTitle("PL/SQL: excel export");
this.setFont(new Font("Tahoma", Font.PLAIN, 14));
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setBounds(guiWindowX, guiWindowY, guiWindowWidth, guiWindowHeight);
this.getContentPane().setBackground(new Color(0xFFF9A1));
this.getContentPane().setLayout(null);

// Object name table
final JScrollPane objectNames = new JScrollPane();
objectNames.setBounds(10, 25, 334, 201);
getContentPane().add(objectNames);

String[] objectColumns = new String[]{"Object", "Value"};
objectModel = new DefaultTableModel(new Object[][] {
{"Function name", "get_xls_function"},
{"Procedure name", "get_xls_from_table"},
{"Record type", "t_type_of_record"},
{"Table as record", "t_type_of_record_tbl"},
{"Table or view", "table_or_view"},
{"Headers background", "FFCC66"},
{"Headers font size", 13},
{"Rows font size", 12},
{"Rows height", 25},
{"Rows bold", false},
{"Rows italic", false},
{"Headers font", "Times New Roman"},
{"Rows font", "Times New Roman"},
{"Horizontal alignment", "center"},
{"Vertical alignment", "center"},
{"Wrap text", true},
},objectColumns) {
final boolean[] columnEditables = new boolean[]{
false, true
};
public boolean isCellEditable(int row, int column) {
return this.columnEditables[column];
}
};
objectTable = new JTable(objectModel);
objectTable.setDefaultRenderer(Object.class, new TableInfoRenderer());
// cell border color
objectTable.setGridColor(new Color(58, 79, 79));
objectTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
// table background color
objectTable.setFillsViewportHeight(true);
objectTable.setBackground(new Color(250, 252, 255));
// headers settings
JTableHeader objectHeader = objectTable.getTableHeader();
objectHeader.setFont(new Font("Tahoma", Font.BOLD, 13));
//cell alignment
TableInfoRenderer objectRenderer = new TableInfoRenderer();
objectRenderer.setHorizontalAlignment(JLabel.LEADING);
objectTable.getColumnModel().getColumn(0).setCellRenderer(objectRenderer);
objectTable.getColumnModel().getColumn(1).setCellRenderer(objectRenderer);
objectTable.setRowHeight(20);
objectTable.setColumnSelectionAllowed(true);
objectTable.setCellSelectionEnabled(true);
objectTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
objectTable.setFont(new Font("SansSerif", Font.PLAIN, 13));
objectTable.getColumnModel().getColumn(0).setPreferredWidth(158);
objectTable.getColumnModel().getColumn(1).setPreferredWidth(158);
//colors
objectTable.setSelectionBackground(new Color(254, 204, 204));
objectNames.setViewportView(objectTable);

// удаление содержимого ячейки кнопкой Delete
objectTable.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode()==127){
objectTable.setValueAt("", objectTable.getSelectedRow(), objectTable.getSelectedColumn());
}
}
});

// Columns table
final JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(10, 255, 336, 283);
this.getContentPane().add(scrollPane);
String[] columns = new String[]{"Header", "Width", "Column", "Type"};
model = new DefaultTableModel(new Object[69][], columns) {
final boolean[] columnEditables = new boolean[]{
true, true, true, true
};
public boolean isCellEditable(int row, int column) {
return this.columnEditables[column];
}
};
table = new JTable(model);

// cell border color
table.setGridColor(new Color(58, 79, 79));
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
// table background color
table.setFillsViewportHeight(true);
table.setBackground(new Color(250, 252, 255));
// headers settings
JTableHeader header = table.getTableHeader();
header.setFont(new Font("Tahoma", Font.BOLD, 13));
//cell alignment
DefaultTableCellRenderer Renderer = new DefaultTableCellRenderer();
Renderer.setHorizontalAlignment(JLabel.CENTER);
table.getColumnModel().getColumn(0).setCellRenderer(Renderer);
table.getColumnModel().getColumn(1).setCellRenderer(Renderer);
table.getColumnModel().getColumn(2).setCellRenderer(Renderer);
table.getColumnModel().getColumn(3).setCellRenderer(Renderer);
table.setRowHeight(20);
table.setColumnSelectionAllowed(true);
table.setCellSelectionEnabled(true);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setFont(new Font("SansSerif", Font.PLAIN, 13));
table.getColumnModel().getColumn(0).setPreferredWidth(90);
table.getColumnModel().getColumn(1).setPreferredWidth(48);
table.getColumnModel().getColumn(2).setPreferredWidth(90);
table.getColumnModel().getColumn(3).setPreferredWidth(90);
//colors
table.setSelectionBackground(new Color(254, 204, 204));
scrollPane.setViewportView(table);

// удаление содержимого ячейки кнопкой Delete
table.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode()==127){
table.setValueAt("", table.getSelectedRow(), table.getSelectedColumn());
}
}
});

// Create objects button
JButton setColumnsBtn = new JButton("Create objects");
setColumnsBtn.setBounds(224, 547, 120, 22);
setColumnsBtn.setBackground(new Color(192, 225, 255));
setColumnsBtn.setFont(new Font("Tahoma", Font.BOLD, 11));
setColumnsBtn.setContentAreaFilled(true);
setColumnsBtn.setBorderPainted(true);
setColumnsBtn.setFocusable(false);
getContentPane().add(setColumnsBtn);
setColumnsBtn.addActionListener((e) -> getValues());

// Clear table
JButton clearTableBtn = new JButton("Clear");
clearTableBtn.setBounds(10, 547, 120, 22);
clearTableBtn.setBackground(new Color(251, 203, 203));
clearTableBtn.setFont(new Font("Tahoma", Font.BOLD, 11));
clearTableBtn.setContentAreaFilled(true);
clearTableBtn.setBorderPainted(true);
clearTableBtn.setFocusable(false);
getContentPane().add(clearTableBtn);

JLabel objectNameLbl = new JLabel("Parameters");
objectNameLbl.setFont(new Font("Tahoma", Font.BOLD, 13));
objectNameLbl.setBounds(10, 5, 334, 18);
getContentPane().add(objectNameLbl);

JLabel columnLbl = new JLabel("Columns");
columnLbl.setFont(new Font("Tahoma", Font.BOLD, 13));
columnLbl.setBounds(10, 235, 334, 18);
getContentPane().add(columnLbl);

clearTableBtn.addActionListener((e) -> {
for (int i = 0; i < table.getRowCount(); i++)
for(int j = 0; j < table.getColumnCount(); j++) {
table.setValueAt("", i, j);
}
});

this.setVisible(true);
}


public static void getValues() {
// определяем количество строк для создания массива (количество столбцов статическое - 4)
int rowCount = 0;
int columnCount = table.getColumnCount();

for (int i = 0; i < columnCount; i++) {
if (table.getValueAt(i, 0) != null) {
rowCount++;
}
}

// параметры
Object[][] objectParameters = new Object[16][2];
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 2; j++) {
if (objectTable.getValueAt(i, j) != null) {
objectParameters [j] = objectTable.getValueAt(i, j);
}
}
}

// столбцы
Object[][] columns = new Object[rowCount][4];
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < columnCount; j++) {
if (table.getValueAt(i, j) != null) {
columns [j] = table.getValueAt(i, j);
}
}
}

// выгрузка запросов в текстовый файл
if (columns.length > 0) {
WriteToFile wr = new WriteToFile();
wr.write(new GetSqlQuery().getQueries(objectParameters, columns));
//System.out.println(sql.getQueries(columns));
}
}

public static class TableInfoRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, false, row, column);

if(column == 0) {
if (isSelected) {
super.setForeground(new Color(99, 9, 9));
} else {
super.setForeground(Color.BLACK);
}
c.setBackground(new Color(219, 234, 201));
c.setHorizontalAlignment(LEFT);
c.setFont(new Font("Tahoma", Font.BOLD,13));
}
else {
c.setBackground(Color.WHITE);
c.setHorizontalAlignment(LEFT);
if (isSelected) {
super.setBackground(new Color(254, 204, 204));
}
}
return c;
}
}
}
В классе GetSqlQuery производится формирование команд для создания объектов PL/SQL на основе ранее введённых данных.
Record создаётся на выбор в 2 вариантах:
1) на основе %TYPE view или таблицы (не %ROWTYPE т.к. чаще нужны не все поля БД для выгрузки);
2) на основе введённых типов в columns.
Пытался сделать красиво, но не обошлось без множества append.. опять же - можно лучше - покажите как надо через pull request на gitHub
package QueriesCreater;

public class GetSqlQuery {

//формирование функции
String getQueries(Object [][] pParameters, Object [][] pColumns) {
Object functionName = pParameters[0][1];
Object procedureName = pParameters[1][1];
Object recordName = pParameters[2][1];
Object typeTableName = pParameters[3][1];
Object tableOrViewName = pParameters[4][1];
Object headersBackground = pParameters[5][1];
Object headersFontSize = pParameters[6][1];
Object rowsFontSize = pParameters[7][1];
Object rowHeight = pParameters[8][1];
Object rowsBold = pParameters[9][1];
Object rowsItalic = pParameters[10][1];
Object xlsFont = pParameters[11][1];
Object rowsFont = pParameters[12][1];
Object horizontalAlignment = pParameters[13][1];
Object verticalAlignment = pParameters[14][1];
Object wrapText = pParameters[15][1];

StringBuilder xlsHeaders = new StringBuilder("-- Package specification\n");
// создаём запись (1 вариант)
xlsHeaders.append("-- version 1\n");
xlsHeaders.append("type ").append(recordName).append(" is record (\n");
for (Object[] column : pColumns) {
xlsHeaders.append("\t").append(column[2]).append(" ").append(tableOrViewName).append(".").append(column[2]).append("%TYPE,\n");
}
// удаляем крайнюю запятую
xlsHeaders.deleteCharAt(xlsHeaders.length() - 2);
xlsHeaders.append(");\n\n");

// создаём запись (2 вариант)
xlsHeaders.append("-- version 2\n");
xlsHeaders.append("type ").append(recordName).append(" is record (\n");
for (Object[] column : pColumns) {
xlsHeaders.append("\t").append(column[2]).append(" ").append(column[3]).append(",\n");
}
// удаляем крайнюю запятую
xlsHeaders.deleteCharAt(xlsHeaders.length() - 2);
xlsHeaders.append(");\n\n");

//создаём тип таблицы на основе записи
xlsHeaders.append("type ").append(typeTableName).append(" is table of ").append(recordName).append(";\n\n");

// создаём функцию на основе типа таблицы
xlsHeaders.append("function ").append(functionName).append("(p_type in ").append(typeTableName).append(") return blob;\n\n");

// тело пакета
xlsHeaders.append("-- Package body\n");
// создание функции
xlsHeaders.append("-- function\n");
xlsHeaders.append("function ").append(functionName).append("(p_type in ").append(typeTableName).append(") return blob is\n")
.append(" row_num number := 0;\n")
.append(" v_file blob;\n")
.append("begin\n")
.append(" as_xlsx.clear_workbook;\n")
.append(" as_xlsx.new_sheet('tab1');\n")
.append(" row_num := row_num + 1;\n")
.append(" as_xlsx.set_row(row_num, p_fontId => as_xlsx.get_font('")
.append(xlsFont)
.append("', p_fontsize => ")
.append(headersFontSize)
.append(", p_bold => true),\n")
.append(" p_fillId => as_xlsx.get_fill('solid', '")
.append(headersBackground)
.append("'), p_borderId => as_xlsx.get_border);\n")
.append("\t-- headers\n")
.append("\tas_xlsx.set_row_height(1, ")
.append(rowHeight)
.append(");\n");

// headers
for (int i = 0; i < pColumns.length; i++) {
int x = i + 1;
xlsHeaders.append("\tas_xlsx.set_column_width(")
.append(x).append(", ").append(pColumns[1]).append("); ")
.append("as_xlsx.cell(").append(x).append(", 1, '").append(pColumns[0]).append("', ")
.append("p_alignment => as_xlsx.get_alignment (")
.append("p_horizontal => '").append(horizontalAlignment).append("', ")
.append("p_vertical => '").append(verticalAlignment).append("', ")
.append("p_wraptext => ").append(wrapText)
.append("));\n");
}
xlsHeaders.append(" \nFOR i IN 1..p_type.count\n" + " loop\n"
+ " row_num := row_num + 1;\n"
+ " as_xlsx.set_row(row_num, p_fontId => as_xlsx.get_font('")
.append(xlsFont)
.append("', p_fontsize => ").append(rowsFontSize)
.append("), p_borderId => as_xlsx.get_border);\n")
.append(" as_xlsx.set_row_height(row_num, ")
.append(rowHeight)
.append(");\n --\n");

// rows
for (int i = 0; i < pColumns.length; i++) {
int x = i + 1;
xlsHeaders.append(" as_xlsx.cell(")
.append(x).append(", i + 1, coalesce(p_type(i).").append(pColumns[2]).append(", '-'), ")
.append("p_alignment => as_xlsx.get_alignment(\n")
.append("p_horizontal => '").append(horizontalAlignment).append("', ")
.append("p_vertical => '").append(verticalAlignment).append("', ")
.append("p_wraptext => ").append(wrapText)
.append("), p_fontId => as_xlsx.get_font('")
.append(rowsFont)
.append("', p_fontsize => ")
.append(rowsFontSize)
.append(", p_bold => ")
.append(rowsBold)
.append(", p_italic => ")
.append(rowsItalic)
.append("));\n");
}
xlsHeaders.append(" end loop;\n"
+ " v_file := as_xlsx.finish;\n"
+ " RETURN v_file;\n"
+ " exception\n"
+ " when others then\n"
+ " sb_util.write_log('package.")
.append(functionName)
.append(" ошибка: ' ||sqlerrm||chr(13)||dbms_utility.format_error_backtrace, 'info'); \n")
.append("END;\n\n");

// Формирование процедуры для скачивания файла
xlsHeaders.append("-- procedure\n");
xlsHeaders.append("procedure ").append(procedureName).append(" is\n")
.append(" l_file BLOB;\n")
.append(" l_type ").append(typeTableName).append(";\n")
.append("begin\n")
.append(" select ");
for (Object[] column : pColumns) {
xlsHeaders.append(column[2]).append(", ");
}
xlsHeaders.deleteCharAt(xlsHeaders.length() - 2).append("\n");
xlsHeaders.append(" bulk collect into l_type\n from ")
.append(tableOrViewName).append("\n");
xlsHeaders.append(" where column_name between sysdate and sysdate + 1;\n" + "\n" + " l_file := ")
.append(functionName).append("(l_type);\n").append("\n")
.append(" if lengthb(l_file) > 0 then\n")
.append(" api_datasource.setColumnValue('BLOB_DUAL_ds.blob', l_file);\n")
.append(" api_datasource.download('BLOB_DUAL_ds.blob', file_name||'.xlsx');\n")
.append(" end if;\n\n")
.append("exception\n" + " when others then\n" + " sb_util.write_log('package.")
.append(procedureName)
.append(" ошибка: ' || sqlerrm ||chr(13)||dbms_utility.format_error_backtrace, 'info');\n")
.append("end;");

return xlsHeaders.toString();
}
}
Класс WriteToFile выгружает запросы в формате txt в указанное вами место (по умолчанию - на Рабочий стол):
package QueriesCreater;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io_OutputStreamWriter;
import java.nio.charset.StandardCharsets;

public class WriteToFile {
void write(String pSql) {
//Save file to
FileNameExtensionFilter filter = new FileNameExtensionFilter("*.txt", "*.txt", "*.TXT", "*.*");
JFileChooser save_to = new JFileChooser();
save_to.setFileFilter(filter);
save_to.setCurrentDirectory(new File
(System.getProperty("user.home") + System.getProperty("file.separator") + "Desktop"));
int ret = save_to.showDialog(null, "Save");
if (ret == JFileChooser.APPROVE_OPTION) {
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(save_to.getSelectedFile() + ".txt"), StandardCharsets.UTF_8)) {
writer.write(pSql);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Завершает парад классов - данный код:
package QueriesCreater;

public class Main {
public static void main(String[] args) {
new Gui();
}
}
В итоге после ввода нужных нам столбцов
9fd532ffbc08534959afa0026ca8f7f3.png

и нажатия кнопки "Create objects" - получаем следующие запросы:
-- Package specification
-- version 1
type t_type_of_record is record (
DB_ID table_or_view.DB_ID%TYPE,
DB_NAME table_or_view.DB_NAME%TYPE,
DB_DATE table_or_view.DB_DATE%TYPE
);

-- version 2
type t_type_of_record is record (
DB_ID NUMBER,
DB_NAME VARCHAR2(69),
DB_DATE DATE
);

type t_type_of_record_tbl is table of t_type_of_record;

function get_xls_function(p_type in t_type_of_record_tbl) return blob;

-- Package body
-- function
function get_xls_function(p_type in t_type_of_record_tbl) return blob is
row_num number := 0;
v_file blob;
begin
as_xlsx.clear_workbook;
as_xlsx.new_sheet('tab1');
row_num := row_num + 1;
as_xlsx.set_row(row_num, p_fontId => as_xlsx.get_font('Times New Roman', p_fontsize => 13, p_bold => true),
p_fillId => as_xlsx.get_fill('solid', 'FFCC66'), p_borderId => as_xlsx.get_border);
-- headers
as_xlsx.set_row_height(1, 25);
as_xlsx.set_column_width(1, 20); as_xlsx.cell(1, 1, 'id', p_alignment => as_xlsx.get_alignment (p_horizontal => 'center', p_vertical => 'center', p_wraptext => true));
as_xlsx.set_column_width(2, 50); as_xlsx.cell(2, 1, 'name', p_alignment => as_xlsx.get_alignment (p_horizontal => 'center', p_vertical => 'center', p_wraptext => true));
as_xlsx.set_column_width(3, 40); as_xlsx.cell(3, 1, 'date', p_alignment => as_xlsx.get_alignment (p_horizontal => 'center', p_vertical => 'center', p_wraptext => true));

FOR i IN 1..p_type.count
loop
row_num := row_num + 1;
as_xlsx.set_row(row_num, p_fontId => as_xlsx.get_font('Times New Roman', p_fontsize => 12), p_borderId => as_xlsx.get_border);
as_xlsx.set_row_height(row_num, 25);
--
as_xlsx.cell(1, i + 1, coalesce(p_type(i).DB_ID, '-'), p_alignment => as_xlsx.get_alignment(
p_horizontal => 'center', p_vertical => 'center', p_wraptext => true), p_fontId => as_xlsx.get_font('Times New Roman', p_fontsize => 12, p_bold => false, p_italic => false));
as_xlsx.cell(2, i + 1, coalesce(p_type(i).DB_NAME, '-'), p_alignment => as_xlsx.get_alignment(
p_horizontal => 'center', p_vertical => 'center', p_wraptext => true), p_fontId => as_xlsx.get_font('Times New Roman', p_fontsize => 12, p_bold => false, p_italic => false));
as_xlsx.cell(3, i + 1, coalesce(p_type(i).DB_DATE, '-'), p_alignment => as_xlsx.get_alignment(
p_horizontal => 'center', p_vertical => 'center', p_wraptext => true), p_fontId => as_xlsx.get_font('Times New Roman', p_fontsize => 12, p_bold => false, p_italic => false));
end loop;
v_file := as_xlsx.finish;
RETURN v_file;
exception
when others then
sb_util.write_log('package.get_xls_function ошибка: ' ||sqlerrm||chr(13)||dbms_utility.format_error_backtrace, 'info');
END;

-- procedure
procedure get_xls_from_table is
l_file BLOB;
l_type t_type_of_record_tbl;
begin
select DB_ID, DB_NAME, DB_DATE
bulk collect into l_type
from table_or_view
where column_name between sysdate and sysdate + 1;

l_file := get_xls_function(l_type);

if lengthb(l_file) > 0 then
api_datasource.setColumnValue('BLOB_DUAL_ds.blob', l_file);
api_datasource.download('BLOB_DUAL_ds.blob', file_name||'.xlsx');
end if;

exception
when others then
sb_util.write_log('package.get_xls_from_table ошибка: ' || sqlerrm ||chr(13)||dbms_utility.format_error_backtrace, 'info');
end;
Итоговый эксель-файл выглядит так:
0da7084eab4efb74edae54473cae6909.png

Мне такая утилита пару лет назад очень бы помогла. Надеюсь Я кому-то помогу этим постом.

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

Источник статьи: https://habr.com/ru/post/567966/
 
Сверху