Опубликован: 25.07.2012 | Уровень: специалист | Доступ: платный
Дополнительный материал 1:

Приложение А. Пример генератора пакетов PL/SQL

< Лекция 9 || Дополнительный материал 1: 1234

Тело генерируемого пакета

Ниже приводится код тела пакета для таблицы drawing в том виде, каким он должен быть сгенерирован.

create or replace package body pkg_drawing as 
    const_sqltxt constant varchar2(1000 char):= 'select '||
                ' id,name, title, revision, type_id, update_date, update_user_id'|| 
                ' from drawing tbl where 1=1 '; 

    --------function to create search query------------------
    function fun_search_query(
        p_name in varchar2,
        p_title in varchar2,
        p_revision in varchar2,
        p_type_id in number
        ) return varchar2 is
        p_wheretxt varchar2(4000);
    begin
        --------------equals------------------------
        p_wheretxt := p_wheretxt || pkg_lib.fun_add_equal_m1('tbl.type_id',p_type_id);

        --------------likes------------------------
        p_wheretxt := p_wheretxt || pkg_lib.fun_add_like('tbl.name',p_name);
        p_wheretxt := p_wheretxt || pkg_lib.fun_add_like('tbl.title',p_title);
        p_wheretxt := p_wheretxt || pkg_lib.fun_add_like('tbl.revision',p_revision);
        
        return const_sqltxt||p_wheretxt;
    end;

    --------procedure to save------------------
    procedure prc_save(p_id number,
        p_name in varchar2,
        p_title in varchar2,
        p_revision in varchar2,
        p_type_id in number,
        p_update_user_id in number) is 
    begin
        if p_id > 0 then 
            update drawing set
                name = p_name,
                title = p_title,
                revision = p_revision,
                type_id = p_type_id,
                update_user_id = p_update_user_id,
                update_date = sysdate
            where id = p_id;
            commit;
        else
            insert into drawing(id,name, title, revision, type_id, update_date, update_user_id)
            values (seq_drawing.nextval,p_name, p_title, p_revision, p_type_id, sysdate, p_update_user_id);
            commit;
        end if;
    end;

    --------search procedure------------------
    procedure prc_search(
        p_name in varchar2,
        p_title in varchar2,
        p_revision in varchar2,
        p_type_id in number,
        p_page in number,
        p_pagesize in number,
        p_order_by varchar2,
        p_order_type varchar2,
        p_recordset out types.ref_cursor) is 
            sqltxt varchar2(4000);
            p_sqltxt varchar2(4000);
            p_sqltxt_page varchar2(4000);
            p_startpage number;
            p_maxpage number;
            p_ordersqltxt varchar2(1000);
    begin
        p_startpage := p_pagesize*(p_page-1);
        p_maxpage := p_pagesize*p_page;
        p_sqltxt := fun_search_query(p_name, p_title, p_revision, p_type_id);
        p_ordersqltxt := pkg_lib.fun_sorting_query(p_order_by,p_order_type,'tbl.id');
        p_sqltxt_page := 'select * from (select s1.*, rownum rnum from ('||p_sqltxt|| p_ordersqltxt|| ') s1) ' ||
                'where rnum<=:max_row_to_fetch and rnum > :min_row_to_fetch ' || p_ordersqltxt;
        open p_recordset for p_sqltxt_page using p_maxpage,p_startpage;
    end;

    --------count procedure------------------
    procedure prc_count(
        p_name in varchar2,
        p_title in varchar2,
        p_revision in varchar2,
        p_type_id in number,
            p_pagesize in number,
            p_recordset out types.ref_cursor) is 
        p_sqltxt varchar2(4000);
    begin
        p_sqltxt := fun_search_query(p_name, p_title, p_revision, p_type_id);
        p_sqltxt := 'select count(1) cnt, ceil(count(1)/:p_pagesize) pagecount from ('||p_sqltxt||') ';
        open p_recordset for p_sqltxt using p_pagesize;
    end;

    --------show one item procedure ------------------
    procedure prc_show_by_id(p_id number,  p_recordset out types.ref_cursor ) is 
    begin
        if p_id > 0 then
            open p_recordset for
            const_sqltxt || ' and tbl.id = :p_id' using p_id;
        end if;
    end;

    --------delete procedure ------------------
    procedure prc_delete(p_id number) is 
        t_var number;
    begin
        select count(1) into t_var from drawing where id = p_id;
        if t_var > 0 then
            delete from drawing where id = p_id;
        commit;
        end if;
    end;

end pkg_drawing;
/
    
Пример A.6.

Прежде чем рассматривать код генератора приведем код класса таблицы, который используется в генераторе:

class DBTable
{
    //Название таблицы
    public string TableName;

    //Количество полей в таблице
    public int FieldCount;

    //Массив с названиями полей таблицы
    public string[] FieldName;

    //Массив с названиями типов полей таблицы
    public string[] FieldTypeName;

    //Массив с указанием длины строковых полей таблицы
    public string[] FieldLength;

    //Часть запроса для сортировки по умолчанию
    public string defaultOrderString;

    //Часть пути для сохранения сгенерированного кода
    public string filesPath;

}
    
Пример A.7.

А теперь приведем код класса CodeGenerator. Пояснения по коду даны в комментариях.

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.IO;

namespace PLSQLCodeGenerator
{
    class CodeGenerator
    {
        //Константы tab1, tab2, tab3, tab4 предназначены для установки отступов
        //в начале строк генерируемого приложения.
        public const string tab1 = "    ";
        public const string tab2 = "        ";
        public const string tab3 = "            ";
        public const string tab4 = "                ";

        //В этих константах хранятся часто применяемые участки кода.
        public const string p_recordset = " p_recordset out types.ref_cursor ";
        public const string add_equal_m1 = "p_wheretxt := p_wheretxt || pkg_lib.fun_add_equal_m1";
        public const string add_like = "p_wheretxt := p_wheretxt || pkg_lib.fun_add_like";

        //Здесь хранятся окончания имен для генерируемых файлов
        //В трех файлах сохраняются запросы на создание объектов,
        //спецификация и тело пакета.
        public const string obj_filename = @"Objects.txt";
        public const string package_spec_filename = @"PackageSpec.txt";
        public const string package_body_filename = @"PackageBody.txt";

        //Сокращенное имя таблицы по умолчанию.
        public const string tbl = "tbl";

        //Процедура для вывода сгенерированного результата в файл.
        public void PutResult(List<string> pList, String filePath)
        {
            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }
            using (StreamWriter sw = File.CreateText(filePath))
            {
                foreach (string str in pList)
                {
                    sw.WriteLine(str);
                }
            }
        }

        //Возвращается текст списка переменных
        //в виде: "p_number, p_title, p_revision, p_type_id",
        //используется при генерации тела пакета.
        public string GetParamListMain(DBTable table)
        {
            string result = "";
            for (int i = 0; i < table.FieldCount; i++)
            {
                result = result + "p_" + table.FieldName[i];
                if (i < table.FieldCount - 1) result = result + ", ";

            }
            return result;
        }

        //К тексту списка переменных добавляются:
        //в начало - следующее значение последовательности
        //в конец - текущая дата и идентификатор пользователя.
        //Используется при генерации запроса вставки строки.
        //Результат получается следующего вида:
        //"seq_drawing.nextval,p_number, p_title, p_revision, p_type_id, sysdate, p_update_user_id".
        public string GetParamListAll(DBTable table)
        {
            return "seq_" + table.TableName + ".nextval," + GetParamListMain(table) +
               ", sysdate, p_update_user_id";
        }

        //Возвращается текст списка полей таблицы
        //в виде: "number, title, revision, type_id".
        //Используется при генерации запросов SELECT и INSERT.
        public string GetFieldListMain(DBTable table)
        {
            string result = "";
            for (int i = 0; i < table.FieldCount; i++)
            {
                result = result + table.FieldName[i];
                if (i < table.FieldCount - 1) result += ", ";
            }
            return result;
        }

        //К тексту списка полей таблицы добавляются
        //поля идентификатора таблицы, даты последнего обновления,
        //а также идентификатора пользователя.
        //Результат получается следующего вида:
        //"id,number, title, revision, type_id, update_date, update_user_id".
        public string GetFieldListAll(DBTable table)
        {
            return "id," + GetFieldListMain(table) + ", update_date, update_user_id";
        }

        //Создается список параметров, применяемый
        //в сигнатурах функций и процедур как в теле,
        //так и в спецификации пакета.
        //Результат получается следующего вида:
        //        p_number in varchar2,
        //        p_title in varchar2,
        //        p_revision in varchar2,
        //        p_type_id in number
        public List<string> GetParametersDeclaration(DBTable table, bool is_last)
        {
            //Переменная result имеет тип List<string> и является набором строк.
            //В ней сохраняется сгенерированный код, который впоследствии
            //может быть выведен в файл.
            List<string> result = new List<string>();
            for (int i = 0; i < table.FieldCount; i++)
            {
                string temp = tab2;
                temp += "p_" + table.FieldName[i] + " in " + table.FieldTypeName[i];
                if (i < table.FieldCount - 1 || !is_last) temp += ",";
                result.Add(temp);
            }
            return result;
        }

        //Формируется запрос на создание таблицы.
        //Результат возвращается в виде набора строк.
        public List<string> GetCreateTable(DBTable table)
        {
            List<string> result = new List<string>();
            result.Add("create table " + table.TableName + " ( ");
            result.Add("id" + tab3 + "number primary key,");
            //В цикле для каждого поля таблицы
            //формируется код объявления.
            for (int i = 0; i < table.FieldCount; i++)
            {
                string temp = "";
                temp = table.FieldName[i] + tab3 + table.FieldTypeName[i];
                if (!(table.FieldLength[i] == ""))
                {
                    temp += "(" + table.FieldLength[i] + ")";
                }
                temp += ",";
                result.Add(temp);
            }
            //Всегда добавляются
            //поля update_date и update_user_id.
            result.Add("update_date" + tab3 + "date,");
            result.Add("update_user_id" + tab3 + "number");
            result.Add(")");
            return result;
        }

        //Формирование запроса на создание последовательности Oracle.
        public List<string> GetCreateSequence(DBTable table)
        {
            List<string> result = new List<string>();
            result.Add("create sequence seq_" + table.TableName);
            result.Add("start with 1");
            result.Add("maxvalue 999999999999999999999999999");
            result.Add("minvalue 1");
            result.Add("nocycle");
            result.Add("nocache");
            result.Add("noorder");
            return result;
        }

        //В одном файле сохраняются запросы
        //на создание таблицы и последовательности.
        public void SaveCreateObjects(DBTable table)
        {
            List<string> result = new List<string>();
            //В набор строк добавляется запрос на создание таблицы.
            result.AddRange(GetCreateTable(table));
            //Запросы будут разделены пустой строкой.
            result.Add("");
            //В набор строк добавляется запрос на создание последовательности.
            result.AddRange(GetCreateSequence(table));
            PutResult(result, table.filesPath + obj_filename);
        }

        //Функция для создания кода спецификации пакета.
        public List<string> GetPackageSpecification(DBTable table)
        {
            List<string> result = new List<string>();
            //Объявление спецификации пакета.
            result.Add("create or replace package pkg_" + table.TableName + " as ");

            //Объявление функции, формирующей текст запроса.
            result.Add("");
            result.Add(tab1 + "--------function to create search query------------------");
            result.Add(tab1 + "function fun_search_query(");
            result.AddRange(GetParametersDeclaration(table, true));
            result.Add(tab1 + ") return varchar2;");

            //Объявление процедуры сохранения записи.
            result.Add("");
            result.Add(tab1 + "--------procedure to save------------------");
            result.Add(tab1 + "procedure prc_save(p_id number,");
            result.AddRange(GetParametersDeclaration(table, false));
            result.Add(tab2 + "p_update_user_id in number);");

            //Объявление процедуры поиска записей по заданным критериям.
            result.Add("");
            result.Add(tab1 + "--------search procedure------------------");
            result.Add(tab1 + "procedure prc_search(");
            result.AddRange(GetParametersDeclaration(table, false));
            result.Add(tab2 + "p_page in number,");
            result.Add(tab2 + "p_pagesize in number,");
            result.Add(tab2 + "p_order_by varchar2,");
            result.Add(tab2 + "p_order_type varchar2,");
            result.Add(tab2 + "p_recordset out types.ref_cursor);");

            //Объявление процедуры подсчета количества записей по заданным критериям.
            result.Add("");
            result.Add(tab1 + "--------count procedure------------------");
            result.Add(tab1 + "procedure prc_count(");
            result.AddRange(GetParametersDeclaration(table, false));
            result.Add(tab2 + "p_pagesize in number,");
            result.Add(tab2 + "p_recordset out types.ref_cursor);");

            //Объявление процедуры выборки одной строки по заданному идентификатору.
            result.Add("");
            result.Add(tab1 + "--------show one item procedure ------------------");
            result.Add(tab1 + "procedure prc_show_by_id(p_id number, " + p_recordset + ");");

            //Объявление процедуры удаления строки.
            result.Add("");
            result.Add(tab1 + "--------delete procedure ------------------");
            result.Add(tab1 + "procedure prc_delete(p_id number);");

            //Конец спецификации пакета
            result.Add("");
            result.Add("end pkg_" + table.TableName + ";");
            result.Add("/");
            
            //Возвращается набор строк, результат работы процедуры.
            return result;
        }

        //Сохранение спецификации пакета в отдельном файле.
        public void SavePackageSpecification(DBTable table)
        {
            PutResult(GetPackageSpecification(table), table.filesPath + package_spec_filename);
        }

        //Здесь формируется код функции создания запроса.
        public List<string> GetSearchFunction(DBTable table)
        {
            List<string> result = new List<string>();
            result.Add(tab1 + "--------function to create search query------------------");
            result.Add(tab1 + "function fun_search_query(");
            result.AddRange(GetParametersDeclaration(table, true));
            result.Add(tab2 + ") return varchar2 is");

            result.Add(tab2 + "p_wheretxt varchar2(4000);");
            result.Add(tab1 + "begin");
            result.Add(tab2 + "--------------equals------------------------");

            //В цикле формируется код вида:
            //p_wheretxt := p_wheretxt || pkg_lib.fun_add_equal_m1('tbl.type_id',p_type_id);
            for (int i = 0; i < table.FieldCount; i++)
            {
                if (table.FieldTypeName[i] == "number")
                {
                    result.Add(tab2 + add_equal_m1 + "('" + tbl + "." + table.FieldName[i] + 
                       "',p_" + table.FieldName[i] + ");");
                }
            }
            result.Add("");
            result.Add(tab2 + "--------------likes------------------------");

            //В цикле формируется код вида:
            //p_wheretxt := p_wheretxt || pkg_lib.fun_add_like('tbl.number',p_number);
            //p_wheretxt := p_wheretxt || pkg_lib.fun_add_like('tbl.title',p_title);
            //p_wheretxt := p_wheretxt || pkg_lib.fun_add_like('tbl.revision',p_revision);
            for (int i = 0; i < table.FieldCount; i++)
            {
                if (table.FieldTypeName[i] == "varchar2")
                {
                    result.Add(tab2 + add_like + "('" + tbl + "." + table.FieldName[i] + 
                       "',p_" + table.FieldName[i] + ");");
                }
            }

            result.Add(tab2);
            result.Add(tab2 + "return const_sqltxt||p_wheretxt;");
            result.Add(tab1 + "end;");
            return result;
        }

        //Формируется код процедуры поиска записей.
        public List<string> GetSearchProcedure(DBTable table)
        {
            List<string> result = new List<string>();
            result.Add(tab1 + "--------search procedure------------------");
            result.Add(tab1 + "procedure prc_search(");
            result.AddRange(GetParametersDeclaration(table, false));
            result.Add(tab2 + "p_page in number,");
            result.Add(tab2 + "p_pagesize in number,");
            result.Add(tab2 + "p_order_by varchar2,");
            result.Add(tab2 + "p_order_type varchar2,");
            result.Add(tab2 + "p_recordset out types.ref_cursor) is ");

            result.Add(tab3 + "sqltxt varchar2(4000);");
            result.Add(tab3 + "p_sqltxt varchar2(4000);");
            result.Add(tab3 + "p_sqltxt_page varchar2(4000);");
            result.Add(tab3 + "p_startpage number;");
            result.Add(tab3 + "p_maxpage number;");
            result.Add(tab3 + "p_ordersqltxt varchar2(1000);");

            result.Add(tab1 + "begin");
            result.Add(tab2 + "p_startpage := p_pagesize*(p_page-1);");
            result.Add(tab2 + "p_maxpage := p_pagesize*p_page;");
            result.Add(tab2 + "p_sqltxt := fun_search_query(" + GetParamListMain(table) + ");");
            result.Add(tab2 + "p_ordersqltxt := pkg_lib.fun_sorting_query(p_order_by,p_order_type,'" 
               + tbl + "." + table.defaultOrderString + "');");
            result.Add(tab2 + "p_sqltxt_page := 'select * from 
               (select s1.*, rownum rnum from ('||p_sqltxt|| p_ordersqltxt|| ') s1) ' ||");
            result.Add(tab4 + "'where rnum<=:max_row_to_fetch and rnum >
            :min_row_to_fetch ' || p_ordersqltxt;");
            result.Add(tab2 + "open p_recordset for p_sqltxt_page using p_maxpage,p_startpage;");
            result.Add(tab1 + "end;");
            return result;
        }

        //Формируется код процедуры сохранения записи.
        public List<string> GetSaveProcedure(DBTable table)
        {
            List<string> result = new List<string>();
            result.Add(tab1 + "--------procedure to save------------------");
            result.Add(tab1 + "procedure prc_save(p_id number,");
            result.AddRange(GetParametersDeclaration(table, false));
            result.Add(tab2 + "p_update_user_id in number) is ");
            result.Add(tab1 + "begin");
            result.Add(tab2 + "if p_id > 0 then ");
            result.Add(tab3 + "update " + table.TableName + " set");

            //В этом цикле генерируется часть запроса UPDATE вида:
            //  number = p_number,
            //  title = p_title,
            //  revision = p_revision,
            //  type_id = p_type_id,
            for (int i = 0; i < table.FieldCount; i++)
            {
                string temp = tab4;
                temp += table.FieldName[i] + " = p_" + table.FieldName[i] + ",";
                result.Add(temp);
            }

            result.Add(tab4 + "update_user_id = p_update_user_id,");
            result.Add(tab4 + "update_date = sysdate");
            result.Add(tab3 + "where id = p_id;");
            result.Add(tab3 + "commit;");
            result.Add(tab2 + "else");
            result.Add(tab3 + "insert into " + table.TableName + 
            "(" + GetFieldListAll(table) + ")");
            result.Add(tab3 + "values (" + GetParamListAll(table) + ");");
            result.Add(tab3 + "commit;");
            result.Add(tab2 + "end if;");

            result.Add(tab1 + "end;");
            return result;
        }

        //Формируется код процедуры подсчета количества записей.
        public List<string> GetCountProcedure(DBTable table)
        {
            List<string> result = new List<string>();
            result.Add(tab1 + "--------count procedure------------------");
            result.Add(tab1 + "procedure prc_count(");
            result.AddRange(GetParametersDeclaration(table, false));
            result.Add(tab3 + "p_pagesize in number,");
            result.Add(tab3 + "p_recordset out types.ref_cursor) is ");
            result.Add(tab2 + "p_sqltxt varchar2(4000);");
            result.Add(tab1 + "begin");
            result.Add(tab2 + "p_sqltxt := fun_search_query
            (" + GetParamListMain(table) + ");");
            result.Add(tab2 + "p_sqltxt := 'select count(1) cnt, ceil(count(1)/:p_pagesize) 
            pagecount from ('||p_sqltxt||') ';");
            result.Add(tab2 + "open p_recordset for p_sqltxt using p_pagesize;");
            result.Add(tab1 + "end;");
            return result;
        }

        //Формируется код процедуры выборки одной записи.
        public List<string> GetShowOneItemProcedure(DBTable table)
        {
            List<string> result = new List<string>();
            result.Add(tab1 + "--------show one item procedure ------------------");
            result.Add(tab1 + "procedure prc_show_by_id(p_id number, 
            " + p_recordset + ") is ");

            result.Add(tab1 + "begin");

            result.Add(tab2 + "if p_id > 0 then");
            result.Add(tab3 + "open p_recordset for");
            result.Add(tab3 + "const_sqltxt || ' and " + tbl + ".id =
             :p_id' using p_id;");
            result.Add(tab2 + "end if;");

            result.Add(tab1 + "end;");
            return result;
        }

        //Формируется код процедуры удаления записи.
        public List<string> GetDeleteProcedure(DBTable table)
        {
            List<string> result = new List<string>();
            result.Add(tab1 + "--------delete procedure ------------------");
            result.Add(tab1 + "procedure prc_delete(p_id number) is ");
            result.Add(tab2 + "t_var number;");
            result.Add(tab1 + "begin");
            result.Add(tab2 + "select count(1) into t_var from " + 
            table.TableName + " where id = p_id;");
            result.Add(tab2 + "if t_var > 0 then");
            result.Add(tab3 + "delete from " + table.TableName + 
            " where id = p_id;");
            result.Add(tab2 + "commit;");
            result.Add(tab2 + "end if;");
            result.Add(tab1 + "end;");
            return result;
        }

        //Функция для создания кода тела пакета.
        public List<string> GetPackageBody(DBTable table)
        {
            List<string> result = new List<string>();
            //Начало пакета.
            result.Add("create or replace package body pkg_" + 
            table.TableName + " as ");

            //Генерация запроса и константы, в которой она будет сохранена.
            result.Add(tab1 + "const_sqltxt constant varchar2(1000 char):= 'select '||");
            result.Add(tab4 + "' " + GetFieldListAll(table) + "'|| ");
            result.Add(tab4 + "' from " + table.TableName + " " +
             tbl + " where 1=1 '; ");

            //Добавление к телу пакета кода функции, возвращающей запрос.
            result.Add("");
            result.AddRange(GetSearchFunction(table));

            //Добавление к телу пакета кода процедуры, сохраняющей запись.
            result.Add("");
            result.AddRange(GetSaveProcedure(table));

            //Добавление к телу пакета кода процедуры, выполняющей поиск записей.
            result.Add("");
            result.AddRange(GetSearchProcedure(table));

            //Добавление к телу пакета кода процедуры, возвращающей количество записей.
            result.Add("");
            result.AddRange(GetCountProcedure(table));

            //Добавление к телу пакета кода процедуры, возвращающей одну запись.
            result.Add("");
            result.AddRange(GetShowOneItemProcedure(table));

            //Добавление к телу пакета кода процедуры, удаляющей запись.
            result.Add("");
            result.AddRange(GetDeleteProcedure(table));

            //Конец тела пакета.
            result.Add("");
            result.Add("end pkg_" + table.TableName + ";");
            result.Add("/");

            return result;
        }

        //Сохранение тела пакета в одном файле.
        public void SavePackageBody(DBTable table)
        {
            PutResult(GetPackageBody(table), table.filesPath + package_body_filename);
        }

        //Конструктор, вызывающий процедуры для генерации кода
        //создания объектов, спецификации и тела пакета
        //и сохранения их в трех файлах.
        public CodeGenerator(DBTable table)
        {
            SaveCreateObjects(table);
            SavePackageSpecification(table);
            SavePackageBody(table);
        }
    }
}
    
Пример A.8.
< Лекция 9 || Дополнительный материал 1: 1234
Дмитрий Клочков
Дмитрий Клочков
Россия, Рубцовск
Волков Олег
Волков Олег
Украина, Днепропетровск