# فناوری جاوا > برنامه‌نویسی جاوا >  آموزش Hibernate

## powerboy2988

آموزش برنامه نویسی Hibernate

در تمام پروژه های نرم افزاری که تا به حال بر روی آنها کار کردیم یکی از مهترین مسائلی که نقش کلیدی در طراحی آن بازی می کند مسئله مانایی داده ها و مدیریت آنهاست . فریم ورک های تحت وب را در نظر بگیرید (Struts vs WebWork) یا فریم ورک های GUI (Swing vs AWT) ویا (JSP vs Velocity) ، هر یک از این ها فواید و اشکالات خاص خود را دارند اما تمام آنها یک کار واحد و راه حل واحدی را ارائه میدهند .
اما متاسفانه در مورد تکنولوژی های موجود در زمینه مانایی داده و کار با دیتابیس این گونه نیست به طوری که در مورد یک مسئله واحد، راه حل های بسیار متفاوت و گوناگونی وجود دارد .
موضوع مانایی (Persistance) برای سالهای زیادی به عنوان یکی از داغترین موضوع گفتگو در اکثر انجمنهای جاوا بوده است . اکثر برنامه نویسان بر روی این مسئله به توافق نرسیده اند . مشکلاتی از قبیل اینکه آیا مسئله مانایی باید از طریق دیتابیس حل شود یا از طریق کامپوننت های جاوا مانند EJB ؟ و یا اینکه آیا باید عملیات CRUD (Create , Read , Update , Delete) را به صورت دستی با SQL و JDBC بنویسیم یا اینکه این عملیات باید به طور خود کار انجام شود ؟ و یا آیا باید به طور کلی استفاده از SQL را کنار گذاشته و به سراغ سیستمهای دیتابیسی دیگر نظیر دیتابیس های شی گرا برویم؟ این گونه مشکلات همچنان به قوت خود باقیست اما اخیرا راه حلی تحت عنوان ORM (Object/Relational Mapping) پیاده سازی شده که مورد قبول اجماع برنامه نویسان میباشد . 
 Hibernate یکی از این گونه سیستم هاست . Hibernate این امکان را به برنامه نویس میدهد تا بدون اینکه خود را درگیر Query زدن کند تمرکز خود را بر روی منطق برنامه خود معطوف سازد . در واقع این Hibernate است که وظیفه ارتباط با دیتابیس را بر عهده دارد و عملیات CRUD را انجام میدهد به عبارت دیگر Hibernate مانند پلی عمل میکند که ارتباط بین دنیای شی گرا و دنیای رابطه ای را برقرار میکند .
اجازه بدهید به همین مقدار توضیح اکتفا کرده و به سراغ پیاده سازی و یک نمونه برنامه برای آشنایی با Hibernate برویم .برای این کار باید سه مرحله طی شود :

1.	پیاده سازی Persistance Class یا همان کلاسهای Entity 
2.	ایجاد جداول متناظر با هر کلاس Entity
3.	ساخت فایلهای .hbm مربوط به هر کلاس Entity

----------


## powerboy2988

1.    Persistance Class

 در این برنامه یک کلاس به عنوان Persistance class  با نام Message  تعریف شده است . ساختار این کلاس در زیر آورده شده است :

            
            public class Message {
                private Long id;
                private String text;
                private Message nextMessage;
                private Message() {}
                public Message(String text) {
                    this.text = text;
                }
                public Long getId() {
                    return id;
                }
                private void setId(Long id) {
                    this.id = id;
                }
                public String getText() {
                    return text;
                }
                public void setText(String text) {
                    this.text = text;
                }
                public Message getNextMessage() {
                    return nextMessage;
                }
                public void setNextMessage(Message nextMessage) {
                    this.nextMessage = nextMessage;
                }
            }
                                    

کلاس های Entity چیزی جز Java bean های معمولی نیستند . وجه تمایز Hibernate با تکنولوژی های قبلی خود نظیر Ejb ها نیز همین است . در EJB نسخه های اولیه هر کلاس Entity باید از کلاس EntityBean مشتق میشدند همچنین به ازای هر کلاس Entity باید چند Interface نیز تعریف میشد اما در اینجا مشاهده میکنید که کلاس Entity در Hibernate یک کلاس ساده Javabean میباشد.

2.    Database Table

هر کلاس Entity لازم است که یه یک جدول در دیتابیس نگاشت شود در اصل این هدف اصلی یک ORM می باشد . لذا باید یک جدول متناظر با کلاس Message ایجاد کنیم . ساختار این جدول به شکل زیر می باشد:


                                                 
            CREATE TABLE `messages` (                      
                       `MESSAGE_ID` bigint(10) NOT NULL,           
                       `MESSAGE_TEXT` varchar(50) NOT NULL,        
                       `NEXT_MESSAGE_ID` bigint(10) default NULL,  
                       PRIMARY KEY  (`MESSAGE_ID`)                 
            )
                         
همانطور که مشاهده میکنید این جدول عینا متناظر با کلاس Message می باشد .

----------


## powerboy2988

3.	Message.hbm.xml

حال زمان آن رسیده است تا کلاس Message را به جدول message نگاشت کنیم . برای این کار باید یک فایل XML و با نام همان کلاس Entity ایجاد کنیم نام این فایل متشکل از نام کلاس Message به همراه کلمه .hbm و با پسوند Xml می باشد . این فایل باید در همان مسیری باشد که فایل Message.class قرار دارد . در این فایل موارد زیر مشخص میشود :
1.	نام کلاس و جدول متناظر با آن .
2.	نام Property های کلاس و ستون متناظر با آن در جدول .
3.	نوع داده ای هر مقدار 
4.	نام فیلدی که در جدول به عنوان ID (Pk) تعریف شده است . و همچنین نحوه ایجاد آن .
5.	تعریف روابط مانند 
•	Composition
•	Association
•	Inheritance

ساختار فایل .hbm مربوط به کلاس Message در زیر آورده شده است :


<?xml version="1.0"?>
			<!DOCTYPE hibernate-mapping PUBLIC
			"-//Hibernate/Hibernate Mapping DTD//EN"
			"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
			<hibernate-mapping>
			<class
			name="Message"
			table="MESSAGES">
			<id
			name="id"
			column="MESSAGE_ID">
			<generator class="increment"/>
			</id>
			<property
			name="text"
			column="MESSAGE_TEXT"/>
			<many-to-one
			name="nextMessage"
			cascade="all"
			column="NEXT_MESSAGE_ID"/>
			</class>
			</hibernate-mapping>


این فایل مشخص می کند که Object های از کلاس Message باید در جدولی به نام MESSAGES ذخیره شوند همچنین متغیر id از کلاس Message باید به جای ستون MESSAGE_ID ، متغیر text در ستون MESSAGE_TEXT وارد شود و فیلد nextMessage که با ستون NEXT_MESSAGE_ID در ارتباط است برای ایجاد یک ارتباط یک به چند مورد استفاده قرار میگیرد . 

Hibernate از طریق این فایل تمام دستورات SQL لازم برای عملیات CRUD را ایجاد میکند . 
حال فرض کنید که میخواهیم یک message را از دیتا بیس خوانده ، یک message جدید ایجاد کرده و message جدید را به عنوان nextMessage در message اولی وارد کنیم . کدهای زیر این کار را انجام میدهند :

1: Session session = getSessionFactory().openSession();
			2: Transaction tx = session.beginTransaction();
			// 1 is the generated id of the first message
			3: Message message =
			(Message) session.load( Message.class, new Long(1) );
			4: message.setText("Greetings Earthling");
			5: Message nextMessage = 
			new Message("Take me to your leader please)");
			6: message.setNextMessage( nextMessage );
			7: tx.commit();
			8: session.close();   


تکه کد بالا منجر به اجرای دستورات SQL زیر می شود : 

select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID
			from MESSAGES m
			where m.MESSAGE_ID = 1
			
			insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)
			values (2, 'Take me to your leader (please)', null)
			
			update MESSAGES
			set MESSAGE_TEXT = 'Greetings Earthling', NEXT_MESSAGE_ID = 2
			where MESSAGE_ID = 1


Hibernate به طور خودکار هر گونه تغییری که ما در message می دهیم را دریافت و آنرا به دیتا بیس منتقل میکند . به این ویژگی Automatic dirty checking گفته می شود . به کمک این ویژگی دیگر لازم نیست که بعد از هرگونه تغییر در یک Persistance class ، بلافاصله Update را صدا بزنیم . مانند آنچه که در کد بالا در خطوط 4 و 6 دیده میشود . 
ویژگی دیگری که Hibernate دارد ویژگی Cascading save میباشد . به کمک این ویژگی نیازی به فراخوانی متد Save() از Hibernate برای ذخیره Message جدید نیست . همانطور که در خط 5 مشاهده میکنید message جدید به طور خودکار در دیتابیس ایجاد میشود و دیگر نیازی به فراخوانی متد save() بعد از آن نیست.
نکته دیگری که باید به آن توجه داشت ترتیب اجرای دستورات SQL میباشد . این ترتیب هیچ ارتباطی به ترتیب اجرای کدهای Hibernate ندارد و خود Hibernate از الگوریتمهای پیشرفته ای برای اجرای مناسب دستورات SQL به کار میبرد تا مشکلی در ذخیره سازی و بروز رسانی اطلاعات مانند (foreign key constraint
violations)در دیتابیس ایجاد نشود .

----------


## powerboy2988

تا به اینجا نحوه برنامه نویسی Hibernate نشان داده شد . حال برای اینکه بتوان این برنامه را اجرا کرد باید ابتدا Hibernate را پیکربندی کرد . معمولا از Hibernate در اغلب مدلهای برنامه نویسی جاوا نظیر برنامه نویسی تحت وب و یا برنامه های کاربردی استفاده میشود . هرچند که مدل برنامه تحت وب رواج بیشتری دارد اما از Hibernate در مدل های کلاینت و سروری یا برنامه های Command line نیز استفاده میشود. برای اینکه به تفاوت بین این دو گونه محیط آشنا شوید اجازه دهید در بخش بعد توضیحاتی در باره این دو محیط ارائه دهیم:

•	Managed Environment

این گونه محیط ها امکان نگه داری منابعی نظیر ارتباطات دیتا بیس را دارد به گونه ای که امکان استفاده از تراکنش و امنیت را به طور declarative فراهم میسازد ( در فایلهای metadata ) . محیط هایی مانند JBoss ، BEA Weblogic نمونه هایی از این گونه محیط ها هستند .

•	Non-Managed Environment

در این گونه محیط ها امکان مدیریت منابع ، امنیت و یا تراکنش های خودکار وجود ندارد و خود برنامه موظف به مدیریت ارتباطات دیتابیس و تعریف تراکنش ها می باشد . مانند Tomcat و یا حتی یه برنامه کاربردی Command line .

Hibernate تلاش دارد تا خود را مستقل از محیط اجرا کند از این رو در محیط های Non-managed ، خود Hibernate مدیریت تراکنش ها و ارتباطات دیتابیس را به دست میگیرد و در محیط های Managed ، از قابلیتهای خود محیط اجرا برای بهره گیری از تراکنش ها و ارتباط دیتابیس استفاده میکند.
در هر حالت اولین کاری که باید انجام دهید راه اندازی hibernate است در عمل این کار با ایجاد یک SessionFactory از کلاس Configuration انجام میشود .

ایجاد یک SessionFactory

برای این کار باید از کلاس Configuration یک نمونه واحد (Single Instance ) ایجاد کرد که معمولا این کار در مرحله Initialize کردن برنامه انجام میشود . بعد از ایجاد Configuration باید مسیر فایلهای mapping مربوط به کلاسهای persistence را به آن بدهیم .
 
  
			1. Configuration cfg = new Configuration();
			2. cfg.addResource("hello/Message.hbm.xml");
			3. cfg.setProperties( System.getProperties() );
			4. SessionFactory sessions = cfg.buildSessionFactory();
 
مسیر فایلهای mapping نسبی بوده و از شاخه ریشه classpath در نظر گرفته می شود . فایلهای Mapping باید در classpath قرار داشته باشند . باید تمام فایلهای mapping را همانند خط 2 به Configuration اضافه کنیم . به جای متد addResource میتوان از متد addClass استفاده نمود که در این صورت باید مسیر کامل کلاس مورد نظر را وارد کنیم . در این حالت hibernate فرض را بر این میگذارد که فایل mapping آن کلاس در همان مسیر و با نام همان کلاس به همراه پسوند.hbm.xml وجود دارد .


			addClass(org.hibernate.auction.model.Item.class);
 


تمام چیزی که یک برنامه Hibernate برای کار با دیتابیس به آن نیاز دارد همان SessionFactory می باشد حال اگر برنامه با بیش از یک دیتابیس کار میکند باید به ازای هر دیتابیس مراحل بالا را برای ایجاد SessionFactory طی نمود . 
مرحله بعد نحوه ارتباط با دیتابیس است که باید برای Hibernate تعریف شود . برای این کار چهار روش وجود دارد :
1. در خط 3 در کد بالا به جای System.getProperties() ، یک java.util.Properties ایجاد کرده ، پارامترهای لازم را در آن مقدار دهی کنیم.
2.	پارامترهای لازم را به شکل زیر در سیستم مقدار دهی کنیم java -Dproperty=value 
3.	پارامترهای لازم را در یک فایل properties تعریف کنیم و آن فایل را در مسیر classpath قرار دهیم .
4.	پارامترهای لازم را در یک فایل بانام hibernate.cfg.xml وارد نماییم و آنرا در مسیر classpath قرار دهیم .

روش اول و دوم به ندرت استفاده میشود و بیشتر در مرحله تست برنامه مورد استفاده قرار میگیرد . اما روش سوم و چهارم بیشتر مورد استفاده قرار می گیرند . عملکرد موارد 3 و 4 شبیه به هم است و در مواردی می توان از هر دو روش 3 و 4 استفاده کرد .

----------


## powerboy2988

*ارتباط با دیتابیس* مهمترین مرحله در برنامه نویسی Hibernate است که این مرحله در محیط های Managed با محیط Non-Managed که در بالا توضیح داده شد متفاوت است. در زیر نحوه کار در هر دو محیط توضیح داده شده است .

•    Configuration in non-managed environments


در این گونه محیط ها ، نظیر Servlet Container و یا CommandLine Application ، این خود برنامه است که مسئول ایجاد ارتباط با دیتابیس می باشد. از آنجایی که Hibernate نیز بخشی از برنامه محسوب میشود لذا این وظیفه بر عهده Hibernate است .
این کار درستی به نظر نمیرسد که در هر بار ارتباط با دیتابیس ، برنامه یک Connection جدید ایجاد کند . به جای این کار برنامه باید از امکان Connection Pool استفاده کند به سه دلیل :

1.    همیشه ایجاد یک ارتباط جدید با دیتابیس کار هزینه بری است.
2.    نگهداری ارتباط های بیکار نیز کاری هزینه بر است .
3.    ایجاد PreparedStatement نیز برای بسیاری از Driver ها کاری هزینه بر است.

شکل زیر نقش Pooling را در محیط اجرای یک برنامه تحت وب نشان میدهد :



از آنجایی که در این گونه محیط ها امکان Connection pooling پیاده سازی نشده است لذا خود برنامه باید یا آنرا پیاده سازی کند و یا اینکه از یک برنامه ثالث مانند C3P0 Connection Pool برای این کار استفاده نماید. بدون Hibernate ، برنامه باید مستقیما ارتباط لازم را از Pool گرفته و با دیتابیس کار کند. 
اما با وجود Hibernate دیگر نیازی به این کار نیست . چون همانطور که در شکل زیر مشاهده میکنید این Hibernate است که با Pool در ارتباط است و برنامه از طریق Hibernate Session عملیات دیتابیسی خود را انجام میدهد .



*استفاده از Connection Poo*l

Hibernate از طریق یک برنامه واسط دیگر که در داخل خود از C3P0 برای کار با Pool استفاده میکند با دیتابیس در ارتباط است . برای این کار باید پارامترهای لازم را برای تنظیمات اولیه آن از طریق یک فایل Properties به Hibernate بدهیم .در زیر نمونه این فایل آورده شده است :
 
            hibernate.connection.driver_class = org.postgresql.Driver
            hibernate.connection.url = jdbc:postgresql://localhost/dbname
            hibernate.connection.username = user
            hibernate.connection.password = secret
            hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect
            hibernate.c3p0.min_size=5
            hibernate.c3p0.max_size=20
            hibernate.c3p0.timeout=300
            hibernate.c3p0.max_statements=50
            hibernate.c3p0.idle_test_period=3000
 


این فایل اطلاعات زیر را در اختیار Hibernate قرار میدهد :

o خط اول ، نام کلاسی است که JDBC Driver مورد نظر را پیاده سازی کرده است ( قابل ذکر است که JAR فایل مربوطه باید در مسیر CLASSPATH قرار داشته باشد)
o    خط دوم ، آدرس URL دیتابیس مربوطه را مشخص می کند.
o    خط سوم ، نام کاربری برای کار با دیتابیس.
o    خط چهارم ، رمز عبور کاربر .
o خط پنجم ، نام Dialect متناسب با دیتابیس استفاده شده را مشخص میکند. Hibernate برای اینکه بتواند Sql های متناسب را برای کار با دیتابیس ایجاد کند به Dialect نیاز دارد . به عبارت دیگر هر دیتابیس ، Dialect مختص به خود را دارد .
o    خط ششم ، کمترین تعداد ارتباط دیتابیسی را که C3P0 باید به صورت آماده در Pool نگه داری نماید.
o خط هفتم ، حد بالای Pool را برای نگه داری ارتباط دیتابیسی مشخص می کند. قابل ذکر است که اگر این پارامتر مشخص نشود در زمان اجرا ، exception اتفاق می افتد.
o    خط هشتم ، مدت زمانی که بعد از آن،  ارتباط های بیکار (Idle) از داخل Pool باید حذف شوند. این زمان به میلی ثانیه می باشد.
o    خظ نهم ، ماکزیمم تعداد PreparedStatement ، که باید نگه داشته شود . این مسئله در بهبود عملکرد Hibernate موثر می باشد.
o    خط دهم ، مدت زمان بیکاری (Idle) قبل از چک شدن خودکار Connection ها .

مشخص کردن پارامترها به شکل hibernate.c3p0.* بیان کننده این مطلب است که Hibernate از C3P0 به عنوان Connection pooling استفاده میکند. به غیر از C3P0 ، چندین Connection Pool دیگر هم وجود دارد نظیر Apache DBCP و یا Proxool . 
همچنین Hibernate از یک مکانیزم Pooling پیش فرض برای Connection Pool استفاده میکند که این روش تنها برای تست و برای برنامه های کوچک مورد استفاده قرار می گیرد و برای برنامه های واقعی که چندین درخواست همزمان وجود دارد باید از روش Pooling دیگری استفاده نمود.

*راه اندازی Hibernate

*بعد از این که پارامترهای گفته شده را در فایلی به نام hibernate.properties وارد کردید تنها کاری که باید انجام دهید اینست که این فایل را در مسیر Classpath سیستم قرار دهید . Hibernate به طور خودکار فایل مورد نظر را پیدا کرده و در مرحله Initialization از آن پارامترها استفاده میکند. این کار در زمانی که شما کلاس Configuration را ایجاد میکنید اتفاق می افتد.
به طور خلاصه در زیر مراحل پیکر بندی Hibernate در محیط های Non-managed آورده شده است : 

1. JDBC Driver متناسب با دیتابیس مورد نظر خود را دانلود کرده و فایل Jar آنرا در مسیر Classpath خود قرار دهید . همین کار را برای فایل Hibernate2.jar انجام دهید.
2. تمام Dependency های Hibernate را از داخل دایرکتوری lib داخل hibernate به داخل Classpath خود کپی کنید . لیست این Jar ها در فایل README.TXT داخل دایرکتوری lib آورده شده است.
3. نوع Connection pool ای را که مایل هستید Hibernate از آن استفاده کند را انتخاب کرده و آنرا در تنظیمات Hibernate وارد نمایید ( مانند C3P0 ) . همین کار را باید برای SQL Dialect انجام دهید .
4. فایل hibernate.properties را ایجاد و پارامترهایی را که در بخش قبل توضیح داده شد را در آن وارد کرده و آنرا در Classpath قرار دهید.
5. در برنامه خود یک Instance از کلاس Configuration ساخته و تمام فایل های .hbm برنامه را از طریق متد addResource و یا متد addClass بار گذاری کنید. سپس با فراخوانی متد buildSessionFactory از کلاس Configuration ، یک SessionFactory ایجاد کنید.

----------


## powerboy2988

- چندتا نکته!!!

1- اینها همه ترجمه کتاب Hibernate in action هستش که از سایت javasource.ir دیدم...
2- فایل hibernate.cfg.xml، رو تو مسیر کلاستون یک folder به اسم conf درست کنید و تو این folder بزارید و تو lib های پروژتون add کنید.
3- فایل Message.hbm.xml رو در کنار کلاسهاتون قرار بدین.

موفق باشید..

ولی حتما کتاب Hibernate in action رو مطالعه کنید

----------


## powerboy2988

راستی این lib های که گذاشتم رو هم حتما باید به برنامتون اضافه کنید :

----------


## powerboy2988

و اینها : 

البته 2 تا jar فایل دیگه هم هست که حجمش زیاد بود نشد اینجا بزارم 
اینم لینک اون 2تا فایل :


   hibernate3.jar :  دانلود 
hsqldb.jar : دانلود

----------


## amirjalili

با تشکر از شما..
این پست 101 بازدید داشته.. فقط 2 تا تشکر..! 
واسم جالبه که اگه هر کسی یک بار یه مقاله یا آموزش اینجا گذاشته باشه میدونه که چقدر وقت و صبر و حوصله لازمه تا یه آموزش اینطوری آماده بشه و دوستان بیان و بخوننش. این در حالی که کسایی که این آموزش ها رو اینجا قرار میدن از خودشون و وقتشون مایه میذارن .. توقع مالی و.. ندارن. پس میشه انتظار داشت که حداقل یه تشکری از این دوستان کنیم تا انگیزه ای هم باشه برای همکاری بیشتر این دوستان.

----------


## powerboy2988

1- هر كسي كه تو اين سايت يك آموزشي ميزاره توقع تشكر نداره ... دوستان اگر ياد بگيرند واسشون كلي تشكره
2- خيلي كم هستند كساني كه از java استفاده مي كنند ....

موفق باشي...

لطفا پستي قرار ندين اينجا تا كسي سر درگم نشه... ممنون از شما دوست عزيز

----------


## javadhsn

مطالب شما در خصوص تکنولوژی HIbernate به نظرم بسیار جالب، خلاصه شده و مفید بود. از زحمات شما صمیمانه تشکر می نمایم.

----------


## handinux

این اولین آموزش پایه ای و بدردبخور هایبر بود.صمیمانه متشکرم.امیدوارم مورد توجه زیاد قرار بگیرد

----------


## ermia2008

سلام
با اینکه دکمه اختصاصی و روش مکانیزه برای تشکر وجود داره ولی دلم نیومد به روش سنتی و خودمنی تشکر نکنم :
دمت گرم. عالی بود!

امیدوارم تداوم داشته باشه.

موفق باشید.

----------


## mahboub39

> 1- هر كسي كه تو اين سايت يك آموزشي ميزاره توقع تشكر نداره ... دوستان اگر ياد بگيرند واسشون كلي تشكره
> 2- خيلي كم هستند كساني كه از java استفاده مي كنند ....
> 
> موفق باشي...
> 
> لطفا پستي قرار ندين اينجا تا كسي سر درگم نشه... ممنون از شما دوست عزيز


خیلی خوب و روون توضیح دادین،ممنونم ازتون ولی می خواستم اگه کسی در مورد استفاده از هایبرنت 3.2 در   اسپرینگ 2.5 اطلاعاتی داره راهنماییم کنه.
 بازم ممنونم

----------


## hasan63

سلام
مطالبتون رو خوندم . مرسي از زحماتتون.استاندارد كاريتون ظاهرا بر اساس مديريت كمپايلر است.من در برنامه نويسي زياد حرفه اي نيستم ولي از نظر من اگر مديريت ارتباط و mapping با استفاده از فايل هاي xml باشه خيلي بهتره و دليلم هم كمپايل نشدن اون در هر بار اجراي برنامه ميدونم .زحمتتون ميشه ولي config با اين فايل رو هم آموزش بديد. موفق باشيد .
ارتباط با من :pooya.jahedi@yahoo.com :تشویق:

----------


## Ammar_iran

من عضو تيم J2OS  هستم
جداً از اینکه اینطور پست open source رو گرفتید ممنونم

----------


## pilomax

این آموزش واقع عالی بود.تنها خواهشی که دارم ادامه بدید. واقعا به این گونه آموزش ها نیازمندیم. بازم ممنون

----------


## akram23

خیلی ممنون عالی بود 
میخواستم خواهش کنم یه نمونه برنامه ساده که توIntellije نوشته شده هم لطف کنید بذارید چون من هر کاری می کنم نمی تونم این کار را در این idea انجام بدم 
با تشکر

----------

