Россия, Рубцовск |
Опубликован: 25.07.2012 | Уровень: специалист | Доступ: платный
Дополнительный материал 1:
Приложение А. Пример генератора пакетов PL/SQL
Тело генерируемого пакета
Ниже приводится код тела пакета для таблицы 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.