скачать Санкт-Петербургский государственный университет Математико-механический факультет Кафедра системного программирования Генерация хранимых процедур MySQL на основе BPEL Дипломная работа студента 544 группы Мерабишвили Георгия Тимуровича Научный руководитель ……………… А. Н. Терехов / подпись / Рецензент ……………… / подпись / “Допустить к защите” заведующий кафедрой, д.ф.- м.н., профессор ……………… А. Н. Терехов / подпись / Санкт-Петербург 2008 ^ Замечания От Сеппеля.
1 Введение Известно, что автоматизированная кодогенерация существенно ускоряет темпа раз-работки ПО, избавляя разработчика от рутинной работы. С применением данной техноло-гии, программирование превращается в более наглядный и контролируемый процесс, об- легчается отладка и сопровождение программного продукта. Практика показывает, что денежные и людские ресурсы, потраченные на создание данной технологии, в случай его удачного завершения, окупаются довольно быстро. В данной работе рассматривается вопрос генерации хранимых процедур СУБД MySQL. Вопросы генерации схемы, контекстных ограниченийи, триггеров БД описаны в дипломной работе Ж`олудева В.В. (2007 г.)[5]. Двойное лицензирование MySQL (GPL и на коммерческой основе), поддержка соyf стороны на многих платформах, языковах программирования, делает его выгодным для исполь- зования во многих проектах. Начиная с версии 5.0 в MySQL стабильно поддерживаются хранимые процедуры и функции. Их применение повышает производительность, расширяет возможности прог-программированияраммирования и поддерживает функцию безопасности данных[10]. Средством для описания логики выбран BPEL (Business Process Execution Language) Ооснованный на XML, используется для формального описания бизнес-процессов и про- токолов, их взаимодействия между собой. BPEL – алгоритмически полный язык, с выразительными управляющими конструк-циями, системой типов которого является система типов XML.? Однако BPEL не позволяет осуществить произвольную передачу внутри бизнес-процесса, грубо говоря, нарисовать “стрелку-переход” между двумя любыми “квадратиками”, изображающими простые дейс- твия, ? в этом ограничений есть определенный смысл. BPEL – удобено для описания web-services, в частности, дипломная работа Сеппеля Е.В [6]. посвящена кодогенерациию из BPEL в C#.net. Подробности BPEL можно прочитать в [9]. Также надо упомянуть CASE-пакет QQREAL, разработанный на кафедре системного программирования математико-механического факультета СПБГУ. Он является частью технологии разработки информационных систем. Это CASE-средство является открытой системой и настраивается под различные задачи. QREAL состоит из репозиторияи, в котором й хранится информация о данных, в виде связанных графов, и, редакторов, с помощью кото-рых в репозиториий загружается информация. С архитектурой QREAL более подробно можно познакомиться в дипломной работе Брыксина Т.А. и Никандрова Г. (2007г.).[3] и [4]. С подробностями CASE-средств и истории REAL в [1] и [2]. С самого начала планировалаось поддержка совместимости кодогенераторов с QREAL, поскольку QREAL разработан с помощью языка программирования С++, а в качестве языка программирования, для реализации кодогенераторов, был выбран C#.net, пришлось создать прослойку для взаимодействия между С++ и .Net, с применением ICE технологии. 2 Постановка задачи Хранимые процедуры являются объектамисущностями базы данных, представляющиеми собой набор SQL-инструкций, которые компилируются один раз и хранятся на серверевыполняются по мере необходимости. (не знаю, не уверен) Хранимые процедуры очень похожи на обыкновенные процедуры языков высокого уровня, у них могут быть входные и выходные параметры, локальные переменные, в них могут производиться числовые вычисления и операции над символьными данными, результаты которых могут присваиваться переменным и параметрам. В хранимых процедурах могут выполняться стандартные операции с базами данных (как DDL—не операции с БД, это, как и DML всего лишь язык, так и DML характера). Кроме того, в хранимых процедурах возможны циклы и ветвления, то есть в них могут использоваться инструкции управления потоком [10]. Соответственно, задача генерации хранимых процедур аналогичноа задачие кодо- генерации в языке программирования высокого уровня. В данном случай надо учесть особенности языка MySQL:
Реализовать универсальную схему генерации хранимых процедур, посредством описания осуществлении простых или сложных(перекрестных) запросов к таблицам, простой или сложной вставки в них.
3.Архитектура решения a) Требования к объектной модели системы. Данный документ описывает типы данных (Объекты, линки, типы данных, корень) которые будут использоваться для общения сервера репозитория с редакторами и кодо-генераторами. Эти типы данных должны служить оболочкой над существующими в classes.h классами. Типы объектов для каждого типа должны быть следующие методы:
Метатипы объектов Необходимо такое понятие, как типы типов данных (метатипы). Хранятся как enum-свойства типов. CС точки зрения BPEL должны быть такие метатипы:
Объекты Для каждого объекта должны быть методы:
Рёбра Для каждого ребра должны быть методы:
Работа с коллекциями свойств (e.g. QMap, QList, QHash, etc.) производится следующим образом: коллекции являются private членами соответствующих классов. Разработчики Real в качестве public методов реализуют лишь простейшие методы, которые уже есть. реализацию "продвинутого" доступа (итераторы, [], size). . ^ Например списков дочерних объектов
b) Требованияе к архитектуре редакторов. Что должен уметь редактор
Что ещё будет делать редактор (не через API системы)
c) Требования к API кодогенераторов Требования
(за реализацию серверной части сетевого интерфейса. Мотивация
d) Описание API редакторов/кодогенераторов на C++. Вот кусокфрагмент кода на C++, описывающий API редакторов и кодогенераторов: namespace QRealTypes { typedef QList // Метатипы enum MetaType { object, //типы объектов на диаграмме (например invoke, exit, if, foreach, throw, reply) link, //Рёбра dataType, //типы данных (например базовые int, double, string, char и расширенные -- записи (структуры), с описаннием в виде xsd) rawType //типы хранимых RAW данных (строк) (например wsdl, xsd, параметры проекта) }; // Типы данных class RealType { public: int getId(); // id QString getName() const; // название void setName(const QString); QString getDescription() const; // описание void setDescription(const QString); void setProperty(const QString name, const QString val ); //установить свойство QString getProperty(const QString name ) const; //в случае отсутсвия возвращает пустую строку "" int getPropertiesCount(); // возвращает кол-во свойств enum MetaType getMetaType() const; // метатип void setMetaType(const enum MetaType); QIntList getObjects() const; // вернуть все объекты этого типа }; class RealObject { public: int getId(); // id QString getName() const; // название void setName(const QString); QString getDescription() const; // описание void setDescription(const QString); void setProperty(const QString name, const QString val ); //установить свойство QString getProperty(const QString name ) const; //в случае отсутсвия возвращает пустую строку "" int getPropertiesCount(); // возвращает кол-во свойств int getTypeId() const; // тип void setTypeId(const int); bool getVisibility() const; // видим ли на диаграмме void setVisibility(const bool); int getContainerId() const; // id контейнера void setContainerId(const int); QString getConfiguration() const; // конфигурация void setConfiguration(const QString); QIntList getInnerElements() const; // коллекция id внутренних элементов void addInnerElement(const int); void deleteInnerElement(const int); QIntList getAllLinks() const; // получить линки QIntList getIncomingLinks() const; QIntList getOutcomingLinks() const; void addIncomingLink(const int); void addOutcomingLink(const int); }; class RealLink { public: int getId(); // id QString getName() const; // название void setName(const QString); void setProperty(const QString name, const QString val ); //установить свойство QString getProperty(const QString name ) const; //в случае отсутсвия возвращает пустую строку "" int getPropertiesCount(); // возвращает кол-во свойств int getFromId() const; // элемент-источник void setFromId(const int); int getToId() const; // элемент-приёмник void setToId(const int); }; } Методы, которые надо добавить в класс RealRepoClient: static QIntList getAllTypes(); // вернуть все типы static QIntList getTypesByMetatype(const enum MetaType); // вернуть типы по метатипу static RealType* getTypeById(const int id); // вернуть тип по id static QIntList getObjects(const int); // вернуть все объекты конкретного типа static int createType(const QString name); //создать тип. Возвращает Id созданного типа. static void deleteType(const int id); //удалить тип static RealObject* getObjectById(const int); // вернуть по id static int createObject(const QString name); //создать. Возвращает Id созданного. static void deleteObject(const int id); //удалить static RealLink* getLinkById(const int); // вернуть по id static int createLink(const QString name); //создать. Возвращает Id созданного. static void deleteLink(const int id); //удалить В списке не включены итераторы и скобки. e) MySQL В MySQL можно выделить следующие элементы: Литеральные величины, например string или number
Литеральные величины:
String – Последовательность битов или символов, заключенный в одинарные Или двойные кавычки. Надо учесть особенности распознавания strings при наличие специальных символов (в начале последовательности)? Numbers – числа представлены как последовательность цифр, ‘.’ Является разделителем десятичной части, отрицательные числа начинаются со знаком - , допускается экспонентныйая запись чисел. Hexadecimal values – последовательность шестнадцатеричных цифр, начинающие символам x или 0x. Boolean values – True или False (1 или 0). Bit Field values – двоичное представление (единицами и нулями). Null – NULL, пустое количество(отсутствие) данных. Идентификаторы: Имена БД, таблиц, индексов, столбцов, представлений, хранимых процедур и т.д. являются идентификаторами, как правило ограничиваются 64-мя символами, надо учесть некие особенности чувствительности к регистру. ^ : В MySQL имеется 200-300 зарезервированных слов и надо учесть их существо- вание при именовании переменных и.т.д. ^ : Переменные, введенные пользователем, начинаются со знаком @ и за ним идет последовательность букв, цифр “.”, “_”, “$”. (надо учесть зависимость чувствительности к регистру от версии MySQL). Комментарии: От “#” или “--” до конца линии, или заключенный между “/*” и “*/”. Сейчас рассмотрим структуру statement: Синтаксис задания данных: ^ ALTER {DATABASE | SCHEMA} [db_name] alter_specification ... alter_specification: [DEFAULT] CHARACTER SET [=] charset_name | [DEFAULT] COLLATE [=] collation_name ALTER TABLE: ^ tbl_name alter_specification [, alter_specification] ... alter_specification: table_option ... | ADD [COLUMN] col_name column_definition [FIRST | AFTER col_name ] | ADD [COLUMN] (col_name column_definition,...) | ADD {INDEX|KEY} [index_name] [index_type] (index_col_name,...) | ADD [CONSTRAINT [symbol]] ^ index_type] (index_col_name,...) | ADD [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] [index_name] [index_type] (index_col_name,...) | ADD [FULLTEXT|SPATIAL] [INDEX|KEY] [index_name] (index_col_name,...) | ADD [CONSTRAINT [symbol]] ^ index_name] (index_col_name,...) reference_definition | ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT} | CHANGE [COLUMN] old_col_name new_col_name column_definition [FIRST|AFTER col_name] | MODIFY [COLUMN] col_name column_definition^ col_name] | DROP [COLUMN] col_name | DROP PRIMARY KEY | DROP {INDEX|KEY} index_name | DROP FOREIGN KEY fk_symbol | DISABLE KEYS | ENABLE KEYS | RENAME [TO] new_tbl_name | ORDER BY col_name [, col_name] ... | CONVERT TO CHARACTER SET charset_name [COLLATE collation_name] | [DEFAULT] CHARACTER SET [=] charset_name^ collation_name] | DISCARD TABLESPACE | IMPORT TABLESPACE index_col_name: col_name [(length)] [ASC | DESC] index_type: USING {BTREE | HASH | RTREE} CREATE DATABASE: ^ db_name [create_specification ...] create_specification: [DEFAULT] CHARACTER SET [=] charset_name | [DEFAULT] COLLATE [=] collation_name CREATE INDEX: ^ index_name [index_type] ON tbl_name (index_col_name,...) index_col_name: col_name [(length)] [ASC | DESC] index_type: USING {BTREE | HASH | RTREE} ^ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name (create_definition,...) [table_option] ... Or: CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] [table_option] ... select_statement Or: ^ tbl_name { LIKE old_tbl_name | (LIKE old_tbl_name) } create_definition: col_name column_definition | [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...) | {INDEX|KEY} [index_name] [index_type] (index_col_name,...) | [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] [index_name] [index_type] (index_col_name,...) | {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (index_col_name,...) | [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...) reference_definition | CHECK (expr) column_definition: data_type^ default_value] [AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY] [COMMENT 'string'] [reference_definition] data_type: BIT[(length)] | TINYINT[(length)] [UNSIGNED] [ZEROFILL] | SMALLINT[(length)] [UNSIGNED] [ZEROFILL] | MEDIUMINT[(length)] [UNSIGNED] [ZEROFILL] | INT[(length)] [UNSIGNED] [ZEROFILL] | INTEGER[(length)] [UNSIGNED] [ZEROFILL] | BIGINT[(length)] [UNSIGNED] [ZEROFILL] | REAL[(length,decimals)] [UNSIGNED] [ZEROFILL] | DOUBLE[(length,decimals)] [UNSIGNED] [ZEROFILL] | FLOAT[(length,decimals)] [UNSIGNED] [ZEROFILL] | DECIMAL(length,decimals) [UNSIGNED] [ZEROFILL] | NUMERIC(length,decimals) [UNSIGNED] [ZEROFILL] | DATE | TIME | TIMESTAMP | DATETIME | YEAR | CHAR(length) ^ charset_name] [COLLATE collation_name] | VARCHAR(length) [CHARACTER SET charset_name] [COLLATE collation_name] | BINARY(length) | VARBINARY(length) | TINYBLOB | BLOB | MEDIUMBLOB | LONGBLOB | TINYTEXT [BINARY] ^ charset_name] [COLLATE collation_name] | TEXT [BINARY] [CHARACTER SET charset_name] [COLLATE collation_name] | MEDIUMTEXT [BINARY] [CHARACTER SET charset_name] [COLLATE collation_name] | LONGTEXT [BINARY] ^ charset_name] [COLLATE collation_name] | ENUM(value1,value2,value3,...) [CHARACTER SET charset_name] [COLLATE collation_name] | SET(value1,value2,value3,...) [CHARACTER SET charset_name] [COLLATE collation_name] | spatial_type index_col_name: col_name [(length)] [ASC | DESC] index_type: ^ reference_definition: REFERENCES tbl_name [(index_col_name,...)] [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE] [ON DELETE reference_option] [ON UPDATE reference_option] reference_option: ^ table_option: {ENGINE|TYPE} [=] engine_name | AUTO_INCREMENT [=] value | AVG_ROW_LENGTH [=] value | [DEFAULT] CHARACTER SET [=] charset_name | CHECKSUM [=] {0 | 1} | [DEFAULT] COLLATE [=] collation_name | COMMENT [=] 'string' | CONNECTION [=] 'connect_string' | DATA DIRECTORY [=] 'absolute path to directory' | DELAY_KEY_WRITE [=] {0 | 1} | INDEX DIRECTORY [=] 'absolute path to directory' | INSERT_METHOD [=] { NO | FIRST | LAST } | MAX_ROWS [=] value | MIN_ROWS [=] value | PACK_KEYS [=] {0 | 1 | DEFAULT} | PASSWORD [=] 'string' | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT} | UNION [=] (tbl_name[,tbl_name]...) select_statement: ^ Some legal select statement) DROP DATABASE: DROP {DATABASE | SCHEMA} [IF EXISTS] db_name DROP INDEX: DROP INDEX index_name ON tbl_name DROP TABLE: DROP [TEMPORARY] TABLE [IF EXISTS] tbl_name [, tbl_name] ... ^ RENAME TABLE: RENAME TABLE tbl_name TO new_tbl_name [, tbl_name2 TO new_tbl_name2] ... Сейчас перейдем непосредственно к структуре хранимых процедур и функциий: Хранимые процедуры и функции являются множествами SQL операторов, они могут быть сохранены на сервере и применяться многократно. Хранимые процедуры и функции задаются операторами CREATE PROCEDURE и CREATE FUNCTION соответственно, вызываются с помощью оператора CALL. CREATE PROCEDURE и CREATE FUNCTION: CREATE ^ user | CURRENT_USER }] PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_body CREATE [DEFINER = { user | CURRENT_USER }] FUNCTION sp_name ([func_parameter[,...]]) RETURNS type [characteristic ...] routine_body proc_parameter: [ IN | OUT | INOUT ] param_name type func_parameter: param_name type type: Any valid MySQL data type characteristic: ^ | [NOT] DETERMINISTIC | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string' routine_body: Valid SQL procedure statement ALTER PROCEDURE и ALTER FUNCTION ^ sp_name [characteristic ...] characteristic: { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string' DROP PROCEDURE и DROP FUNCTION ^ sp_name CALL CALL sp_name([parameter[,...]]) CALL sp_name[()] BEGIN … END [begin_label:] BEGIN [statement_list] END [end_label] DECLARE //Тут объявляются переменные, см. переменные определенные пользователем. DECLARE локальных переменных DECLARE var_name[,...] type [DEFAULT value] SET SET var_name = expr [, var_name = expr] ... ^ SELECT col_name[,...] INTO var_name[,...] table_expr DECLARE условный DECLARE condition_name CONDITION FOR condition_value condition_value: SQLSTATE [VALUE] sqlstate_value | mysql_error_code ^ DECLARE handler_type HANDLER FOR condition_value[,...] statement handler_type: CONTINUE | EXIT | UNDO condition_value: SQLSTATE [VALUE] sqlstate_value | condition_name | SQLWARNING | NOT FOUND | SQLEXCEPTION | mysql_error_code CURSORS DECLARE cursor_name^ select_statement OPEN cursor_name FETCH cursor_name INTO var_name [, var_name] ... CLOSE cursor_name Конструкторы потока управления: IF IF search_condition THEN statement_list [ELSEIF search_condition THEN statement_list] ... [ELSE statement_list] END IF CASE CASE case_value WHEN when_value THEN statement_list [WHEN when_value THEN statement_list] ... [ELSE statement_list] END CASE Or: CASE WHEN search_condition THEN statement_list [WHEN search_condition THEN statement_list] ... [ELSE statement_list] END CASE LOOP [begin_label:] LOOP statement_list END LOOP [end_label] LEAVE LEAVE label ITERATE ITERATE label REPEAT [begin_label:] REPEAT statement_list UNTIL search_condition ^ end_label] WHILE [begin_label:] WHILE search_condition DO statement_list END WHILE [end_label] Вот пример небольшой процедуры MySQL: mysql> delimiter // mysql> CREATE PROCEDURE simpleproc (OUT param1 INT) -> BEGIN -> SELECT COUNT(*) INTO param1 FROM t; -> END; -> // Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> CALL simpleproc(@a); Query OK, 0 rows affected (0.00 sec) mysql> SELECT @a; +------+ | @a | +------+ | 3 | +------+ 1 row in set (0.00 sec) Более подробно с синтаксисом и особенностьями MySQL можно познакомится в [11]. f) BPEL TODO:тут надо разобраться с форматированием текста, а то каша какая-то Сейчас зададим архитектуру реализации, то, каким образом в BPEL можно реализовать вышеописанные конструкции. В терминах BPEL 1 бизнес-процесс у нас 1 хранимая процедура. Вместо указания http://localhost:1507/Service1.asmх и далее, к примеру, операции HelloWorld, будет указание в духе mysql://root:pass@localhost:3306/ksn и далее хранимая процедура. Подробнее в [9]. Основная сущность – процесс (process), в нашем случай он один web-service или одна хранимая процедура. Началоа процесса = recieve pick (with an OnMessage event) |flow (containing one or more Receive or Pick activities) // при этом recieve|pick с которого начинается должны иметь createInstance=Yes Середина = activities, running in sequence or concurrently. Конец = An activity defines that the process is complete|A fault reaches a process scope, and the process exits ^ Замечание: Там, где упоминаются фразы "вложенные ноды" и "содержит в себе" – имеется в виду, что элемент является контейнером и может в себе содержать что-то. X)partnerLinks: ей принадлежит partnerLink . Просто описания линков. Аттрибуты: name, partnerLinkType, partnerRole, myRole, condition (т.е. линки обязательно имеют название) Краткая расшифровка. Подразумевается, что в репозитарии хранится таблица partnerLinks, её элементы -- partnerLink. В редакторе они должны отображаться в специальном боковом окошке для работы с ними. X)variables : ей принадлежит variable Аттрибуты: name, type, messageType Могут иметь внутри себя иИнициализирующее значение – ноду. from может иметь внутри себя literal со значением внутри себя. Для упрощения это следует реализовать как аттрибут from который может иметь просто текстовое значение. X)faultHandlers: catch, catchAll элементы (порядок важен). Для faultHandlers необходима отдельная канва. Т.е. отдельная диаграмма в рамках того же процесса. Аттрибуты: faultMessageType, faultName, faultVariable, [comment] Является контейнером. ^ Необходма отдельная канва. X) Compenstaion Handler: Необходима отдельная канва. X) Termination Handler: Необходима отдельная канва. X)flow: -- содержит в себе основную бизнес-логику приложения. Основная канва. Содержит links и активности. links содержит элементы link link name. Т.е. нужно сделать возможность соединения линками элементов. Линки имеют имена и они направленные. Из каждого элемента могут исходить неограниченное и входить тоже неограниченное количест-вол линков. ^ [suppressJoinFailure,comment, joinCondition], name. Каждый аттрибут может содержать элементы sources, targets. Это нужно для поддержки линков. ^ Внутри sources и targets каждого элемента описаны source и target соответсвенно. У каждого из них в свою очередь есть параметр linkName -- имя ребра. А имена рёбер заданы пунктом выше. ^ Во всех контейнерах если поместить в них несколько кубиков и не соединить их, то порядок в котором они будут исполняться не определён. Поэтому на первое время мы договариваемся, что в каждом контейнере есть первый кубик, а остальные соединены цепочкой за ним. Это не касается кубика Sequence -- в нём все кубики выполняются строго в порядке следования и соединений между ними нету. По-умолчанию элементы не контейнеры. ^ 1. recieve -- контейнер Аттрибуты: [createInstance], operation partnerLink portType variable вместо variable может содержать FromPart (см. ниже). 2. invoke -- контейнер Аттрибуты: inputVariable operation [outputVariable] partnerLink portType Может содержать toParts/fromParts -- чтобы создавать/расформировывать структуры, когда сообщение состоит из нескольких частей. input заменяется на toParts, output -- FromParts 3. reply -- контейнер Аттрибуты: operation partnerLink portType variable [faultName] вместо variable может содержать toPart 4. assign --– контейнер. содержит один или несколько Copy copy аттрибуты: fromPart fromVariable fromExpression toPart toVariable toExpression 5. Throw Аттрибут [faultVariable] 6. rethrow 7. exit 8. empty 9. if -- контейнер с двумя отсеками (if и else) аттрибуты: condition conditionExpressionLanguage 10. sequence -- контейнер. Для того что в нём лежит определён порядок в котором оно лежит. см. выше. 11. flow -- контейнер. 12. while -- контейнер аттрибуты: condition, conditionExpressionLanguage 13. pick -- контейнер. 14. ext:break 15. ext:continue Используемые ноды в контейнерах: toParts (контейнер) -- содержит один или несколько toPart toPart Аттрибуты: fromVariable part. fromParts (контейнер) -- содержит один или несколько fromPart fromPart Аттрибуты: toVariable part. . InvokeSQL (Вызов SQL) . Link (Логическая свяь, в ней может быть нагрузка -- например проверка условий, операции над переменными). . Catch -- обработка ошибок, . While и иные циклы . If и иные условия g) WSDL Структура WSDL проста, для представления достаточно привести небольшой пример, простой web-service, который описывается в WSDL. public struct TestStruct { public int id; public string message; public int[] ids; public string[] messages; public double pi; } [WebMethod] public TestStruct HelloWorld(int id, string message) { } вот и WSDL описание: Основная информация содержится в тэеге Мы описали краткую структуру всех составляющих компонентов, теперь зададим архитеуктуру их взаимодействия между собой. Подробнее про WSDL см. [7]. h) Взаимодействие разных компонентов 4 Область применения a)Обзор MVC парадигмы До того, как перейдем к реализации, было бы разумно сделать обзор возникнове- ния этой идеи, возможного практического применения (на примере конкретных приклад- них проектов информационных систем). Общеизвестна сущность MVC (Model View Controller) парадигмы. Когда модель данных приложения, пользовательский интерфейс и управляющая логика разделены на три отдельных компонента, так, что модификация одного из компонентов оказывает минимальное воздействие на другие компоненты. минимальное воздействие на другие компоненты. Шаблон MVC позволяет разделить данные, представление и обработку действий пользователя на три отдельных компонента. пользователя на три отдельных компонента.
реагирует на запросы (обычно от контролера ), изменяя свое состояние . реагирует на запросы (обычно от контроллера ), изменяя свое состояние.
интерфейс).
информирует модель и представление о необходимости соответствующей реакции. ![]() Важно отметить, что как представление, так и поведение зависят от модели. Однако модель не зависит ни от представления, ни от поведения. Это одно из ключевых достоинств подобного разделения. Оно позволяет строить модель независимо от визуального представления. В классическом случай модель -- это база данных, поведение – бизнес-логика, а пред- ставлением является клиентское приложение. b) MVC парадигма с особенностью ИИдея написания кодогенераторов в MySQL и C#.Net (см. дипмломная работа Сеппель 2008г.[6]) возникла от из прикладного проекта, который разрабоатывался по MVC схе- ме. Но имеются особенности, первая из них заключается в том, что все запросы, измене- ния к БД осуществляются через хранимые процедуры. Вместо хранения часто используемого запроса, клиенты могут ссылаться на соответствующую хранимую процедуру. При вызове хранимой процедуры её содержимое сразу же обрабатывается сервером. Кроме собственно выполнения запроса, хранимые процедуры позволяют также производить вычисления и манипуляцию данными - изменение, удаление, выполнять DDL-операторы и вызывать другие хранимые процедуры, выполнять сложную тран-закционную логику. Один-единственный оператор позволяет вызвать сложный сценарий, который содержится в хранимой процедуре, что позволяет избежать пересылки через сеть сотен команд и, в особенности, необходимости передачи больших объёмов данных с кли-ента на сервер. В большинстве СУБД при первом запуске хранимой процедуры она компилируется (выполняется синтаксический анализ и генерируется план доступа к данным). В дальне-йшем её обработка осуществляется быстрее. Созданную хранимую процедуру можно вызвать в любой момент, что обеспечивает модульность и стимулирует повторное использование кода. Последнее облегчает сопровождение базы данных, так как она становится изолированной от меняющихся бизнес-правил. Модифицировать хранимую процедуру в соответствии с новыми правилами можно в любой момент. После этого все приложения, использующие её, автоматически придут в соответствие с новыми бизнес-правилами без непосредственной модификации. С использованием такого подхода, база данных, с одной стороны, модель, превра- щается в часть представлении(Controller), ибо через его же состоавляющегоею (в виде хра- нимых процедур) осуществляется имзменение модели и получение данных от него.хранимые процедуры являются частью представления (Controller). Продемонстрируем небольшой пример, для лучшего ознакомления с подробнос-тиями архитектуры. Допрустим в БД имеется таблица, для хранения данных о зарегистрированных пользователейях: CREATE TABLE `siss_staff` ( `id` int(3) NOT NULL auto_increment, //id `fio` varchar(40) NOT NULL, //ФИО `type` enum('administrator','operator') default 'operator' not Null, //Админ или оператор `identification_date` datetime default NULL, //Когда зарегистрирован. `password` varchar(100) default NULL, //Пароль `login` varchar(25) default NULL, //Имя в системе UNIQUE KEY `id` (`id`), UNIQUE KEY `id_2` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; Рассмотрим хранимые процедуры, для идентификации пользователя и для добавления Пользователя в системе. create procedure logon(user_id int, pass varchar(20), rmt_id int, phone varchar(20)) begin select password into @pass from siss_staff where user_id = siss_staff.id; set @f = (password(pass) = @pass); if @f = 1 then set @a := rand(); set @x := @a * 10000000000; set @b := round(@x); set @c := left(cast(@b as char), 3); insert into session values(null, rmt_id, user_id, @c, phone, now()); select session_id, session_key from session order by session_id desc limit 1; end if; end if; end Когда клиент просит запрашивает идентификацию в системе, бизнес-логика вызывает данную процедуру с соответствующими параметрами, в случаей правильной идентификации регис- трируется новая сессия ( insert into session values(null, rmt_id, user_id, @c, phone, now()) ) и возвращается строка id сессии и ключ сессии(который случайно генерируется в теле процедуры) для сеанса. Если уже зарегистрированный пользователь, имеющийи соответствующие полно- мочия, просит зарегистрировать новогоый пользователья системы, то вызывается данная про- цедура: create procedure register(fio varchar(25), type enum('administartor', 'operator'), password varchar(20)) begin insert into siss_staff values(null, fio, type, now(), password(password), null); select last_insert_id() as 'id'; insert into called_procedures_list values(null, 'register', now(), null); select max(id) into @max_id from called_procedures_list; insert into parameters_list values(@max_id, 'fio', fio); insert into parameters_list values(@max_id, 'type', type); end И в системе (в таблице siss_staff) появляется новый пользователь с указанным ФИО, полномочиями(оператор или админристратор) и пароль. Соответственно, результат проделанной работы может применяться в информацион- ных проектах, основанных на вышеописанный подход. 5 Реализация a) Обзор В качестве алгоритма кодогенерации выбрали, так называемую, “кодогенерацию в лоб”. Кодогенераторы хранимых процедур и веб-сервисов унаследуются от базового класса public abstract class GenericCodegen : Idisposable. Где изложены базовые методы и данные, такие как, protected RepoClientIcePrx repoClient(для поддержки взаимодействия между C# и C++), protected List<StreamWriter> swrs(коллекция потокок для чтения и записи файлов), public void Dispose() (для освобождения удерживаемых ресурсов), конструктор, деструктор и т.д. За генерацию хранимых процедур отвечает класс public class CodegenSQL: GenericCodegen. В нем задаются такие параметры, как директория выходного файла, имя процедуры и т.д. Дается структура аргументов процедуры(как имя и тип) со своим конструктором. Дается структура для описания самой процеуры как имя, и связанная с ним диаграмма в репозитории(public RealObjectIcePrx diagram). Примитивные методы, наподобие public void BeginService(), protected void createSQL(), public void EndProcedure(), public void createProcedure() и т.д. Добавляют в начале и в конце файла, содержащего процедуру, специальные строчки (к примеру, delimiter // или delimiter ;) public List Обращаем внимание на метод protected void writeBody(), который способствует формированию тела процедуры. Дадим скелет описания генератора на языке C#: using System; using System.IO; using RepoIce; using System.Collections.Generic; namespace RealCodegen { public class CodegenSQL : GenericCodegen { //swrs[0] -- .sql protected string procedureName; protected string nameSpace; protected List // Описание аргумента процедуры public struct Argument { public string type; public string name; public Argument(string type, string name) { this.type = type; this.name = name; } } //описание процедуры в репозитарии public struct Procedure { public string name; public RealObjectIcePrx diagram; public Procedure(string name, RealObjectIcePrx diagram) { this.name = name; this.diagram = diagram; } } public CodegenSQL(RepoClientIcePrx repoClient, string procedureName, string dirname, string nameSpace) : base(repoClient, dirname) { this.procedureName = procedureName; this.nameSpace = nameSpace; } public void BeginService() { DirectoryInfo dir = new DirectoryInfo("."); dir.CreateSubdirectory(dirname); FileInfo fileInfo = new FileInfo(dirname + "/" + procedureName + ".sql"); swrs.Add(fileInfo.CreateText()); } protected void createSQL() { swrs[0].WriteLine("delimiter //"); swrs[0].WriteLine(""); swrs[0].Write("create procedure " + procedureName); } public void EndProcedure() { swrs[0].WriteLine("end//"); swrs[0].WriteLine(""); swrs[0].WriteLine("delimiter ;"); swrs[0].WriteLine("\n}"); } public void createProcedure(string procedureName, string returnType, Argument[] arguments) { swrs[0].Write(" public " + returnType + " " + procedureName + " ("); for (int i = 0; i < arguments.Length; i++) { swrs[1].Write(arguments[i].type + " " + arguments[i].modifier + " " + arguments[i].name); if (i != arguments.Length - 1) swrs[0].Write(", "); } swrs[0].WriteLine(")"); writeBody(); swrs[0].WriteLine(""); } protected void writeBody() { swrs[0].WriteLine(" return 0;"); } public List findProcedures() { List list = new List (); int[] idiagrams = repoClient.getObjectsListByType(repoClient.getTypeIdByName("krnnDiagram")); foreach (int idiagram in idiagrams) { RealObjectIcePrx diagram = repoClient.getObjectById(idiagram); Procedure procedure = new Procedure(diagram.getName(), diagram); Console.WriteLine("Found Dia: " + diagram.getName() + " with id: " + idiagram + " WITH ID: " + diagram.getId() + " Desc: " + diagram.getDescription() + " Language: " + diagram.getProperty("Language")); //TODO: ещё тут надо проверять, правда ли что это диаграмма описывающая сервис а не типы данных //if (diagram.getProperty("Language") == "BPEL") list.Add(method); } return list; } public override void generateProcedures() { BeginService(); //обходим все диаграммы в репозитарии, ищем из них те, которые содержат описания процедур List procedures = findProcedures(); //для каждой такой диаграммы проводим кодогенерацию процедуры foreach (Procedure procedure in procedures) { //Console.WriteLine("found: " + method.name); generateProcedure(procedure); } EndService(); } protected void generateProcedure(Procedure procedure) { //createMethod(method.name, string returnType, Argument[] arguments) } } } b)WSDL парсер Рассмотрим парсер на примере простейшего веб-метода: public struct TestStruct { public int id; public string message; public int[] ids; public string[] messages; public double pi; } [WebMethod] public TestStruct HelloWorld(int id, string message) { } Парсер – класс на C++, созданный исключительно на основе библиотеки qt. Конструктору класса передается WSDL файл и строка, указывающая на то, каким инструментом сгенерирован этот файл(Eclipse или .Net). Основной метод WSDLData parse() выводит информацию о типах данных и методах, описанных в WSDL. WSDLData имеет следующую структуру: class WSDLData{ public: map1 m1; map2 m2; }; Где map1 и map2: typedef QMap typedef QMap В QmapDataSetting хранится информация о типах данных: class QMapDataSettings{ public: QString type; //Тип int isCompound; //Сложный или примитивный. примитивные -- int,string,double, //etc. Сложные -- массивы, структуры. QString xmlDefinition; //XML описание, в случае сложного … }; В QmapDataSetting хранится информация о веб-сервисах: class QMapServiceSettings{ public: QString type; //Тип возвращаемого элемента QStringList parameters; //Список параметров … }; Метод WSDLData parse() получает из WSDL файлов информацию указанного вида(для типов данных – название типа данных, сложный или нет, XML описание в случае сложного типа. Для веб-сервисов – название, название типа возвращаемого значения и список названии типов параметров, в заданном порядке). 6) Заключение В данной работе были получены следующие результаты:
Список литературы:
|