(1)配置hibernate-cfg.xml java
<hibernate-configuration> mysql
<session-factory> sql
<!-- hibernate的方言,用來肯定鏈接的數據庫 --> 數據庫
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> 數組
<!-- 數據庫的鏈接類 --> 緩存
<property name="hibernate.connection.driver_class"> 安全
com.mysql.jdbc.Driver session
</property> 併發
<!-- 數據庫的鏈接字符串和用戶名密碼 --> app
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/itat_hibernate
</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">
123456
</property>
<!-- 在使用hibernate時會顯示相應的SQL -->
<property name="show_sql">true</property>
<!-- 會自動完成類到數據表的轉換 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 加入實體類的映射文件 -->
<mapping resource="org/zttc/itat/model/User.hbm.xml"/>
<mapping resource="org/zttc/itat/model/Book.hbm.xml"/>
</session-factory>
</hibernate-configuration>
(2)建立實體映射文件:
(略)
(3)代碼編寫:建立SessionFatory:
SessionFactory是線程安全的,因此SessionFatory要基於單利模式來建立。
@Test
public void test01() {
Configuration cfg = new Configuration().configure();
//cfg.buildSessionFactory();
//在hibernate3中都是使用該種方法建立,可是在4中被禁用了,使用如下方法
ServiceRegistry serviceRegistry =
new ServiceRegistryBuilder()
.applySettings(cfg.getProperties()).buildServiceRegistry();
SessionFactory factory =
cfg.buildSessionFactory(serviceRegistry);
Session session = null;
try {
session = factory.openSession();
//開啓事務
session.beginTransaction();
User u = new User();
u.setNickname("張三");
session.save(u);
//提交事務
session.getTransaction().commit();
} catch (HibernateException e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
if(session!=null) session.close();
}
}
【總結】:
臨時狀態 à 持久狀態:作save操做;
離線狀態 à 臨時狀態:作delete操做;
有時候不知道對象是離線仍是臨時狀態,這時候能夠調用session的saveandupdate()方法
l Transient:是指臨時狀態,即new出一個對象,可是數據庫中尚未這個對象;
l Persistent:new一個對象並save後就是Persistent狀態(持久化狀態);
l Detached:當session關閉而且對象保存在了數據庫中,這時候就是離線狀態。
關於Persistent:
@Test
public void testTransient() {
Session session = null;
try {
session = HibernateUtil.openSession();
session.beginTransaction();
User u = new User();
u.setBorn(sdf.parse("1976-2-3"));
u.setUsername("zxl");
u.setNickname("趙曉六");
u.setPassword("123");
//以上u就是Transient(瞬時狀態),表示未被session管理且數據庫中沒有
//save以後,被session所管理,且數據庫中已經存在,此時就是Persistent狀態
session.save(u); //u變成持久化狀態,被sessio管理,正常保存
u.setNickname("趙曉其");//更新緩存內容,提交時候更新數據庫
//若是沒有提交那麼上面的(從save開始)就是持久化狀態,session中有完整的對象的//信息處於持久化狀態的對象設置了新數據,提交的時候就會作更新操做
session.getTransaction().commit();//致使更新操做執行
} catch (Exception e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
HibernateUtil.close(session);
}
}
繼續解釋:
@Test
public void testPersistent02() {
Session session = null;
try {
session = HibernateUtil.openSession();
session.beginTransaction();
User u = new User();
u.setBorn(sdf.parse("1976-2-3"));
u.setUsername("zxq");
u.setNickname("趙曉八");
u.setPassword("123");
//save後緩存(session)保存這個u對象信息
session.save(u);
u.setPassword("222");//不會致使緩存中內容變化
//緩存內容沒有變化,因此不會執行(這部分有些不明白??)
session.save(u);
u.setNickname("趙曉吧");
//下面語句不執行(持久化狀態的update不執行,提交時候纔會執行update)
session.update(u);
u.setBorn(sdf.parse("1988-12-22"));
//沒有意義
session.update(u);
//執行update
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
HibernateUtil.close(session);
}
}
load出來的對象也是持久化對象:
//此時u是Persistent
User u = (User)session.load(User.class, 10);
@Test
public void testDetach05() {
Session session = null;
try {
session = HibernateUtil.openSession();
session.beginTransaction();
User u = new User();
//u.setId(110);
u.setNickname("abc");
//若是u是離線狀態就執行update操做,若是是瞬時狀態就執行Save操做
//可是注意:該方法並不經常使用
session.saveOrUpdate(u);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
HibernateUtil.close(session);
}
}
當使用load加載一個對象的時候,若是取出的對象沒有使用,那麼就不會發sql——這既是Hibernate的「延遲加載(懶加載)」。且,該對象實際上是一個代理對象,該對象中只有一個id的值,若打印該id也不會發sql(不用從數據庫中取)。
@Test
public void testLazy02() {
Session session = null;
try {
session = HibernateUtil.openSession();
User u = (User)session.load(User.class, 1);
//此時一條sql都沒有發,這就是hibernate的延遲加載
/**延遲加載指的就是,當完成load操做以後,並不會馬山發出sql語句,只有在使用到該對象時纔會發出sql,當完成load以後,u實際上是一個代理對象,這個代理對象中僅僅只有一個id的值*/
//此時不會發sql
System.out.println(u.getId());
//nickname沒有值,必須去數據庫中取。因此會發出sql
System.out.println(u.getNickname());
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
【例】:
@Test
public void testLazy04() {
Session session = null;
try {
session = HibernateUtil.openSession();
//get是隻要一執行就會發出sql,get沒有延遲加載
User u = (User)session.get(User.class, 101);
//get的時候發現數據庫中並無該數據,因此u是null,打印u.getId(),會拋出空指針異常,若是是使用load的話,會打印101(代理對象存儲了id的值)
System.out.println(u.getId());
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
【例】:
@Test
public void testLazy05() {
Session session = null;
try {
session = HibernateUtil.openSession();
User u = (User)session.load(User.class, 101);
//因爲id已經存在,因此不會拋出異常
System.out.println(u.getId());
//此時會去數據庫中取數據,發現沒有這個對象,可是u並非空,因此會拋出ObjectNotFoundException
System.out.println(u.getNickname());
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
【情景演示】:
Dao中的load方法:
public User load(int id) {
Session session = null;
User u = null;
try {
session = HibernateUtil.openSession();
u = (User)session.load(User.class, 1);
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
return u; //這裏返回的僅是一個代理對象,且此時session已經關閉
}
控制器中調用這個方法用來在頁面顯示:
@Test
public void testLazyQuestion() {
UserDao ud = new UserDao();
/*因爲使用了load,load是有延遲加載的,返回的時候的u是一個代理對象,僅僅只有一個id
* 可是在返回的時候Session已經被關閉了,此時當須要使用u的其餘屬性時就須要去數據庫中
* 可是Session關閉了,因此就拋出org.hibernate.LazyInitializationException no session
*/
User u = ud.load(2);
System.out.println(u.getUsername());
}
解決方法:
方法1:使用get方法;
方法2:在過濾器中獲取session放到context中,而後在過濾器中關閉。Spring中使用openSessionInView。
<hibernate-mapping package="org.zttc.itat.model">
<class name="Book" table="t_book">
<id name="id">
// assigned:表示要手動指定主鍵
// uuid:表示自動生成一個uuid字符串,因此主鍵必須是String
// native:也是自動生成,生成的是1、2、3等有序列,因此檢索會快些
// native的缺點是每次插入一條數據庫都會查詢一下數據庫,開發中查詢
// 操做會比較多,因此開發中會使用native
<generator class="uuid"/>
</id>
<property name="name"/>
<property name="price"/>
<property name="bookPage" column="book_page" type="int"/>
</class>
</hibernate-mapping>
【提示】:
l 實體字段名,好比bookName,映射到數據庫最好映射成book_name
<property name="bookPage" column="book_page" type="int"/>
l 在實際開發中,通常不會使用Hibernate來生成表,通常的開發中,都是首先用PowerDesigner設計好數據庫,而後生成SQL語句,而後用這個SQL語句來生成表。
這樣的好處是,在開發數據庫的時候能夠爲數據庫建立索引和視圖。
l 對效率要求不高的項目適合使用Hibernate;
l 對效率比較高的項目,若是使用Hibernate的話,若想要效率也高,則能夠這麼作:增刪改使用Hibernate,而查詢使用原生態的SQL。
l 寫Hibernate語句時,好比清楚一句Hibernate代碼執行了幾條SQL語句,不然性能可能大降!
l 關係通常是由「多」的一方來維護;
<!-- inverse=true表示不在本身這一端維護關係 -->
<set name="stus" lazy="extra" inverse="true">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
l 添加的時候,通常都是先添加「一」的一方;
l 懶加載會多發一條SQL語句(抓取策略能夠只發一條SQL語句……?)
l 關聯(cascade):不要在「多」的一方使用關聯。由於,好比,刪除多的一方的一條記錄,同時會刪除「一」的一方對應的記錄,可是若是「一」的一方的該條記錄又關聯了其餘記錄的話,就不能刪除了。使用cascade的狀況通常是:在「一」的一方刪除時使用,特殊狀況纔會在add上作級聯。
<property name="title"/>
<property name="content"/>
<!-- 使用了lazy=extra以後會稍微智能一些,會根據去的值的不一樣來判斷是調用count和獲取投影 ,好比取出總條數通常狀況下會取出全部數據的list而後計算list的size,可是用了extra後就會直接count(*)-->
<set name="comments" lazy="extra">
<!-- key用來指定在對方的外鍵的名稱 -->
<key column="mid"/>
<!-- class用來設置列表中的對象類型 -->
<one-to-many class="Comment"/>
</set>
</class>
(略)
l 一個Hibernate項目是使用註解仍是Annotation呢?
——小項目使用註解,大項目(一兩百萬行代碼的)使用xml。由於大項目的類比較多,看xml文件更方便。大項目不用外鍵的方式????
@Entity
@Table(name="t_classroom")
public class Classroom {
private int id;
private String name;
private int grade;
private Set<Student> stus;
// mappedBy表示關係由對方的classroom屬性維護,若是沒有mappedBy的話,會生
// 成中間表
@OneToMany(mappedBy="classroom")
// 使用extra後sql語句會較智能,好比計算條數會自動使用count(*)
@LazyCollection(LazyCollectionOption.EXTRA)
public Set<Student> getStus() {
return stus;
}
public void setStus(Set<Student> stus) {
this.stus = stus;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
}
@Entity
@Table(name="t_student")
public class Student {
private int id;
private String name;
private String no;
private Classroom classroom;
@Id
@GeneratedValue
public int getId() {
return id;
}
// fetch的默認值是EAGER,表示不使用懶加載,一條語句就能把相關的數據查詢
// 出來;若是值設置爲LAZY的話,就會使用懶加載,會多發一條sql
@ManyToOne(fetch=FetchType.LAZY)
// 在本身這邊生成的外鍵名是cid,哪一方維護關係就在哪一方加JoinColumn(就是
// 外鍵的意思)通常都由「多」的一方維護關係,即在本身這方配置對方的外鍵。
@JoinColumn(name="cid")
public Classroom getClassroom() {
return classroom;
}
}
(主要內容就是使用Extra的問題,查詢屬於教室的學生的數目)。
以上都不須要中間表,而這個「多對多」關係須要中間表。
【提示】:
多對多關係開發中通常不用,而是使用兩個一對多關係來代替。
@Entity
@Table(name="t_admin")
public class Admin {
private int id;
private String name;
private Set<Role> roles;
@Id
@GeneratedValue
public int getId() {
return id;
}
// 設置由對方維護關係
@ManyToMany(mappedBy="admins")
public Set<Role> getRoles() {
return roles;
}
}
@Entity
@Table(name="t_role")
public class Role {
private int id;
private String name;
private Set<Admin> admins;
public Role() {
admins = new HashSet<Admin>();
}
public void add(Admin admin) {
admins.add(admin);
}
@ManyToMany(mappedBy="admins")
public Set<Role> getRoles() {
return roles;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
// joinColumns:用來設置本身在中間表的外鍵名;inverseJoinColumns用來設置
// 對方在中間表的外鍵名
@ManyToMany
@JoinTable(name="t_role_admin",
joinColumns={@JoinColumn(name="rid")},
inverseJoinColumns={@JoinColumn(name="aid")})
public Set<Admin> getAdmins() {
return admins;
}
public void setAdmins(Set<Admin> admins) {
this.admins = admins;
}
}
專業-教室:1對多
教室-學生:1對多
@Entity
@Table(name="t_stu")
//@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Student {
private int id;
private String name;
private String sex;
private Classroom classroom;
private int version;
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
//LAZY就是XML中的select,EAGER就表示XML中的join
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="cid")
public Classroom getClassroom() {
return classroom;
}
}
@Entity
@Table(name="t_classroom")
@BatchSize(size=20)
public class Classroom {
private int id;
private String name;
private int grade;
private Set<Student> stus;
private Special special;
@Id
@GeneratedValue
public int getId() {
return id;
}
//由多的一方維護關係
@OneToMany(mappedBy="classroom")
@LazyCollection(LazyCollectionOption.EXTRA)
@Fetch(FetchMode.SUBSELECT)
public Set<Student> getStus() {
return stus;
}
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="spe_id")
public Special getSpecial() {
return special;
}
}
@Entity
@Table(name="t_special")
public class Special {
private int id;
private String name;
private String type;
private Set<Classroom> clas;
@Id
@GeneratedValue
public int getId() {
return id;
}
@OneToMany(mappedBy="special")
@LazyCollection(LazyCollectionOption.EXTRA)
public Set<Classroom> getClas() {
return clas;
}
}
略,重點提示:
(1) 設置<set>的時候,使用inverse,表示不在本身這邊維護關係,以下配置classroom:
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="subselect">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
/*基於?的條件的查詢,特別注意:jdbc設置參數的最小下標是1,hibernate是0*/
List<Student> stus = session
.createQuery("from Student where name like ?")
.setParameter(0, "%李%")
.list();
/*還能夠基於別名進行查詢,使用:xxx來講明別名的名稱,基於列表查詢必定要用別名*/
List<Student> stus = session
.createQuery("from Student where name like :name and sex=:sex")
.setParameter("name", "%劉%")
.setParameter("sex", "男")
.list();
/*使用uniqueResult能夠返回惟一的一個值注意返回值類型(uniqueResult的返回類型是Object)*/
Long stus = (Long)session
.createQuery("select count(*)
from Student where name like :name and sex=:sex")
.setParameter("name", "%劉%")
.setParameter("sex", "男")
.uniqueResult();
/*基於投影的查詢,經過在列表中存儲一個對象的數組,注意返回值類型
基於投影的查詢還能夠基於DTO——DTO使用來傳輸數據*/
List<Object[]> stus = session
.createQuery("select stu.sex,
count(*) from Student stu group by stu.sex").list();
for(Object[] obj:stus) {
System.out.println(obj[0]+":"+obj[1]);
}
【個人總結】:
group by的做用,select後的顯示、計算是按照group by後進行的(group by後的記錄至關於一張張字表,對這些字表進行select操做)。
/*若是對象中相應的導航對象,能夠直接導航完成查詢*/
List<Student> stus = session
.createQuery("select stu from Student stu
where stu.classroom.name=? and stu.name like ?")
.setParameter(0, "計算機教育班")
.setParameter(1, "%張%")
.list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
/*可使用in來設置基於列表的查詢,此處的查詢須要使用別名進行查詢。
特別注意:使用in的查詢必須在其餘的查詢以後*/
List<Student> stus = session
.createQuery("select stu from Student stu
where stu.name like ? and stu.classroom.id in (:clas)")
.setParameter(0, "%張%")
.setParameterList("clas", new Integer[]{1,2})
.list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
【提示】:
基於列表查詢要用別名:name。
/*能夠經過is null來查詢爲空的對象,和sql同樣不能使用=來查詢null的對象*/
List<Student> stus = session
.createQuery("select stu from Student stu
where stu.classroom is null")
.setFirstResult(0)
.setMaxResults(15)
.list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
/*使用對象的導航(內部使用Cross JOIN<笛卡爾積>)能夠完成鏈接,可是是基於Cross JOIN,效率不高,能夠直接使用JOIN(Join = Inner Join)來完成鏈接*/
List<Student> stus = ession
.createQuery("select stu from Student stu
left join stu.classroom cla where cla.id=2")
.setFirstResult(0)
.setMaxResults(15).list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
鏈接(Join)有三種:
(1) left join:
如下sql:t1(left)中不符合條件(t1.id=t2.cid)的也打印出來,無值的字段爲null
select * from t_classroom t1
left join t_stu t2 on(t1.id=t2.cid)
(2) right join:同理(1)
(3) inner join(即join):兩邊都有的才顯示。
左、右鏈接經常使用語統計數據。
// 如下sql語句顯示不正常,本意是想顯示每一個班級的學生人數,可是沒有學生的班級也會顯示人數:
select t1.name,count(*) from t_classroom t1
join t_stu t2
on(t1.id=t2.cid) group by t1.id
// 改爲如下sql:
select t1.name,count(t2.cid) from t_classroom t1
left join t_stu t2
on(t1.id=t2.cid) group by t1.id
l 查詢每一個班級的男生和女生的人數:
select t1.name,t2.sex,count(t2.cid)
from t_classroom t1
left join t_stu t2
on(t1.id=t2.cid)
group by t1.id,t2.sex
【提示】:
Hibernate的導航鏈接內部使用CROSS JOIN(全鏈接),效率很低!
l 查詢2班的全部學生:
/*使用對象的導航能夠完成鏈接,可是是基於Cross JOIN,效率不高,能夠直接使用JOIN來完成鏈接*/
List<Student> stus = session
.createQuery("select stu from Student stu
left join stu.classroom cla where cla.id=2").setFirstResult(0)
.setMaxResults(15).list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
l 查詢每一個班的人數
/*使用對象的導航能夠完成鏈接,可是是基於Cross JOIN,效率不高,能夠直接使用JOIN來完成鏈接
思考:若是把classroom放前面呢?若是使用count(*)會如何?*/
List<Object[]> stus = session
.createQuery("select
cla.name,count(stu.classroom.id)
from Student stu
right join stu.classroom cla
group by cla.id").list();
for(Object[] stu:stus) {
System.out.println(stu[0]+","+stu[1]);
}
l 查詢學生的信息、包括所在班級(班級名)、專業名
SQL_1:
List<Ojbet[]> stus =
session.createQuery(「select stu.id,stu.name,stu.sex,cla.name,spe.name
from Student stu
left join stu.classroom cla
left join cla.special spe」).list();
for( Object[] obj:stus ){
……
}
以前查詢的字段,而後獲取信息的時候,都是用Object[]接收返回值,而後經過Object的索引獲取索引值的。可是實際開發中若是這樣作就很不方便(如SQL_1),由於一一的取出索引值。這時候就可使用DTO了,專門用來傳輸數據,沒有任何存儲意義。
>>>使用DTO解決以上問題:
新建一個DTO對象,要從數據庫查詢的哪些字段名就添加哪些屬性名:
public class StudentDto {
private int sid;
private String sname;
private String sex;
private String cname;
private String spename;
get()…;
set()…;
}
HQL查詢:
/*直接可使用new XXObject完成查詢,注意,必定要加上Object的完整包名這裏使用的new XX,必須在對象中加入相應的構造函數*/
List<StudentDto> stus = session
.createQuery("select
new org.zttc.itat.model.StudentDto(
stu.id as sid,stu.name as sname,stu.sex as
sex,cla.name as cname,spe.name as spename)
from Student stu
left join stu.classroom cla
left join cla.special spe").list();
for(StudentDto stu:stus) {
System.out.println(stu.getSid()
+","+stu.getSname()+","+stu.getSex()+","
+stu.getCname()+","+stu.getSpename());
}
【提示】:
Criteria基本上在開發中不用!(咱們項目組居然用了……)
l 統計每一個專業的學生的人數:
List<Object[]> stus = session.createQuery("
select spe.name,count(stu.classroom.special.id)
from Student stu
right join
stu.classroom.special spe
group by spe");
l 統計人數大於150的專業:
/*having是爲group來設置條件的*/
List<Object[]> stus = session.createQuery("
select spe.name,
(count(stu.classroom.special.id))
from Student stu
right join
stu.classroom.special spe
group by spe
having count(stu.classroom.special.id)>150").list();
for(Object[] obj:stus) {
System.out.println(obj[0]+":"+obj[1]);
}
l 統計每一個專業的男女生人數:
/*having是爲group來設置條件的*/
List<Object[]> stus = session.createQuery("
select stu.sex,spe.name
(count(stu.classroom.special.id))
from Student stu
right join
stu.classroom.special spe
group by spe,stu.sex").list();
for(Object[] obj:stus) {
System.out.println(obj[0]+":"+obj[1]+","+obj[2]);
}
使用映射文件的狀況下:
/*
(1)默認狀況會發出3條SQL語句,一條取student,一條取Classroom,一條取Special
(3)經過設置XML中的<many-to-one name="classroom" column="cid" fetch="join"/>能夠完成對抓取的設置
(4)若是使用Annotation的話,Hibernate會自動使用Join查詢,只發出一條SQL
使用Annotation默認就是基於join抓取的,因此只會發出一條sql*/
session = HibernateUtil.openSession();
Student stu = (Student)session.load(Student.class, 1); System.out.println(stu.getName()+","+stu.getClassroom().getName()+","+stu.getClassroom().getSpecial().getName());
若是不想發出3條SQL,能夠在映射文件中這麼配置,使用fetch=」join」(默認值是select):
<class name="Student" table="t_stu">
<!-- <cache usage="read-only"/> -->
<id name="id">
<generator class="native"/>
</id>
<version name="version"/>
<property name="name"/>
<property name="sex"/>
<many-to-one name="classroom" column="cid" fetch="join"/>
</class>
專業是經過classroom來獲取的,因此,同時要在classroom中配置fetch=」join」:
<class name="Classroom" table="t_classroom">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="subselect">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
【警告】:
以上使用fetch=」join」會有這樣的問題,就是,即便我query中不查詢班級和專業,發出的SQL也會去查專業和學生!也就是說,使用了fetch=」join」後,延遲加載就不生效了!
/*使用fetch=join雖然能夠將關聯對象抓取,可是若是不使用關聯對象也會一併查詢出來這樣會佔用相應的內存*/
session = HibernateUtil.openSession();
Student stu = (Student)session.load(Student.class, 1);
//使用fetch=」join」,延遲加載就失效
System.out.println(stu.getName());
【對於Annotation的問題】:
由於在使用Annotation的狀況下,默認就是基於join的抓取策略,因此,好比,咱們只查學生的話,同時會把班級、專業信息都查出來,即把關聯的信息都查出來。
【基於「多」的一方進行抓取:實例及解決方案】
session = HibernateUtil.openSession();
/**
* 在XML中配置了fetch=join僅僅只是對load的對象有用,對HQL中查詢的對象無用,
* 因此此時會發出查詢班級的SQL,解決的這個SQL的問題有兩種方案,
* (1)設置對象的抓取的batch-size
* (2)在HQL中使用fecth來指定抓取
* 特別注意,若是使用了join fetch就沒法使用count(*)
*/
List<Student> stus = session.
createQuery("select stu from Student stu).list();
for(Student stu:stus) {
System.out.println(stu.getName()+","+stu.getClassroom());
}
XML配置:
// batch-size:值默認爲1,這裏設置成20,即一次抓取20個班;
// 缺點是佔用內存較大,且session關閉後數據就丟失(??),這裏能夠看出
// Hibernate的一個缺點就是很容易影響性能
<class name="Classroom" table="t_classroom" batch-size="20 ">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="subselect">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
爲了解決batch-size的問題,咱們能夠在xml中不使用batch-size,在HQL使用fetchy來指定抓取:
// 沒有fetch進行查詢會發出多條sql,有了fetch後只發一條
List<Student> stus = session.
createQuery("select stu from
Student stu join fetch stu.classroom").list();
for(Student stu:stus) {
System.out.println(stu.getName()+","+stu.getClassroom());
}
【提示】:
由於HQL使用了join fetch,就沒法使用count(*)了,若是還想使用count(*),能夠將HQL格式化,將fetch替換成空。
【小結】:
抓取策略有兩種:
(1) batch-size:在被抓取方的xml中配置,能夠一次抓取大量的數據;
(2) HQL中使用fetch。
針對「一」的抓取通常不會使用雙向關聯,只會作「單向關聯」。
l 取出班級和班級對應的學生
session = HibernateUtil.openSession();
Classroom cla = (Classroom)session.load(Classroom.class, 1);
/*此時會在發出一條SQL取class對象*/
System.out.println(cla.getName());
/*取學生姓名的時候會再發一條SQL取學生對象,能夠在Classroom的配置文件中對學生屬性添加fetch=」join」,就會用一條sql完成相關查詢了*/
for(Student stu:cla.getStus()) {
System.out.println(stu.getName());
}
classroom的xml配置文件中加入fetch=join:
<class name="Classroom" table="t_classroom">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="join">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
l 取出一組班級
(classroom的xml配置仍是使用實例1中的配置內容。)
List<Classroom> clas = session.createQuery("from Classroom").list();
for(Classroom cla:clas) {
System.out.println(cla.getName());
/*對於經過HQL取班級列表且獲取相應學生列表時,fecth=join就無效了,這裏,每次查詢一個班級就會發出一個SQL語句,這樣確定有問題,解決方法:
(1)第一種方案能夠設置set的batch-size來完成批量的抓取
(2)能夠設置fetch=subselect,使用subselect會完成根據查詢出來的班級進行一次對學生對象的子查詢(推薦)*/
for(Student stu:cla.getStus()) {
System.out.print(stu.getName());
}
}
l 方法(1)的xml配置:
<class name="Classroom" table="t_classroom">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="join" batch-size="400">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
【說明】:
這裏設置batch-size=400後,雖然發出的SQL大大減小,可是,即便咱們實際使用10條,Hibernate也會查詢出400條,這樣很耗內存!(實際怎麼使用仍是要根據項目實際狀況來定)。
l 方法(2)的xml配置:
<class name="Classroom" table="t_classroom">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="grade"/>
<many-to-one name="special" column="spe_id" fetch="join"/>
<set name="stus" inverse="true" lazy="extra" fetch="subselect" >
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
【說明】:
這中方法只發出兩條sql:查班級和查學生(subselect)。
另:這裏好像<many-to-one>的fetch只有兩個選項:join和select,而<set>的fetch多一個subselect。
@Entity
@Table(name="t_stu")
//@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Student {
private int id;
private String name;
private String sex;
private Classroom classroom;
private int version;
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
//LAZY就是XML中的select,EAGER就表示XML中的join
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="cid")
public Classroom getClassroom() {
return classroom;
}
}
@Entity
@Table(name="t_classroom")
@BatchSize(size=20)
public class Classroom {
private int id;
private String name;
private int grade;
private Set<Student> stus;
private Special special;
@Id
@GeneratedValue
public int getId() {
return id;
}
@OneToMany(mappedBy="classroom")
@LazyCollection(LazyCollectionOption.EXTRA)
@Fetch(FetchMode.SUBSELECT)
public Set<Student> getStus() {
return stus;
}
//使用註解默認都是join查詢的,因此這裏的配置默認是:
//fetch=FetchType.EAGER,因此這裏默認會將專業信息也查詢出來
//因此這裏要配置成LAZY
//可是,若是配置成LAZY後,若是去班級再取專業,就會多發一條SQL查詢專業
//這個的解決方法有有兩種:
//(1)在HQL中使用fetch join cla.special
//(2)
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="spe_id")
public Special getSpecial() {
return special;
}
}
/* 基於Annotation因爲默認的many-to-one的抓取策略是EAGER的,因此當抓取classroom時會自動發出多條SQL去查詢相應的Special;
此時:
(1)能夠經過join fecth繼續完成對關聯的抓取(這樣SQL很容易變得很長,尤爲是大項目中);
(2)直接將關聯對象的fecth設置爲LAZY(就是說不但願查出關聯對象),可是(因此)使用LAZY所帶來的問題是在查詢關聯對象時須要發出相應的SQL,不少時候也會影響效率(要查出關聯對象的話仍是要發出SQL的)
(效率是第一重要的!)*/
List<Student> stus = session.createQuery("select stu from " +
"Student stu join fetch stu.classroom cla join fetch cla.special").list();
for(Student stu:stus) {
System.out.println(stu.getName()+","+stu.getClassroom());
}
session = HibernateUtil.openSession();
Classroom cla = (Classroom)session.load(Classroom.class, 1);
System.out.println(cla.getName());
/*此時會在發出兩條SQL(取班級、取學生)取學生對象*/
for(Student stu:cla.getStus()) {
System.out.println(stu.getName());
}
只發出一條SQL的方法,配置classroom實體:
@Entity
@Table(name="t_classroom")
@BatchSize(size=20)
public class Classroom {
private int id;
private String name;
private int grade;
private Set<Student> stus;
private Special special;
@Id
@GeneratedValue
public int getId() {
return id;
}
@OneToMany(mappedBy="classroom")
@LazyCollection(LazyCollectionOption.EXTRA)
@Fetch(FetchMode.JOIN) //方法1:使用join
@Fetch(FetchMode.SUBSELECT) //方法2:使用subselect(同時會查專業,另外,即便只查一個班級也會發出兩條sql語句進行查班級和查學生)
public Set<Student> getStus() {
return stus;
}
@ManyToOne(fetch=FetchType.LAZY) //使用lazy不關聯不查詢
@JoinColumn(name="spe_id")
public Special getSpecial() {
return special;
}
}
實例1:基本查詢:
/*此時會發出一條sql取出全部的學生信息*/
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
實例2:使用Iterator展現N+1問題:
/*若是使用iterator方法返回列表,對於hibernate而言,它僅僅只是發出取id列表的sql。在查詢相應的具體的某個學生信息時,會發出相應的SQL去取學生信息,這就是典型的N+1問題。
存在iterator的緣由是,有可能會在一個session中查詢兩次數據,若是使用list每一次都會把全部的對象查詢上來,而使用iterator僅僅只會查詢id,此時全部的對象已經存儲在一級緩存(session的緩存)中,能夠直接獲取*/
session = HibernateUtil.openSession();
Iterator<Student> stus = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).iterate();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
實例3:用Iterator從一級緩存(session)中取值
/*此時會發出一條sql取出全部的學生信息*/
//發出查class的sql
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*使用iterate僅僅只會取Student的id,此時Student的數據已經在緩存中,因此不會在出現N+1*/
//發出查id的sql
stus = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).iterate();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
實例4:實例3的變形(注意和實例3對比)
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*會發出SQL取完整的學生對象,佔用內存相對較多*/
ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
【小結】:
(1) 屢次在session中查詢使用Iterato(此狀況不多!);
(2) 一級緩存釋義;
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*id=1的Student對象已經在session的緩存(一級緩存)中,此時就不會發sql去取Student*/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
(3) 一級緩存中session關閉,一級緩存就關閉。
【提示】:
(1) 二級緩存是sessionFactory級別的緩存;
(2) 二級緩存是優化比較好、使用比較多的緩存。
實例1:演示一級緩存
@SuppressWarnings("unchecked")
public class TestCache {
@Test
public void test01() {
Session session = null;
try {
/*此時會發出一條sql取出全部的學生信息*/
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*id=1的Student對象已經在session的緩存(一級緩存)中,此時就不會發sql去取Student*/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
try {
session = HibernateUtil.openSession();
/*上一個Session已經關閉,此時又得從新取Student,這裏會再發一條sql*/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
實例2:使用二級緩存
使用二級緩存步驟:
步驟1:要在配置文件(Hibernate.cfg.xml)中配置,打開二級緩存。
<!-- 設置二級緩存爲true -->
<property name="hibernate.cache.use_second_level_cache">
true
</property>
步驟2:加入二級緩存包(經常使用的是ehache-corte、Hibernate-ehcache,在Hibernate的optional目錄下);
步驟3:要在配置二級緩存提供的類:
<!-- 設置二級緩存所提供的類 -->
<property name="hibernate.cache.provider_class">
net.sf.ehcache.hibernate.EhCacheProvider
</property>
步驟4:Hibernate4.0中還要配置工廠(目的是爲了提升效率):
<!-- 在hibernate4.0以後須要設置facotory_class -->
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>
步驟5:設置ehcache.xml文件,在該文件中配置二級緩存的參數:
從Hibernate的project目錄中拷貝ehcache.xml配置文件到src目錄下
<ehcache>
<diskStore path="java.io.tmpdir"/>
<!—默認緩存 ->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<!-- 每個獨立的cache能夠單獨爲不一樣的對象進行設置
沒有配置的就使用默認的->
<cache name="org.zttc.itat.model.Student"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
<cache name="sampleCache2"
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
/> -->
</ehcache>
步驟6:在Hibernate.cfg.xml中配置ehcache.xml配置文件的路徑
<!-- 說明ehcache的配置文件路徑 -->
<propertyname="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</property>
步驟7:開啓二級緩存
<hibernate-mapping package="org.zttc.itat.model">
<class name="Student" table="t_stu">
//這個使用了鎖的機制。也能夠是read-write,可是效率更低了。
<cache usage="read-only"/>
<id name="id">
<generator class="native"/>
</id>
<version name="version"/>
<property name="name"/>
<property name="sex"/>
<many-to-one name="classroom" column="cid" fetch="join"/>
</class>
</hibernate-mapping>
二級緩存使用實例:
/*此時會發出一條sql取出全部的學生信息*/
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*id=1的Student對象已經在session的緩存(一級緩存)中,此時就不會發sql去取Student*/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
try {
//Student配置了二級緩存了,因此還能從緩存中取出數據
session = HibernateUtil.openSession();
/*上一個Session已經關閉,此時又得從新取Student*/
Student stu = (Student)session.load(Student.class, 1);
//由於二級緩存設置了read-only的話,若改變值stu.setName(「」)會報錯
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
l 二級緩存緩存的是對象
二級緩存是把全部的對象都緩存到內存中,是基於對象的緩存。
【實例】:
try {
/*此時會發出一條sql取出全部的學生信息*/
session = HibernateUtil.openSession();
List<Object[]> ls = session
.createQuery("select stu.id,stu.name from Student stu")
.setFirstResult(0).setMaxResults(50).list();
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
try {
session = HibernateUtil.openSession();
session.beginTransaction();
/*以上代碼僅僅取了id和name,而二級緩存是緩存對象的,因此上一段代碼不會
將對象加入二級緩存此時就是發出相應的sql*/
Student stu = (Student)session.load(Student.class, 1);
//會報錯,由於二級緩存設置爲read-only
//stu.setName("abc");
System.out.println(stu.getName()+",---");
session.getTransaction().commit();
}
l 二級緩存和Iterator配合使用
Iterator的最主要做用就是在此了!
Session session = null;
try {
/*此時會發出一條sql取出全部的學生信息*/
session = HibernateUtil.openSession();
List<Object[]> ls = session
.createQuery("select stu from Student stu")
.setFirstResult(0).setMaxResults(50).list();
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
try {
session = HibernateUtil.openSession();
/*因爲學生的對象已經緩存在二級緩存中了,此時再使用iterate來獲取對象的時候,首先會經過一條取id的語句,而後在獲取對象時去二級緩存中,若是發現就不會再發SQL,這樣也就解決了N+1問題並且內存佔用也很少*/
Iterator<Student> stus = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).iterate();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
} catch (Exception e) {
}
l 若是使用list()若是想少發SQL就須要使用查詢緩存:
session = HibernateUtil.openSession();
/*這裏發出兩條如出一轍的sql,此時若是但願不發sql就須要使用查詢緩存*/
List<Student> ls = session
.createQuery("select stu from Student stu")//HQL和上面的同樣
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
查詢緩存是針對HQL語句的緩存,查詢緩存僅僅只會緩存id而不會緩存對象。
在Hibernate.cfg.xml中配置查詢緩存:
<!--設置相應的查詢緩存 -->
<property name="hibernate.cache.use_query_cache">true</property>
l 代碼中的用法
session = HibernateUtil.openSession();
List<Student> ls = session.createQuery("from Student")
.setCacheable(true)//開啓查詢緩存,查詢緩存也是SessionFactory級緩存
.setFirstResult(0).setMaxResults(50).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*會發出SQL取完整的學生對象,佔用內存相對較多*/
ls = session.createQuery("from Student")
.setCacheable(true)
.setFirstResult(0).setMaxResults(50).list();
stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
session = HibernateUtil.openSession();
List<Student> ls = session
.createQuery("from Student where name like ?")
.setCacheable(true)
// 開啓查詢緩存,查詢緩存也是SessionFactory級別的緩存
.setParameter(0, "%王%").setFirstResult(0).setMaxResults(50)
.list();
Iterator<Student> stus = ls.iterator();
for (; stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
session = null;
try {
/*若是兩條sql不同,就不會開啓查詢緩存,查詢緩存緩存的是HQL語句只有兩個HQL徹底一致(且參數也要一致)才能使用查詢緩存*/
session = HibernateUtil.openSession();
List<Student> ls = session
.createQuery("from Student where name like ?")
.setCacheable(true)
// 開啓查詢緩存,查詢緩存也是SessionFactory級別的緩存
.setParameter(0, "%王%").setFirstResult(0).setMaxResults(50)
.list();
Iterator<Student> stus = ls.iterator();
for (; stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*查詢緩存緩存的不是對象而是id,關閉二級緩存後很容易發出大量的sql*/
session = HibernateUtil.openSession();
List<Student> ls = session
.createQuery("from Student where name like ?")
.setCacheable(true)
// 開啓查詢緩存,查詢緩存也是SessionFactory級別的緩存
.setParameter(0, "%王%").setFirstResult(0).setMaxResults(50)
.list();
Iterator<Student> stus = ls.iterator();
for (; stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
session = null;
try {
/*查詢緩存緩存的是id,此時因爲在緩存中已經存在了這樣的一組學生數據,可是僅僅只是緩存了id,因此此處會發出大量的sql語句根據id取對象,這也是發現N+1問題的第二個緣由 因此若是使用查詢緩存必須開啓二級緩存*/
session = HibernateUtil.openSession();
List<Student> ls = session
.createQuery("from Student where name like ?")
.setCacheable(true)
// 開啓查詢緩存,查詢緩存也是SessionFactory級別的緩存
.setParameter(0, "%王%").setFirstResult(0).setMaxResults(50)
.list();
Iterator<Student> stus = ls.iterator();
for (; stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
配置實體中這麼配置,其餘的查詢語句跟在xml中同樣:
@Entity
@Table(name="t_stu")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Student {
private int id;
private String name;
private String sex;
private Classroom classroom;
private int version;
@Version
public int getVersion() {
return version;
}
【小結】:
l 常常修改的對象不用二級緩存;
l 用list仍是Iterator?若是對象取出來就用Iterator沒有的話;
l 查詢緩存緩存的是id,可是隻有在hql是同樣的狀況下才使用查詢緩存,因此通常不建議使用查詢緩存;
在test01()方法中將id爲1的學生取出來,並將名字修改成」Jack」,在test02()方法中,也將id爲1的學生取出來,並將名字改成」Tom」,而且在對方commit以前完成更名操做,這樣的話,名字不會被改掉。
因此,通常狀況下,併發會致使更新丟失。
解決方法有兩種:
(1) 悲觀鎖:
悲觀鎖是Hibernate基於數據庫的機制實現的,但是在Hibernate3和Hibernate4它們的實現機制是不同的——Hibernate3是基於同步的機制實現的(同步的大量的併發就容易致使效率的問題),因此在Hibernate4中就沒有使用同步,若是兩個同時修改同一個記錄的話,就拋出異常。解釋錯誤了,Hibernate3和Hibernate4的機制是同樣的,都是同步的。
(2) 樂觀鎖:
樂觀鎖是在數據庫中增長一個version的字段來實現的,每次修改都會讓這個字段的數字增長1,在讀取的時候根據Version這個版本的值來讀取,這樣若是併發修改就會拋異常。
<class name="Student" table="t_stu">
<cache usage="read-only"/>
<id name="id">
<generator class="native"/>
</id>
//數據庫會增長一個version這個字段
<version name="version"/>
<property name="name"/>
<property name="sex"/>
<many-to-one name="classroom" column="cid" fetch="join"/>
</class>
(1) 在作關係時,儘量使用單向關聯,不要使用雙向關聯;
(2) 在大項目中(數據量超過百萬條)使用Hibernate能夠考慮如下幾個原則(我的總結):
【原則1】:不要使用對象關聯,儘量使用冗餘字段來替代外鍵(由於百萬級別的數據使用跨表查速度會很是慢);
【基於冗餘的關聯-實例】:
l 上面的實例中,咱們的學生表是和班級表關聯的,可是,實際上,顯示學生的信息時,通常都只是但願顯示班級名稱便可,並不須要更多的班級信息。
【設置冗餘字段的方法:】
1) 給班級增長一個用當前時間毫秒級+隨機數做爲班級的主鍵;
2) 咱們給Student實體增長班級編號(classBh)和班級名(className)這兩個特冗餘字段;這樣咱們在顯示班級名的時候就不須要關聯班級表了;
3) 一樣,學生還須要專業信息,咱們就再在Student實體中增長專業編號(speBh)和專業名稱(speName),同時,班級實體中也增長專業的編號和專業的名稱;
4) 使用冗餘的缺點:好比,咱們修改了班級名稱的時候,學生表裏的班級名稱也要跟着作修改(可是性能基本沒有影響)
Q:可是,若是有一個對象同時須要學生對象、班級對象、專業對象,那又怎麼辦呢?
——答案是使用DTO(數據傳輸對象)。
public class StudentDto {
private Student stu;
private Classroom cla;
private Special spe;
}
【原則2】:不使用HQL,而所有使用SQL;若是須要緩存,就使用本身的緩存,而不適用Hibernate的緩存(Hibernate的緩存在Session和SessionFatory會有耗內存)
(3)
public void test01() {
Session session = null;
try {
session = HibernateUtil.openSession();
List<Student> stus = session
.createSQLQuery("select * from t_stu where name like ?")
.addEntity(Student.class)
.setParameter(0, "%孔%")
.setFirstResult(0).setMaxResults(10)
.list();
for(Student stu:stus) {
System.out.println(stu.getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}
session = HibernateUtil.openSession();
//注意SQL語句上加了{},返回值用Object來接收,實例3中使用DTO接收
List<Object[]> stus = session
.createSQLQuery("select {stu.*},{cla.*},{spe.*} from
t_stu stu left join t_classroom cla
on(stu.cid=cla.id)
left join t_special spe on(spe.id=cla.spe_id)
where stu.name like ?")
.addEntity("stu",Student.class)
.addEntity("cla",Classroom.class)
.addEntity("spe",Special.class)
.setParameter(0, "%孔%")
.setFirstResult(0).setMaxResults(10)
.list();
Student stu = null;
Classroom cla = null;
Special spe = null;
List<StuDto> list = new ArrayList<StuDto>();
for(Object[] obj:stus) {
stu = (Student)obj[0];
cla = (Classroom)obj[1];
spe = (Special)obj[2];
list.add(new StuDto(stu, cla, spe));
//這裏會將學生姓名打印3遍,因此sql語句中加了花括號,用來自動將結果映射
//到前綴對應的對象中
System.out.println(stu.name+cla.name+spe.name);
}
l StudentDto實體:
public class StudentDto {
private int sid;
private String sname;
private String sex;
private String cname;
private String spename;
public StudentDto(int sid, String sname, String sex, String cname,
String spename) {
super();
this.sid = sid;
this.sname = sname;
this.sex = sex;
this.cname = cname;
this.spename = spename;
}
public StudentDto() {
}
}
l 查詢
session = HibernateUtil.openSession();
//這裏的sql爲了和DTO對應,
//能夠在select中new com.zmj…StudentDto(stu.id as sid,……)
List<StudentDto> stus = session.createSQLQuery("select
stu.id as sid,
stu.name as sname,
stu.sex as sex,
cla.name as cname,
spe.name as spename
from t_stu stu
left join t_classroom cla
on(stu.cid=cla.id) "
left join t_special spe
on(spe.id=cla.spe_id)
where stu.name like ?")
//這裏第一感受可能會想到使用setEntity,setEntity的參數必須是和數據庫
//對應的,可是DTO明顯不須要和數據庫對應,因此這裏使用如下這個方法:
.setResultTransformer(Transformers.aliasToBean(StudentDto.class))
.setParameter(0, "%孔%")
.setFirstResult(0).setMaxResults(10)
.list();
for(StudentDto sd:stus) {
System.out.println(sd);
}
【提示】:
這樣使用Hibernate的方法(查詢使用sql,增、刪、改使用Hibernate),效率會大大提升!