510 likes | 710 Views
Технология ORM и. её реализации. Что такое ORM?. ORM (Object-relational mapping) —. технология программирования, которая связывает базы данных с. концепциями объектно-ориентированных языков программирования, создавая «виртуальную объектную базу данных». Что такое ORM?.
E N D
ТехнологияORMи еёреализации
ЧтотакоеORM? ORM(Object-relationalmapping)— технологияпрограммирования,которая связываетбазыданныхс концепциямиобъектно-ориентированных языковпрограммирования,создавая «виртуальнуюобъектнуюбазуданных».
ЧтотакоеORM? ЗадачаORMсостоитвуправлениитрансляцией объектныхтиповвзаписибазданныхиобратно. Основнаяпроблемасостоитвтом,чтообъекты имеютиерархическуюструктуру,абазыданных— реляционную.
Классы Классыопределяютсущность Классымогутсодержатьданныеиметоды Классымогутнаследоватьданныеи интерфейсдругихклассов Вкачестведанныхклассымогут содержатьэкземплярыдругихклассов,в томчислесписки.
Базыданных ОсновнымэлементомБДявляется таблица Таблицымогутсодержатьтолькопростые типыданных Таблицынемогутсодержатьмассивыи списки Таблицымогутбытьсвязанывнешними ключами
ЗачемнужныORM? Ручноепреобразованиереляционныхданныхв объектыдостаточнотрудоѐмкийпроцесс, которыйведѐткувеличениючислаошибок. ORMберѐтнасебяоперациипо преобразованиюданныхвобъекты, абстрагируяпрограммистаотзнанияо конкретныхСУБД. Объектысостояниекоторыхможетбыть сохранено,азатемвосстановленоназываются хранимымиилиперсистентными(отангл. «persistent»—постоянный,устойчивый).
МинусыORMрешений Дополнительныйслойабстракцииможет сказатьсянапроизводительности. Решениепростыхзадачможетоказаться слишкомсложным. ORMрешениеможетнебытьдостаточно гибким. Дизайнсистемыможетоказаться зависимымотконкретнойORM- библиотеки.
NHibernate NHibernate—ORM-решениедля платформыMicrosoft.NET,портированное сJava. Этобесплатнаябиблиотекасоткрытым кодом,распространяетсяподлицензией GNULGPL. Текущаяверсия:3.1.0.
NHibernate NHабстрагируетвашеприложениеотлежащейв основеСУБДидиалектаязыкаSQL. ПоддерживаемыеСУБД: MicrosoftSQLServer Oracle MicrosoftAccess Firebird PostgreSQL DB2UDB MySQL SQLite
ИспользованиеNHibernate ПроцессиспользованияNHibernateсостоитиз четырѐхэтапов: Определениеконфигурацииподключенияк БД(типСУБД,SQL-диалект,строку подключения). Определениедоменныхклассов,которые будутотображатьсянатаблицыБД. НаписаниеконфигурационныхXMLфайлов, осуществляющихотображение(mapping) реляционнойструктурыбазыданныхна доменныеклассывашегоприложения. ПодключениекБДиманипуляцияданнымив терминахдоменныхклассов.
ИспользованиеNHibernate Управлениепараметрамиподключенияможет осуществлятьсяспомощьюобъекта Configurationследующимиспособами: ВызовамиметодаSetProperty. ОпределениемконфигурационногоXMLфайлас именемвида*.cfg.xmlизагрузкойегометодом Configure. МетодConfigureможетбытьвызванбез параметра,чтоуказываетнанеобходимость загрузкиконфигурационногофайласименем hibernate.cfg.xml
ИспользованиеNHibernate Configurationconfiguration=newConfiguration(); configuration.Configure();
ИспользованиеNHibernate КонфигурационныеXMLфайлы, осуществляющиеотображение, должныиметьимявида*.hbm.xmlи подключатьсякпроектувкачестве EmbeddedResource. Загрузкаmapping-файлов осуществляетсявызовомметода AddAssemblyклассаConfiguration: Configurationconfiguration= newConfiguration(); configuration.Configure(); configuration.AddAssembly("NHibernateDemo");
NHibernate.Пример. Рассмотримпростойпример. Схемабазыданныхимеетследующийвид:
NHibernate.Пример. Самыйпростойкласснашегодоменаимеетследующийвид: namespaceBooks.Domain { publicclassLanguage { publicvirtualintId {get;set;} publicvirtualstringName {get;set;} } } Отметим,чтосвойствакласса,которыеотображаютсяна колонкитаблиц,должныбытьвиртуальными(особенность реализацииNH).
NHibernate.Пример. ЧастьXML-файла,котораяотвечаетзаотображение данныхизтаблицыLanguageнакласс Books.Domain.Language,имеетследующийвид: <classname="Books.Domain.Language,NHibernateDemo" table="Language"> <idname="Id"type="System.Int32"> <columnname="Id"not-null="true"/> <generatorclass="identity"/> </id> <propertyname="Name"column="Name"/> </class>
NHibernate.Пример. Ключевымиэлементамиконфигурации являются: <class>―связываетхранимыйклассс таблицейБД. <id>―связываетсвойствоклассас ключевойколонкойтаблицыБД. <property>―связываетсвойствоклассас колонкойтаблицыБД.
NHibernate.Пример. ПерейдемкклассуAuthor.Обратимвнимание,что сущностиBookиAuthorнаходятсявотношении «многиекомногим»: namespaceBooks.Domain { publicclassAuthor { publicvirtualintId{get;set;} publicvirtualstringFirstName{get;set;} publicvirtualstringLastName{get;set;} publicvirtualintYearOfBirth{get;set;} publicvirtualIesi.Collections.Generic.ISet<Book>Books {get;set;} } }
NHibernate.Пример. НастройкаотображениятаблицыAuthorна соответствующийклассбудетвыглядетьтак: <classname="Books.Domain.Author,NHibernateDemo"table="Author"> <idname="Id"type="System.Int32"> <columnname="Id"not-null="true"/> <generatorclass="identity"/> </id> <propertyname="FirstName"column="FirstName"/> <propertyname="LastName"column="LastName"/> <propertyname="YearOfBirth"type="System.Int32" column="YearOfBirth"/> <setname="Books"table="BookAuthor"> <keycolumn="AuthorId"></key> <many-to-manyclass="Books.Domain.Book,NHibernateDemo" column="BookId"></many-to-many> </set> </class>
NHibernate.Пример. NHibernateпредоставляетнаборатрибутовдляотображения связеймеждутаблицамивсписки. <set>―управляетотображениемданныхвсвойстватипаISet илиISet<T>(коллекциибезотношенияпорядка,не допускающиедубликаты). <list>―управляетотображениемданныхвсвойстватипа IListилиIList<T>(коллекциисотношениемпорядка, допускающиедубликаты). КрометогоNHibenateподдерживаетсвязитипа: многиекомногим; одинкомногим; многиекодному.
NHibernate.Пример. РассмотримклассBook,которыйимеетвнешнийключ натаблицуLanguage: namespaceBooks.Domain { publicclassBook { publicvirtualintId{get;set;} publicvirtualstringTitle{get;set;} publicvirtualLanguageOriginalLanguage {get;set;} } }
NHibernate.Пример. НастройкаотображениятаблицыBookна соответствующийклассбудетвыглядетьтак: <classname="Books.Domain.Book,NHibernateDemo"table="Book"> <idname="Id"type="System.Int32"> <columnname="Id"not-null="true"/> <generatorclass="identity"/> </id> <propertyname="Title"column="Title"/> <many-to-onename="OriginalLanguage" column="OriginalLanguageId" class="Books.Domain.Language,NHibernateDemo"/> </class>
NHibernate.Пример. Теперьрассмотрим,какпослеопределения отображениятаблицнадоменныеклассы оперироватьсданнымивэтихтаблицах. КлючевымисущностямиNHibernateявляются: ISessionFactory―объект,создаваемыйв одномэкземпляренабазуданных. ISession―ключевойобъектдляоперацийнад данными.Позволяетполучатьисохранять информацию. ITransaction―инкапсулируеттранзакциибазы данных.
Началоработы Передтемкакначатьоперациисбазойданных, необходимокнейподключиться.Дляэтогопотребуется определитьконтекстдляNHibernate.Сделаемэто определивконфигурационныйфайлNHibernate: <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"> <session-factory> <propertyname="connection.driver_class"> NHibernate.Driver.SqlClientDriver </property> <propertyname="connection.connection_string"> Server=(local);initialcatalog=books_nhibernate </property> <propertyname="dialect"> NHibernate.Dialect.MsSql2008Dialect </property> </session-factory> </hibernate-configuration>
NHibernate.Пример. Рассмотримсозданиепростейшегоприложения,котороевыводит списоквсехкниг,имеющихсявбазеданных: publicvoidShowBooks() { Configurationconfiguration=newConfiguration(); configuration.Configure(); configuration.AddAssembly("NHiberanteDemo"); ISessionFactoryfactory=configuration.BuildSessionFactory(); ISessionsession=factory.OpenSession(); //создаёмзапрос IQueryquery=session.CreateQuery("fromBook"); //выполняемзапросиполучаемданные IListbooks=query.List(); foreach(Bookbookinbooks) Console.WriteLine(book.Title); }
ПостроениезапросовкБД ДляпостроениязапросовкБДNHibernate предоставляетнесколькомеханизмов: CriteriaAPI HQL QBE
CriteriaAPI РассмотримпримерзапросакБДспомощьюCriteria API: publicvoidShowBooks() { Configurationconfiguration=newConfiguration(); configuration.Configure(); configuration.AddAssembly("NHiberanteDemo"); ISessionFactoryfactory=configuration.BuildSessionFactory(); ISessionsession=factory.OpenSession(); ICriteriacriteria=session.CreateCriteria(typeof(Book)); criteria.SetMaxResults(40); IListbooks=criteria.List(); foreach(Bookbookinbooks) Console.WriteLine(book.Title); IListbooksStartWithA= session.CreateCriteria(typeof(Book)) .Add(Restrictions.Like("Title","A%")) .List(); foreach(BookbookinbooksStartWithA) Console.WriteLine(book.Title); }
HQL HQL(HibernateQueryLanguage)―SQL-подобныйязык запросов,используемыйвбиблиотекеHibernate. РассмотримпримерыиспользованияHQLдля полученияданных: publicvoidShowNumberOfBooks() { Configurationconfiguration=newConfiguration(); configuration.Configure(); configuration.AddAssembly("NHiberanteDemo"); ISessionFactoryfactory=configuration.BuildSessionFactory(); ISessionsession=factory.OpenSession(); IQueryquery=session.CreateQuery("selectcount(*)fromBook"); intbookCount=(int)query.UniqueResult(); Console.WriteLine(bookCount); }
QBE QBE(QueryByExample)―механизмполучениягруппы объектовпохожихнапредоставленныйобъект. Примериспользования: publicvoidQBEExample(ISessionsession) { AuthorexampleAuthor=newAuthor(); exampleAuthor.FirstName="Лев"; exampleAuthor.LastName="Тостой"; IListresults= session.CreateCriteria(typeof(Author)) .Add(Example.Create(exampleAuthor)) .List(); foreach(Authorauthorinresults) Console.WriteLine(author.FirstName+""+author.LastName); }
Модификацияданных Модификацияданныхосуществляетсяприпомощиобъектасессии имеханизматранзакций: publicvoidAddAuthorAndBook(Configurationconfiguration) { ISessionFactoryfactory=configuration.BuildSessionFactory(); ISessionsession=factory.OpenSession(); ITransactiontransaction=session.BeginTransaction(); BooknewBook=newBook(); newBook.Title="Надпропастьюворжи"; session.Save(newBook); AuthornewAuthor=newAuthor(); newAuthor.FirstName="Джером"; newAuthor.LastName="Сэлинджер"; newAuthor.Books.Add(newBook); session.Save(newAuthor); transaction.Commit(); session.Close(); }
Doctrine Doctrine—ORM-решениедляязыкаPHP. ОднойизключевыхвозможностейDoctrine являетсязаписьзапросовкБДнасобственном объектно-ориентированномдиалектеDQL (DoctrineQueryLanguage),которыйбазируется наидеяхHQL. Начинаясверсии2.0,требуетPHP5.3+. Текущаяверсия:2.0.1
Doctrine.Началоработы ПроцессиспользованияDoctrineсостоитиз следующихэтапов: Определениеконфигурацииподключения кБД. Определениедоменныхклассов,которые будутотображатьсянатаблицыБД. Написаниеконфигурационныхmapping файлов(XMLилиYAML). ПодключениекБДиманипуляция даннымивтерминахдоменныхклассов.
КонфигурацияDoctrine Процессконфигурированиясостоитиз несколькихчастей: Запускавтозагрузкиклассов. Определениекаталогадлягенерации Proxy-классов. ОпределениетипаMapping-файлов. Определениепараметровподключения.
КонфигурацияDoctrine Запускавтозагрузки классовиопределение каталогадлягенерации Proxy-классов: $classLoader=new\Doctrine\Common\ClassLoader('Doctrine','doctrine-orm'); $classLoader->register(); $config=newDoctrine\ORM\Configuration(); $config->setProxyDir('proxies'); $config->setProxyNamespace('DoctrineDemo\Proxies'); $config->setAutoGenerateProxyClasses(true);
КонфигурацияDoctrine Автозагрузкаклассов—этоноваявозможностьPHP5,которая позволяетподключатьфайлысклассамипомереих необходимости.Этавозможностьреализуетсяпутѐмрегистрации callback-функции,котораявызывается,еслибылобнаруженне определѐнныйранеекласс. Следующаястрокасоздаѐткласс,которыйотвечаетзазагрузку классовDoctrine: $classLoader=new\Doctrine\Common\ClassLoader('Doctrine','doctrine-orm'); Первымпараметромконструкторклассапринимаетпространство имѐн,вкоторомнаходятсяклассыDoctrine.Вторымпараметром являетсяпуть,покоторомунаходитсябиблиотека. Следующийвызоврегистрируетcallback-функциюзагрузки классов,предоставляемуюуказаннымвышеклассом: $classLoader->register();
КонфигурацияDoctrine Дляреализации«ленивой»загрузкиданныхDoctrineгенерирует специальныеproxy-классы,которыезагружаютданныепомере обращенияксвойствамсоответствующихдоменныхклассов. Конфигурациятребуетопределитькаталог,вкоторыйбудут генерироватьсяproxy-классы,ипространствоимѐн,вкоторомони будутнаходиться: $config->setProxyDir('proxies'); $config->setProxyNamespace('DoctrineDemo\Proxies'); Следующеесвойствоопределяетбудутлиproxy-классы генерироватьсякаждыйраз,когдавнихвозникаетнеобходимость илинет: $config->setAutoGenerateProxyClasses(true); Этосвойствонеобходимоустановитьвfalse,приразвертывании приложения,чтобыизбежатьнакладныхрасходовсвязанныхс генерациейproxy-классов.
КонфигурацияDoctrine DoctrineподдерживаетMapping-файлывформатах XMLиYAML. YAML(YAMLAin'tMarkupLanguage)—человекочитаемый форматсериализацииданных. Соответствующиетипыопределяютсяследующим образом: //ОпределениеXMLMappingфайлов $driverImpl= newDoctrine\ORM\Mapping\Driver\XmlDriver("mappings/xml"); //ОпределениеYAMLMappingфайлов $driverImpl= newDoctrine\ORM\Mapping\Driver\YamlDriver("mappings/yml"); $config->setMetadataDriverImpl($driverImpl);
КонфигурацияDoctrine Mapping-файлыдолжнынаходитсявуказанныхкаталогах. ИменаMapping-файловдляXMLиYAMLформатовдолжны быть*.dcm.xmlи*.dcm.ymlсоответственно.
YAML Языкизначальнобылзадуманкакязыкразметкиидажерассматривался какконкурентXML,однакопозжесталиспользоватьсявосновномкак форматконфигурационныхфайлов.СинтаксисYAMLминимален,особенно посравнениюссинтаксисомXML. РассмотримXML-файл,которыйхранитсвойстваподключенийкБД: <?xmlversion="1.0"encoding="utf-8"?> <connections> <connectionname="connection01"> <host>localhost</host> <database>MyDB</database> <password>root</password> <login>root</login> </connection> <connectionname="connection02"> <host>192.168.0.1</host> <database>production</database> <password>sb593f4s</password> <login>ds8(dg#1a</login> </connection> </connections>
YAML ДревовиднаяструктураYAMLопределяетсяприпомощиотступов. АналогичнаяинформацияспомощьюYAMLможетбытьзаписана следующимобразом: -connection:connection01 host:localhost database:MyDB password:root login:root -connection:connection02 host:192.168.0.1 database:production password:sb593f4s login:ds8(dg#1a Болееподробнооязыке:http://yaml.org/
КонфигурацияDoctrine Определениепараметровподключения: $connection=array( 'driver'=>'pdo_mysql', 'host'=>'localhost', 'dbname'=>'doctrine_demo', 'user'=>'root', 'password'=>'root' ); $entityManager=\Doctrine\ORM\EntityManager::create($connection,$config); Doctrineподдерживаетследующиетипыдраверов: pdo_mysql pdo_sqlite pdo_pgsql pdo_oci oci8
Doctrine.Пример Определимдоменныеклассы.Класскнигибудет выглядетьследующимобразом: classBook { private$id; private$title; private$originalLanguage; publicfunctiongetId(){return$this->id;} publicfunctionsetId($id){$this->id=$id;} publicfunctiongetTitle(){return$this->title;} publicfunctionsetTitle($title){$this->title=$title;} publicfunctiongetOriginalLanguage() {return$this->originalLanguage;} publicfunctionsetOriginalLanguage($originalLanguage) {$this->originalLanguage=$originalLanguage;} }
Doctrine.Пример Класс,определяющийсущностьязыка: classLanguage { private$id; private$name; publicfunctiongetId(){return$this->id;} publicfunctionsetId($id){$this->id=$id;} publicfunctiongetName(){return$this->name;} publicfunctionsetName($name){$this->name=$name;} }
Doctrine.Пример Класс,определяющийавтора: classAuthor { private$id; private$firstName; private$lastName; private$yearOfBirth; private$books; publicfunction__construct() { $this->books=array(); } publicfunctiongetId(){return$this->id;} publicfunctionsetId($id){$this->id=$id;} //... publicfunctiongetBooks(){return$this->books;} }
Doctrine.Пример ОпределимXML-файл,отображающийсущностьязыка насоответствующуютаблицу: <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entityname="Language"table="language"> <idname="id"type="integer"> <generatorstrategy="AUTO"/> </id> <fieldname="name"type="string"/> </entity> </doctrine-mapping>
Doctrine.Пример АналогичныйYAMLфайлбудетследующим: Language: type:entity table:language id: id: type:integer generator: strategy:AUTO fields: name: type:string length:100
Doctrine.Пример ОпределимXML-файл,отображающийсущностькниги насоответствующуютаблицу: <doctrine-mapping...> <entityname="Book"table="book"> <idname="id"type="integer"> <generatorstrategy="AUTO"/> </id> <fieldname="title"type="string"/> <many-to-onefield="originalLanguage"target-entity="Language"> <join-columnname="original_language_id"referenced-column-name="id"/> </many-to-one> </entity> </doctrine-mapping>
Doctrine.Пример ОпределимXML-файл,отображающийсущностьавторана соответствующуютаблицу: <doctrine-mapping ...> <entityname="Author"table="author"> <idname="id"type="integer"> <generatorstrategy="AUTO"/> </id> <fieldname="firstName"column="first_name"type="string"/> <fieldname="lastName"column="last_name"type="string"/> <fieldname="yearOfBirth"column="year_of_birth"type="integer"/> <many-to-manyfield="books"target-entity="Book"> <join-tablename="book_author"> <join-columns> <join-columnname="author_id"referenced-column-name="id"/> </join-columns> <inverse-join-columns> <join-columnname="book_id"referenced-column-name="id"/> </inverse-join-columns> </join-table> </many-to-many> </entity> </doctrine-mapping>
Doctrine.Пример Рассмотримпростейшийслучайполученияданныхиз базы: $entityManager=\Doctrine\ORM\EntityManager::create($connection,$config); $author=$entityManager->find("Author",1); echo$author->getFirstName().''.$author->getLastName(); foreach($author->getBooks()as$book) echo$book->getTitle();
Doctrine.Пример Основныеманипуляциисданнымивыполняютсяпри помощиязыкаDQL: $booksQuery=$entityManager->createQuery( "SELECTbFROMBookbWHEREb.titleLIKE'В%'"); $booksQuery->setMaxResults(30); $books=$booksQuery->getResult(); foreach($booksas$book) echo$book->getTitle();