Hibernate 學(xué)習(xí)筆記
一 hibernate配置
1.下載所需包
下載hibernate2.13, hibernate-extension2.13,Middlegen(已裝了jdk和ant)
2.創(chuàng)建數(shù)據(jù)庫(例子:student;course,stu_cou)
Student(id,name)
Course(id,name)
Stu_cou(stu_id,cou_id)(分別是student表和course表的外健)
3.使用MiddleGen(見附錄)
二 hibernate映射關(guān)系(many-to-many)
1. 分析inverse參數(shù)(student.hbm.xml course.hbm.xml)
inverse該參數(shù)用來負(fù)責(zé)映射之間的關(guān)系,設(shè)置為false的一方可以對(duì)他們的關(guān)系進(jìn)行維護(hù)
設(shè)置inverse = false的實(shí)體,表示在映射中是它是主動(dòng)關(guān)系,由它來維戶關(guān)系中的數(shù)據(jù)
設(shè)置inverse = true 的實(shí)體,表示在映射中是它是被動(dòng)關(guān)系,它不維護(hù)關(guān)系中的數(shù)據(jù)
1) inverse=false的情況
a) 添加記錄的情況
student.hbm.xml是主動(dòng)方,由它來決定stu_cou表中的記錄,故inverse=false
當(dāng)向stu_cou中寫入記錄時(shí)候,
/*
* 測試目的:在stu_cou中添加一組對(duì)應(yīng)關(guān)系(student,course)
* 測試結(jié)果:當(dāng)執(zhí)行完該段代碼后
* 如stu_cou中沒有(student,course)時(shí),自動(dòng)加入(student,course)
* 如stu_cou中存在(student,course)時(shí),什么都不作,不會(huì)存在主健沖突
Student student=(Student)session.load(Student.class,student2.getId())
Course course=(Course)session.load(Course.class,course2.getId());
student.getCourses().add(course);
session.save(student);
執(zhí)行完后,hibenrate成功顯示
Hibernate: select course0_.id as id0_, course0_.name as name0_ from course course0_ where course0_.id=?
Hibernate: select student0_.id as id0_, student0_.name as name0_ from student student0_ where student0_.id=?
Hibernate: select courses0_.cid as cid__, courses0_.sid as sid__ from stu_cou courses0_ where courses0_.sid=?
Hibernate: insert into stu_cou (sid, cid) values (?, ?)
b) 刪除記錄的情況
Student student=(Student)session.load(Student.class,student2.getId())
Course course=(Course)session.load(Course.class,course2.getId());
student.getCourses().讓remove(course);
session.save(student);
代碼執(zhí)行完后,可以成功執(zhí)行,hibernate顯示:
Hibernate: select course0_.id as id0_, course0_.name as name0_ from course course0_ where course0_.id=?
Hibernate: select student0_.id as id0_, student0_.name as name0_ from student student0_ where student0_.id=?
Hibernate: select courses0_.cid as cid__, courses0_.sid as sid__ from stu_cou courses0_ where courses0_.sid=?
Hibernate: delete from stu_cou where sid=?
2) inverse=true的情況
student.hbm.xml是被動(dòng)方,設(shè)置它的inverse=true
a) 添加記錄情況
* 測試目的:在stu_cou中添加一組對(duì)應(yīng)關(guān)系(student,course)
* 測試結(jié)果:當(dāng)執(zhí)行完該段代碼后
* 如stu_cou中沒有(student,course)時(shí),自動(dòng)加入(student,course)
* 如stu_cou中存在(student,course)時(shí),什么都不作,不會(huì)存在主健沖突
Student tudent=(Student)session.load(Student.class,student2.getId());
Course course=(Course)session.load(Course.class,course2.getId());
student.getCourses().add(course);
session.save(student);
代碼執(zhí)行完后,hibernate顯示,沒有插入記錄
Hibernate: select course0_.id as id0_, course0_.name as name0_ from course course0_ where course0_.id=?
Hibernate: select student0_.id as id0_, student0_.name as name0_ from student student0_ where student0_.id=?
Hibernate: select courses0_.cid as cid__, courses0_.sid as sid__ from stu_cou courses0_ where courses0_.sid=?
b) 刪除記錄情況
當(dāng)向stu_cou中刪除記錄時(shí)候,
Student student=(Student)session.load(Student.class,student2.getId())
Course course= (Course)session.load(Course.class,course2.getId());
student.getCourses().remove(course);
session.save(student);
執(zhí)行完,hibernate顯示如下,證明沒有刪除記錄
Hibernate: select course0_.id as id0_, course0_.name as name0_ from course course0_ where course0_.id=?
Hibernate: select student0_.id as id0_, student0_.name as name0_ from student student0_ where student0_.id=?
Hibernate: select courses0_.cid as cid__, courses0_.sid as sid__ from stu_cou courses0_ where courses0_.sid=?
Hibernate: select course0_.id as id0_, course0_.name as name0_ from course course0_ where course0_.id=?
Hibernate: select course0_.id as id0_, course0_.name as name0_ from course course0_ where course0_.id=?
2. 分析cascade參數(shù)(student.hbm.xml course.hbm.xml)
只有設(shè)置了inverse=false才可以操縱stu_cou的數(shù)據(jù)
當(dāng)inverse=true的時(shí)候,下列情況1中b)提示錯(cuò)誤
原因: stu_cou中存在外健約束,你無法直接刪除student表中的數(shù)據(jù)
1) cascade=save-update的情況
a)添加記錄情況
Set set=new HashSet();
set.add(course1);
set.add(course2);
set.add(course3);
student3.setCourses(set);
session.save(student3);
執(zhí)行后hibernate提示:
Hibernate: insert into student (name) values (?)
Hibernate: insert into course (name) values (?)
Hibernate: insert into course (name) values (?)
Hibernate: insert into course (name) values (?)
Hibernate: insert into stu_cou (sid, cid) values (?, ?)
可見,插入了student,course,和stu_cou三個(gè)表
b)刪除記錄情況
Student student=(Student)session.load(Student.class,new Integer(22));
session.delete(student);
成功執(zhí)行后,hibernate提示:
Hibernate: select student0_.id as id0_, student0_.name as name0_ from student student0_ where student0_.id=?
Hibernate: delete from stu_cou where sid=?
Hibernate: delete from student where
2) cascade=all的情況
a) 添加記錄情況
Set set=new HashSet();
set.add(course1);
set.add(course2);
set.add(course3);
student3.setCourses(set);
session.save(student3);
執(zhí)行后hibernate提示:成功添加級(jí)聯(lián)關(guān)系
Hibernate: insert into student (name) values (?)
Hibernate: insert into course (name) values (?)
Hibernate: insert into course (name) values (?)
Hibernate: insert into course (name) values (?)
Hibernate: insert into stu_cou (sid, cid) values (?, ?)
b) 刪除記錄情況
Student student=(Student)session.load(Student.class,new Integer(22));
session.delete(student);
測試結(jié)果(所有和student(22)相關(guān)的記錄均被刪除)
刪除student表中的student(22)記錄
刪除stu_cou表中的student(22)的記錄
刪除course表中的student(22)對(duì)應(yīng)的course的記錄
2) cascade=none情況
a) 添加記錄情況
Set set=new HashSet();
set.add(course1);
set.add(course2);
set.add(course3);
student3.setCourses(set);
session.save(student3);
失敗,因?yàn)闆]有級(jí)聯(lián)關(guān)系,所以course記錄添加不進(jìn)去,造成外健失敗
b) 刪除記錄情況
Student student=(Student)session.load(Student.class,new Integer(22));
session.delete(student);
正常,刪除了student表中的記錄student(22),同時(shí)刪除了stu_cou中對(duì)應(yīng)的student(22)的記錄
附錄
1.MiddleGen的使用
1環(huán)境變量設(shè)置
%Hibernate_Home%/lib/*.jar到%MiddleGen%/lib下
%Hibernate_Home%/hibernate2.jar到%MiddleGen%/lib下
%Hibernate-Extension_Home%/tools/lib/*.jar到%MiddleGen%/lib下
%Hibernate-Extension_Home%/tools/hibernate-tools.jar到%MiddleGen%/lib下
2 MiddleGen的配置
配置目標(biāo)數(shù)據(jù)庫參數(shù)
進(jìn)入MiddleGen 目錄下的\config\database 子目錄,根據(jù)我們實(shí)際采用的數(shù)據(jù)庫打開對(duì)應(yīng)的配置文件。如這里我用的是mysql數(shù)據(jù)庫,對(duì)應(yīng)的就是mysql.xml文件。
<property
name="database.script.file" value="${src.dir}/sql/${name}-mysql.sql"/>
<property
name="database.driver.file" value="${lib.dir}/mysql.jar"/>
<property
name="database.driver.classpath" value="${database.driver.file}"/>
<property
name="database.driver" value="org.gjt.mm.mysql.Driver"/>
<property
name="database.url" value="jdbc:mysql://localhost/sample"/>
<property
name="database.userid" value="user"/>
<property
name="database.password" value="mypass"/>
<property
name="database.schema" value=""/>
<property
name="database.catalog" value=""/>
<property
name="jboss.datasource.mapping" value="mySQL"/>
其中下劃線標(biāo)準(zhǔn)的部分是我們進(jìn)行配置的內(nèi)容,分別是數(shù)據(jù)url以及數(shù)據(jù)庫用
戶名和密碼。
1) 修改Build.xml
修改MiddleGen 根目錄下的build.xml 文件,此文件是Middlegen-Hibernate 的Ant構(gòu)建配置。Middlegen-Hibernate將根據(jù)build.xml文件中的具體參數(shù)生成數(shù)據(jù)庫表映射文件。可配置的項(xiàng)目包括:
a) 目標(biāo)數(shù)據(jù)庫配置文件地址
查找關(guān)鍵字 ”!ENTITY”,得到:
<!DOCTYPE project [
<!ENTITY database SYSTEM
"file:./config/database/hsqldb.xml">
]>
默認(rèn)情況下,MiddleGen 采用的是hsqldb.xml,
將其修改為我們所用的數(shù)據(jù)庫配置文件(mysql.xml):
<!DOCTYPE project [
<!ENTITY database SYSTEM
"file:./config/database/mysql.xml">
]>
b) Application name
查找:
<property value="airline"/>
“aireline”是MiddleGen原始配置中默認(rèn)的 Application Name,將其修改為我們
所希望的名稱,如“HibernateSample”:
<property value="HibernateSample"/>
c) 輸出目錄
查找關(guān)鍵字“name="build.gen-src.dir"”,得到:
<property
value="${build.dir}/gen-src"/>
修改value="${build.dir}/gen-src"使其指向我們所期望的輸出目錄,
這里我們修改為:<property
value="C:\sample"/>
d) 對(duì)應(yīng)代碼的Package name
查找關(guān)鍵字“destination”,得到:
<hibernate
destination="${build.gen-src.dir}"
package="${name}.hibernate"
genXDocletTags="false"
genIntergratedCompositeKeys="false"
javaTypeMapper=
"middlegen.plugins.hibernate.HibernateJavaTypeMapper"
/>
可以看到,hibernate 節(jié)點(diǎn)package 屬性的默認(rèn)設(shè)置實(shí)際上是由前面的
Application Name (${name})和“.hibernate”組合而成,根據(jù)我們的需要,
這里還有一個(gè)屬性genXDocletTags,如果設(shè)置為true,則生成的代碼將包含
xdoclet tag,這為以后在開發(fā)過程中借助xdoclet進(jìn)行映射調(diào)整提供了幫助。注意,如果使用的數(shù)據(jù)庫為SQLServer,需要將build.xml部分刪除(參見夏?^的文檔),否則Middlegen會(huì)報(bào)出找不到表的錯(cuò)誤。
至此為止,MiddleGen 已經(jīng)配置完畢,在MiddleGen 根目錄下運(yùn)行ant,就將出現(xiàn)
MiddleGen的界面:
2 *.xml到.java
在得到.xml后,使用ant hbm2java 即可得到相應(yīng)的java文件
總結(jié):
我第一次運(yùn)行成功,但是后來則出現(xiàn)如下類似錯(cuò)誤,檢查發(fā)現(xiàn)在MiddleGen的lib目錄下存在多個(gè)類似的jar包
(velocity.jar,velocity1-0.8.jar),刪除舊的jar包后,問題解決
[middlegen] java.lang.IncompatibleClassChangeError
[middlegen] at [middlegen] at org.apache.velocity.Template.process(Template.java:136)
2.(評(píng)論)關(guān)于Many-to-Many需要注意的問題
比如group/user,user/role,group/role,都是多對(duì)多的關(guān)系。
所以有必要搞搞清楚。以group/user為例
(1)group.getUsers() & user.getGroups()雙向映射。在這里,考慮到user.setGroups()是不會(huì)放出來的。所以在user端加入inverse=true,就是說,讓Group端來維護(hù)兩者的關(guān)系表。
(2)于是,添加的時(shí)候,要把user加入到group的user set中,保存,才有意義!
(3)如果要?jiǎng)h除group_user的關(guān)系,而不是group/user本身,那么,需要在group端設(shè)定casade=save-update。否則,group.getUsers().remove(user)不會(huì)起作用!
(4)由于有了FK保護(hù),你是無法單獨(dú)刪除User的。同時(shí),由于你指定了inverse=true,所以無法光光的設(shè)置user.getGroups().clear就能夠搞定一切。你必須首先在每個(gè)Group.getUsers中刪除這個(gè)user,才能最后刪除。所以,UserManagerImpl代碼可能看起來像這樣:
public void delUser(User user) {
if (user == null) {
logger.info("Got a null user object!");
return;
}
Session session = null;
Transaction trans = null;
boolean bSuccess = false;
try {
String username = new String(user.getName());
session = PLUtils.getSession();
trans = session.beginTransaction();
session.update(user);
Set groups = ((UserEntity)user).getGroups();
for(Iterator i = groups.iterator();i.hasNext();){
GroupEntity g = (GroupEntity)i.next();
session.update(g);
g.getUsers().remove(user);
session.update(g);
}
// ((UserEntity)user).getGroups().clear();
session.delete(user);
bSuccess = true;
logger.debug("User " + username + " deleted ok!");
}
catch (ObjectNotFoundException onfe) {
logger.info("user not found!", onfe);
}
catch (Exception e) {
logger.error("failed!", e);
}
finally {
try {
if (bSuccess)
trans.commit();
else
trans.rollback();
session.close();
}
catch (Exception ex) {
}
}
}