Hibernate 基本操作、懶載入以及緩存

来源:https://www.cnblogs.com/god23bin/archive/2023/05/10/hibernate_crud_lazy_load_and_cache.html
-Advertisement-
Play Games

上一篇咱們介紹了 Hibernate 以及寫了一個 Hibernate 的工具類,快速入門體驗了一波 Hibernate 的使用,我們只需通過 Session 對象就能實現資料庫的操作了。 現在,這篇介紹使用 Hibernate 進行基本的 CRUD、懶載入以及緩存的知識。 ...


前言

上一篇咱們介紹了 Hibernate 以及寫了一個 Hibernate 的工具類,快速入門體驗了一波 Hibernate 的使用,我們只需通過 Session 對象就能實現資料庫的操作了。

現在,這篇介紹使用 Hibernate 進行基本的 CRUD、懶載入以及緩存的知識。

提示:如果你還沒看上一篇,那麼建議你看完上一篇再來看這篇。

上一篇:一文快速入門體驗 Hibernate

基本的 CRUD

以下代碼均寫在測試類 HibernateTest 中

插入操作

這個在上一篇已經演示過,這裡便不再演示。

查詢操作

查詢有 2 種方式,通過 Session 對象的 get 方法 或者 load 方法來實現查詢,主要將查詢的數據結果封裝到一個 Java 對象中。

  1. get 方法
    @Test
    public void queryByGet() {
        // 獲取 Session 對象
        Session session = HibernateUtil.getSession();
        try {
            // 使用 get() 方法,第一個參數是持久化類的類型參數,第二個參數是主鍵標識參數,如果沒有匹配的記錄,那麼會返回 null
            User user = session.get(User.class, new Integer("1"));
            System.out.println("用戶ID:" + user.getId());
        } catch (Exception e) {
            System.out.println("查詢User數據失敗!");
            e.printStackTrace();
        } finally{
            // 關閉 Session 對象
            HibernateUtil.closeSession();
        }
    }

控制台輸出:可以看到,執行了查詢 SQL,並列印了用戶 ID。

INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
五月 08, 2023 11:38:59 下午 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
Hibernate: 
    select
        user0_.id as id1_0_0_,
        user0_.name as name2_0_0_,
        user0_.password as password3_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?
用戶ID:1

  1. load 方法
    @Test
    public void queryByLoad() {
        // 獲取 Session 對象
        Session session = HibernateUtil.getSession();
        try {
            // 使用 load() 方法,它返回對象的代理,只有該代理被調用時,Hibernate 才會真正去執行 SQL 查詢
            User user = session.load(User.class, new Integer("1"));
            // ID 是已知的,不用進行查詢
            System.out.println("用戶ID:" + user.getId());
            // 此時該代理被調用,就執行 SQL 語句,得到真正的數據記錄
            System.out.println("用戶名稱:" + user.getName());
        } catch (Exception e) {
            System.out.println("查詢User數據失敗!");
            e.printStackTrace();
        } finally{
            // 關閉 Session 對象
            HibernateUtil.closeSession();
        }
    }

控制台輸出:

五月 08, 2023 11:40:13 下午 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
五月 08, 2023 11:40:14 下午 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
用戶ID:1
Hibernate: 
    select
        user0_.id as id1_0_0_,
        user0_.name as name2_0_0_,
        user0_.password as password3_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?
用戶名稱:god23bin

可以看到,是先列印用戶ID的,這裡還沒有執行查詢 SQL,直到下一條語句中的 user.getName() 的執行,查詢的 SQL 語句才被 Hibernate 執行

修改操作

想對某條數據進行修改操作,那麼需要將它先查詢出來,然後進行修改。這裡就執行了兩條 SQL,保險起見,開啟事務,然後執行這兩條 SQL,接著提交事務。當然,這兩條 SQL,Hibernate 幫我們寫的啦!

    @Test
    public void update() {
        // 獲取 Session 對象
        Session session = HibernateUtil.getSession();
        try {
            // 開啟事務
            session.beginTransaction();
            // 進行查詢,將結果封裝成 user 對象
            User user = session.get(User.class, new Integer("1"));
            // 對 user 對象進行修改
            user.setName("公眾號:god23bin");
            user.setPassword("456789");
            // 提交事務
            session.getTransaction().commit();
        } catch (Exception e) {
            // 發生異常,則回滾事務
            session.getTransaction().rollback();
            System.out.println("修改User數據失敗!");
            e.printStackTrace();
        } finally{
            // 關閉 Session 對象
            HibernateUtil.closeSession();
        }
    }

控制台輸出:

五月 09, 2023 12:00:16 上午 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
五月 09, 2023 12:00:17 上午 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
Hibernate: 
    select
        user0_.id as id1_0_0_,
        user0_.name as name2_0_0_,
        user0_.password as password3_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?
Hibernate: 
    update
        user 
    set
        name=?,
        password=? 
    where
        id=?
        

可以看到運行前和運行後,數據的變化,如圖:

image-20230509000227733

如果屏幕前的小伙伴是按照我的步驟一步一步跟下來,那麼你可能會遇到中文亂碼的問題,此時需要在 hibernate.cfg.xml 配置文件中修改 URL,加上兩個參數 useUnicode=true&characterEncoding=UTF-8,如下:

<property name="connection.url">jdbc:mysql://localhost:3306/demo_hibernate?useUnicode=true&amp;characterEncoding=UTF-8</property>

刪除操作

刪除操作需要先把數據查詢出來,然後通過 Session 對象的 delete 方法將其刪除。代碼如下:

    @Test
    public void delete() {
        // 獲取 Session 對象
        Session session = HibernateUtil.getSession();
        try {
            session.beginTransaction();
            User user = session.get(User.class, new Integer("1"));
            // 刪除操作
            session.delete(user);
            session.getTransaction().commit();
        } catch (Exception e) {
            session.getTransaction().rollback();
            System.out.println("刪除User數據失敗!");
            e.printStackTrace();
        } finally{
            // 關閉 Session 對象
            HibernateUtil.closeSession();
        }
    }

控制台輸出:

五月 09, 2023 12:10:09 上午 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
五月 09, 2023 12:10:10 上午 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
Hibernate: 
    select
        user0_.id as id1_0_0_,
        user0_.name as name2_0_0_,
        user0_.password as password3_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?
Hibernate: 
    delete 
    from
        user 
    where
        id=?

關於 Hibernate 中對象的狀態

在Hibernate中,對象的狀態有 4 種,分別為 Transient、Persistent、Detached、Removed,譯名就比較多,方便起見,我選擇 3 個字的譯名:

  1. 瞬時態(Transient):當一個對象被實例化後,它處於瞬時態,簡單理解,就是 new 操作之後。瞬時態的對象沒有與之關聯的資料庫記錄,並且沒有被 Hibernate 的 Session 管理。當將瞬時態的對象關聯到持久態對象或通過 Session 對象的 savepersist 等方法進行持久化操作後,該對象的狀態會發生變化,轉成持久態。
  2. 持久態(Persistent):當一個對象與 Hibernate 的 Session 關聯後,它就處於持久態。持久態的對象有與之對應的資料庫記錄,並且被 Hibernate 的 Session 管理。對持久態對象的任何更改都會自動同步到資料庫。持久態對象可以通過Session的 getload 等方法從資料庫中獲取,或者通過 saveupdatepersist 等方法進行持久化操作。
  3. 游離態(Detached):當一個持久態對象與 Hibernate 的 Session 分離後,它處於游離態。游離態的對象仍然有與之對應的資料庫記錄,但不再受 Hibernate 的 Session 管理。對游離態對象的更改不會自動同步到資料庫。可以通過 Session 的 evictclear 等方法將持久態對象轉變為游離態對象,或者通過 Session 的 merge 方法將游離態對象重新關聯到 Session 中。
  4. 刪除態(Removed):當一個持久態對象被從 Hibernate 的 Session中刪除後,它處於刪除態。刪除態的對象仍然有與之對應的資料庫記錄,但即將被從資料庫中刪除。刪除態對象可以通過 Session 的delete 方法進行刪除操作。

Hibernate 通過跟蹤對象的狀態變化,實現了對象與資料庫的同步。在 Hibernate 的事務管理中,對象的狀態轉換是自動進行的,我們無需手動操作,Hibernate 會根據對象的狀態進行相應的資料庫操作,保證對象與資料庫的一致性。

需要註意的是,Hibernate 的對象狀態與資料庫的操作並不是一一對應的,Hibernate 提供了一系列的持久化方法和操作,我們可以根據具體的需求選擇合適的方法來進行對象狀態的轉換和資料庫操作。對於複雜的業務邏輯和數據處理,需要仔細理解和管理對象的狀態,以避免數據不一致的問題。

懶載入

Hibernate 的懶載入(Lazy Loading)是一種延遲載入策略,它允許程式在需要訪問相關數據時才從資料庫中載入關聯對象的屬性或集合。

在 Hibernate 中,懶載入是通過使用代理對象來實現的。實際上,我們在演示 Session 對象的 load() 方法時,就是懶載入了,一開始返回的是代理對象,並沒有直接查詢資料庫,而是直到該代理對象的屬性或方法被調用時,Hibernate 會根據需要自動執行額外的資料庫查詢,從而延遲載入關聯的數據。

這就是懶載入,等到需要的時候才去載入。

懶載入的主要優點是可以提高系統性能和減少不必要的資料庫查詢。如果一個對象關聯的屬性或集合在業務邏輯中很少被使用,懶載入可以避免不必要的資料庫訪問,減輕資料庫負載。

除了 load 方法實現的懶載入,我們還可以通過設置映射文件中的 <property> 標簽的 lazy 屬性實現懶載入:

<property name="name" type="string" lazy="true" /> <!-- name 屬性被設置成懶載入-->

緩存

緩存是一種臨時存儲數據的方式,將數據保存在更快速的存儲介質(如記憶體)中,以便將來能夠快速訪問和檢索。

Hibernate 提供了緩存的技術,主要用於存儲實體對象以及查詢的結果集。緩存分為一級緩存(Session 緩存)和二級緩存(Session Factory 緩存)

一級緩存

一級緩存是與 Session 相關聯的緩存,它存儲了從資料庫中讀取的實體對象。在同一個 Session 中,當多次查詢相同的數據時,Session 首先會根據對應的持久化類和唯一性標識(一般指的是ID)去緩存中查找是否存在該數據。如果存在,則直接從緩存中獲取,而不再訪問資料庫;如果不存在,則繼續向二級緩存種查找。

一級緩存是預設開啟的,可以提高讀取性能。

示例:

    @Test
    public void testFirstLevelCache() {
        Session session = HibernateUtil.getSession();
        try {
            System.out.println("第一次查詢:");
            User user = session.get(User.class, new Integer("2"));
            System.out.println("用戶名:" + user.getName());
            
            System.out.println("第二次查詢:");
            User user2 = session.get(User.class, new Integer("2"));
            System.out.println("用戶名:" + user2.getName());
        } catch (Exception e) {
            System.out.println("查詢User數據失敗!");
            e.printStackTrace();
        } finally{
            // 關閉 Session 對象
            HibernateUtil.closeSession();
        }
    }

控制台輸出:

五月 09, 2023 9:35:31 下午 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
五月 09, 2023 9:35:32 下午 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
第一次查詢:
Hibernate: 
    select
        user0_.id as id1_0_0_,
        user0_.name as name2_0_0_,
        user0_.password as password3_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?
用戶名:god23bin
第二次查詢:
用戶名:god23bin

可以看到,第二次查詢是沒有執行 SQL 的,直接從一級緩存中獲取。

二級緩存

二級緩存是在 SessionFactory 級別上的緩存,用於緩存多個 Session 之間共用的數據。它可以減少對資料庫的訪問次數,提高性能和擴展性。二級緩存可以存儲實體對象、集合對象以及查詢結果集。

由於 Hibernate 本身並未提供二級緩存的具體實現,所以需要藉助其他緩存插件或者說策略來實現二級緩存。比如 Ehcache、Redis 等。

我們這裡直接使用 Ehcache。

  1. 引入依賴項
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
    <version>5.6.14.Final</version>
</dependency>

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.0</version>
</dependency>
  1. 開啟二級緩存

二級緩存預設是關閉的,我們需要手動開啟。在 hibernate.cfg.xml 中開啟二級緩存:

<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
		...
        <!-- 開啟二級緩存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <!-- 開啟查詢緩存 -->
        <property name="hibernate.cache.use_query_cache">true</property>
        <!-- 指定使用的緩存實現類 -->
        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.SingletonEhcacheRegionFactory</property>
    </session-factory>
</hibernate-configuration>
  1. 創建緩存配置文件

我們在 /src/main/resources 目錄下創建緩存配置文件 ehcache.xml:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <!-- 硬碟存儲:將緩存中暫時不使用的對象,持久化到硬碟 -->
    <!-- path 屬性:指定在硬碟上存儲對象的路徑 -->
    <!-- java.io.tmpdir 是預設的臨時文件路徑。 可以通過右邊的方式列印出具體的文件路徑: System.out.println(System.getProperty("java.io.tmpdir")); -->
    <diskStore path="java.io.tmpdir"/>


    <!-- defaultCache:預設的緩存策略 -->
    <!-- eternal 屬性:設定緩存的elements是否永遠不過期。如果為true,則緩存的數據始終有效,如果為false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷 -->
    <!-- maxElementsInMemory 屬性:在記憶體中緩存的element的最大數目 -->
    <!-- overflowToDisk 屬性:如果記憶體中數據超過記憶體限制,是否要緩存到硬碟上 -->
    <!-- diskPersistent 屬性:是否在硬碟上持久化。指重啟 JVM 後,數據是否有效。預設為false -->
    <!-- timeToIdleSeconds 屬性:對象空閑時間(單位:秒),指對象在多長時間沒有被訪問就會失效。只對eternal為false的有效。預設值0,表示一直可以訪問 -->
    <!-- timeToLiveSeconds 屬性:對象存活時間(單位:秒),指對象從創建到失效所需要的時間。只對eternal為false的有效。預設值0,表示一直可以訪問 -->
    <!-- memoryStoreEvictionPolicy 屬性:緩存的 3 種清除策略,因為緩存區域是一定的,滿了之後就需要清除不需要的數據 -->
    <!-- 1. FIFO:first in first out (先進先出). 先緩存的數據會先被清除-->
    <!-- 2. LFU:Less Frequently Used (最少使用).意思是一直以來最少被使用的。緩存的元素有一個 hit 屬性,hit 值最小的將會被清除 -->
    <!-- 3. LRU:Least Recently Used(最近最少使用). (ehcache 預設值).緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清除 -->

    <defaultCache eternal="false"
                  maxElementsInMemory="1000"
                  overflowToDisk="false"
                  diskPersistent="false"
                  timeToIdleSeconds="0"
                  timeToLiveSeconds="600"
                  memoryStoreEvictionPolicy="LRU"/>

    <!-- name: Cache的名稱,必須是唯一的(ehcache會把這個cache放到HashMap里)-->
    <cache name="userCache"
           eternal="false"
           maxElementsInMemory="100"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="300"
           memoryStoreEvictionPolicy="LRU"/>
</ehcache>

  1. 在持久化類的映射文件中指定緩存策略

User.hbm.xml:

<?xml version='1.0' encoding='utf-8'?>
<!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="cn.god23bin.demo.domain.entity.User" table="user">
        <!-- 指定緩存策略 -->
        <cache usage="read-only"/>
		...
    </class>
</hibernate-mapping>

測試二級緩存:

    @Test
    public void testSecondLevelCache() {
        Session session1 = HibernateUtil.getSession();
        Session session2 = HibernateUtil.getSession();
        try {
            System.out.println("第一個 Session 去查詢數據並封裝成對象");
            User user1 = session1.get(User.class, new Integer("2"));
            System.out.println("用戶名:" + user1.getName());

            System.out.println("第二個 Session 去查詢同一數據並封裝成對象");
            User user2 = session2.get(User.class, new Integer("2"));
            System.out.println("用戶名:" + user1.getName());
        } catch (Exception e) {
            System.out.println("查詢User數據失敗!");
            e.printStackTrace();
        } finally{
            // 關閉 Session 對象
            HibernateUtil.closeSession();
        }
    }

控制台輸出:

五月 09, 2023 11:18:31 下午 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
第一個 Session 去查詢數據並封裝成對象
Hibernate: 
    select
        user0_.id as id1_0_0_,
        user0_.name as name2_0_0_,
        user0_.password as password3_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?
用戶名:god23bin
第二個 Session 去查詢同一數據並封裝成對象
用戶名:god23bin

總結

本篇文章主要講了基本的 CRUD 操作,都是通過 Session 去操作的,根據一個持久化類的類型以及一個唯一標識進行相關操作,然後講了 Hibernate 中的對象的狀態,有 4 種,分別是瞬時、持久、游離、刪除。

接著說了 Hibernate 的懶載入,有利於降低資料庫的開銷,當然緩存也是,除了加快我們的訪問速度,也降低了直接訪問資料庫的開銷,緩存就兩種,一級和二級,一級預設是開啟的,二級需要引入相關的依賴項,然後進行配置,開啟二級緩存,配置緩存策略。

這裡附上整個項目的目錄結構,便於對照:

image-20230509234027184

以上,就是本篇的內容,這些都應該掌握。咱們下期再見。

最後的最後

希望各位屏幕前的靚仔靚女們給個三連!你輕輕地點了個贊,那將在我的心裡世界增添一顆明亮而耀眼的星!

咱們下期再見!


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

-Advertisement-
Play Games
更多相關文章
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 先上效果 前言 最近在學Three.js.,對著文檔看了一周多,正好趕上碼上掘金的活動,就順便寫了一個小demo,手搓一個羅盤特效。 太極 先來看一下太極的實現方式,這裡我們使用CircleGeometry,將其分解開來可以看出是由圓形和 ...
  • 我們是袋鼠雲數棧 UED 團隊,致力於打造優秀的一站式數據中台產品。我們始終保持工匠精神,探索前端道路,為社區積累並傳播經驗價值。。 本文作者:奇銘(掘金) 需求背景 前段時間,離線計算產品接到改造數據同步表單的需求。 一方面,數據同步模塊的代碼可讀性和可維護性比較差,相對應的,在數據同步模塊開發新 ...
  • <meta> 標簽是 HTML 中用於描述網頁元信息的元素。它位於 <head> 部分,不會顯示在頁面內容中,但對於瀏覽器、搜索引擎等具有重要作用。主要作用有:定義文檔的字元編碼、提供網頁的描述信息、關鍵詞、作者、視口設置等,這些信息有助於搜索引擎理解和索引網頁內容。 <meta> 標簽的主要屬性有 ...
  • 當前,前端對二進位數據有許多的API可以使用,這豐富了前端對文件數據的處理能力,有了這些能力,就能夠對圖片等文件的數據進行各種處理。 本文將著重介紹一些前端二進位數據處理相關的API知識,如Blob、File、FileReader、ArrayBuffer、TypeArray、DataView等等。 ...
  • 模式動機 原型模式(Prototype Pattern)結構較為簡單,它是一種特殊的創建型模式,當需要創建大量相同或者相似對象時,可以通過對一個已有對象的複製獲取更多對象。Java語言提供了較為簡單的原型模式解決方案,只需要創建一個原型對象,然後通過在類中定義的克隆方法複製自己。該模式應用較為廣泛, ...
  • 開發環境 以下是我的開發環境 JDK 1.8 Maven 3.6.3 IDEA 2019(2019 無所畏懼,即使現在已經 2023 年了哈哈哈) 使用 Maven 的方式創建 Spring Boot 項目 下麵的內容可能會因 IDEA 版本不同,而有些選項不同,但是大同小異。 1. 打開 IDEA ...
  • 大家是否見過這種for迴圈,在for迴圈前加了個標記的: outerLoop: for (; ; ) { for (; ; ) { break outerLoop; } } 我之前有一次在公司業務代碼中見過有這種寫法的,沒在意,今天在看JDK線程池的代碼時,又看到ThreadPoolExecutor ...
  • 大家好,3y啊。好些天沒更新了,並沒有偷懶,只不過一直在安裝環境,差點都想放棄了。 上一次比較大的更新是做了austin的預覽地址,把企業微信的應用和機器人消息各種的消息類型和功能給完善了。上一篇文章也提到了,austin常規的功能已經更新得差不多了,剩下的就是各種細節的完善。 不知道大家還記不記得 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...