Hibernate是一個開放源代碼的ORM(對象關系映射)框架,它對JDBC進行了輕量級的封裝,Java程序員可以使用面向對象的編程思維來操縱數(shù)據(jù)庫,它通過對象屬性和數(shù)據(jù)庫表字段之間的映射關系,將對象持久化到數(shù)據(jù)庫中,可以說Hibernate就是將數(shù)據(jù)從對象形式轉換成表字段后存入數(shù)據(jù)庫的一種框架。hibernate移植非常好,因為它用到了方言配置,可以根據(jù)不同的數(shù)據(jù)庫自動發(fā)出不同的sql。
下面是我感覺比較重要的方面就列出來了:
首先是搭建環(huán)境:
1、我建的是個Java項目,第一步先引入HIbernate的jar包:hibernate3.jar這是核心包,還有l(wèi)ib下的所有包。
2、我用oracle做測試,所以在引入oracle的驅動包,classes12.jar。
3、在從源碼包中拷貝一個hibernate.cfg.xml(放到src下,而且這個文件的配置可以參考etc/hibernate.properties 這個文件)這是Hibernate的默認配置文件名字,和User.hbm.xml(此文件跟你的pojo放在一塊)映射配置文件。
這樣Hibernate的環(huán)境基本上就搭建好了。
4、hibernate可以根據(jù)映射文件和pojo自動創(chuàng)建數(shù)據(jù)庫表(用SchemaExport這個類)。
5、hibernate默認事務是手動的,所以我們必須手動獲取事務,開啟和關閉它。
6、在hibernate中持久化對象有三個狀態(tài),這個面試時可能會問到:
(1)transient瞬時態(tài):在數(shù)據(jù)庫中沒有與之匹配的數(shù)據(jù),一般就是只new出了這個對象,并且在session緩存中也沒有即此對象沒 有納入session的管理,此狀態(tài)的對象不能直接存入數(shù)據(jù)庫(會拋出TransientObjectException)。
(2)persistent持久態(tài):在數(shù)據(jù)庫中有與之匹配的數(shù)據(jù),并且納入了session的管理(一般就是執(zhí)行了save、update、load、get 后的對象),在提交事務時(清理session緩存時)會和數(shù)據(jù)庫同步更新,持久態(tài)對象才可以存入數(shù)據(jù) 庫。
(3)detached游離態(tài):在數(shù)據(jù)庫中有與之匹配的數(shù)據(jù),但沒有納入session緩存的管理。
7、(1) 討論下hibernate的增(save)、刪(delete)、改(update)、查(get,load)。
增沒什么說的,關于刪除和修改時我們完全可以new出一個對象進行操作,只要給這個對象的標示賦值為庫中存在的對象就行了,這個過程應該是這樣的,當我們new出對象后并設置了相應的id(標示),然后執(zhí)行session.delete()或者session.update()后,此對象在session緩存中就有了一份,再當我們commit提交
事務時(清理緩存),數(shù)據(jù)庫中有同樣id標示的對象就會與session緩存中的持久態(tài)對象進行同步更新,所以這樣更新和刪除完全可以實現(xiàn),但是我們一般不能這么做, 最好是先查出來在去更新或者刪除,比如:當我們要更新一個User對象時,沒有先查出來而是用new的方法并給這個新對象設置了一個與庫中有對應ID的形式去更新,那么更新完后,如果沒有賦值的屬性都會變成null,這個可不是我們想要的結果哦。
(2)在說說get和load這兩個單一對象的查詢方法:
<1>get方法不支持lazy延遲加載,即一執(zhí)行到get方法立刻發(fā)出sql語句,而load有延遲加載,即 load方法執(zhí)行后,并不會執(zhí)行查詢sql只是返回了一個代理對象,只有等真正用到了這個對象才會發(fā)出查詢 語句。
<2>當get方法查找的對象不存在則返回null,而load會拋出異常。
共同點:load和get只能通過主鍵標示加載實體對象。
8、 數(shù)據(jù)庫的隔離級別決定了只發(fā)出插入sql而沒有提交事務后是否能select看到數(shù)據(jù)。其實只要發(fā)出了插入的sql語句,即使你 沒有提交事務,庫里面就已經有了數(shù)據(jù),只是數(shù)據(jù)庫的默認隔離級別不會讓你看到數(shù)據(jù),這時你就可以回滾事務,取消 數(shù)據(jù)插入,而且這種隔離級別還可以設置的。
9、在hibernate中,持久態(tài)對象不能引用瞬時態(tài)對象。(插入有關聯(lián)關系的對象時可能會遇到這種異常)。
10、hiberntate級聯(lián)(cascade)只對增、刪、改有作用,與查詢沒關系,默認不會級聯(lián)即值為none。
11、在hibernate中對象之間的映射關系通常都配成雙向的,比如雙向一對一外鍵關聯(lián)映射(這也是一對一中最常用的)。
12、一對一主鍵關聯(lián)默認就有級聯(lián)關系,其他關聯(lián)關系都沒有這種默認設置。
13、<many-to-one>標簽會在當前表中加字段即外鍵,而<one-to-one name="user">標簽不會加字段,
指示hibernate如何加載關聯(lián)對象,它默認就去另一張表中找主鍵與當前表主鍵相等的數(shù)據(jù)。
14、在主鍵生成器中uuid和native比較常用,它們的區(qū)別是:
(1)uuid在執(zhí)行完save后只會把此對象放在session緩存中,并為其賦id值(是一個32位字符串),它只有事務提交時 也就是清理了緩存時才會發(fā)出sql。
(2)native在執(zhí)行完save后就立刻發(fā)出插入sql語句,即此時庫中已經有了此對象數(shù)據(jù),而且native是根據(jù)本地數(shù)據(jù)庫 進行自增的,比如你用的是oracle,那它就會通過序列進行自增。
15、hibernate中的flush方法可以清理session緩存并執(zhí)行相應的sql,我們可以手動執(zhí)行,但提交事務前也會被自動執(zhí)行的。
16、hibernate中的雙向多對一和一對多是同一個概念,而且一對多和多對一都是在多的一端加個外鍵來指向一的一端,一對多單 向會發(fā)出多余的update語句(所以一般都配成雙向 )。
17、hibernate一般在持久類中用到集合映射時都用Set(放入的對象不可重復)很少用List,而且還不能用Hashset具體類去聲明 ,必須用接口Set去聲明 ,因為hibernate對Set進行了擴展。
18、hibernate映射文件的單雙向都是針對加載而言的。
19、hibernate支持lazy延遲加載(只有真正使用該對象時才會創(chuàng)建,即發(fā)出相應的sql語句),默認lazy都是true而且Session不 能關,否則lazy就失效了,如果你在Session關閉后在把延遲加載的對象輸出給頁面就會拋出異常即特別是用load(它支持lazy) 進行加載時Session關閉后在返回所查詢的對象時就會拋此異常 org.hibernate.LazyInitializationException,但用get()加 載時就不存在這問題,因為lazy的生命周期和Session是同步的,這個面試可能會問到,解決方法就是不關Session,直到查詢 的內容已經返回給頁面在關閉,Spring框架就提供了一個Filter(過濾器)實現(xiàn)了此功能即從請求訪問一直到響應返回后在關 閉Session。hibernate的延遲加載實現(xiàn)用的是第三方庫cglib.jar,這是個動態(tài)代理類庫,跟JDK的動態(tài)代理是不一樣的,JDK的 動態(tài)代理只能代理實現(xiàn)了一定的接口的類。hibernate的lazy可以應用的標簽類型有<class>、<property>、<one-to-one>、 <many-to-one>、集合。
20、get和load兩個方法在同一個session中都會用到session緩存(也就是hibernate一級緩存)在執(zhí)行這兩個方法后都會給 session緩存中放一份,即第二次查詢同一對象時不會在發(fā)出sql,只會從一級緩存中直接讀取數(shù)據(jù),一級緩存只緩存實體對象 ,不緩存普通屬性,而且一級緩存的生命周期和Session的生命周期是一致的,不同的Session之間不能共享Session緩存中的數(shù) 據(jù)。
21、session.save()時也用到了一級緩存,即添加完后會給session緩存中放一份對象。
22、一級緩存不能控制,包括設置容量、或者取消,只能通過session.clear()等方法清空它。一級緩存是session緩存即同一個 session共享,二級緩存是進程級的緩存即SessionFactory級緩存(它的生命周期和SessionFactory一致),不同session之間 是可以共享的,它們的共同點是都緩存對象,不緩存普通屬性。Hibernate的二級緩存集成了第三方庫,比如:ehcache,而且 使用時必須配置(如:配置用哪種二級緩存,哪些實體類使用二級緩存)。
二級緩存的簡單示例:
比如當二級緩存沒配置時(默認是開啟的)——>我們用load或者get查詢時先會去一級緩存中查,沒有找到再發(fā)出SQL。
當二級緩存開啟并配置后——>查詢時先去一級緩存,如果沒有在去二級緩存找,再沒有就發(fā)出相應的SQL.。
當二級緩存開啟并配置后,load和get查詢默認后會給一級緩存和二級緩存中都放一份(可以配置不往二級緩存中放),二級緩存可以被SessionFactory去直接管理,比如:清空。
Hibernate還有一種緩存叫查詢緩存(Hibernate默認是關閉的)此緩存對Query.iterate()不起作用,它是緩存普通屬性和對象id標示的,它的生命周期不可控制(與session的生命周期沒有關系的),利用率不高,此緩存使用時必須配置。
忠告:Hibernate用不好會發(fā)出N多條sql語句,效率會大大降低。
23、用Query或者save操作大量實體對象時給Session緩存中都會放一份,這樣會影響效率,很可能內存溢出,所以大量數(shù)據(jù)操作不 適合用hibernate。
24、每次執(zhí)行l(wèi)ist()默認都會像數(shù)據(jù)庫發(fā)出sql語句,但它會一次性把集合中的所有元素都查出來,它只會為一級緩存中放數(shù)據(jù), 但不會從緩存中取,而iterate()方法會先從緩存中取,如果緩存中沒有就會很可能發(fā)生N+1問題。
N+1問題:1—>首先發(fā)出一條查詢對象id列表的sql。N—>根據(jù)id的列表一個一個去一級緩存中查找,如果沒有就每個id發(fā)出一條sql,即N個id就發(fā)出N條查詢語句。
25、hibernate 還支持HQL語句外置,即把HQL語句放到任何一個的hbm.xml映射文件中。
26、hibernate 還支持查詢過濾器,即在相應的hbm.xml映射配置文件中配置條件后,在執(zhí)行action中的HQL查詢時就會自動加上 映射文件中配置的條件。
27、hibernate 也帶left join和right join,左連接就是左邊表全部顯示,右邊為空的就顯示null。右連接正好相反。
28、HQL也支持DML風格語句,即用HQL的update、delete等,這個不建議使用,關鍵是我也沒用過,嘿嘿。
29、hibernate的抓取策略默認就是fetch="select",意思是用到關聯(lián)對象時,會再發(fā)一條select查詢語句。如果手動設置 fetch="join"抓取策略,Hibernate會通過一條select語句使用外連接直接查出實體對象和與其關聯(lián)的對象或者集合(即使你 不使用那些相關聯(lián)的對象,照樣會一次性查出來,影響效率)。
Hibernate還有其他抓取策略,等用到了再去看吧!
30、hibernate 還可以配置批量查詢、更新,即在配置文件中可以配置一個最大返回記錄數(shù),一般可以設為50,意思就是當你一次 性查詢10000條數(shù)據(jù)時,底層數(shù)據(jù)庫都是50條50條的給你返回,這個功能需要特定的數(shù)據(jù)庫支持才行。
31、Hibernate的映射配置文件還是比較復雜,但它有注解配置,這個現(xiàn)在用的也非常多,個人也比較喜歡這種配置方式。
32、最后再說下Hibernate還集成了一個測試工具類,junit這是個第三方庫,只要我們寫的類繼承了TestCase,然后
方法必須是公共的返回值為空,且方法名以小寫test開頭,不能帶參數(shù)。(類名無限制,但推薦最好以大寫的Test結尾),這樣就可以很方便的進行測試了。
下面貼出一些測試用例代碼:
第一、hibernate.cfg.cml是Hibernate的主配置文件(這個名字可以隨便起):
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- 配置與數(shù)據(jù)庫連接有關的信息,并引入映射文件,此配置文件的屬性都比較簡單就不用再38了 -->
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:ORCL9I</property>
<property name="connection.username">test</property>
<property name="connection.password">test</property>
<property name="dialect">org.hibernate.dialect.Oracle9Dialect</property>
<!--下面這個屬性配置成update后,則我們從新生成表時,沒有改變的表將不會被drop(刪了從建),只會新建有變動的表,而且這個屬性設置后我們也不用hibernate的生成表工具(SchemaExport)去執(zhí)行創(chuàng)建表,而是當我們插入數(shù)據(jù)時,hibernate會自動幫我們先創(chuàng)建表,再去插入數(shù)據(jù)。 -->
<property name="hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<mapping resource="many_to_many_shuangxiang/User.hbm.xml"/>
<mapping resource="many_to_many_shuangxiang/Role.hbm.xml"/>
</session-factory>
</hibernate-configuration>
第二、列出Hibernate中比較復雜的多對多雙向關聯(lián)映射配置(如果我是項目經理,這些對象的所有關心都不會配置,呵呵)
就以用戶和角色為例:
1、用戶的映射文件如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 加入這個package屬性后,下面就不用寫全包名了 -->
<hibernate-mapping package="many_to_many_shuangxiang">
<class name="User" table="t_user">
<id name="userId">
<generator class="uuid"/>
</id>
<property name="userName"/>
<!-- set 中的table會創(chuàng)建個中間表-->
<set name="roles" table="user_role">
<!-- 下面這個key是中間表中的外鍵,引用當前表的主鍵 -->
<key column="userId"/>
<!-- many-to-many中的column也是中間表的外鍵(中間表用的是復合主鍵),引用class屬性中持久類Role對應表的主鍵 -->
<many-to-many class="Role" column="roleId"/>
</set>
</class>
</hibernate-mapping>
2、角色的映射文件如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 加入這個package屬性后,下面就不用寫全包名了 -->
<hibernate-mapping package="many_to_many_shuangxiang">
<class name="Role" table="t_role">
<id name="roleId">
<generator class="uuid"/>
</id>
<property name="roleName"/>
<set name="users" table="user_role">
<key column="roleId"/>
<many-to-many class="User" column="userId"/>
</set>
</class>
</hibernate-mapping>
第三、Hibernate根據(jù)持久化類(pojo+映射文件)去創(chuàng)建對應的數(shù)據(jù)庫表:
public static void main(String[] args) {
//加載hibernate的配置文件
//Configuration cfg = new Configuration();這樣會默認讀取的是properties文件;
Configuration cfg = new Configuration().configure();//調用了這個方法后才會默認去讀取hibernate.cfg.xml配置文件。
SchemaExport export = new SchemaExport(cfg);//用這個類就可以生成數(shù)據(jù)庫表。
export.create(true,true);//此方法生成表。
}
第四、SessionFactory是個重量級對象,所以最好在整個系統(tǒng)就只創(chuàng)建一次,再給Session設計個單態(tài)模式:
public class SessionUtil {
private static SessionFactory factory;
private static Session session;
static{
Configuration cfg = new Configuration().configure();
factory = cfg.buildSessionFactory();
session = factory.openSession();
}
public static Session getSession(){
return session;
}
public static void closeSession(Session session){
if(session != null) session.close();
}
}
第五、Hibernate 的增、刪、改、查:
增——>session.save(user);
刪——>session.delete(user),這個通常是先查出來
改——>session.update(user),同樣是先查出來,此時可以不調用update(),set()修改完屬性后直接提交事務就行了。
查——>session.load(user.class,id) , session.get(user.class,id)個人比較喜歡用這種方式。;
第六、列出常用的HQL查詢語句:
1、簡單實體列表查詢:
//里面?zhèn)鞯氖荋QL查詢語言,這里的User類跟此類沒在同一個包下,hibernate會默認找到User所在的包。
Query query = session.createQuery("from User");
List list = query.list();
2、Hibernate分頁查詢:
//里面?zhèn)鞯氖荋QL查詢語言,查詢的直接是對象集合。
Query query = session.createQuery("from User u order by u.userName");
query.setFirstResult(0);//設置從庫中第一條數(shù)據(jù)開始查詢,即傳0,在數(shù)據(jù)庫中是從0開始的。
query.setMaxResults(2); //設置每次查詢的記錄數(shù)為2,即查詢的對象個數(shù)。
List list = query.list();
3、單個實體查詢:
User user = (User)session.createQuery("from User as u where u.userId='uuid1'").uniqueResult();
4、對象屬性查詢(直接用iterate()方法):
Iterator iterator = session.createQuery("select u.userName from User u where u.userId=? and u.userName=?")
.setString(0,"8a88831127f5fd8d0127f5fd90b20003")
.setString(1,"馬文濤")//這兩個參數(shù)就可以從頁面?zhèn)鬟^來
.iterate();
while(iterator.hasNext()){
String userName = (String)iterator.next();
System.out.println(userName);
}
5、使用where、like、分組、過濾、排序、內置函數(shù)進行條件查詢
List list = session.createQuery("select count(u),u.userName from User u where u.userName " +
"like '%文%' group by u.userName having count(u)>2 order by u.userName").list();
for(Iterator i = list.iterator();i.hasNext();){
Object[] o = (Object[])i.next();
long count = (Long)o[0];//這個count()返回的類型是long,不能用int接受。
String name = (String)o[1];
System.out.println(name+"它的個數(shù)為:"+count);
}:
/*這里的List返回的是一個對象數(shù)組(Object[]),數(shù)組長度就是select返回的屬性類型個數(shù),
數(shù)組元素中的類型就跟對應屬性的類型一致。*/
6、用hibernate提供的內部函數(shù)包含(in)查詢:
List list = session.createQuery("select userName from User where userName in(:name)")
.setParameterList("name",new Object[]{"寶寶","濤哥"})
.list();
7、用hibernate對象導航查詢,用點進行導航(u.userName)如果有其他屬性都可以點。
List list = session.createQuery("select u.userName from User u where u.userName=:name")
.setParameter("name","寶寶")
.list();
for(Iterator i = list.iterator();i.hasNext();){
String userName = (String)i.next();
System.out.println(userName);
}
好啦,不能在寫了,剛搬了房子公交車很不方便,晚上209路居然7點半就是最后一趟了,昨天都意外打出租了,嘿嘿!
本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請
點擊舉報。