1. 程式人生 > >Java - 框架之 Hibernate

Java - 框架之 Hibernate

持久化 setuid string 找到 id屬性 ade form 並發 rac

一: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