配置映射文件和數(shù)據(jù)庫(kù)的關(guān)系
<property name="hibernate.hbm2ddl.auto">update</property>
- update:如果數(shù)據(jù)庫(kù)沒有創(chuàng)建表,自動(dòng)創(chuàng)建表
- create:每次啟動(dòng)hibernate都會(huì)創(chuàng)建表
- create-drop:每次啟動(dòng)hibernate都會(huì)創(chuàng)建表,并執(zhí)行完后刪除表
- validate :檢查hbm的文件,如果和數(shù)據(jù)庫(kù)的字段不一致會(huì)拋異常
數(shù)據(jù)庫(kù)方言
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
*hbm.xml映射文件解說
實(shí)體類entity(model)的編寫規(guī)則
- 我們?cè)谑褂肏ibernate時(shí),書寫User類,這個(gè)類我們稱為JavaBean
- JavaBean可以理解為可以提供私有屬性,并提供get和set方法
- POJO(Plain Ordinary Java Object)其實(shí)就是一個(gè)普通的JavaBean
- 實(shí)體類也被稱為模型,在Hibernate中,也稱為實(shí)體類,是與數(shù)據(jù)庫(kù)表直接關(guān)聯(lián)的
編寫規(guī)則如下
- 提供一個(gè)無參public控制符的構(gòu)造方法
- 提供一個(gè)標(biāo)識(shí)(屬性、映射數(shù)據(jù)表主鍵字段,提供id)
- 所有屬性提供public訪問控制符的get和set方法
- 標(biāo)識(shí)屬性應(yīng)盡量使用基本類型的包裝類
- 不要使用final修飾實(shí)體(將無法生成代理對(duì)象進(jìn)行優(yōu)化)
持久化對(duì)象的唯一標(biāo)識(shí)OID
- Java按地址區(qū)分同一個(gè)類的不同對(duì)象
- 關(guān)系數(shù)據(jù)庫(kù)使用主鍵區(qū)分同一條記錄
- Hibernate使用OID來建立內(nèi)存中的對(duì)象和數(shù)據(jù)庫(kù)記錄的對(duì)應(yīng)關(guān)系(結(jié)論:對(duì)象的OID和數(shù)據(jù)庫(kù)的表的主鍵對(duì)應(yīng))
- 保證OID的唯一性,應(yīng)該讓Hibernate來為OID進(jìn)行賦值
區(qū)分自然主鍵和代理主鍵
- 主鍵需要具備不為空/不能重復(fù)/不能被修改
- 自然主鍵:在業(yè)務(wù)中,某個(gè)屬性符合主鍵的三個(gè)要求,該屬性可以作為主鍵列(登錄名可以是自然主鍵)
- 代理主鍵:在業(yè)務(wù)中,不存在符合以上三個(gè)條件的屬性,那么就增加一個(gè)沒有意義的列作為主鍵
基本數(shù)據(jù)類型和包裝類型
- 基本數(shù)據(jù)類型和包裝類型對(duì)應(yīng)hibernate的映射類型相同
- 基本類型無法表達(dá)null,數(shù)字類型的默認(rèn)值是0
- 包裝類默認(rèn)值為null,當(dāng)對(duì)于默認(rèn)值由業(yè)務(wù)意義的時(shí)候需要使用包裝類型
SQL、Hibernate和對(duì)象類型對(duì)應(yīng)表

主鍵的生成策略【非常重要】
1、id主鍵,如果屬性與表字段不一樣需要指定column
2、column屬性對(duì)應(yīng)表的列名
3、type數(shù)據(jù)類型
主鍵生成策略 常用2、5、6、7
1、increment由hibernate自己維護(hù)自動(dòng)增加,原理使用max函數(shù),然后+1,不建議使用,因?yàn)橛芯€程安全問題
2、identity:hibernate使用數(shù)據(jù)庫(kù)自帶的自動(dòng)增長(zhǎng)方式
例如:mysql是auto_increment
3、squence:hibernate使用采用數(shù)據(jù)庫(kù)系統(tǒng)
例如:oracle提供的系統(tǒng)
4、hilo:hibernate自己實(shí)現(xiàn)的算法,自己生成主鍵(hilo算法)幾乎不用
5、native根據(jù)數(shù)據(jù)庫(kù)自動(dòng)選擇identity、squnce、hilo
6、uuid采用字符串唯一值
7、assigned自然主鍵,由程序自動(dòng)維護(hù)
具體代碼如下:
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.gfy.hibernate.domain.User" table="t_user">
<!--1、id主鍵,如果屬性與表字段不一樣需要指定column
2、column屬性對(duì)應(yīng)表的列名
3、type數(shù)據(jù)類型-->
<id name="uid" column="id">
<!--主鍵生成策略 常用2、5、6、7
1、increment由hibernate自己維護(hù)自動(dòng)增加,原理使用max函數(shù),然后+1,不建議使用,因?yàn)橛芯€程安全問題
2、identity:hibernate使用數(shù)據(jù)庫(kù)自帶的自動(dòng)增長(zhǎng)方式
例如:mysql是auto_increment
3、squence:hibernate使用采用數(shù)據(jù)庫(kù)系統(tǒng)
例如:oracle提供的系統(tǒng)
4、hilo:hibernate自己實(shí)現(xiàn)的算法,自己生成主鍵(hilo算法)幾乎不用
5、native根據(jù)數(shù)據(jù)庫(kù)自動(dòng)選擇identitysqunce、hilo中的幾乎不用
6、uuid采用字符串唯一值
7、assigned自然主鍵,由程序自動(dòng)維護(hù)-->
<generator class="native"></generator>
</id>
<property name="username"></property>
<property name="password"></property>
<property name="gender"></property>
</class>
</hibernate-mapping>
普通屬性
class標(biāo)簽的dynamic-insert=“true”,是否動(dòng)態(tài)生成插入語句【如果屬性字段為空,就不會(huì)有這些字段的插入語句】
<class name="com.gfy.hibernate.domain.User" table="t_user" dynamic-insert="true">
class標(biāo)簽的dynamic-update=“true”與insert類似
type的使用和設(shè)置,如圖:

Date類型
- property中type不寫,數(shù)據(jù)庫(kù)對(duì)應(yīng)的類型,datetime【年,月,日,時(shí),分,秒】
- type中date數(shù)據(jù)庫(kù)對(duì)應(yīng)的類型,date【年、月、日】
- type中time,數(shù)據(jù)庫(kù)對(duì)應(yīng)的類型time【時(shí)、分、秒】
- type中timestamp,數(shù)據(jù)庫(kù)對(duì)應(yīng)的類型timestamp【時(shí)間戳】不太好用,如果需要時(shí)間戳,建議使用long類型
Hibernate實(shí)體的狀態(tài)
狀態(tài)介紹
- 實(shí)體Entity有三種狀態(tài),瞬時(shí)狀態(tài)、持久狀態(tài)、脫管狀態(tài)
- 瞬時(shí)狀態(tài) transient、session沒有緩存,數(shù)據(jù)庫(kù)也沒有記錄,oid沒有值
- 持久狀態(tài),persistent、session有緩存,數(shù)據(jù)庫(kù)有記錄,oid有值
- 脫管狀態(tài) datached session沒有緩存,數(shù)據(jù)庫(kù)有記錄,oid有值
瞬時(shí)轉(zhuǎn)換持久:新創(chuàng)建一個(gè)對(duì)象,經(jīng)過save或者saveOrUpdate調(diào)用后,會(huì)變成持久狀態(tài)
總結(jié)狀態(tài)轉(zhuǎn)換過程:查詢操作get、load、createQuery、createCriteria等獲的都是持久狀態(tài),瞬時(shí)狀態(tài)調(diào)用save、update之后變成持久狀態(tài)
持久狀態(tài)轉(zhuǎn)換脫管狀態(tài):session.clear()清除所有、session.close()關(guān)閉、session.evict(obj)清除指定的PO對(duì)象
一級(jí)緩存概念
一級(jí)緩存又稱為session級(jí)別的緩存,當(dāng)獲得一次會(huì)話(session)、hibernate在session中創(chuàng)建多個(gè)集合(map)用于存放操作數(shù)據(jù)(PO對(duì)象)為程序,優(yōu)化服務(wù),如果之后需要相應(yīng)的數(shù)據(jù),hibernate優(yōu)先從session中緩存中獲取,如果有就使用,如果沒有在查詢數(shù)據(jù)庫(kù),當(dāng)session關(guān)閉時(shí),一級(jí)緩存銷毀
User user = (User) session.get(User.class,1);
System.out.println(user);
User user1 = (User) session.get(User.class,1);
System.out.println(user1);
移除緩存
//方法一
session.clear();
//方法二
session.evict(user)
一級(jí)緩存快照【掌握】
快照與一級(jí)緩存一樣的存放位置,對(duì)一級(jí)緩存數(shù)據(jù)進(jìn)行備份,保證數(shù)據(jù)庫(kù)的數(shù)據(jù)與一級(jí)緩存的數(shù)據(jù)必須一致,如果一級(jí)緩存修改了,在執(zhí)行commit提交時(shí),將自己刷新為一級(jí)緩存,執(zhí)行update語句,將一級(jí)緩存的數(shù)據(jù)與更新到數(shù)據(jù)庫(kù)

Session session = HibernateUtils.openSession();
session.beginTransaction().begin();
User user = (User) session.get(User.class,1);
user.setUsername("lisi02");
session.beginTransaction().commit();
session.close();
session.flush()手動(dòng)刷新、保持一致緩存與數(shù)據(jù)庫(kù)一致
此時(shí)會(huì)執(zhí)行兩條update語句,如果去除flush代碼只有一條update語句
一級(jí)緩存細(xì)節(jié)
HQL的結(jié)果或會(huì)進(jìn)入一級(jí)緩存,控制臺(tái)查看只有一次查詢語句
List list = session.createQuery("from User").list();
User user = (User) session.get(User.class,1);
System.out.println(user);
SQL不會(huì)對(duì)數(shù)據(jù)進(jìn)行session緩存,控制臺(tái)查看有兩條查詢語句
SQLQuery query = session.createSQLQuery("select * from t_user");
System.out.println(query.list());
User user = (User) session.get(User.class,1);
System.out.println(user);
Criteria 對(duì)數(shù)據(jù)進(jìn)行一級(jí)緩存,控制臺(tái)查看只有一次查詢語句
Criteria criteria = session.createCriteria(User.class);
System.out.println(criteria.list());
User user = (User) session.get(User.class,1);
System.out.println(user);
其他API
save和persist方法的區(qū)別
save方法:瞬狀態(tài)轉(zhuǎn)換持久態(tài),會(huì)初始化OID
1、執(zhí)行save方法,立即觸發(fā)insert語句,從數(shù)據(jù)庫(kù)獲得的主鍵的值OID
2、執(zhí)行save之前,設(shè)置OID將忽略
3、如果執(zhí)行查詢session緩存移除時(shí),在執(zhí)行save方法,將執(zhí)行insert
User user = new User();
user.setUid(2);
user.setUsername("April");
user.setPassword("123456");
user.setGender("female");
session.save(user);


在執(zhí)行save方法,立即觸發(fā)insert語句,從數(shù)據(jù)庫(kù)中獲取主鍵的值
persist方法:瞬時(shí)狀態(tài)轉(zhuǎn)換持久態(tài)
- 在保存對(duì)象之前,不能設(shè)置id,否則會(huì)報(bào)錯(cuò)
- save和persist都是持久化對(duì)象的作用
- save因?yàn)樾枰祷匾粋€(gè)主鍵,因此會(huì)立即執(zhí)行insert語句,而Persist在事務(wù)外部,調(diào)用時(shí)則不會(huì)立即執(zhí)行insert語句,在事務(wù)內(nèi)調(diào)用還會(huì)立即執(zhí)行insert語句
Hibernate的多表關(guān)聯(lián)關(guān)系映射

一對(duì)多:一表(主表)必須有主鍵,多表(從表)必須外鍵,主表主鍵與從表外鍵,形成主外鍵關(guān)系
一對(duì)一:主外鍵關(guān)系
案例:顧客與訂單的關(guān)系
分析:一個(gè)顧客有多個(gè)訂單,一個(gè)訂單只能有一個(gè)顧客,因此這種關(guān)系是一對(duì)多的關(guān)系,顧客是主表
顧客類
public class Customer {
private Integer id;
private String name;
private Set<Order> orders = new HashSet<Order>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
}
訂單類
public class Order {
private Integer id;
private String orderNo;
private Customer customer;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
Customer.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.gfy.hibernate.domain">
<class name="Customer" table="t_customer">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"></property>
<set name="orders">
<key column="customer_id"></key>
<one-to-many class="Order"></one-to-many>
</set>
</class>
</hibernate-mapping>
Order.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.gfy.hibernate.domain">
<class name="Order" table="t_order">
<id name="id">
<generator class="native"></generator>
</id>
<property name="orderNo"></property>
<many-to-one name="customer" column="customer_id"></many-to-one>
</class>
</hibernate-mapping>
具體執(zhí)行的代碼如下:
Session session = HibernateUtils.openSession();
session.beginTransaction().begin();
Customer customer = new Customer();
customer.setName("April");
Order order = new Order();
order.setCustomer(customer);
order.setOrderNo("JD"+Math.random()*10000);
order.setName("lipstick");
Order order1 = new Order();
order1.setCustomer(customer);
order1.setOrderNo("TB"+Math.random()*10000);
order1.setName("eyebrow pencil");
Set<Order> orders = new HashSet<>();
orders.add(order);
orders.add(order1);
customer.setOrders(orders);
//因?yàn)橛唵我蕾囍麈I的id 所以這里需要先保存顧客
session.save(customer);
session.save(order);
session.save(order1);
session.beginTransaction().commit();
session.close();
數(shù)據(jù)庫(kù)查詢結(jié)果


那么問題來了,為啥我們?cè)趫?zhí)行上面的代碼的時(shí)候查詢數(shù)據(jù)庫(kù)在沒有創(chuàng)建顧客和訂單表的情況下能夠查詢到這兩張表的數(shù)據(jù)呢,表是如何創(chuàng)建和插入數(shù)據(jù)的,其實(shí)我們?cè)谖恼麻_頭都已經(jīng)講過了,配置文件配置如下就可以了
<property name="hibernate.hbm2ddl.auto">update</property>
當(dāng)然我們還需要把JavaBean實(shí)體類的映射屬性和數(shù)據(jù)庫(kù)表的屬性進(jìn)行關(guān)聯(lián)
<mapping resource="com/gfy/hibernate/domain/Customer.hbm.xml"/>
<mapping resource="com/gfy/hibernate/domain/Order.hbm.xml"/>