Java - 框架之 Hibernate
一:hibernate.cfg.xml 配置
<!-- 1、配置數據庫連接的4個參數 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <!-- 連接數據庫,本地數據庫可以不用設置主機和端口 --> <property name="hibernate.connection.url">jdbc:mysql:///hibernate_01</property> <!--jdbc:mysql://localhost:3306/數據庫名 --> <!-- 用戶名/密碼 --> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">chaoqi</property> <!-- 是否顯示sql語句 --> <property name="show_sql">true</property> <!--是否格式化sql語句 --> <property name="format_sql">true</property> <!-- 是否自動提交事務:針對insert有效,針對delete無效 --> <property name="hibernate.connection.autocommit">true</property> <!-- 配置值 JavaBean 與表的映射文件 --> <mapping resource="com/q/hibernate/user.hbm.xml" />
二:類 xml 配置
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- name: 映射的類,table: 操作的數據庫 --> <class name="com.q.hibernate.User" table="t_user"> <!-- <id>: 與數據庫對應id值, name: 類中id屬性, column: 表中id屬性 (類和表中的字段如果一樣可以不用寫 column) --> <id name="uid" column="id"> <!-- 設置自增ID --> <generator class="native"></generator> </id> <!-- 添加字段(name, password) 由於數據庫中字段名一樣所以不用寫 column 屬性 --> <property name="username"></property> <property name="password"></property> </class> </hibernate-mapping>
三:插入數據
// 1. 獲取核心配置對象,默認是加載src的hibernate.cfg.xml文件 Configuration cfg = new Configuration().configure(); // 2. 創建會話工廠 SessionFactory factory = cfg.buildSessionFactory(); // 3. 創建會話【相當於連接 Connect】 Session session = factory.openSession(); // 開啟事務 Transaction trans = session.getTransaction(); trans.begin(); // 實例化要插入數據的對象 User user = new User(); user.setUsername("q3"); user.setPassword("123"); // 保存在 session 中 session.save(user); // 提交事務 trans.commit(); // 4. 關閉會話 session.close(); // 5. 關閉工廠,釋放資源 factory.close();
四. sessionFactory 提供類兩個方式獲取 session
1. factory.openSession() # 獲取一個權限的 session (重新開啟一個新的線程, 需要手動 close )
2. factory.getCurrentSession() # 獲取一個與當前線程綁定的 session (同一個線程裏面調用返回同一個對象)
- 配置: <property name="hibernate.current_session_context_class">thread</property>
// 開啟一個線程: new Thread(){ public void run(){ System.out.println("new thread"); } }.start();
五. Transaction 事務
1. 默認需要手動提交事務:
// 開啟事務 Transaction trans = session.getTransaction(); trans.begin(); // 提交事務 trans.commit();
2. 可在 hibernate.cfg.xml 文件中自動配置提交
<!-- 是否自動提交事務:針對insert有效,針對delete無效 --> <property name="hibernate.connection.autocommit">true</property>
5.5. session 的 get / load 方法
1. 通過 id 獲取數據
2. get : 未找到數據返回 null (直接查詢數據庫)
User user1 = (User) session.get(User.class, 1);
3. load: 未找到數據報錯 (懶加載,用到時才去查詢數據庫,返回的是對象的一個代理)
User user2 = (User) session.load(User.class, 1);
六. session 的 delete / update 方法
1. update:
// 如果是從數據庫中獲取的對象,會自動 update // 如: // User user = (User) session.get(User.class, 1); // user.setPassword("123"); // 創建的對象需要手動更新 User user = new User(); user.setUid(1); user.setUsername("a"); user.setPassword("b"); session.update(user); // 更新時, 一定要有id, 否則會出現異常 session.saveOrUpdate(user); // 有就更新,沒有就創建
2. delete : 需要手動提交事務
// 開啟事務 session.getTransaction().begin(); 方式一:(創建一個 User,設置 id, 調用delete) User user = new User(); user.setUid(1); session.delete(user); 方式二:(獲取刪除對象,調用delete) User user = (User) session.get(User.class, 1); session.delete(user); // 提交事務 session.getTransaction().commit();
七. Query 查詢 (HQL)
1. // 創建查詢對象 User username password Query query = session.createQeury("from 類名 where 字段=? and 字段?") query.setParameter(0, "a"); query.setParameter(1, "b"); // 執行 (返回一條數據) User user = (User) query.uniqueResult();
2. 分頁查詢 limit
// 創建查詢對象 Query query = session.createQuery("from User"); // 分頁查詢 [limit 3,3] query.setFirstResult(3); // 起始位置 query.setMaxResults(3); // 返回數據條數 // 返回多條數據 List<User> list = query.list(); for(User obj : list){ System.out.println(obj); }
八. Criteria 查詢 (條件查詢)
// 創建 Criteria 查詢 Criteria criteria = session.createCriteria(User.class); // 1. eq : = criteria.add(Restrictions.eq("username","q")); criteria.add(Restrictions.eq("username","123")); System.out.println(criteria.uniqueResult()); // 2. gt 等於, ge 大於等於 criteria.add(Restrictions.gt("id", 3)); System.out.println(criteria.list()); // 3. lt 小於, le 小於等於 // 4. like 模糊查詢 criteria.add(Restrictions.like("username", "%q%")); System.out.println(criteria.list()); // 看文檔...
九. SQLQuery 查詢 原生(SQL)
// 查詢 SQLQuery query = session.createSQLQuery("select * from user"); // 返回的數組封裝到了集合,集合中是數組 List<Object[]> list = query.list(); for(Object[] objs : list){ for(Object obj : objs){ System.out.println(obj); } }
十. hibernate 配置文件
1. update : 更新,如果數據庫沒有表,會自動創建。
2. create : 每次啟動 hibernate 都會創建表。
3. create-drop : 每次啟動 hibernate 都會創建表,執行結束後會刪除表。
4. 數據庫方言配置:
mysql: <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</proterty>
十一. 實體類 entity (model) 的編寫規則 和 OID
entity:
1. 必須提供無參數的構造方法。
2. 提供一個標識屬性,映射數據庫主鍵字段,提供id。
3. 所有屬性提供 public 訪問控制符的 get/set 方法 (JavaBean)。
4. 標識屬性盡量使用包裝類型。
5. 不能使用 final 修飾實體 (將無法生成代理對象進行優化)。
OID: 持久化對象的唯一標識
1. 對象的 OID 和 數據庫中標的主鍵對應。
十二. 主鍵生成
< id name="id" column="id"> 1. native : mysql 數據庫自增, oracle數據庫自增。 2. uuid : 需要將類中的 id 字段改為 字符類型,自動設置 uud。 3. increment : 自增,hibernate 執行 select max(id) 查詢,有並發問題。 4. sequence : oracle 數據庫使用。 5. assigned : 需手動設置 id <generator class="native"></generator> </id>
十三. hibernate 實體的狀態
三種狀態:瞬時狀態,持久狀態,脫管狀態
1. 瞬時狀態:transient, session 沒有緩存,數據庫沒有記錄,oid 沒有值。
load, get 返回持久對象,關閉session或清除後會變成脫管狀態。
2. 持久狀態:persistent, session 有緩存,數據庫有記錄,oid 有值。
使用 save, saveOrUpdate 。
3. 脫管狀態:detached, session 沒有緩存,數據庫有記錄,oid 有值。
十四. 一級緩存
當獲取一次 session ,hibernate 在 session 中創建多個集合,用於存放操作數據,優先從session緩存中獲取,
當 session 關閉時,一級緩存銷毀。
HQL / Criteria 有一級緩存
SQL 沒有一級緩存
1. 使用 get 方法時會觸發一級緩存,查詢同一條語句時,只會執行一條 查詢sql
2. 清除緩存: session.clear(); / session.evict(s);
3. 一級緩存快照:與一級緩存一樣的存放位置,對一級緩存數據備份。保證數據庫的數據與一級緩存中的數據一致,
如果一級緩存修改了,在執行 commit 提交時,將自動刷新一級緩存,執行 update 語句,並更新到數據。
4. 一級緩存刷新:手動刷新,讓一級緩存的數據和數據庫保持一致。
sesson.flush();
十五. save / persist
1. save:
// 執行save方法,觸發 insert 語句,從數據庫獲取主鍵。
// 執行save前,設置oid會被忽略。
// 如果執行查詢,session緩存被清除了,再次執行save方法,將執行 insert
2. persist
// 保存對象前,不能設置 Id ,否則報錯。
// 在事務外部調用不會執行 insert ,在事務內部中結束後執行 insert。
十六. 一對多
1. Customer
private Integer id; private String name; // 一對多屬性描述 private Set<Order> orders = new HashSet<Order>();
2. Order
private Integer id; private String name; private Customer customer;
3. customer.hbm.xml
<hibernate-mapping package="domain"> <class name="Customer" table="t_customer"> <id name="id"> <generator class="native" /> </id> <property name="name" /> <!-- cascade : 級聯 save-update : 級聯保存,級聯修改,保存A的同時保存B。 delete : 級聯刪除,刪除A的同時刪除B。(先刪B,再刪A) delete-orphan : 刪除A關聯的B ,但不刪除A all = [ save-update, delete ] (級聯保存, 級聯刪除) all-delete-orphan = [ save-update, delete, delete-opphan ] --> <set name="orders" cascade="all-delete-orphan"> <key column="customer_id"></key> <one-to-many class="Order"></one-to-many> </set> </class> </hibernate-mapping>
4. order.hbm.xml
<hibernate-mapping package="domain"> <class name="Order" table="t_order"> <id name="id"> <generator class="native" /> </id> <property name="name" /> <many-to-one name="customer" class="Customer" column="customer_id" /> </class> </hibernate-mapping>
# 一對多操作
5. 創建數據
// 0. 開啟事務 session.getTransaction().begin(); // 1. 創建客戶對象 Customer customer = new Customer(); customer.setName("q"); // 2. 創建訂單對象 Order order1 = new Order(); order1.setName("商品1"); Order order2 = new Order(); order2.setName("商品2"); // 3. 給訂單建立客戶關系 order1.setCustomer(customer); order2.setCustomer(customer); // 4. 給客戶添加訂單 customer.getOrders().add(order1); customer.getOrders().add(order2); // 5. 保存數據 session.save(customer); session.save(order1); session.save(order2); // 6. 結束事務 session.getTransaction().commit();
6. 查詢數據
Customer customer = (Customer) session.get(Customer.class, 1); // 獲取用戶 String customer1 = customer.getName(); System.out.println(customer1); // 獲取用戶當前所有訂單 Set<Order> orders = customer.getOrders(); for(Order order : orders){ System.out.println(order.getName()); }
7. 刪除數據
Customer customer = (Customer) session.get(Customer.class, 1); session.getTransaction().begin(); // 更新訂單 for(Order order : customer.getOrders()){ order.setCustomer(null); } // 刪除用戶 session.delete(customer);
8. 級聯更新
Customer customer = new Customer(); customer.setName("q"); Order order1 = new Order(); Order order2 = new Order(); order1.setName("d1"); order2.setName("d2"); order1.setCustomer(customer); order2.setCustomer(customer); customer.getOrders().add(order1); customer.getOrders().add(order2); session.save(customer);
9. 級聯刪除 先刪 B 再刪 A
session.getTransaction().begin(); Customer customer = (Customer) session.get(Customer.class, 2); session.delete(customer); session.getTransaction().commit();
10. 級聯刪除 刪除與A關聯的B 但是不刪除 A
session.getTransaction().begin(); Customer customer = (Customer) session.get(Customer.class, 1); Set<Order> orders = customer.getOrders(); Iterator<Order> iterator = orders.iterator(); while(iterator.hasNext()){ iterator.next(); // 取出下一個元素 iterator.remove(); // 移除當前元素 } session.getTransaction().commit();
十七. 多對多
1. Order
private Integer id; private String name; private Customer customer;
2. Course
private Integer cid; private String name; private Set<Student> students = new HashSet<Student>();
3. Student.hbm.xml
<hibernate-mapping package="domain"> <class name="Order" table="t_order"> <id name="id"> <generator class="native" /> </id> <property name="name" /> <many-to-one name="customer" class="Customer" column="customer_id" /> </class> </hibernate-mapping>
4. Course.hbm.xml
<hibernate-mapping package="domain"> <class name="Course" table="t_course"> <id name="cid" column="id"> <generator class="native"></generator> </id> <property name="name" /> <set name="students" table="t_student_course" cascade="save-update"> <key column="cid"></key> <many-to-many class="Student" column="sid" /> </set> </class> </hibernate-mapping>
# 多對多操作
註意事項:
1. 在xml配置文件中 set 屬性要加上: cascade="save-update" 才會自動創建表
2. 如果在 Student配置 inverse="true", 由Course來維護外鍵關系,中間表沒數據。
3. 默認Student配置 inverse="false", 由 Student來維護外鍵關系,中間表有數據。
4. 多對多,inverse 不能兩邊都為 true, 如果兩邊都為 true, 不管保存哪個對象,中間表都沒數據。
5. 創建數據
session.getTransaction().begin(); // 創建學生對象 Student stu1 = new Student("ming"); Student stu2 = new Student("pang"); // 創建課程對象 Course c1 = new Course("Java"); Course c2 = new Course("Python"); // 將課程綁定學生 stu1.getCourses().add(c1); stu1.getCourses().add(c2); stu2.getCourses().add(c1); stu2.getCourses().add(c2); // 創建保存 session.save(stu1); session.save(stu2); session.getTransaction().commit();
6. 查詢
Student stu1 = (Student) session.get(Student.class,3); System.out.println(stu1); System.out.println(stu1.getCourses()); // Set<Course> courses = stu1.getCourses(); // System.out.println(courses);
十八. 鎖
1. 寫鎖
當線程A拿到 數據行 d 的鎖時,其它線程無法對 數據行 d 進行操作,會阻塞住,直到線程A釋放(commit)。
session.getTransaction().begin(); //Hibernate的寫鎖/排他鎖實現 /** * 演示客戶名,查找id為1 * A線程【命令行】,開啟事務->讀取一行數據加鎖 * B線程【應用程序】,開啟事務->讀取一行數據加鎖 */ //執行sql語句,使用寫鎖 /*Customer customer = (Customer) session.get(Customer.class,1, LockOptions.UPGRADE); System.out.println(customer);*/ /** * HQL的from後面不能寫for update * 調用query.setLockOptions(LockOptions.UPGRADE); */ Query query = session.createQuery("from Customer where id=?"); query.setLockOptions(LockOptions.UPGRADE); query.setParameter(0,1); Customer customer = (Customer) query.uniqueResult(); System.out.println(customer); session.getTransaction().commit();
2. 樂觀鎖
在數據庫增加一個字段(verson),每次更新不同的數據會對字段值+1(verson+1) ,
如果本次更新值後verson+1 小於數據中verson值,那麽會報錯。
# 需要配置 XML 中的 verson
<!--version 放在id和property的中間,這個由dtd約束決定--> <version name="version"></version>
# 操作
session.getTransaction().begin(); //樂觀鎖:每次更新,版本會加1 /** * 如果當前的版本【2】比數據庫【3】低,就不更新,出錯 * 樂觀鎖是hibernate自己實現 * for update 是mysql實現 */ Customer customer = (Customer) session.get(Customer.class,1); customer.setName("abc232"); session.getTransaction().commit();
Java - 框架之 Hibernate