اگر یادتون باشه در بخش 2 آموزش هایبرنیت یک اشاره ای به ارث بری کردیم حالا در این بخش به بررسی کاملتر پیاده سازی ارث بری در دنیای رابطه ای می پردازیم
اگر یادتون باشه در بخش 2 آموزش هایبرنیت یک اشاره ای به ارث بری کردیم حالا در این بخش به بررسی کاملتر پیاده سازی ارث بری در دنیای رابطه ای می پردازیم
توسط Criteria API میتوان یک کوئری را بصورت داینامیک و بدون استفاده از HQL ایجاد کرد
Criteria ctr = session.createCriteria (Cat.class); ctr.setMaxResult(50); List<Cat> cats = ctr.list();
کد بالا معادل select زدن روی جدولی است که در Entity کلاس Cat تعریف شده است و limit آن روی 50 است
در Criteria ما موارد خواسته شده را با متد هایش ارسال میکنیم و آن برای ما کوئری مورد نیاز را میسازد
List<Cat> cats = session.createCriteria (Cat.class) .add(Restrictions.like("name","Fritz%")) .add(Restriction.between("weight",minWeight,maxWeight)) .list();
در کد بالا روی کلاس Cat کوئری زده که name آن با Fritz شروع شده باشد و weight آن بین دو عدد باشد
List cats = session.createCriteria(Cat.class) .add(Restriction.in("name",new String[]{"Fritz","Izi","Pk"})) .add(Restriction.disjunction() .add(Restriction.isNull("age")) .add(Restriction.eq("age",new Integer(0))) .add(Restriction.eq("age",new Integer(1))) .add(Restriction.eq("age",new Integer(2))) ) .list();
با disjunction یک عبارت OR داخل پرانتزی ایجاد شده است و هر چه add در داخل disjunction قرار بگیرد نقش OR را خواهد داشت و برای اینکه بجای OR از AND استفاده کنیم کافی است بجای disjunction از junction استفاده کنیم
میتوان بجای پاس کردن مقدار "age" از property آن استفاده کرد
Property age = Property.forName("age");
sql restriction : گاهی نیاز است که شروط sql را بکار ببریم
List cats = session.createCriteria(Cat.class) .add(Restriction.sqlRestriction("lower({alias}.name) like lower(?)" , "Fritz%" , Hibernate.STRING)) .list();
در عبارت بالا alias همیشه به root entity alias اشاره میکند که در اینجا Cat است (در criteria باید از یک entity ساخته شود همان entity که ابتدای ساخت criteria بکار میبریم root entity است)
Order By در Criteria :
List cats = session.createCriteria(Cat.class) .add( Restrictions.like("name","F%") .addOrder(Order.asc("name").nullls(NullPrecedence.LAST)) .addOrder(Order.desc("age"))) .setMaxResults(50) .list();
در عبارت بالا کوئری را بر اساس name صعودی و بردن null ها به انتها و age بصورت نزولی است
join زدن : در criteria بصورت پیش فرض از inner join استفاده میکند
List cats = session.createCriteria(Cat.class) .add( Restrictions.like("name","F%")) .createCriteria("kittens") .add( Restrictions.like("name","F%")) .list();
در عبارت بالا kittens اشاره به جدولی دارد که با Cat رابطه ای دارد (مثلا ManyToOne) ومیتوان روی آن join زد در شرط دوم که بعد از kittens آمده است شرط روی جدول kittens اعمال میشود
Alias : با متد createAlias میتوان Alias ساخت تا کوئری ها کوتاه تر شوند
تعریف کردن نوع join :
چون در criteria برای روابط بصورت پیش فرض از inner join استفاده میشود میتوان نوع آنرا تغییر داد
List cats = session.createCriteria(Cat.class) .createAlias("mate","mt",Criteria.LEFT_JOIN,Resctrictions.like("mt.name","good%")) .addOrder(Order.asc("mt.age")) .list();
تعریف کردن Fetch type :
List cats = session.createCriteria(Cat.class) .add(Restrictions.in("name","Fritz%")) .setFetchMode("mate",FetchMode.EAGER) .setFetchMode("kittens",FetchMode.EAGER) .list();
Example Query : در criteria میتوان با استفاده از ابجکت Example و دادن یک نمونه از Entity ، داینامیک بیشتری به کوئری داد :
Cat cat = new Cat(); cat.setName("kittyCat"); cat.setColor(Color.Black); List cats = session.createCriteria(Cat.class) .add(Example.create(cat)) .list();
استفاده از Example با محدودیت هایی روبرو است مثلا نمیتوان روی Compose Object ها کوئری زد و یا like زدن روی رشته ها دشوار تر است
**نکته : در Criteria میتوان به root entity یک alias هم داد و اگر ندهیم خودش یک alias بنام entity_ در نظر میگیرد
**نکته : در هایبرنیت Criteria منسوخ شده است و پیشنهاد میدهد از JPA استفاده کنیم البته Criteria در JPA پیشرفته تر و پیچیده تر است و کمی با criteria در hibernate متفاوت است ولی اصول کارشان یکسان است
Projection : با projection میتوان column هایی که میخواهیم در result داشته باشیم را تعیین کنیم
List cats = session.createCriteria(Cat.class) .setProjection(Projections.projectionList() .add(Projections.id()) .add(Projections.property("Entity_field_name_1")) .add(Projections.property("Entity_field_name_2")) ).list();
در جواب لیستی از آرایه بر میگرداند که میتوان صراحتا نوع آنرا هم مشخص کرد و یک DTO ساخت :
List cats = session.createCriteria(Cat.class) .setProjection(Projections.projectionList() .add(Projections.id()) .add(Projections.property("Entity_field_name_1")) .add(Projections.property("Entity_field_name_2")) ) .setResultTransformer(new ResultTransformer(){ @Override public Object transformTuple(Object[] objects , String[] strings){ Cat cat = new Cat(); cat.setId((Integer)objects[0]); cat.setName((String)objects[1]); ... return cat; } @Override public List transformList(List list){ return list; } ) .list();
در متد transformList میتوان نحوه ساخت list را هم مشخص کرد و آنرا بر گرداند
** نکته : میتوان از پیاده سازی های پیش فرض جهت setResultTransformer استفاده کرد که سازنده آنها در Criteria.Projection و ... قرار دارند
sub query با criteria : میتوان با criteria کوئری های داخلی هم ایجاد کرد که قبل از کوئری اصلی باید کوئری های داخلی با استفاده از DetachedCriteria ساخته شوند و بعد روی کوئری اصلی استفاده شوند :
DetachedCriteria avgWeightForName = DetachedCriteria.forClass(Cat.class,"cat2") .setProjection(Property.forName("weight").avg()) .add(Property.forName("cat2.name").eqProperty("cat.name")); List cats = session.createCriteria(Cat.class,"cat") .add(Property.forName("weight").gt(avgWeightForName)) .list();
HQL :
زبانی شبیه SQL برای کوئری زدن در هایبرنیت که به جای بکار گرفتن نام جداول و ستونها با entity ها و فیلد ها کار میکند
نمونه : from Cat as cat برای select زدن روی جدولی که در کلاس Cat تعریف شده است
میتوانیم از دو جدول هم کوئری بگیریم که جواب آن بصورت ضرب دکارتی بر میگردد
from Formula , Parameter
و خروجی را بصورت ArrayList ای از آرایه ها خواهیم داشت و اندیس اول از Formula و اندیس دوم از Parameter خواهد بود و اگر بخواهیم لیست برگشتی فقط لیستی از Formula باشد میتوان HQL را به این صورت نوشت :
select f from Formula f , Parameter
و اگر بخواهیم بجای List ای از آرایه ها List ای از List ها بگیریم میتوان HQL را به این صورت تغییر داد :
select new List(f) from Formula f , Parameter
از همین روش بالا میتوان ابجکت های model ای داشت که در constractor آرگومان هایی را دریافت کند و در HQL صراحتا مقادیر دریافتی را به آن ارگومان ها پاس بدهیم و خروجی List ای از آن ابجکت دریافت کنیم
**نکته : اصولی نیست که کوئری را برای دو Entity بزنیم و باید تا آنجا که ممکن است دوری کنیم
fetch : در کوئری های join زدن ها اگر بخواهیم ستون های جدول دوم را هم دریافت کنیم میتوانیم بعد از کلمه join از fetch استفاده کنیم
**نکته : left join پرفرمانس دیتابیس را تا حد زیادی کم میکنم و راه حل این است که بجای اینکه مقادیر f.key را optional بگیریم حتما برای مقادیر optional از یک مقدار پیش فرض که توسط خودمان تعیین میشود استفاده کنیم که همیشه بتوانیم از inner join استفاده کنیم
Named Query :
میتوان در کلاس های Entity کوئری های از پیش تعریف شده ای با یک نام ایجاد کرد و در طول برنامه با استفاده از نام کوئری از آن استفاده کرد
@Entity @NamedQueries( @NamedQuery(name="getUserById" , query="from User where userId = :pUserId") ) public class User { private int userId; private String username; ... }
با استفاده از Name Query ها کوئری ها از لحاظ ساختاری و اجر چک میشوند و آنها در حافظه cache می شوند
Query query = session.createNamedQuery("getUserById") .setParameter("pUserId",1); List<User> list = query.list();
Native Query :
هایبرنیت از کلیه دستورات و امکانات SQL استفاده نمیکند مثل Union و یا میخواهیم بجای HQL از SQL استفاده کنیم ، ما میتوانیم با استفاده از Native Query از آنها استفاده کنیم
session.createNativeQuery ("select * from user" , User.class).list();
که همانند NamedQueries@ میتوان از NativeQueries@ استفاده کرد و دسته ای از Native Query ها را ایجاد کرد
وضعیتهای ابجکت های Entity ایجاد شده از Session :
- Transient : اگر Entity را ما خودمان ایجاد کرده باشیم و یا بدست آورده باشیم وضعیت آن entity بصورت Transient است که هنوز نه ذخیره شده است و نه مدیریت آن به Session مرتبط است مثلاً زمانی که Entity با عملیات Delete حذف شده است و از Session جدا شده است
- Persistent : یا بهش Managed هم میگویند ، در این وضعیت Entity از Session دریافت شده است
- Detached : در این وضعیت entity قبلاً از Session دریافت شده است و اکنون که Session بسته (close) شده از Session جدا شده است
**نکته : در طراحی جداول میتوانیم ستون هایی برای نگهداری History یا سابقه رکورد داشته باشیم مثلاً مشخص کنیم کدام عامل این رکورد را ایجاد کرده
وقتی روابط One to Many یا Many to Many داریم مجبوریم دیتا ها را در Collection نگهداری کنیم در این بخش نگاهی میکنیم به Collection های مورد استفاده در Hibernate و annotation های کاربردی برای روابط چندگانه و انواع Fetch Type و نحوه تعریف Cascade