1.引入SSH框架java
Struts框架,基於MVC 模式的應用層框架技術mysql
Hibernate,基於持久層框架(數據訪問層使用)面試
Dao代碼編寫的幾種方式:sql
1. 原始jdbc操做,Connection/Statement/ResultSet數據庫
2. 自定義一個持久層框架,封裝dao的通用方法api
3. DbUtils組件,輕量級數據訪問技術數組
4. Hibernate技術【最終執行的就是jdbc代碼!】緩存
2. 開始學習SSH框架服務器
a) ORM概念session
Object Realtion Mapping:對象關係映射
用於:直接將對象存入數據庫;直接從數據庫拿到對象!
想要作到這點:映射,映射信息存入xml中
映射中信息:1.對象與表,2.屬性與字段,3.類型的對應
O R M三者間的關係
b) 組件的學習:源碼引入jar文件
c) 環境搭建
咱們也能夠直接添加jar包,以後只有再添加一個鏈接數據庫的包就行了
hibernate3.jar核心+required必須引入的(6個)+jpa目錄+數據庫驅動包required中的jar:antlr-2.7.6;commons-collections-3.1;dom4j-1.6.1;javassist-3.12.0.GA;jta-1.1;slf4j-api-1.6.1
Jpa:hibernate-jpa-2.0-api-1.0.0.Final.jar
數據庫驅動:mysql-connector-java-5.1.12-bin.jar
例子:
獲取sessionFactory的工具類
public class HibernateUtils { private static SessionFactory sf; static{ sf=new Configuration().configure().buildSessionFactory(); } public static Session getSession(){ return sf.openSession(); } }
d) 核心API
Configuration 配置管理類對象
Config.configure(); 加載主配置文件的方法(hibernate.cfg.xml)
默認加載src/hibernate.cfg.xml
Config.configure(cn/config/hibernate.cfg.xml);加載指定路徑下的
config.buildSessionFactory();建立session的工廠對象
SessionFactory Session的工廠(或者說表明hibernate.cfg.xml文件)
sf.openSession(); 建立一個session對象
sf.getCurrentSession();建立一個session或者取出session對象
Session session對象維護了一個鏈接(Connection),表明與數據庫鏈接的回話,hibernate最重要的一個對象
session.beginTransaction();開啓一個事務,hibernate要求全部與數據庫的操做都要有事務,不然報錯
更新:
Session.save(obj)
Session.update(obj)
session.saveOrUpdate(obj);保存或者更新方法
—》有主鍵執行更新
—》沒主鍵執行保存
—》若是設置的主鍵不存在報錯
主鍵查詢:
Session.get(obj.class,1) 主鍵查詢
Session.load(obj.class,1)主鍵查詢(支持懶加載)
HQL查詢:
與SQL區別:SQL(結構化查詢語句)面向表查詢,不區分大小寫
HQL:Hebernate query language面向對象的查詢語句,由於是對象,區分大小寫
//適合有數據庫基礎的HQL面向對象,SQL面向表 public static void HQLQuery(){ /*這裏的Hibernate的SQL與jdbc中的sql不一樣,對象對象的,jdbc是面向表字段的*/ Query q=session.createQuery("from Employee where empId=1 or empId=2"); List<Employee> list=q.list(); System.out.println(list); }
Criteria查詢:沒有數據庫基礎也能夠用徹底面向對象的查詢
//QBC查詢,query by criteria 徹底面向對象的查詢 public static void CriteriaQuery(){ Criteria criteria=session.createCriteria(Employee.class); //加條件 criteria.add(Restrictions.eq("empId", 1)); List<Employee > list=criteria.list();//開始查詢 System.out.println(list); }
本地SQL查詢:原生sql,缺點不能數據庫跨平臺
//本地事務查詢sql直接查詢 public static void SqlQuery(){ //把每一行數數據封裝爲對象數組,再添加list集合 //SQLQuery sq=session.createSQLQuery("select * from employee"); SQLQuery sq=session.createSQLQuery("select * from employee").addEntity(Employee.class); //想要封裝對象-》 List<?> list=sq.list(); System.out.println(list); }
e)可能會遇到的問題
1.ClassNotFoundException...,缺乏jar文件!
2.若是程序執行,hibernate也生成sql語句,但數據沒有結果影響:
通常是事務沒有提交。。
f)Hibernate crud
增刪改查方法
package crud; import hello.Employee; import java.io.Serializable; import java.util.List; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import utils.HibernateUtils; public class EmployeeImpl implements EmployeeDao { @Override public void save(Employee emp) { // TODO Auto-generated method stub //獲取session Session session=null; Transaction ts=null; try { //業務邏輯 session=HibernateUtils.getSession(); ts=session.beginTransaction(); } catch (HibernateException e) { // TODO Auto-generated catch block throw new RuntimeException(); }finally{ ts.commit(); session.close(); } } @Override public void update(Employee emp) { // TODO Auto-generated method stub //獲取session Session session=null; Transaction ts=null; try { session = HibernateUtils.getSession(); ts = session.beginTransaction(); //業務邏輯 session.update(emp); } catch (HibernateException e) { // TODO Auto-generated catch block throw new RuntimeException(); }finally{ ts.commit(); session.close(); } } @Override public Employee findById(Serializable id) { //獲取session Session session=null; Transaction ts=null; try { session = HibernateUtils.getSession(); ts = session.beginTransaction(); //業務邏輯 return (Employee)session.get(Employee.class, id); } catch (HibernateException e) { // TODO Auto-generated catch block throw new RuntimeException(); }finally{ ts.commit(); session.close(); } } @Override public List<Employee> getAll() { //獲取session Session session=null; Transaction ts=null; try { session = HibernateUtils.getSession(); ts = session.beginTransaction(); //業務邏輯 return session.createQuery("from Employee").list(); } catch (HibernateException e) { // TODO Auto-generated catch block throw new RuntimeException(); }finally{ ts.commit(); session.close(); } } @Override public List<Employee> getAll(String employeeName) { // TODO Auto-generated method stub //獲取session Session session=null; Transaction ts=null; try { session = HibernateUtils.getSession(); ts = session.beginTransaction(); //業務邏輯 Query q =session.createQuery("from Employee where empName=?"); q.setParameter(0, employeeName); return q.list(); } catch (HibernateException e) { // TODO Auto-generated catch block throw new RuntimeException(); }finally{ ts.commit(); session.close(); } } @Override public List<Employee> getAll(int index, int count) { //獲取session Session session=null; Transaction ts=null; try { session = HibernateUtils.getSession(); ts = session.beginTransaction(); //業務邏輯 Query q= session.createQuery("from Employee"); q.setFirstResult(index);//查詢起始行 q.setMaxResults(count);//查詢的行數 return q.list(); } catch (HibernateException e) { // TODO Auto-generated catch block throw new RuntimeException(); }finally{ ts.commit(); session.close(); } } @Override public void delete(Serializable id) { Session session = null; Transaction tx = null; try { session = HibernateUtils.getSession(); tx = session.beginTransaction(); // 先根據id查詢對象,再判斷刪除 Object obj=session.get(Employee.class, id); if(obj!=null){ session.delete(obj); } } catch (Exception e) { throw new RuntimeException(e); } finally { tx.commit(); session.close(); } } }
g)配置文件
主配置:hibernate.cfg.xml:主要配置數據庫信息、其餘參數、映射信息
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <!-- 一般,一個session-factory節點表明一個數據庫 --> <session-factory> <!-- 1. 數據庫鏈接配置 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql:///hib_demo</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!-- 數據庫方法配置, hibernate在運行的時候,會根據不一樣的方言生成符合當前數據庫語法的sql --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- 2. 其餘相關配置 --> <!-- 2.1 顯示hibernate在運行時候執行的sql語句 --> <property name="hibernate.show_sql">true</property> <!-- 2.2 格式化sql --> <property name="hibernate.format_sql">true</property> <!-- 2.3 自動建表 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 3. 加載全部映射 --> <mapping resource="hello/Employee.hbm.xml"/> </session-factory> </hibernate-configuration>
其中自動建表:就算數據庫中的表被刪了只要配置好就能自動在數據庫中建表
<property name="hibernate.hbm2ddl.auto">update</property>
#hibernate.hbm2ddl.auto create-drop 每次在建立sessionFactory時候執行建立表;
當調用sesisonFactory的close方法的時候,刪除表!
#hibernate.hbm2ddl.auto create 每次都從新建表; 若是表已經存在就先刪除再建立
#hibernate.hbm2ddl.auto update 若是表不存在就建立; 表存在就不建立;
#hibernate.hbm2ddl.auto validate (生成環境時候) 執行驗證: 當映射文件的內容與數據庫表結構不同的時候就報錯!
自動建表代碼方式:SchemaExport export = new SchemaExport(config);主要是建立工具類對象
export.creat(true,true),參數1顯示在colsole,第二個是在數據庫中執行
// 自動建表 @Test public void testCreate() throws Exception { // 建立配置管理類對象 Configuration config = new Configuration(); // 加載主配置文件 config.configure(); // 建立工具類對象 SchemaExport export = new SchemaExport(config); // 建表 // 第一個參數: 是否在控制檯打印建表語句 // 第二個參數: 是否執行腳本
映射配置:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 映射文件: 映射一個實體類對象; 描述一個對象最終實現能夠直接保存對象數據到數據庫中。 --> <!-- package: 要映射的對象所在的包(可選,若是不指定,此文件全部的類都要指定全路徑) auto-import 默認爲true, 在寫hql的時候自動導入包名 若是指定爲false, 再寫hql的時候必需要寫上類的全名; 如:session.createQuery("from cn.itcast.c_hbm_config.Employee").list(); --> <hibernate-mapping package="cn.itcast.c_hbm_config" auto-import="true"> <!-- class 映射某一個對象的(通常狀況,一個對象寫一個映射文件,即一個class節點) name 指定要映射的對象的類型 table 指定對象對應的表; 若是沒有指定表名,默認與對象名稱同樣 --> <class name="Employee" table="employee"> <!-- 主鍵 ,映射--> <id name="empId" column="id"> <!-- 主鍵的生成策略 identity 自增加(mysql,db2) sequence 自增加(序列), oracle中自增加是以序列方法實現 native 自增加【會根據底層數據庫自增加的方式選擇identity或sequence】 若是是mysql數據庫, 採用的自增加方式是identity 若是是oracle數據庫, 使用sequence序列的方式實現自增加 increment 自增加(會有併發訪問的問題,通常在服務器集羣環境使用會存在問題。) assigned 指定主鍵生成策略爲手動指定主鍵的值 uuid 指定uuid隨機生成的惟一的值 foreign (外鍵的方式, one-to-one講) --> <generator class="uuid"/> </id> <!-- 普通字段映射 property name 指定對象的屬性名稱 column 指定對象屬性對應的表的字段名稱,若是不寫默認與對象屬性一致。 length 指定字符的長度, 默認爲255 type 指定映射表的字段的類型,若是不指定會匹配屬性的類型 java類型: 必須寫全名 hibernate類型: 直接寫類型,都是小寫 --> <property name="empName" column="empName" type="java.lang.String" length="20"></property> <property name="workDate" type="java.util.Date"></property> <!-- 若是列名稱爲數據庫關鍵字,須要用反引號或改列名。 --> <property name="desc" column="`desc`" type="java.lang.String"></property> </class> </hibernate-mapping>
其中複合主鍵:一個表中主鍵除了一個可能還會有2個或更多在一塊兒做爲複合主鍵
映射文件中配置複合主鍵<composite-id>
// 複合主鍵類 public class CompositeKeys implements Serializable{ private String userName; private String address; // .. get/set } public class User { // 名字跟地址,不會重複 private CompositeKeys keys; private int age; } User.hbm.xml <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.d_compositeKey" auto-import="true"> <class name="User"> <!-- 複合主鍵映射 --> <composite-id name="keys"> <key-property name="userName" type="string"></key-property> <key-property name="address" type="string"></key-property> </composite-id> <property name="age" type="int"></property> </class> </hibernate-mapping> App.java public class App2 { private static SessionFactory sf; static { // 建立sf對象 sf = new Configuration() .configure() .addClass(User.class) //(測試) 會自動加載映射文件:Employee.hbm.xml .buildSessionFactory(); } //1. 保存對象 @Test public void testSave() throws Exception { Session session = sf.openSession(); Transaction tx = session.beginTransaction(); // 對象 CompositeKeys keys = new CompositeKeys(); keys.setAddress("廣州棠東"); keys.setUserName("Jack"); User user = new User(); user.setAge(20); user.setKeys(keys); // 保存 session.save(user); tx.commit(); session.close(); } @Test public void testGet() throws Exception { Session session = sf.openSession(); Transaction tx = session.beginTransaction(); //構建主鍵再查詢 CompositeKeys keys = new CompositeKeys(); keys.setAddress("廣州棠東"); keys.setUserName("Jack"); // 主鍵查詢 User user = (User) session.get(User.class, keys); // 測試輸出 if (user != null){ System.out.println(user.getKeys().getUserName()); System.out.println(user.getKeys().getAddress()); System.out.println(user.getAge()); } tx.commit(); session.close(); } }
hibernate執行週期:
-------------------------------------------------------------------------
關聯映射
1. 集合映射
開發流程:需求分析/數據庫設計、項目設計/ 編碼/測試/實施部署上線/驗收
需求:一個用戶有多個收貨地址,經過映射建立映射表到數據庫中,並對錶數據作處理
核心思路:配置文件中<property name="hibernate.hbm2ddl.auto">update</property>設置爲自動生成表
映射文件配置set映射配置:沒有啥特殊之處,對應的只需外鍵就能夠
List集合映射:List有記住存儲順序的能力,處了外鍵,還需能記住存儲順序的list-index
Map集合映射:map有兼職對的能力,處了外鍵還需鍵的存儲,值得存儲
查詢數據時,當查詢Map映射信息,或list信息,都屬於懶加載,代碼寫到時纔會從數據庫發送sql
<!-- set集合屬性的映射 name 指定要映射的set集合的屬性 table 集合屬性要映射到的表 key 指定集合表(t_address)的外鍵字段 element 指定集合表的其餘字段 type 元素類型,必定要指定 --> <set name="address" table="t_address"> <key column="uid"></key> <element column="address" type="string"></element> </set>
完整代碼:
// javabean設計 public class User { private int userId; private String userName; // 一個用戶,對應的多個地址 private Set<String> address; private List<String> addressList = new ArrayList<String>(); //private String[] addressArray; // 映射方式和list同樣 <array name=""></array> private Map<String,String> addressMap = new HashMap<String, String>(); } <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.a_collection"> <class name="User" table="t_user"> <id name="userId" column="id"> <generator class="native"></generator> </id> <property name="userName"></property> <!-- set集合屬性的映射 name 指定要映射的set集合的屬性 table 集合屬性要映射到的表 key 指定集合表(t_address)的外鍵字段 element 指定集合表的其餘字段 type 元素類型,必定要指定 --> <set name="address" table="t_address"> <key column="uid"></key> <element column="address" type="string"></element> </set> <!-- list集合映射 list-index 指定的是排序列的名稱 (由於要保證list集合的有序) --> <list name="addressList" table="t_addressList"> <key column="uid"></key> <list-index column="idx"></list-index> <element column="address" type="string"></element> </list> <!-- map集合的映射 key 指定外鍵字段 map-key 指定map的key element 指定map的value --> <map name="addressMap" table="t_addressMap"> <key column="uid"></key> <map-key column="shortName" type="string" ></map-key> <element column="address" type="string" ></element> </map> </class> </hibernate-mapping> // 保存set @Test public void testSaveSet() throws Exception { Session session = sf.openSession(); session.beginTransaction(); //-- 保存 Set<String> addressSet = new HashSet<String>(); addressSet.add("廣州"); addressSet.add("深圳"); // 用戶對象 User user = new User(); user.setUserName("Jack"); user.setAddress(addressSet); // 保存 session.save(user); session.getTransaction().commit(); session.close(); } // 保存list/map @Test public void testSaveList() throws Exception { Session session = sf.openSession(); session.beginTransaction(); User user = new User(); user.setUserName("Tom"); // // 用戶對象 -- list // user.getAddressList().add("廣州"); // user.getAddressList().add("深圳"); // // 保存 // session.save(user); // 用戶對象 -- Map user.getAddressMap().put("A0001", "廣州"); user.getAddressMap().put("A0002", "深圳"); // 保存 session.save(user); session.getTransaction().commit(); session.close(); }
2. 關聯映射 (重點)
有7中關聯映射:1.單向一對一,單項一對一惟一外鍵關聯;2.單向一對多,3.單向多對一,4.單向多對多,5.雙向多對多,6.雙向一對多,7.雙向一對一
1)引入:集合映射,映射的集合元素,都是普通的類型, 可否爲對象類型
2)需求1:
部門與員工:一個部門有多個員工【一對多】;多個員工,屬於一個部門【多對一】
需求2:
項目與開發員工:一個項目有多個開發人員,一個開發人員參與多個項目【多對多】
3)多對一映射與一對多
映射配置:
<!-- 一對多關聯映射配置 (經過部門管理到員工) casecade="save-update,delete";//關聯表的聯機影響 inverse=false set集合映射的默認值; 表示有控制權 --> <set name="emps" cascade="save-update,delete" table="t_employee" inverse="true"> <!-- table="t_employee" --> <key column="dept_id"></key> <one-to-many class="Employee"/> </set>
<!--
多對一關聯映射配置
-->
<many-to-one name="dept" column="dept_id" class="Dept"></many-to-one>
Inverse屬性
維護關係的時候起做用,表示控制權是否轉移(只在一的一方有做用)
inverse=false 不反轉;當前方有控制權
inverse-true 反轉:當前方沒有控制權,因此在以一對多存儲(最後由一的結束控制關係)時就沒有控制到關係了
影響
1. 保存數據 有影響。 若是設置控制反轉,即inverse=true, 而後經過部門方維護關聯關係。在保存部門的時候,同時保存員工, 數據會保存,但關聯關係不會維護。即外鍵字段爲NULL 2. 獲取數據 無。 3. 解除關聯關係? 有影響。 inverse=false, 能夠解除關聯 inverse=true, 當前方(部門)沒有控制權,不能解除關聯關係 (不會生成update語句,也不會報錯) 4. 刪除數據對關聯關係的影響? 有影響。 inverse=false, 有控制權, 能夠刪除。先清空外鍵引用,再刪除數據。 inverse=true, 沒有控制權: 若是刪除的記錄有被外鍵引用,會報錯,違反主外鍵引用約束! 若是刪除的記錄沒有被引用,能夠直接刪除。
casecade屬性 【能夠設置到一的方和多的方】
none 不級聯操做, 默認值
save-update 級聯保存或更新
delete 級聯刪除
save-update,delete 級聯保存、更新、刪除
all 同上。級聯保存、更新、刪除
例子:
public class Dept { private int deptId; private String deptName; // 【一對多】 部門對應的多個員工 private Set<Employee> emps = new HashSet<Employee>(); public class Employee { private int empId; private String empName; private double salary; // 【多對一】員工與部門 private Dept dept; Dept.hbm.xml <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.b_one2Many"> <class name="Dept" table="t_dept"> <id name="deptId"> <generator class="native"></generator> </id> <property name="deptName" length="20"></property> <!-- 一對多關聯映射配置 (經過部門管理到員工) Dept 映射關鍵點: 1. 指定 映射的集合屬性: "emps" 2. 集合屬性對應的集合表: "t_employee" 3. 集合表的外鍵字段 "t_employee. dept_id" 4. 集合元素的類型 --> <set name="emps"> <!-- table="t_employee" --> <key column="dept_id"></key> <one-to-many class="Employee"/> </set> </class> </hibernate-mapping> Employee.hbm.xml <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.b_one2Many"> <class name="Employee" table="t_employee"> <id name="empId"> <generator class="native"></generator> </id> <property name="empName" length="20"></property> <property name="salary" type="double"></property> <!-- 多對一映射配置 Employee 映射關鍵點: 1. 映射的部門屬性 : dept 2. 映射的部門屬性,對應的外鍵字段: dept_id 3. 部門的類型 --> <many-to-one name="dept" column="dept_id" class="Dept"></many-to-one> </class> </hibernate-mapping> 測試 public class App { private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(Dept.class) .addClass(Employee.class) // 測試時候使用 .buildSessionFactory(); } // 保存, 部門方 【一的一方法操做】 @Test public void save() { Session session = sf.openSession(); session.beginTransaction(); // 部門對象 Dept dept = new Dept(); dept.setDeptName("應用開發部"); // 員工對象 Employee emp_zs = new Employee(); emp_zs.setEmpName("張三"); Employee emp_ls = new Employee(); emp_ls.setEmpName("李四"); // 關係 dept.getEmps().add(emp_zs); dept.getEmps().add(emp_ls); // 保存 session.save(emp_zs); session.save(emp_ls); session.save(dept); // 保存部門,部門下全部的員工 session.getTransaction().commit(); session.close(); /* * 結果 * Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) Hibernate: insert into t_dept (deptName) values (?) Hibernate: update t_employee set deptId=? where empId=? 維護員工引用的部門的id Hibernate: update t_employee set deptId=? where empId=? */ } // 【推薦】 保存, 部員方 【多的一方法操做】 @Test public void save2() { Session session = sf.openSession(); session.beginTransaction(); // 部門對象 Dept dept = new Dept(); dept.setDeptName("綜合部"); // 員工對象 Employee emp_zs = new Employee(); emp_zs.setEmpName("張三"); Employee emp_ls = new Employee(); emp_ls.setEmpName("李四"); // 關係 emp_zs.setDept(dept); emp_ls.setDept(dept); // 保存 session.save(dept); // 先保存一的方法 session.save(emp_zs); session.save(emp_ls);// 再保存多的一方,關係回自動維護(映射配置完) session.getTransaction().commit(); session.close(); /* * 結果 * Hibernate: insert into t_dept (deptName) values (?) Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) 少生成2條update sql */ } }
總結:多的一方來維護關係能提升效率
一對多的保存:先保存員工,再保存部門,關係有部門來維護,多了些update代碼,不推薦
多對一的保存:先保存部門再保存員工,關係有員工來維護,效率高些
例子中兩種保存都在測試App中save(),save2()
配置一對多與多對一,這種叫「雙向關聯」
只配置一對多, 叫「單項一對多」
只配置多對一, 叫「單項多對一」
注意:
配置了哪一方,哪一方纔有維護關聯關係的權限!
3. 多對多映射
項目與開發人員
電商系統 OA系統 曹吉 王春
完整代碼:
/** * 開發人員 * * @author Jie.Yuan * */ public class Developer { private int d_id; private String d_name; // 開發人員,參數的多個項目 private Set<Project> projects = new HashSet<Project>(); } /** * 項目 * * @author Jie.Yuan * */ public class Project { private int prj_id; private String prj_name; // 項目下的多個員工 private Set<Developer> developers = new HashSet<Developer>(); Project.hbm.xml <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.c_many2many"> <class name="Project" table="t_project"> <id name="prj_id"> <generator class="native"></generator> </id> <property name="prj_name" length="20"></property> <!-- 多對多映射: 1. 映射的集合屬性: 「developers」 2. 集合屬性,對應的中間表: 「t_relation」 3. 外鍵字段: prjId 4. 外鍵字段,對應的中間表字段: did 5. 集合屬性元素的類型 --> <set name="developers" table="t_relation" cascade="save-update"> <key column="prjId"></key> <many-to-many column="did" class="Developer"></many-to-many> </set> </class> </hibernate-mapping> Developer.hbm.xml <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.c_many2many"> <class name="Developer" table="t_developer"> <id name="d_id"> <generator class="native"></generator> </id> <property name="d_name" length="20"></property> <!-- 多對多映射配置: 員工方 name 指定映射的集合屬性 table 集合屬性對應的中間表 key 指定中間表的外鍵字段(引用當前表t_developer主鍵的外鍵字段) many-to-many column 指定外鍵字段對應的項目字段 class 集合元素的類型 --> <set name="projects" table="t_relation"> <key column="did"></key> <many-to-many column="prjId" class="Project"></many-to-many> </set> </class> </hibernate-mapping>
多對多配置:
<set name="projects" table="t_relation"> <key column="did"></key> <many-to-many column="prjId" class="Project"></many-to-many> </set>
在多對多中設置關係維護inverse屬性:
1) 保存數據 有影響。 inverse=false ,有控制權,能夠維護關聯關係; 保存數據的時候會把對象關係插入中間表; inverse=true, 沒有控制權, 不會往中間表插入數據。 2) 獲取數據 無。 3) 解除關係 // 有影響。 // inverse=false ,有控制權, 解除關係就是刪除中間表的數據。 // inverse=true, 沒有控制權,不能解除關係。 4) 刪除數據 有影響。 // inverse=false, 有控制權。 先刪除中間表數據,再刪除自身。 // inverse=true, 沒有控制權。 若是刪除的數據有被引用,會報錯! 不然,才能夠刪除
映射總結:
一對多: <set name="映射的集合屬性" table="(可選)集合屬性對應的外鍵表"> <key column="外鍵表的,外鍵字段" /> <one-to-many class="集合元素的類型" /> </set> 多對一: <many-to-one name="對象屬性" class="對象類型" column="外鍵字段字段" /> 多對多 <set name="" table=""> <key column="" /> <many-to-many column="" class=""> </set>
-------------------------------------------------------------------
1.對象狀態
臨時狀態、持久化狀態、遊離狀態
臨時狀態:new建立出來的,不禁session管理,數據庫中沒有對應的值
持久化狀態:session來調用save()方法,管理對象,數據庫中有數據
session.save(user); //持久化狀態後再對user處理就會反應到數據庫中
user.setUserName("Jack333333"); // 會反映到數據庫
遊離狀態:session管理,數據庫中有值管理,通常都是持久化對象session關閉後
對對象的處理,不一樣結果是由對象當前的狀態決定的!!!
user = (User) session.get(User.class, 5);// 先檢查緩存中是否有數據,若是有不查詢數據庫,直接從緩存中獲取
對象轉換例子:
//1. 對象狀態的轉換 @Test public void testSaveSet() throws Exception { Session session = sf.openSession(); session.beginTransaction(); // 建立對象 【臨時狀態】 User user = new User(); user.setUserName("Jack22222"); // 保存 【持久化狀態】 session.save(user); user.setUserName("Jack333333"); // 會反映到數據庫 // 查詢 /*User user = (User) session.get(User.class, 5); user.setUserName("Tomcat");// hibernate會自動與數據庫匹配(一級緩存),若是同樣就更新數據庫 */ session.getTransaction().commit(); session.close(); user.setUserName("Jack444444444"); // 打印 【遊離狀態】 System.out.println(user.getUserId()); System.out.println(user.getUserName()); }
2.緩存
緩存目的:減小對數據庫的訪問,從而提供hibernate的執行效率
1)一級緩存
session的緩存就是一級緩存;
1.如何將對象存入緩存:session調用方法save/saveOrUpdate/get/load/list/iterator方法時會將對象存入緩存
2.緩存感受:小數據感受不到,當操做數據庫次數多時就能體現出來了
3.如何處理session的緩存:用戶不能操做緩存數據,若是想要操做它的緩存數據必須經過hibernate提供的evit(),clear()方法
4.緩存的幾個方法:
session.flush();//讓一級緩存與數據庫同步,同步後就至關事務提交,session就失去管理,對象處於遊離狀態
session.evit(arg0);//清空緩存中指定的對象
session.clear();//清空緩存中全部的對象
5.什麼狀況用上面的方法:批量操做的時候
Session.flush(); // 先與數據庫同步
Session.clear(); // 再清空一級緩存內容
6.面試題:
一:session是否會共享緩存數據?不會!
二:list,iterator查詢區別:
list查詢會將全部的查詢的記錄都放入緩存,但不會緩存中獲取數據
iterator:查詢了N+1次,N是全部的查詢記錄,先將全部記錄的主鍵(1)存入緩存,以後再經過緩存中的主鍵得到每條記錄
/** * list與iterator區別 * 1. list 方法 * 2. iterator 方法 * 3. 緩存 * @throws Exception */ //1. list 方法 @Test public void list() throws Exception { Session session = sf.openSession(); session.beginTransaction(); // HQL查詢 Query q = session.createQuery("from User "); // list()方法 List<User> list = q.list(); for (int i=0; i<list.size(); i++){ System.out.println(list.get(i)); } session.getTransaction().commit(); session.close(); } //2. iterator 方法 @Test public void iterator() throws Exception { Session session = sf.openSession(); session.beginTransaction(); // HQL查詢 Query q = session.createQuery("from User "); // iterator()方法 Iterator<User> it = q.iterate(); while(it.hasNext()){ // 獲得當前迭代的每個對象 User user = it.next(); System.out.println(user); } session.getTransaction().commit(); session.close(); }
3.懶加載:
1)session.get()不是懶加載,session.load()默認是懶加載
2)懶加載對於set集合的配置:
<!-- 集合屬性,默認使用懶加載 lazy true 懶加載 extra 懶加載(智能) false 關閉懶加載 --> <set name="emps" lazy="extra"> <key column="dept_id"></key> <one-to-many class="Employee"/> </set>
3)懶加載運行路線:
在真正使用數據的時候才向數據庫發送查詢的sql;
若是調用集合的size()/isEmpty()方法,只是統計,不真正查詢數據!
4)懶加載異常
Session關閉後,不能使用懶加載數據!
若是session關閉後,使用懶加載數據報錯:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
如何解決session關閉後不能加載數據:
1.在關閉以前使用下數據;//dept.getDeptName();
2.強迫代理初始化://.Hibernate.initialize(dept);
3.直接關閉懶加載://lazy=false//在配置文件中直接修改
5)例子:
//1. 主鍵查詢,及區別 @Test public void get_load() { Session session = sf.openSession(); session.beginTransaction(); Dept dept = new Dept(); // get: 及時查詢 //dept = (Dept) session.get(Dept.class, 1); //System.out.println(dept.getDeptName()); // load,默認懶加載, 及在使用數據的時候,才向數據庫發送查詢的sql語句! dept = (Dept)session.load(Dept.class, 1); // 方式1: 先使用一下數據 dept.getDeptName(); // 方式2:強迫代理對象初始化 //Hibernate.initialize(dept); // 方式3:關閉懶加載 session.getTransaction().commit(); session.close(); // 在這裏使用 System.out.println(dept.getDeptName()); } //1. 主鍵查詢,及區別 @Test public void set() { Session session = sf.openSession(); session.beginTransaction(); Dept dept = (Dept) session.get(Dept.class, 10); System.out.println(dept.getDeptName()); System.out.println("------"); System.out.println(dept.getEmps().isEmpty()); // SQL,不是真正的查詢 session.getTransaction().commit(); session.close(); }
4.映射
多對一;一對多;多對多;一對一(多對一的特例);組件;繼承
1)一對一映射
例如案例:用戶和身份證信息
基於外鍵設計:
須要注意點:user對象的idcard屬性:沒有外鍵方只有主鍵方用<one-to-one name="idCard" class="IdCard"></one-to-one>
IdCard對象的user屬性:有外鍵方用 <many-to-one name="user" column="user_id" class="User" unique="true" cascade="save-update"></many-to-one>
一對一是特殊的多對一:idCard的user_id外鍵對應惟一一個主鍵;
// 身份證 public class IdCard { // 身份證號(主鍵) private String cardNum;// 對象惟一表示(Object Identified, OID) private String place; // 身份證地址 // 身份證與用戶,一對一的關係 private User user; // 用戶 public class User { private int userId; private String userName; // 用戶與身份證信息, 一對一關係 private IdCard idCard; <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.c_one2one"> <class name="IdCard" table="t_IdCard"> <id name="cardNum"> <generator class="assigned"></generator> </id> <property name="place" length="20"></property> <!-- 一對一映射,有外鍵方 unique="true" 給外鍵字段添加惟一約束 --> <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one> </class> </hibernate-mapping> <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.c_one2one"> <class name="User" table="t_user"> <id name="userId"> <generator class="native"></generator> </id> <property name="userName" length="20"></property> <!-- 一對一映射: 沒有外鍵方 --> <one-to-one name="idCard" class="IdCard"></one-to-one> </class> </hibernate-mapping>
基於主鍵設計:
須要注意點:主鍵是Icard表user_id是主鍵,也將是User表的外鍵
IdCard的主鍵user_id的設置:,而且須要給IdCard的user對象設置其外鍵約束<one-to-one name="user" class="User" constrained="true" cascade="save-update"></one-to-one>
User的配置就簡單了idCard配置:<one-to-one name="idCard" class="IdCard"></one-to-one>
<id name="user_id"> <!-- id 節點指定的是主鍵映射, 即user_id是主鍵 主鍵生成方式: foreign 即把別的表的主鍵做爲當前表的主鍵; property (關鍵字不能修改)指定引用的對象 對象的全名 cn..User、 對象映射 cn.User.hbm.xml、 table(id) --> <!-- 這裏講user表的主鍵做爲Idcard的外鍵 --> <generator class="foreign"> <!-- 經過他能找到User表的主鍵 --> <param name="property">user</param> </generator> </id>
// 身份證 public class IdCard { private int user_id; // 身份證號 private String cardNum; private String place; // 身份證地址 // 身份證與用戶,一對一的關係 private User user; <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.c_one2one2"> <class name="IdCard" table="t_IdCard"> <id name="user_id"> <!-- id 節點指定的是主鍵映射, 即user_id是主鍵 主鍵生成方式: foreign 即把別的表的主鍵做爲當前表的主鍵; property (關鍵字不能修改)指定引用的對象 對象的全名 cn..User、 對象映射 cn.User.hbm.xml、 table(id) --> <generator class="foreign"> <param name="property">user</param> </generator> </id> <property name="cardNum" length="20"></property> <property name="place" length="20"></property> <!-- 一對一映射,有外鍵方 (基於主鍵的映射) constrained="true" 指定在主鍵上添加外鍵約束 --> <one-to-one name="user" class="User" constrained="true" cascade="save-update"></one-to-one> </class> </hibernate-mapping>
2)組件映射,繼承映射
類關係分爲:組合關係:,繼承關係;
1.組件映射:例如Car ,Wheel就屬於組件映射
主要Car的映射配置:
<component name="wheel" class="Wheel"><property name="count"></property><property name="size"></property></component>
完整例子:
public class Car { private int id; private String name; // 車輪 private Wheel wheel; } // 車輪 public class Wheel { private int count; private int size; } <hibernate-mapping package="cn.itcast.d_component"> <class name="Car" table="t_car"> <id name="id"> <generator class="native"></generator> </id> <property name="name" length="20"></property> <!-- 組件映射 --> <component name="wheel"> <property name="size"></property> <property name="count"></property> </component> </class> </hibernate-mapping>
2.繼承映射
1)簡單繼承映射:
直接寫子類映射就能夠了,父類的屬性直接寫上;
簡單繼承映射,有多少個子類,寫多少個映射文件!
// 動物類 public abstract class Animal { private int id; private String name; <!-- 簡單繼承 --> <hibernate-mapping package="cn.itcast.e_extends1"> <class name="Cat" table="t_Cat"> <!-- 簡單繼承映射: 父類屬性直接寫 --> <id name="id"> <generator class="native"></generator> </id> <property name="name"></property> <property name="catchMouse"></property> </class> </hibernate-mapping> @Test public void getSave() { Session session = sf.openSession(); session.beginTransaction(); // 保存 // Cat cat = new Cat(); // cat.setName("大花貓"); // cat.setCatchMouse("抓小老鼠"); // session.save(cat); // 獲取時候注意:當寫hql查詢的使用,經過父類查詢必須寫上類的全名 Query q = session.createQuery("from cn.itcast.e_extends1.Animal"); List<Animal> list = q.list(); System.out.println(list); session.getTransaction().commit(); session.close(); }
2)複雜繼承映射
需求:貓,狗,動物
1.全部子類映射到一張表
1)運用場景:子類較多隻有個別屬性;
2)好處缺點:只有一個映射文件,可是不符合數據庫的設計原則
3)保存在數據庫中的表樣子:
4)配置文件:
<subclass name="Cat" discriminator-value="cat_">
每一個子類節點都用subclass節點映射,discriminator-value鑑別器字段
<class name="Animal" table="t_animal"> <id name="id"> <generator class="native"></generator> </id> <!-- 指定鑑別器字段(區分不一樣的子類) --> <discriminator column="type_"></discriminator> <property name="name"></property> <!-- 子類:貓 每一個子類都用subclass節點映射 注意:必定要指定鑑別器字段,不然報錯! 鑑別器字段:做用是在數據庫中區別每個子類的信息, 就是一個列 discriminator-value="cat_" 指定鑑別器字段,即type_字段的值 若是不指定,默認爲當前子類的全名 --> <subclass name="Cat" discriminator-value="cat_"> <property name="catchMouse"></property> </subclass> <!-- 子類:猴子 --> <subclass name="Monkey" discriminator-value="monkey_"> <property name="eatBanana"></property> </subclass> </class>
2.每一個類映射到一張表【3張表】
1)配置文件;
joined-subclass關鍵字表示子類
<class name="Animal" table="t_animal"> <id name="id"> <generator class="native"></generator> </id> <property name="name"></property> <!-- 子類:貓 t_cat key 指定_cat表的外鍵字段 --> <joined-subclass name="Cat" table="t_cat"> <key column="t_animal_id"></key> <property name="catchMouse"></property> </joined-subclass> <!-- 子類:猴子 t_monkey --> <joined-subclass name="Monkey" table="t_monkey"> <key column="t_animal_id"></key> <property name="eatBanana"></property> </joined-subclass> </class>
2)優勢缺點:一個映射文件存儲全部子類;子類父類都有映射;但表結構比較複雜,子類插入一條數據執行2條sql
3.(推薦)每一個子類映射一張表,父類不對應表(2張表)
1)配置文件:
父類設置abstract="true"指定實體對象不對應表,即在數據庫中不生成表
union-subclass節點存儲子類
<!-- abstract="true" 指定實體類對象不對應表,即在數據庫段不生成表 --> <class name="Animal" abstract="true"> <!-- 若是用union-subclass節點,主鍵生成策略不能爲自增加! --> <id name="id"> <generator class="uuid"></generator> </id> <property name="name"></property> <!-- 子類:貓 t_cat union-subclass table 指定爲表名, 表的主鍵即爲id列 --> <union-subclass name="Cat" table="t_cat"> <property name="catchMouse"></property> </union-subclass> <!-- 子類:猴子 t_monkey --> <union-subclass name="Monkey" table="t_monkey"> <property name="eatBanana"></property> </union-subclass> </class>
-------------------------------------------------------------------------------------------
1.hibernate查詢
1)Get/load主鍵查詢
Dept dept = (Dept) session.get(Dept.class, 12);
Dept dept = (Dept) session.load(Dept.class, 12);
2)對象導航查詢
Dept dept = (Dept) session.get(Dept.class, 12);
System.out.println(dept.getDeptName());
System.out.println(dept.getEmps());
3)HQL查詢Hibernate Query language hibernate 提供的面向對象的查詢語言。
// 注意:使用hql查詢的時候 auto-import="true" 要設置true,
若是是false,寫hql的時候,要指定類的全名
Query q = session.createQuery("from Dept");
System.out.println(q.list());
查詢的幾種例子:函數查詢,鏈接查詢
// a. 查詢所有列 // Query q = session.createQuery("from Dept"); //OK // Query q = session.createQuery("select * from Dept"); //NOK, 錯誤,不支持* // Query q = session.createQuery("select d from Dept d"); // OK // System.out.println(q.list()); // b. 查詢指定的列 【返回對象數據Object[] 】 // Query q = session.createQuery("select d.deptId,d.deptName from Dept d"); // System.out.println(q.list()); // c. 查詢指定的列, 自動封裝爲對象 【必需要提供帶參數構造器】 // Query q = session.createQuery("select new Dept(d.deptId,d.deptName) from Dept d"); // System.out.println(q.list()); // d. 條件查詢: 一個條件/多個條件and or/between and/模糊查詢 // 條件查詢: 佔位符 // Query q = session.createQuery("from Dept d where deptName=?"); // q.setString(0, "財務部"); // q.setParameter(0, "財務部"); // System.out.println(q.list()); // 條件查詢: 命名參數 // Query q = session.createQuery("from Dept d where deptId=:myId or deptName=:name"); // q.setParameter("myId", 12); // q.setParameter("name", "財務部"); // System.out.println(q.list()); // 範圍 // Query q = session.createQuery("from Dept d where deptId between ? and ?"); // q.setParameter(0, 1); // q.setParameter(1, 20); // System.out.println(q.list()); // 模糊 // Query q = session.createQuery("from Dept d where deptName like ?"); // q.setString(0, "%部%"); // System.out.println(q.list()); // e. 聚合函數統計 // Query q = session.createQuery("select count(*) from Dept"); // Long num = (Long) q.uniqueResult(); // System.out.println(num); // f. 分組查詢 //-- 統計t_employee表中,每一個部門的人數 //數據庫寫法:SELECT dept_id,COUNT(*) FROM t_employee GROUP BY dept_id; // HQL寫法 // Query q = session.createQuery("select e.dept, count(*) from Employee e group by e.dept"); // System.out.println(q.list()); //1) 內鏈接 【映射已經配置好了關係,關聯的時候,直接寫對象的屬性便可】 // Query q = session.createQuery("from Dept d inner join d.emps"); //2) 左外鏈接 // Query q = session.createQuery("from Dept d left join d.emps"); //3) 右外鏈接 Query q = session.createQuery("from Employee e right join e.dept"); q.list();
迫切鏈接
//1) 迫切內鏈接 【使用fetch, 會把右表的數據,填充到左表對象中!】 // Query q = session.createQuery("from Dept d inner join fetch d.emps"); // q.list(); //2) 迫切左外鏈接 Query q = session.createQuery("from Dept d left join fetch d.emps"); q.list();
HQL優化:HQL放到映射文件中
映射文件:
<!-- 存放sql語句 --> <query name="getAllDept"> <![CDATA[ from Dept d where deptId < ? ]]> </query>
// HQL 放到映射文件中 Query q = session.getNamedQuery("getAllDept"); q.setParameter(0, 10); System.out.println(q.list());
4)Criteria查詢,徹底面向對象查詢(Query By Criteria ,QBC)
Criteria criteria = session.createCriteria(Employee.class); // 構建條件 criteria.add(Restrictions.eq("empId", 12)); // criteria.add(Restrictions.idEq(12)); // 主鍵查詢
5)SQLQuery本地查詢
SQLQuery q = session.createSQLQuery("SELECT * FROM t_Dept limit 5;") .addEntity(Dept.class); // 也能夠自動封裝 System.out.println(q.list());
6)分頁:
Query q = session.createQuery("from Employee"); // 從記錄數 ScrollableResults scroll = q.scroll(); // 獲得滾動的結果集 scroll.last(); // 滾動到最後一行 int totalCount = scroll.getRowNumber() + 1;// 獲得滾到的記錄數,即總記錄數 // 設置分頁參數 q.setFirstResult(0); q.setMaxResults(3); // 查詢 System.out.println(q.list()); System.out.println("總記錄數:" + totalCount);
2.Hibernate對鏈接池的支持
1)hibernate自帶鏈接池和配置文件和c3p0配置
Hibernate 自帶的也有一個鏈接池,且對C3P0鏈接池也有支持! Hbm 自帶鏈接池: 只維護一個鏈接,比較簡陋。 能夠查看hibernate.properties文件查看鏈接池詳細配置: ################################# ### Hibernate Connection Pool ### ################################# hibernate.connection.pool_size 1 【Hbm 自帶鏈接池: 只有一個鏈接】 ########################### ### C3P0 Connection Pool### 【Hbm對C3P0鏈接池支持】 ########################### #hibernate.c3p0.max_size 2 最大鏈接數 #hibernate.c3p0.min_size 2 最小鏈接數 #hibernate.c3p0.timeout 5000 超時時間 #hibernate.c3p0.max_statements 100 最大執行的命令的個數 #hibernate.c3p0.idle_test_period 3000 空閒測試時間 #hibernate.c3p0.acquire_increment 2 鏈接不夠用的時候, 每次增長的鏈接數 #hibernate.c3p0.validate false
2)如何選擇本身的鏈接池:能夠在Hibernate.cfg.xml中配置
<!-- 【鏈接池配置】 --> <!-- 配置鏈接驅動管理類 --> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> <!-- 配置鏈接池參數信息 --> <property name="hibernate.c3p0.min_size">2</property> <property name="hibernate.c3p0.max_size">4</property> <property name="hibernate.c3p0.timeout">5000</property> <property name="hibernate.c3p0.max_statements">10</property> <property name="hibernate.c3p0.idle_test_period">30000</property> <property name="hibernate.c3p0.acquire_increment">2</property>
3.二級緩存
1)引入二級緩存i
一級緩存Session只在當前的有效,Sesssion關閉就會失效,而二級緩存做用了應用程序,能夠跨越多個session
2)優勢:享用二級緩存在配置文件中配置下就好,不想用刪了就能夠;也能夠換成本身的緩存框架
3)配置二級緩存
hibernate.properties中對二級緩存的配置
########################## ### Second-level Cache ### ########################## #hibernate.cache.use_second_level_cache false【二級緩存默認不開啓,須要手動開啓】 #hibernate.cache.use_query_cache true 【開啓查詢緩存】 ## choose a cache implementation 【二級緩存框架的實現】 #hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider #hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider 默認實現 #hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider #hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider #hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider
二級緩存的使用步驟:
打開二級緩存-》選擇緩存框架-》指定哪些類加入緩存-》測試
其中指定哪些類加入緩存能夠用緩存策略:
HQL查詢 【setCacheable 指定從二級緩存找,或者是放入二級緩存】
<class-cache usage="read-only"/> 放入二級緩存的對象,只讀; <class-cache usage="nonstrict-read-write"/> 非嚴格的讀寫 <class-cache usage="read-write"/> 讀寫; 放入二級緩存的對象能夠讀、寫; <class-cache usage="transactional"/> (基於事務的策略)
例子:
集合緩存:
<usage="read-wirte" collection="Dept.emps">集合緩存的元素對象也加入二級緩存
查詢緩存:list()默認存入緩存不會存入一級,只能存入二級中
Hibernate.cfg.xml <!--****************** 【二級緩存配置】****************** --> <!-- a. 開啓二級緩存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <!-- b. 指定使用哪個緩存框架(默認提供的) --> <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property> <!-- 開啓查詢緩存 --> <property name="hibernate.cache.use_query_cache">true</property> <!-- c. 指定哪一些類,須要加入二級緩存 --> <class-cache usage="read-write" class="cn.itcast.b_second_cache.Dept"/> <class-cache usage="read-only" class="cn.itcast.b_second_cache.Employee"/> <!-- 集合緩存[集合緩存的元素對象,也加加入二級緩存] --> <collection-cache usage="read-write" collection="cn.itcast.b_second_cache.Dept.emps"/> App 測試類 public class App { private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(Dept.class) .addClass(Employee.class) // 測試時候使用 .buildSessionFactory(); } // 1. 測試二級緩存的使用 // 沒有/有用 二級緩存 @Test public void testCache() { Session session1 = sf.openSession(); session1.beginTransaction(); // a. 查詢一次 Dept dept = (Dept) session1.get(Dept.class, 10); dept.getEmps().size();// 集合 session1.getTransaction().commit(); session1.close(); System.out.println("------"); // 第二個session Session session2 = sf.openSession(); session2.beginTransaction(); // a. 查詢一次 dept = (Dept) session2.get(Dept.class, 10); // 二級緩存配置好; 這裏不查詢數據庫 dept.getEmps().size(); session2.getTransaction().commit(); session2.close(); } @Test public void listCache() { Session session1 = sf.openSession(); session1.beginTransaction(); // HQL查詢 【setCacheable 指定從二級緩存找,或者是放入二級緩存】 Query q = session1.createQuery("from Dept").setCacheable(true); System.out.println(q.list()); session1.getTransaction().commit(); session1.close(); Session session2 = sf.openSession(); session2.beginTransaction(); q = session2.createQuery("from Dept").setCacheable(true); System.out.println(q.list()); // 不查詢數據庫: 須要開啓查詢緩存 session2.getTransaction().commit(); session2.close(); } }
4)Session的管理方式:
能夠sf.openSession()方式建立;
能夠經過線程的方式建立 :首先要在配置文件中配置:
<property name="hibernate.current_session_context_class">thread</property>
以後:經過sf.getCurrentSession()得到當前的線程的session或者存入session
@Test public void testSession() throws Exception { //openSession: 建立Session, 每次都會建立一個新的session Session session1 = sf.openSession(); Session session2 = sf.openSession(); System.out.println(session1 == session2); session1.close(); session2.close(); //getCurrentSession 建立或者獲取session // 線程的方式建立session // 必定要配置:<property name="hibernate.current_session_context_class">thread</property> Session session3 = sf.getCurrentSession();// 建立session,綁定到線程 Session session4 = sf.getCurrentSession();// 從當前訪問線程獲取session System.out.println(session3 == session4); // 關閉 【以線程方式建立的session,能夠不用關閉; 線程結束session自動關閉】 //session3.close(); //session4.close(); 報錯,由於同一個session已經關閉了! }
4.Hibernate與struts小案例