Hibernate 之折騰單向多對一映射及其衍生問題

来源:http://www.cnblogs.com/zhaozihan/archive/2016/09/02/5834161.html
-Advertisement-
Play Games

由於在數據表之間可以通過外鍵進行關聯,在使用Hibernate操作映射到存在關聯關係的數據表的對象時,需要將對象的關聯關係與數據表的外鍵關聯進行映射。 1.前言 這篇文章標題為單向多對一,其實是為以後的種種映射和HQL操作作一個鋪墊,實現單向多對一或者一對多再或者雙向多對一都很簡單,畢竟現在的工具實 ...


  由於在數據表之間可以通過外鍵進行關聯,在使用Hibernate操作映射到存在關聯關係的數據表的對象時,需要將對象的關聯關係與數據表的外鍵關聯進行映射。

1.前言

  這篇文章標題為單向多對一,其實是為以後的種種映射和HQL操作作一個鋪墊,實現單向多對一或者一對多再或者雙向多對一都很簡單,畢竟現在的工具實在太多,但是當我們用工具的同時卻很少考慮它的機制,就比如用實體類自動生成hbm.xml文件或者通過反向工程由數據表生成hbm.xml文件,它的主鍵生成機制就不是一樣的。廢話不多說,開始搬磚。

  首先建立hibernate.cfg.xml和會話工廠類HibernateUtil,然後添加兩個待操作的實體類和相應的映射文件。

HibernateUtil如下:

package com.zzh.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    private static SessionFactory sessionFactory;
    private static Session session;

    static {
        // 創建Configuration對象,讀取hibernate.cfg.xml文件,完成初始化
        Configuration config = new Configuration().configure();
        StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder()
                .applySettings(config.getProperties());
        StandardServiceRegistry ssr=ssrb.build();
        sessionFactory=config.buildSessionFactory(ssr);
    }
    
    //獲取SessionFactory
    public static SessionFactory getSessionFactory(){
        return sessionFactory;
    }
    
    //獲取Session
    public static Session getSession(){
        session=sessionFactory.openSession();
        return session;
    }
    
    //關閉Session
    public static void closeSession(Session session){
        if(session!=null){
            session.close();
        }
    }
}
View Code

 

兩個實體類班級Grade和學生Student

package com.zzh.entity;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

public class Grade implements Serializable {
    private int gid;
    private String gname;
    private String gdesc;

    public int getGid() {
        return gid;
    }

    public void setGid(int gid) {
        this.gid = gid;
    }

    public String getGname() {
        return gname;
    }

    public void setGname(String gname) {
        this.gname = gname;
    }

    public String getGdesc() {
        return gdesc;
    }

    public void setGdesc(String gdesc) {
        this.gdesc = gdesc;
    }

    public Grade() {
        super();
    }


    public Grade(int gid, String gname, String gdesc) {
        super();
        this.gid = gid;
        this.gname = gname;
        this.gdesc = gdesc;
    }

    public Grade(String gname, String gdesc) {
        super();
        this.gname = gname;
        this.gdesc = gdesc;
    }

}

 

package com.zzh.entity;

import java.io.Serializable;

public class Student implements Serializable {
    private int sid;
    private String sname;
    private String sex;
    //在多方定義一個一方的引用
    private Grade grade;

    public Grade getGrade() {
        return grade;
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Student() {
        super();
    }

    public Student(String sname, String sex) {
        super();
        this.sname = sname;
        this.sex = sex;
    }

}

 

可以看出我在Student類中使用Grade類聲明瞭grade屬性,並添加了getter和setter,以體現實體類Student對Grade的關聯關係,在下麵的映射表中只需要在“多”的一方配置。註意,Student類中添加的grade屬性為Grade,它是一個持久化類Grade的對象屬性,不是一個基本類型屬性,因此不能用<property>元素來映射grade屬性,又因為是多對一關聯關係,要使用<many-to-one>元素。

Grade.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-31 11:19:40 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
    <class name="com.zzh.entity.Grade" table="GRADE">
        <id name="gid" type="int">
            <column name="GID" />
            <generator class="increment"/>
        </id>
        <property name="gname" type="java.lang.String">
            <column name="GNAME" />
        </property>
        <property name="gdesc" type="java.lang.String">
            <column name="GDESC" />
        </property>
    </class>
</hibernate-mapping>
View Code

 

Student.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-31 11:19:40 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
    <class name="com.zzh.entity.Student" table="STUDENT">
        <id name="sid" type="int">
            <column name="SID" />
            <generator class="increment" />
        </id>
        <property name="sname" type="java.lang.String">
            <column name="SNAME" />
        </property>
        <property name="sex" type="java.lang.String">
            <column name="SEX" />
        </property>
        <many-to-one name="grade" class="com.zzh.entity.Grade" fetch="join">
            <column name="GRADE" />
        </many-to-one>
    </class>
</hibernate-mapping>

 

<many-to-one>元素中的name指定Student類中關聯類的屬性名,column指定數據表關聯的外鍵。換句話說,實體類Student對Grade的多對一關聯在本質上是通過數據表student中的外鍵GRADE與數據表grade關聯實現的,但Hibernate將表之間的關聯通過<many-to-one>元素進行了封裝

 

2.編寫與資料庫交互的UserDAO介面和其實現類UserDAOImpl,將數據的添加,查找,修改,刪除等方法封裝其中。

UserDAO:

package com.zzh.dao;

public interface UserDAO {
    void save(Object obj);
    Object findById(int id,Object obj);
    void delete(Object obj);
    void update(Object obj);
}

 

UserDAOImpl:

package com.zzh.dao;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.zzh.util.*;

public class UserDAOImpl implements UserDAO {

    @Override
    public void save(Object obj) {
        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        try {
            session.save(obj);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        } finally {
            HibernateUtil.closeSession(session);
        }

    }

    @Override
    public Object findById(int id, Object obj) {

        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        try {
            obj = session.get(obj.getClass(), id);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            HibernateUtil.closeSession(session);
        }
        return obj;
    }

    @Override
    public void delete(Object obj) {
        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        try {
            session.delete(obj);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            HibernateUtil.closeSession(session);
        }

    }

    @Override
    public void update(Object obj) {
        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        try {
            session.update(obj);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            HibernateUtil.closeSession(session);
        }

    }

}

 

註意:我的這個DAO使用的是Object對象,很多書上都是針對多個的對象建立多個DAO,也就是拆分為StudentDAOImpl和GradeDAOImpl,這樣裡面的Object就變為了相應的Student或者Grade。

如果你要改寫為我這樣的通用DAO,尤其要註意裡面的findById()方法,對於拆分寫的DAO,這個方法很簡單,只需要傳入參數id然後Grade grade=(Grade) session.get(Grade.class, id);而我的方法因為是通用的,所有要傳入id和對應的實體對象,obj = session.get(obj.getClass(), id),這裡面運用了java的有關反射的知識,順便拓展一下。

1.利用對象調用getClass()方法獲取該對象的Class實例。比如 Students s = new Students();  Class c = s.getClass();2.使用Class的靜態方法forName(),用類的名字獲取一個Class實例;比如 Class c = Class.forName("Students");3.運用.class的方式獲取Class實例,對基本數據類型的封裝類,還可以採用.TYPE來獲取對應的基本數據類型的Class實例;Class c = Students.class; 所以對於我的例子而言,就是利用了obj.getClass()等價於Object.class這個特性來修改這個方法,大家可以自己試試。

 

3.添加測試案例JUnit

  請註意看我上面給出的hbm.xml文件,主鍵生成機制是increment

                                                   

現在問題又來了!!!當我用工具通過實體類生成hbm.xml文件時,主鍵生成機制是

assigned表示對象標識符由應用程式產生,如果不指定<generator>節點,則預設使用該策略。此時數據表還沒有建立,當我開始測試的時候,Hibernate根據cfg.xml中的資料庫信息和相關映射信息建立表,問題來了,Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '0' for key 'PRIMARY'。 數據表grade中出現 “0 Java一班 Java軟體開發一班 ”一條記錄,數據表student中出現 “ 0 慕女神  女 0 ”一條記錄,提示已經告訴我們主鍵重覆,0主鍵已經存在,不能再進行添加了。assigned表示主鍵有程式代碼負責,你要保證這個主鍵數據是唯一的。這時我在想,那我用identity生成策略會怎麼樣,identity表示對象標識符由底層資料庫的自增主鍵生成機制產生。這樣一搞 Caused by: java.sql.SQLException: Field 'SID' doesn't have a default value,馬上反應過來自己沒有在資料庫設置AUTO_increment,需要去設置兩個表的這個值。

這樣一搞,就可以正常添加了。

當我設置好自動遞增後,又想試試assigned,於是又改為了assigned,問題又來了Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1 ;百度之後,有人這樣解釋:這個異常是由於主鍵設置為自增長,而在我們插入記錄的時候設置了ID的值導致的。懵逼了,到現在我還在找怎樣解決,如果你有答案請告訴我。

之後我選用了increment生成策略,這個策略不需要在資料庫中設置自動遞增,感覺應付這種小例子很方便,不過他的局限性在於如果有多個應用實例向同一張表中插入數據時,則會出現重覆的主鍵。需謹慎使用。

4.總結

  這篇文章感覺挺凌亂的,一會是DAO修改一會又是反射,最後還自己搞了一堆BUG,自己還是需要慢慢積累知識才行,如果你覺得這篇文章有點用,請幫忙點個贊,謝謝觀看。

  

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 之前一直沒搞懂這個怎麼計算的,主要也因為之前沒怎麼用到過這個,但現在想把它搞懂,所以索性一次性搞懂為妙,以後就不用再糾結了哈。 按位運算符就是把數字看作二進位來進行計算的,並且再把計算出來的二進位給它轉換成十進位,也就是最終的結果了。 比如: 7&9 這個是按位與,先不討論與不與的,總之它現在會把7 ...
  • mybatis的映射文件寫法多種多樣,不同的寫法和用法,在實際開發過程中所消耗的開發時間、維護時間有很大差別,今天我就把我認為比較簡單的一種映射文件寫法記錄下來,供大家修改建議,爭取找到一個最優寫法~~: 以User對象和UserMap.xml為例講解,代碼如下: User為用戶實體類(僅作為講解, ...
  • 實習就要結束了,最終要上交的是網上書城項目和一份文檔。接下來簡要介紹一下項目。 項目是根據三層架構來寫,數據訪問層(DAO層)、系統業務邏輯控制處理層(servlet層)、和界面層(jsp)。 首先在DAO層構造好SQL語句,對資料庫進行操作,在servlet層調用Dao層相關方法進行相應的邏輯處理 ...
  • java中獲取網頁的get或post數據的方法有以下三種: getParameter(name); 獲取單參數的方法。 getParameterValues(name); 獲取多參數值的方法,如覆選框的值,返回一個數組 getParameterNames(name); 獲取request中的所有參數 ...
  • RT,you get 是一個視頻離線下載工具, https://github.com/soimort/you get 另一個同類工具 youtube dl 也是python 實現,雖然名為 youtube dl 但是從文檔上看,支持很多網站 https://github.com/rg3/youtub ...
  • 最近由於要做一個爬蟲項目,要對很多網站進行爬取,所以一直都在看這方面的文章。在翻閱了很多資料後,下載了一個curl庫,著實對項目有了很大的幫助。 一、LibCurl基本編程框架libcurl是一個跨平臺的網路協議庫,支持http, https, ftp, gopher, telnet, dict, ...
  • 一.為什麼要進行數據校驗 對於一個web應用而言,所有的應用數據都是通過瀏覽器收集的,用戶的輸入信息是非常複雜的,對於一些用戶操作不熟練,輸入出錯,等網路傳輸不穩定,這些都有可能導致異常輸入。 異常的輸入,輕則導致系統非正常中斷,重則導致系統崩潰,應用程式必須能正常處理表現層接收的異常數據,通常的做 ...
  • 一.與Servlet API解耦的訪問方式 1.為了避免與Servlet API耦合在一起,方便Action類做單元測試, Struts2對HttpServletRequest、HttpSession和ServletContext進行了封裝, 構造了三個Map對象來替代這三種對象,在Action中, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...