Слайд 2
План
ORM
NHibernate: Basics
NHibernate: Advanced
Что дальше?
Слайд 4
ORM
Object
Relational
Mapping
реляционная БД язык программирования
Слайд 5
ORM только на .NET / Java? – нет!
С++
/ Java / .NET / Flex / Delphi /
Objective-C / Perl / PHP / Python / Ruby и даже VisualBasic 6.0
Альтернатива – no SQL решения
Слайд 6
Пример проекта
10 классов-моделей
Необходимо CRUD приложение
Некоторые сущности связаны
Некоторые поля
сущностей «сложные»
В будущем нужен репортинг
Слайд 7
Вариант 1 – не используем ORM
… на самом
деле пишем «свой»
У нас больше контроля
Но мы пишем больше
кода
Больше шансов ошибиться
Слайд 8
Вариант 2 – используем ORM
Используем уже существующие решения
Ограничены
возможностями библиотеки
Пишем больше «конфигурации»
Пишем меньше кода
Меньше шансов ошибиться
Слайд 9
Вывод
Не использовать готовое – не вариант
Вариант – использовать
лучшее из готового
Чистый SQL никто не отменяет
Слайд 11
Кратко о библиотеке
Оригинальная Java версия – Hibernate
Версия 3.2.0
Сайт
http://nhforge.org
Слайд 14
Сущности и их состояния
transient
persistent
detached
Слайд 15
Сами сущности
POCO classes
Свойства вместо полей
Конструктор по умолчанию
Видимость не
важна
Equals / GetHashCode
Слайд 16
Динамические модели
Можно обойтись без классов моделей
Dictionary
В мэппингах
– entity-name у класса
В конфиге - default_entity_mode
В коде –
у сессии EntityMode.Map
Слайд 18
Жизненный цикл
interface ILifecycle
void OnSave(ISession s, object id);
LifecycleVeto OnSave(ISession
s);
LifecycleVeto OnUpdate(ISession s);
LifecycleVeto OnDelete(ISession s);
Слайд 19
Валидация
IValidatable
void Validate();
ValidationFailure exception
Время вызова – не гарантируется
Слайд 21
Mapping through XML
Расширение .hbm.xml
Embedded resource
Регистрация через конфигурационный файл
Слайд 22
Дополнительные объекты БД
В mapping файле
В классе реализующем NHibernate.Mapping.IAuxiliaryDatabaseObject
Можно
параметризовать
Можно выполнять только для определенных диалектов БД
Слайд 23
CREATE TRIGGER my_trigger ...
DROP TRIGGER my_trigger
parameterValue
Слайд 24
Mapping Fluent NHbibernate (3.1)
Пишется код для формирования mapping
Конфигурируется
тоже через код
Примеры
Слайд 26
Конфигурация через hibernate.cfg.xml
NHibernate.Connection.DriverConnectionProvider
NHibernate.Driver.SqlClientDriver
Server=localhost;initial catalog=nhibernate;User Id=;Password=
false NHibernate.Dialect.MsSql2000Dialect resource="NHibernate.Auction.Item.hbm.xml" assembly="NHibernate.Auction" />
resource="NHibernate.Auction.Bid.hbm.xml" assembly="NHibernate.Auction" />
Слайд 27
Mapping через Fluent Nhibernate:
public class CatMap : ClassMap
{
public CatMap()
{
Id(x
=> x.Id);
Map(x => x.Name).Length(16).Not.Nullable();
Map(x => x.Sex);
References(x => x.Mate);
HasMany(x => x.Kittens);
}
}
Конфигурация:
Fluently.Configure().Database(SQLiteConfiguration
.Standard
.UsingFile("firstProject.db"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf
()) .BuildSessionFactory();
Слайд 28
Mapping via code NHbibernate (3.2)
Пишется код для формирования
mapping
Конфигурируется тоже через код
Примеры
Слайд 29
ModelMapper mapper = new ModelMapper();
mapper.Class(m =>
{
m.Id(k => k.Id, g=>g.Generator(Generators.Native));
m.Table("People");
m.Property(k
=> k.Name);
m.Bag(k => k.Addresses,
t => { t.Table("PeopleAddresses"); t.Key(c=>c.Column("PersonId"));
t.Inverse(true); },
rel => rel.ManyToMany(many => many.Column("AddressId")) );
});
mapper.Class
(m =>
{
m.Id(k => k.Id, g => g.Generator(Generators.Native));
m.Table("Addresses");
m.Property(p => p.City);
m.Join("PeopleAddresses", z =>
{
z.Property(p => p.IsDefault);
z.Property(p => p.ValidFrom);
z.Property(p => p.ValidTo);
z.Key(k => k.Column("PersonId"));
});
});
Слайд 31
One-to-One
Primary-key based
Foreign-key based
Слайд 32
One-to-Many
- ISet
- IList
- IDictionary
-
IList
и - []
В коллекциях – сущности / простые типы
Слайд 35
Bi-directional associations
Два разных представления в памяти
Возможные проблемы решаются
с помощью inverse=“true”
inverse cascade
Слайд 36
Cascade
all
save-update
delete
all-delete-orphan
Слайд 38
Механизм
Возвращается прокси а не сам объект
Данные загружаются только
по необходимости
Возникают проблемы если сессия закрыта
По умолчанию включен для
классов
Два подхода – класс наследник, либо реализация интерфейса класса
Слайд 39
Ограничения
Класс не sealed
Свойства и методы – virtual
Конструктор по
умолчанию не private
Если proxy строится на базе интерфейса –
ограничения не действуют
Слайд 40
s = sessions.OpenSession();
User u = (User) s.Find("from User
u where u.Name=?", userName, NHibernateUtil.String)[0];
IDictionary permissions = u.Permissions;
s.Close();
...
int accessLevel
= (int) permissions["accounts"]; // Error!
Слайд 41
Lazy initialization и коллекции
Можно отключать
Аттрибут lazy у коллекций
Не
усердствовать с lazy=“false”
Слайд 42
LazyInitializationException
Не закрывать сессию (до завершения обработки запроса)
Вызвать руками
NHibernateUtil.Initialize() передав туда коллекцию
Привязать объект к сессии через Update
/ Lock
Слайд 43
Вариант получения общего кол-ва элементов в коллекции не
загружая ее из базы:
ICollection countColl = s.Filter( collection,
"select count(*)" );
IEnumerator countEn = countColl.GetEnumerator();
countEn.MoveNext();
int count = (int) countEn.Current;
Слайд 45
3 стратегии
Table per hierarchy
Table per subclass
Table per class
Если
в иерархии есть абстрактные классы – abstract=true
column="PAYMENT_TYPE" type="String"/>
...
...
...
...
name="Amount" column="AMOUNT"/>
...
...
...
...
column="PAYMENT_TYPE" type="string"/>
...
...
...
...
column="AMOUNT"/>
...
...
...
...
name="Amount" column="CREDIT_AMOUNT"/>
...
...
...
Слайд 52
Загрузка по Id
.Load(object, id)
.Load(id)
.Get(id)
Load – пробросит exception
Get –
вернет null
Слайд 53
Save / Update / Delete
Save ~ INSERT
Update ~
UPDATE
Delete ~ DELETE
SaveOrUpdate ~ INSERT || UPDATE
Для persistent объектов
в рамках их контекста можно ничего дополнительно не вызывать
Слайд 54
Критерии
Session.CreateCriteria
ICriteria.Add( Restrictions. …)
Restrictions: Eq / Gt / Lt
/ In …
Но лучше – QueryOver API
Session.QueryOver
Слайд 55
C использованием критериев:
.Add(Restrictions.And(
Restrictions.Eq("Name", "test name"),
Restrictions.Or(
Restrictions.Gt("Age", 21),
Restrictions.Eq("HasCar", true))))
С использованием QueryOver
.Where(p => p.Name == "test name" && (p.Age > 21 || p.HasCar))
session.QueryOver()
.JoinQueryOver(c => c.Kittens)
.Where(k => k.Name == "Tiddles");
.Where(p => p.BirthDate.YearPart() == 1971)
.Select( p => Projections.Concat(p.LastName, ", ", p.FirstName), p => p.Height.Abs())
Слайд 56
Fetch Mode
User user = (User) session.CreateCriteria(typeof(User)) .SetFetchMode("Permissions", FetchMode.Join)
.Add( Restrictions.Eq("Id", userId) ) .UniqueResult();
Слайд 57
Batch Size
На коллекциях и классах в mapping файлах
Слайд 58
Second Level Cache
Read only
Read / Write
Non-strict Read /
Write
Слайд 59
Query Cache
.SetCacheable(true)
.SetCacheableRegion(“”)
Для сброса кэша
.SetForceCacheRefresh(bool)
ISessionFactory.EvictQueries()
Слайд 60
Multi Criteria & Queries
Можно выполнить несколько SQL запросов
за один заход к серверу
Весьма полезно при реализации paging
Слайд 61
IMultiCriteria multiCrit = s.CreateMultiCriteria()
.Add(s.CreateCriteria(typeof(Item))
.Add(Expression.Gt("Id", 50))
.SetFirstResult(10))
.Add(s.CreateCriteria(typeof(Item))
.Add(Expression.Gt("Id", 50))
.SetProject(Projections.RowCount()));
IList results = multiCrit.List();
IList items = (IList)results[0];
long count = (long)((IList)results[1])[0];
Слайд 62
Работа с сессиями
Можно задать настройку hibernate.current_session_context_class
Теперь можно использовать
ISessionFactory.GetCurrentSession()
Слайд 63
Генерация схемы
Configuration cfg = ....;
new SchemaExport(cfg).Create(false, true);
Слайд 64
Further Reading:
http://nhforge.org/doc/nh/en/index.html - документация
http://nhforge.org/blogs/nhibernate/default.aspx - блог