1. 程式人生 > >Hibernate框架技術視頻課程——筆記(一)

Hibernate框架技術視頻課程——筆記(一)

course flush cte found cti 關閉session localhost trace follow

視頻課程鏈接:http://edu.51cto.com/course/10747.html

Hibernate框架技術,主講:湯小洋

一、Hibernate簡介

1. Hibernate是什麽?

hibernate單詞本義是“冬眠”,是一個持久層框架,或稱為ORM(Object Relational Mapping)框架,類似的還有MyBatis

  • 用來訪問數據庫,做數據持久化操作
  • 本質上只是對JDBC操作進行封裝,Java訪問數據庫的唯一方式就是JDBC
  • 簡化JDBC繁瑣的操作

版本:hibernate 3.x 4.x 5.x

2. 學習參考

官網

中文官方文檔:Hibernate_Reference_zh_CN.pdf

二、回顧

1. JDBC訪問數據庫的步驟

Class.forName(driverClassName)
Connection conn=DriverManager.getConnection(url,user,password);
PreparedStatement ps=conn.preparedStatement(sql);
//ps.executeUpdate();
ResultSet rs=ps.executeQuery();
while(rs.next){
  //RM(RowMapper)行映射
}
rs.close();
ps.close();
conn.close();

2. 數據庫操作中的可變部分

2.1 連接信息

? driverClassName、url、user、password

? 也稱為數據源datasource

2.2 SQL語句

? sql

2.3 行映射RM

? 映射關系:

一條數據 對象
屬性

3. MyBatis訪問數據庫的步驟

3.1 添加jar包

? mybatis-3.x.jar

? mysql-connector-java-x.jar

3.2 添加dtd文件

? mybatis-config.dtd

? mybatis-mapper.dtd

3.3 兩個核心文件

? 核心配置文件config --> 數據源、mybatis運行時的環境變量、映射文件的路徑

? 映射文件mapper --> sql語句、映射關系

3.4 測試類

1. 獲取SessionFactory
    2. 獲取Session
    3. 調用Session中的方法

三、Hibernate訪問數據庫的數據

1. 添加jar包

1.1 下載hibernate官方包

? hibernate-release-4.3.0.Final.zip

1.2 添加相關庫及依賴

? hibernate-release-4.3.0.Final/lib/required目錄下所有的jar包

? 數據庫驅動jar包

1.3 添加hibernate源碼

? 右擊"hibernate-core-4.3.0.Final.jar"——>Properties——>Java Source Attachment——>External Folder,選擇hibernate官方包中的hibernate-release-4.3.0.Final/project/hibernate-core/src/main/java

2. 關於dtd文件

? MyEclipse對Hibernate做了集成,內置了Hibernate的dtd文件

3. 兩個核心文件

3.1 hibernate.cfg.xml

? hibernate的核心配置文件,用來配置數據源,指定映射文件等,類似於MyBatis中的config文件

? 該文件一般位於src目錄下,文件名可自定義,一般都默認為hibernate.cfg.xml

<hibernate-configuration>
    <!-- 配置數據庫datasource -->
    <session-factory>
        <!-- 配置連接信息 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/hibernate?useUnicode=true&amp;characterEncoding=utf8</property>
        <property name="connection.username">root</property>
        <property name="connection.password"></property>
        <!-- <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
        <property name="connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property>
        <property name="connection.username">scott</property>
        <property name="connection.password">tiger</property> -->

        <!-- 方言,詳見文檔 3.4.1 SQL方言 -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <!-- <property name="dialect">org.hibernate.dialect.OracleDialect</property> -->

        <!-- 在控制臺顯示執行的sql語句 -->
        <property name="show_sql">true</property>  
        <!-- 格式化sql語句,更易讀 -->
        <property name="format_sql">false</property>
        <!-- 更多配置項,詳見 3.4 可選的配置項 -->

    </session-factory>
</hibernate-configuration>

3.2 xxx.hbm.xml

3.2.1 hibernate映射文件

? 用來配置映射關系,類似於MyBatis中的mapper文件

? 但是hibernate中的映射文件只配置映射關系,不配置sql語句,與MyBatis有所不同

<hibernate-mapping>
    <!-- 配置映射關系,將類與表進行映射 -->
    <class name="day01.User" table="t_user">
        <!-- 將列和屬性映射 -->
        <!-- 
            name:指的是類中的屬性,準備的來說是屬性的set方法
            column:指的是表中的列,也可以使用<column>子元素來指定 
         -->
        <id name="id" column="id">
            <!-- <column name="id"></column>  -->
            <!-- 
                配置主鍵的生成策略,不同的數據庫主鍵生成方式有所不同
                    mysql:自增長 auto_increment
                    oracle:序列 sequence
                    sql server:自增長 identity
                具體的生成策略,詳見文檔 5.1.4.1 Generator,常用的如下:
                    native:hibernate根據數據庫自動的選擇主鍵生成策略
                            mysql:自增長
                            oracle:序列,hibernate會自動創建序列hibernate_sequence,但只會創建一個,多表共用,從而導致主鍵不連續
                    increment:自增長,適應於mysql和sql server
                            也可以為oracle設置自增長,增長方式如下:
                                先查詢要插入表的主鍵的最大值,將該最大主鍵值+1,然後將新值作為要插入記錄的主鍵值
                    sequence:序列,適應於oracle
                            需要指定序列的名稱,也就是在oracle中創建的序列
                            如果不指定,則默認使用hibernate自動創建的序列
                    assigned:由用戶自己指定主鍵
                    uuid:使用uuid算法,生成唯一的uuid         
             -->
            <generator class="native"></generator>
            <!-- <generator class="sequence">
                <param name="sequence">seq_user</param>
            </generator> -->
        </id>

        <!-- 其他屬性 -->
        <property name="username" column="name"></property>
        <property name="password" column="pwd"></property>
        <property name="age" column="age"></property>
    </class>

</hibernate-mapping>
3.2.2 持久化類

? 創建持久化類User.java

? 簡單的持久化類也稱為POJO(Plain Old Java Object),即簡單傳統的Java對象

? 實體類、持久化類、POJO、java bean

4. 測試類

4.1持久化操作的步驟

public static void main(String[] args) {
  // 1. 創建Configuration對象
  Configuration config = new Configuration().configure();
  // 讀取hibernate.cfg.xml文件,默認讀取src下的hibernate.cfg.xml文件,如果同名則可以省略
  // config.configure("hibernate.cfg.xml");
  // config.configure();

  // 2.讀取並解析映射信息,創建會話工廠SessionFactory
  // hibernate3.x中可以調用buildSessionFactory()方法獲取SessionFactory,在4.x中已過時
  // SessionFactory sf = config.buildSessionFactory();
  // hiberate4.x中通過ServiceRegistry接口來獲取,目的是解耦合
  StandardServiceRegistryBuilder ***b = new StandardServiceRegistryBuilder()
    .applySettings(config.getProperties());
  // ***b.applySettings(config.getProperties());
  StandardServiceRegistry *** = ***b.build();
  SessionFactory sf = config.buildSessionFactory(***);

  // 3. 獲取會話Session
  Session session = sf.openSession();

  // 4.開啟事務Transaction(增、刪、改),Hibernate默認關閉自動提交事務
  Transaction tx = session.beginTransaction();

  try {
    // 5.執行持久化操作
    session.save(new User(null, "tom", "123", 21));
    // 6.提交事務
    tx.commit();
  } catch (Exception e) {
    tx.rollback(); // 回滾事務
    e.printStackTrace();
  } finally {
    // 7.關閉會話
    session.close();
  }
}

4.2 自動生成數據庫表結構

<!-- 
   根據映射關系自動生成數據庫表結構,常用取值:
    create:每次創建SessionFactory時都執行建表語句
    update:當映射關系與數據庫表結構不一致時更新表
   -->
<property name="hbm2ddl.auto">update</property>

5. 解決進程不關閉的問題

? hibernate 4.x版本中,在持久化操作執行結束並關閉session後,進程並不會關閉,如何解決?

/*
 * hibernate4.x在創建SessionFactory時,會自動創建一個線程
 * 由該線程創建和管理SessionFactory,所以需要關閉SessionFactory,目的是為了關閉該線程
 */
sf.close();
//銷毀StandardServiceRegistry實例
StandardServiceRegistryBuilder.destroy(***);

6. 創建HibernateUtil工具類

/*
 * Hibernate工具類
 */
public class HibernateUtil {

    private static StandardServiceRegistry ***;
    private static SessionFactory sessionFactory;
    private static ThreadLocal<Session> local=new ThreadLocal<Session>();

    static {
        try {
            Configuration config = new Configuration().configure();
            *** = new StandardServiceRegistryBuilder().applySettings(
                    config.getProperties()).build();
            sessionFactory=config.buildSessionFactory(***);
        } catch (HibernateException e) {
            e.printStackTrace();
        }
    }

    public static Session getSession() {
        Session session=local.get();
        if(session==null||!session.isOpen()){
            session=sessionFactory.openSession();
            local.set(session);
        }
        return session;
    }

    public static void close(){
        Session session=local.get();
        if(session!=null){
            session.close();
            local.remove();
            /*
             * 為了程序能夠及時結束,此處將sessionFactory關閉並銷毀***
             * 實際開發中,該操作都由其他容器(spring框架)負責,無需自己操作
             */
            sessionFactory.close();
            StandardServiceRegistryBuilder.destroy(***);
        }
    }
}

四、Hibernate中對象的狀態

1. 簡介

? Hibernate通過Session來管理對象的狀態,對於Hibernate而言,對象共有三種狀態

2. 三種狀態

2.1 瞬時狀態(Transient)

? 內存中有,在數據庫中沒有對應的記錄,對象不受session管理

? 也稱為臨時狀態

2.2 遊離狀態(Detached)

? 內存中有,在數據庫中有對應的記錄,對象不受session管理

? 也稱為托管狀態

2.3 持久狀態(Persistent)

? 內存中有,在數據庫有對應的記錄,對象受session管理

  • 什麽叫有對應的記錄

    在數據庫中能找到主鍵id對應的數據

  • 什麽叫受session管理

    由session查詢出的對象或作為參數交給session處理的對象

特點:如果對象的狀態改變,hibernate會自動更新到數據庫,保存數據的一致性

3.臟對象和臟檢查

? 臟對象:在hibernate中狀態前後發生改變的對象,稱為臟對象

? 臟檢查:當提交事務或刷新Session緩存時hibernate會對Session中的持久狀態的對象進行檢測,判斷對象的數據是否發生了改變(與之前的快照做對比),如果發生改變則更新同步到數據庫中,以保持數據的一致性,稱為刷新緩存

? 刷新緩存的時機:

  • 提交事務,調用Transaction的commit()
  • 刷新緩存,調用Session的flush()

區別:commit()方法會先調用flush()方法,然後再提交事務

? flush()方法只會刷新緩存,不會提交事務,所以還需要手動提交事務

五、Session API

1. save方法

執行插入操作

Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
User user=new User(null, "fff", "777", 29); //瞬時狀態
user.setAge(40);
session.save(user); //參數為瞬時狀態的對象
tx.commit();
HibernateUtil.close();

2. delete方法

執行刪除操作,根據主鍵id進行刪除,參數對象的id屬性必須有值,與其他屬性無關

Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
User user=new User(7, "abc", "777", 29); //遊離狀態
session.delete(user); //參數為持久狀態或遊離狀態的對象
tx.commit();
HibernateUtil.close();
  • 如果對象id屬性有值,且在數據庫中存在對應的記錄,則正常執行刪除
  • 如果對象id屬性沒有值(id==null),則不會執行刪除操作
  • 如果對象id屬性有值,但在數據庫中沒有對應的記錄,會報錯
 org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

3. load和get方法

3.1 load方法

執行查詢操作,根據主鍵id查詢對象,參數1表示對應類的class對象或類的全名,參數2表示主鍵id

Session session = HibernateUtil.getSession();
// 兩個參數,第一個參數表示要查詢的表對應的類的Class對象或類的全名,第二個參數表示主鍵id
// User user=(User) session.load(User.class, 2); //只能根據主鍵查詢
User user = (User) session.load("day01.User", 2);
System.out.println(user);
HibernateUtil.close();

查詢操作無需開啟事務

3.2 get方法

與load類似

User user = (User) session.get(User.class, 2); // 只能根據主鍵查詢

3.3 get和load區別

  • 當數據庫不存在對應的記錄

    get:不報錯,返回null

    load:報錯,拋異常org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [day01.User#22]

  • 查詢操作執行的時機

    get:調用get方法時立即執行查詢操作

    load:第一次訪問查詢對象時才真正執行查詢操作,稱為延遲加載或懶加載

4. update方法

4.1 執行更新操作

? 根據主鍵id進行更新

Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();

User user=new User(3,"ggg","999",18); //遊離狀態
session.update(user);

tx.commit();
System.out.println("success");
HibernateUtil.close();

如果參數對象的主鍵id在數據庫中不存在對應的記錄,會報錯

org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

4.2 存在的問題

//存在的問題:只想修改age,但會發現username和password也被修改了,如何解決?
User user=new User();
user.setId(3);
user.setAge(10);

兩種解決方式:

  • 方式1:先使用load/get獲取對象,然後修改對象,最後再update更新對象
//方式1:需要註意的是該方式仍會更新所有字段,只不過已獲取所有字段的原數據
User user=(User) session.load(User.class, 4);
user.setAge(20);
  • 方式2:動態DML操作

4.3 動態DML操作

? 主要是用來優化sql性能,默認值都為false

  • dynamic-insert="false" 只插入不為null的字段

  • dynamic-update="false" 只更新發生變化的字段

    ? 要求對象必須是持久狀態,即只針對持久狀態對象的更新才有效

// 方式2:只更新發生變化的字段,SQL優化
// User user=(User) session.load(User.class, 4);
// user.setAge(40);

4.4 禁止插入或更新特定字段

<property name="password" column="pwd" update="false" insert="false"></property>

5. saveOrUpdate方法

執行插入或更新操作,具體情況如下:

  • 如果傳入參數是瞬時對象,且對象沒有主鍵id,則執行save操作
  • 如果傳入參數是遊離或持久對象,則執行update操作
  • 如果傳入參數是瞬時對象,但對象有主鍵id,則執行update操作,此時會報錯,因為數據庫沒有對應的主鍵記錄
// User user=new User(null,"itany","123",20); //瞬時狀態
// User user=new User(13,"welcome","111",20); //遊離狀態
User user = new User(40, "hello", "111", 20); // 瞬時狀態
session.saveOrUpdate(user);

總結:如果對象id為null,執行save操作;如果對象id不為null,則執行update操作

6. merge方法

與saveOrUpdate類似,區別在於merge方法不會修改傳入的參數對象狀態,而是返回操作後的對象副本(修改後,持久狀態)

User user = new User(null, "world", "123", 20); // 瞬時狀態
User u = (User) session.merge(user);
// 此時user仍為瞬時狀態,返回的對象u為持久狀態
System.out.println(user.getId());
System.out.println(u.getId());

六、關於no session的問題

1. 創建事務管理類

public class TransactionManager {
    public static void beginTransaction(){
        HibernateUtil.getSession().beginTransaction();
    }

    public static void commit(){
        HibernateUtil.getSession().getTransaction().commit();
        HibernateUtil.close();
    }

    public static void rollback(){
        HibernateUtil.getSession().getTransaction().rollback();
        HibernateUtil.close();
    }
}

2. no session問題

分層開發中,在DAO層獲取數據,在Service層處理事務並關閉Session,在Action中獲取並操作數據時如果是懶加載,則會報錯:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

原因:懶加載,獲取的是代理對象,當真正訪問數據並進行查詢時發現session已關閉

解決:使用get方法

Session session=HibernateUtil.getSession();
User user=(User) session.load(User.class, 1); //class day01.User_$$_jvst724_0 代理對象
//      User user=(User) session.get(User.class, 1); //使用get,立即加載數據
System.out.println(user.getClass());

Hibernate框架技術視頻課程——筆記(一)