جاوا و تکنولوژی های آن

java programming language

در این وبلاگ به بررسی نکات موجود در جاوا و تکنولوژی های آن می پردازیم

طبقه بندی موضوعی

List : در روش لیست نیاز به index دارد و در جدول یک column برای index ها در نظر میگیرد و موقع select کردن و جای گزاری در List طبق همان index ها و ترتیبی ابجکت ها پر میشوند

 

Set : همانند List است با این تفاوت که index ندارد و ترتیبی هم در موقع واکشی ندارد 

 

Map : در مورد جدول هایی که فقط شامل دو ستون است و ماهیت کلید و مقداری دارد مثل ثابت هایی که در برنامه تعریف میشوند مانند UserRole ها

 

Array یا [] : میتوانیم دیتا ها را بصورت آرایه هم داشته باشیم و همانند List است و index دارد ، آرایه ها کاربرد کمتری دارند چون سخت تر قابل تغییر هستند 

 

Bag : نوعی collection است که هایبرنیت ساخته و همانند List است ولی index ندارد و مرتب سازی ندارد 

 

Idbag : برای روابط چند به چند استفاده میشده که امروز بجاش از Set یا List استفاده میکنیم 

 

 

 دو راه برای تعریف روابط خارجی توسط annotation ها وجود دارد : 

 - Foreign Key ها 

 -  استفاده از جدول های واسط


کلاس های زیر و روابطی که دارند را در نظر بگیرید : 


@Entity
@Table(name="SHOWROOM")
public class Showroom {

	@OneToMany
	@Cascade(CascaseType.ALL)
	private List<Car> cars = null;

}

@Entity
@Table(name="CAR")
public class Car {

	@ManyToOne
	@JoinColumn(name="SHOWROOM_ID")
	private Showroom showroom;

}


هر Car در یک Showroom قرار گرفته و هر Showroom دارای چندین Car است پس هر Car با Showroom خود رابطه ManyToOne دارد و هر Showroom با ماشین های قرار گرفته درون نمایشگاه رابطه OneToMany است  که در بالا میبینیم 

حالا برای برقرار روابط بین این دو جدول باید مشخص کنیم که این رابطه از چه طریقی قابل شناسایی است ؟ در جدول Car یک F.key به Showroom داریم که showroom_id ستون primary key در Showroom است و با JoinColumn@ اون فیلد را تعیین میکنیم 

ولی در جدول Showroom چطور باید ماشین های قرار گرفته درونش را مشخص کنیم ؟ می آییم با استفاده از Cascase@ میگوییم که بر اساس Join ای که در Car وجود دارد رابطه را برقرار کند 


وقتی بین دو جدول رابطه یک به چند داریم یک List باید استفاده کنیم و از طرف جدول دیگر رابطه بصورت چند به یک خواهیم داشت و نوع دیتا از نوع ابجکت تکی است

 

در رابطه چند به یک بجای Column@ از JoinCoulmn@ استفاده میکنیم و داخلش پارامتر name را برابر نام Foreign Key قرار میدهیم و این یعنی برای رسیدن به آن ابجکت باید join زده شود تا به ابجکت برسد و برای تعریف نوع رابطه از ManyToOne@ استفاده میکنیم

 

اگر در یک رابطه چند به یک که برای پیدا کردن ابجکت نیاز به join زدن داشته باشد پارامتر optional=false را مشخص نکنیم هایبرنیت در نظر میگیرد که ممکن است رابطه ما Mandatory نباشد و از left outer join استفاده میکند و این یعنی ممکن است رکوردی در join زدن وجود داشته باشد یا خیر و اگر بخواهیم صراحتاً اعلام کنیم که رابطه ما از نوع mandatory است و مطمئن هستیم به ازای هر join یک رکورد وجود دارد باید از optional=false در ManyToOne@استفاده کنیم که در کد زیر میبینیم


و برعکس برای جدول متناظر رابطه بصورت چند به یک خواهیم داشت و برای فیلد مورد نظر از List با نوع ابجکت مقابل (ابجکتی که با این جدول رابطه many to one دارد) قرار میدهیم و روی آن از OneToMany@ استفاده میکنیم و برای اینکه تناظر بر قرار شود داخلش پارامتر mappedBy را با مقدار اسم متغییر ابجکت در entity مقابل را قرار میدهیم اینطوری هایبرنیت از روی entity مقابل رابطه را پیدا میکند و وقتی تست میکنیم متوجه می‌شویم که دوبار select زده شده یکی برای بدست آوردن لیستی از جدول اول و بعد select میزند با لیستی از f.key ها و این در جداول بزرگ و پر حجم پرفرمانس را کم میکند و بعداً میتوانیم با استفاده از lazy fetch پرفرمانس را بهتر کنیم

 

 @Entity
@Table(name="SHOWROOM")
public class Showroom {

	@OneToMany(mappedBy="SHOWROOM_ID")
	private List<Car> cars = null;

}

@Entity
@Table(name="CAR")
public class Car {

	@ManyToOne(optional=false)
	@JoinColumn(name="SHOWROOM_ID")
	private Showroom showroom;

}

 



**نکته : اگر بخواهیم برای DDL هایبرنیت F.key ها را هنگام ساخت جداول تولید کند در JoinColumn@ پارامتر foreignKey را با ForeignKey@ پر میکنیم

 

همانطور که تا الان دیدم اگر رابطه OneToMany@ داشته باشیم ManyToOne@ هم خواهیم داشت حال سوال اینجاست که آیا میتوان تنها یک OneToMany@ داشته باشیم ؟

بله، در صورتی که یک فیلد کلاس که از نوع List ای از یک ابجکت باشد را OneToMany@ کنیم در این حالت هایبرنیت فرض میکند که جدول واسطی هست بین کلاس Entity کنونی و کلاسی که List از ان نوع پر شده است و موقع Insert سعی میکند روابط را در جدول واسط هم ثبت کند و هر وقت بخواهد کوئری بزند با اون جدول واسط join میزند تا لیست از ابجکت که بصورت فیلد است را دریافت کند و در حقیقت یک join سه طرفه میشود

پس OneToMany@ بدون ManyToOne@ هم امکانش است ولی بسیار کم کاربرد است 

ولی اگر در OneToMany@ از mappedBy استفاده کنیم این حالت پیش نمیاید و صریحا نوع رابطه را اعلام میکنیم





**نکته :هایبرنیت اجازه نمیده که جدولی بدون Primary Key داشته باشیم و اگر جدولی به این صورت و دستی تعریف کنیم موقع استفاده به مشکل خواهیم خورد یک راه حل این است که Primary Key را بصورت ترکیبی تعریف کنیم

 

 

ManyToMany@:

برای پیاده‌سازی رابطه چند به چند معمولاً یک جدول واسط داریم که دو عضو P.key جدول اول و P.key جدول دوم را دارد و حالا هر چند تا از ابجکت های کلاس میتواند هر چند تا از ابجکت های کلاس دوم را داشته باشد و بالعکس


و در دو کلاس‌ Entity هر کدام List ای از اعضای جدول مقابل دارند و روی فیلد آن از ManyToMany@ استفاده میکنیم

 

@ManyToMany
@JoinTable(name = "middle_table"
                    ,joinColumns = @JoinColumn(name = "column1")
                    ,inverseJoinColumns = @JoinColumn(name = "column2") )
private List<Entity2> entity2List ;

 

 

و برای مشخص کردم جدول واسط که تناظر آن‌ها را برقرار میکند از JoinTable@ استفاده میکنیم و پارامترهایش را مشخص میکنیم با name نام جدول واسط را تعیین میکنیم و پارامتر joinColumn مشخص میکند که کدام ستون از جدول واسط F.key شده به P.key جدول(مربوط به Entity) جاری

و با پارامتر inverseJoinColumns مشخص میکنیم که ستون F.key مربوط به P.key جدول (کلاس entity دیگر ) مقابل کدام است

 

و برای Entity جدول اصلی دوم و پیاده‌سازی ManyToMany@ در آن میتوانیم مثل Entity اول JoinTable@ استفاده کنیم ولی راه حل بهتر استفاده از پارامتر mappedBy در ManyToMany است و هایبرنیت از روی رابطه و تعریف پارامتر های JoinTable@ در کلاس entity اول میتواند رابطه Entity کلاس دوم را پر کند

 

 

 

OneToOne@ :

در‌واقع همان OneToMany است با این تفاوت که F.key بصورت Unique است این رابطه تنها موقعی برقرار است که به ازای هر رکورد تنها یک بار F.key یکتا خواهیم داشت

دو حالت برای پیاده سازی OneToOne ها  ممکن است در معماری پروژه پیش بیاید :

- دو کلاس داریم که هر دو به ازای هم تنها یکبار ایجاد میشوند مثل جدول کارمند و جدول آدرس کارمند هر آدرس کارمندی تنها متعلق به یک کارمند است و هر کارمند حتما یک آدرس دارد در این حالت هر دو جدول باید F.key به Primary key جدول متقابل داشته باشند

- دو کلاس داریم که کلاس اصلی میتواند رابطه یک به یک با کلاس دیگری داشته باشد مثلا کلاس کارمند با کلاس پروفایل کارمند ، یک کارمند میتواند پروفایل خود را تکمیل کند و یا پروفایل نداشته باشد ولی اگر وجود داشت رابطه یک به یک برقرار است و پروفایل یک کارمند متعلق به کارمند دیگری نیست یا مثال دیگر میتوان به نویسنده و کتاب اشاره کرد (فرض کنید هر کتاب یک نویسنده بیشتر ندارد) ، هر کتاب متعلق به یک نویسنده است و ان کتاب را نویسنده دیگری ننوشته پس رابطه یک به یک با نویسنده خود دارد که در ادامه آنرا پیاده میکنیم :


کلاس entity نویسنده که اصلی است :

@Entity
@Table(name = "author")
public class Author {
 
    @Id
    @GeneratedValue
    @Column(name = "author_id")
    private long id;
 
    @Column(name = "name")
    private String name;
 
    @Column(name = "email")
    private String email;
 
    public Author() { }
 
    public Author(String name, String email) {
        this.name = name;
        this.email = email;
    }
 
    public long getId() {
        return id;
    }
 
    public void setId(long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }  
}

کلاس Book Entity که رابطه OneToOne@ با نویسنده خود دارد :


@Entity
@Table(name = "book")
public class Book {
 
    @Id
    @GeneratedValue
    @Column(name = "book_id")
    private long id;
 
    @Column(name = "title")
    private String title;
 
    @Column(name = "description")
    private String description;
 
    @Column(name = "published")
    private Date publishedDate;
 
    @JoinColumn(name = "author_id")
    @OneToOne(cascade = CascadeType.ALL)
    private Author author;
 
    public long getId() {
        return id;
    }
 
    public void setId(long id) {
        this.id = id;
    }
 
    public String getTitle() {
        return title;
    }
 
    public void setTitle(String title) {
        this.title = title;
    }
 
    public String getDescription() {
        return description;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
    public Date getPublishedDate() {
        return publishedDate;
    }
 
    public void setPublishedDate(Date publishedDate) {
        this.publishedDate = publishedDate;
    }
 
    public Author getAuthor() {
        return author;
    }
 
    public void setAuthor(Author author) {
        this.author = author;
    }
}


در این حالت در جدول book یک F.key به جدول author خواهیم داشت و رابطه OneToOne از طریق Primary key برقرار میشود 


هنگام کوئری زدن برای کلاس Book دو بار select خواهد زد یکبار خود Book و یکبار با برابر قرار دادن F.key و select زدن روی جدول author چون دوبار select داریم میتوانیم select دوم را Lazy کنیم که هر وقت نیاز بود واکشی انجام شود پس روی فیلد author در کلاس Book  و هنگام تعریف رابطه آنرا Lazy میکنیم :   (OneToOne(fetch = FetchType.LAZY@


روش های استفاده از جدول واسط هم داریم که بهش نپرداختیم

 



Fetch و انواع آن :

موقعی که ما نیاز داریم برای واکشی دیتا از دیتابیس یک استراتژی را تعیین کنیم از Fetch استفاده میکنیم

که بر دو نوع Eager و Lazy است

 

FetchType.EAGER : به محض اینکه در روابط بین دو جدول و join زدن‌ها نیاز به دیتای متقابل داریم آنرا کوئری میزند و از دیتابیس دریافت میکند

 

FetchType.LAZY : زمانی که نیاز داریم دیتا واکشی شده طی روابط بین جدول ها خوانده شود آنرا دریافت میکند و نه به محض کوئری زدن

 

**نکته : پیش‌فرض FetchType در OneToMany@ و ManyToMany@ بصورت Lazy است ولی در رابطه ManyToOne@ نوع آن بصورت Eager است که میتوان آنرا عوض کرد

 

** نکته : چطور Lazy Fetch را پیاده میکند و به محض اینکه ما دیتای lazy  شده را بخواهیم بخوانیم آنرا دریافت میکند ؟ 

از دیزاین پترن Proxy استفاده میکند و به محض درخواست اطلاعات هایبرنیت اونو از طریق Proxy متوجه میشود و آنرا دریافت و جایگذاری میکند




Cascade :

رکورد هایی داریم که بصورت F.key در جداول دیگر رابطه دارند و اگر عملیاتی در جدول اصلی صورت بگیرد مثل آپدیت کردن یا حذف ، چه واکنشی باید در رکورد های دیگر جداول که F.key به جدول اول دارند صورت گیرد و آنرا با Cascade تعریف میکنیم که هم Annotaion دارد و هم بصورت پارامتر در رابطه‌ها قابل تعریف است که Cascade ها انواع محتلفی دارند :

تعریف توسط Cascade@ :

@Entity
@Table(name="SHOWROOM")
public class Showroom {

	@OneToMany(mappedBy="SHOWROOM_ID")
	@Cascade(CascadeType.REMOVE)
	private List<Car> cars = null;

}

چون Cascade@ از کلاس های Hibernate است و ما ممکن است ORM را بخواهیم تغییر دهیم این روش توصیه نمیشود و بهتر است از روش دوم استفاده کنیم 


تعریف روی رابطه که استاندارد JPA است : 

@Entity
@Table(name="SHOWROOM")
public class Showroom {

	@OneToMany(mappedBy="SHOWROOM_ID" , cascade=CascadeType.ALL)
	private List<Car> cars = null;

}


 - ALL : هر اتفاقی که برای رکود افتاد برای رکورد متناظر هم بیوفتد که شامل همه نوع عملیات است 

- PERSIST : اگر نیاز بود رکوردی دیگر علاوه بر رکورد جاری باهم ایجاد شود

- MERGE : در حالت آپدیت تغییرات اعمال می‌شود

- REMOVE : با حذف رکورد از جدول اصلی بقیه رکورد ها در جدول دیگر که F.key به جدول اصلی دارد حذف می‌شوند

- REFERESH : در هایبرنیت متدی برای referesh کردن داریم که یکبار دیگر رکورد را دریافت میکند و در این حالت referesh شدن در جداول ثانویه هم اتفاق میوفتد

- DETACH : زمانی استفاده میشود که بیش از یک Entity به دیگر Entity رابطه داشته باشد و ما میخواهیم وقتی یکی از آنها از Session Context جدا شد بقیه هم جدا شوند

 

 

OrderColumn@ : جهت مشخص کردن ستون index های ترتیب ذخیره شده List تعیین میکند که رکورد ها در کدام index از List باید جایگذاری شوند که واقعا کاربردی خیلی خیلی کمی دارد 

 

MapKeyColumn@ : برای جداولی که دیتا حالت Map و بصورت Key/Value هستند استفاده می‌شود

 

 ما حتی میتوانیم Enum های جاوا توسط ORM پر کنیم که کاربرد های خاص دارد ولی مشکلی که دارد این است که در صورتی که یک دیتا به جدول ما اضافه شود باید کد جاوا و Enum را هم تغییر دهیم که نگهداری کد را ضعیف تر میکند





 

نظرات  (۱)

بسیار عالی و روان و قابل فهم موفق باشید

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی