Hibernate學習筆記1

来源:http://www.cnblogs.com/dongkuo/archive/2017/02/25/6442517.html
-Advertisement-
Play Games

Hibernate學習筆記1 本筆記學習自 "Hibernate官方文檔" 。 1. Architecture(總體結構) 俗話說有圖有真相,先看一張圖: 上圖清楚地描述了Hibernate在一個Java應用中所處的位置:位於 Data Access Layer(數據訪問層) 和 Relation ...


Hibernate學習筆記1

本筆記學習自Hibernate官方文檔

1. Architecture(總體結構)

俗話說有圖有真相,先看一張圖:

上圖清楚地描述了Hibernate在一個Java應用中所處的位置:位於Data Access Layer(數據訪問層)Relation Database(關係型資料庫)之間。最原始地,我們在數據訪問層中是通過JDBC去操作資料庫的,但JDBC編程起來非常繁瑣,並且需要我們手動去處理資料庫中每條數據與其對應對象的轉換(即所謂的ORM,Object Relational Mapping,對象關係映射),Hibernate很好地解決了以上問題。Hibernate實現了JPA(Java Persistence API,Java官方給出的持久化API),因此我們可以使用JPA去使用Hibernate,當然也可以使用Hibernate自己的(原生的)API。

再看一個圖,這是我們常用的Hibernate的介面(類)的繼承體繫結構圖,以後再作介紹,先有個大致的印象:

2. Domain Model(領域模型)

Domain Model(領域模型)是一個抽象的系統,它描述了我們關註的問題所包含的知識、所產生的影響或活動的範圍,該模型可以幫助我們解決與該領域相關的問題。

對編程而言,我們通常編寫的POJO/JavaBean便是領域模型(或者更具體地叫做anemic domain model,貧血領域模型),這是Hibernate的中心角色。

2.1 快速入門

概念講了一大堆,我想你更加關心的是這東西到底咋用啊。下麵來一個入門例子。例子很簡單,我們有一個User(用戶)POJO,我們要對其進行CURD(增刪改查)操作。User數據結構如下:

public class User {

    private Long id;
    private String name;

    // setter、getter方法略
}

首先創建一個Maven項目(強烈建議使用Maven,Gradle等項目構建工具來開發,不要再到處找jar包導入了),然後在pom.xml中引入Hibernate和一些必要的依賴,如下:

<dependencies>
    <dependency>
        <groupId>org.hibernate</groupId>
         <!--如果使用Hibernate原生API,使用hibernate-core代替hibernate-entitymanager -->
        <artifactId>hibernate-entitymanager</artifactId>
        <version>5.2.8.Final</version>
    </dependency>
    <!--mysql驅動-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>6.0.3</version>
    </dependency>
    <!--測試框架-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

依賴搞定,在這例子中我們使用JPA,現在配置Hibernate。在resources目錄(如果不是Maven項目就在類的根目錄)下新建META-INF文件夾,再在META-INF下新建配置文件persistence.xml,這個配置文件到時候會自動被Hibernate讀取。

persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
    <persistence-unit name="cn.derker">
        <class>cn.derker.model.User</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/test?useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="root"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

配置項根據名稱應該能大致猜出是什麼意思。可以看出我測試的是MySQL資料庫。解釋一下幾個配置的意思:persistence-unitname屬性的值是自己定義的,但之後在Java代碼中會用到。class標簽的內容是需要持久化的對象的全類名;hibernate.hbm2ddl.auto配置項的取值和含義如下:

  • create:啟動的時候先drop定義的持久化類對應的表,再重新create,這樣表中的數據全沒了,慎用!!!
  • create-drop: 啟動時create表,關閉時drop表;
  • update: 啟動時會去檢查各個表的schema是否一致,如果不一致會做更新表結構
  • validate: 啟動時驗證各個表的schema與在hibernate中定義的持久化類數據結構信息是否一致。如果不一致就拋出異常,並不做更新。

在以上配置中,我們告訴了hibernate去連接哪一個資料庫,包括連接資料庫的用戶名,密碼,以及哪些POJO類需要藉助hibernate,但我們沒有告訴hibernate如何去定義這些POJO類對應的表結構。hibernate提供了兩種方式定義(映射):一種是xml配置;另一種是註解配置。鑒於xml配置不如註解配置方便,下麵的例子一律採用註解配置。怎麼將User類的結構描述給hibernate呢?如下:

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }
    // setter、getter方法略
}

首先是在類的名稱上聲明瞭@Entity註解;然後在id欄位上聲明瞭@Id@GeneratedValue(strategy = GenerationType.IDENTITY)。其中@Id表明把id欄位定義為主鍵;@GeneratedValue說明瞭主鍵的生成策略是自動增長的(當然,這需要你使用的資料庫支持這一特性)。這樣所有的配置就搞定了。

接下來就可以測試CURD操作了,直接貼出測試類的代碼:

public class TestApp {

    private EntityManagerFactory entityManagerFactory;

    @Before
    public void init() {
        // cn.derker與上面配置的persistence.xml中persistence-unit的name值一致
        entityManagerFactory = Persistence.createEntityManagerFactory("cn.derker");
    }
    
    @After
    public void destroy() throws Exception {
        entityManagerFactory.close();
    }

    @Test
    public void testSave() throws Exception {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        entityManager.persist(new User("張三")); // 為User添加了一個有參的構造方法,但千萬別忘了聲明一個無參的構造方法
        entityManager.getTransaction().commit();
    }

    @Test
    public void testFind() throws Exception {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        User user = entityManager.find(User.class, 1L);
        Assert.assertEquals("張三", user.getName());
        entityManager.getTransaction().commit();
    }

    @Test
    public void testUpdate() throws Exception {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        User user = entityManager.find(User.class, 1L);
        user.setName("李四"); // 這裡不必再調用update方法,hibernate會自動更新
        entityManager.getTransaction().commit();
    }

    @Test
    public void testDelete() throws Exception {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        User user = entityManager.find(User.class, 1L);
        entityManager.remove(user);
        entityManager.getTransaction().commit();
    }
}

2.2 映射類型

Hibernate將數據的類型分為兩大類:value typesentity types。value types類型的數據依附於entity typed類型的數據。舉個慄子:

class User{
    Long id;
    String name;
    Address address;
}

User便是entity type,它有自己的生命周期;而想Long、String類型的id、name包括Address類型的address都是value type,它們不會單獨存在,而是依附於User。

顯然,value type就包括3種類型的數據,一種是basic types,比如一些表示數值、表示字元、表示日期的類型等(見這裡);第二種是embeddable types,像上面例子中的Address類型;最後還有一種是collection types,這很明顯。

關於basic typesembeddable types之後小節會詳細介紹。

2.3 命名策略

如果你運行了上面入門中的慄子,你會發現Hibernate預設幫我們建立的表的表名和列名與我們在類中定義的名稱是一致的,這是Hibernate的預設命名策略。如果我們想自定義表名或者列名,有兩種方法可以實現。

一種是被稱為ImplicitNamingStrategy的方案。做法很簡單,只需要使用@Table@Column註解即可,其中name屬性的值就是表或列的名字,慄子如下:

@Entity
@Table(name = "tb_user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "col_name")
    private String name;
    ...
}

另一種方案叫做PhysicalNamingStrategy,這種方案可以進行“大批量”地命名。比如有些人習慣將表名命名成"tb_" + 蛇形命名的類名,將列名命名為"col_" + 蛇形命名的欄位名,如果採用上一種命名策略方案將會寫大量的註解,若採用該命名策略方案則只需定義一個類即可,該類需要實現PhysicalNamingStrategy介面,慄子如下:

public class MyNamingStrategy implements PhysicalNamingStrategy {

    @Override
    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return name;
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return name;
    }

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return jdbcEnvironment.getIdentifierHelper().toIdentifier("tb_" + camel2Snake(name.getText()));
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return name;
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        if ("id".equals(name.getText())) {
            return name;
        }
        return jdbcEnvironment.getIdentifierHelper().toIdentifier("col_" + camel2Snake(name.getText()));
    }

    /**
     * 駝峰命名轉蛇形命名
     */
    private String camel2Snake(String name) {
        StringBuilder sb = new StringBuilder(name.length() * 2);
        for (int i = 0; i < name.length(); i++) {
            char c = name.charAt(i);
            if(c == ' '){
                sb.append('_');
                continue;
            }
            if (Character.isUpperCase(c)) {
                if (i != 0) {
                    sb.append('_');
                }
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}

以上代碼主要是在toPhysicalTableNametoPhysicalColumnName方法中做了“手腳”,它們的返回值分別決定了表名和列名。

定義了MyNamingStrategy後還需要將它“告訴”Hibernate,很簡單,只需要在配置文件persistence.xml中加上一個配置參數就搞定了:

<properties>
    <property name="hibernate.physical_naming_strategy" value="cn.derker.strategy.MyNamingStrategy"/>
</properties>

2.4 Basic Types

嚴格地講,basic types 類型的數據必須要使用@Basic註解,但是我們之前說過,basic types 類型包括表示數值,表示字元,表示時間等數據類型,這是我們經常大量定義的類型,因此@Basic可以省略不寫,它預設被假定。一個嚴格的User

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic
    private Long id;
    @Basic
    private String name;
    ...
}

事實上,@Basic註解還有兩個屬性:

  1. optional - boolean (defaults to true):定義是否允許值為null
  2. fetch - FetchType (defaults to EAGER):定義是否採用懶載入策略。FetchType.EAGER表示不使用;FetchType.LAZY表示使用。事實上,對於basic type,Hibernate實現的時候忽略了該屬性

我們知道Java的基本數據類型和Sql的數據類型不是一一對應的,存在著一些差別,而在上面的慄子中我們並沒有“告訴”Hibernate如何去處理這些差異帶來的問題,Hibernate是如何知道怎麼將Java的某種基本數據類型轉換成Sql數據類型呢?其實在Hibernate中已經定義好了許多這樣實現數據類型轉換功能的類,比如針對java.lang.Stringorg.hibernate.type.StringType,處理java.lang.Integerorg.hibernate.type.IntegerType。這啟示我們,可以寫一個類似這樣的類,去處理一些Hibernate還不支持的Java類型的轉換,比如下麵的慄子,自定義一個basic type,可以將String[]類型的數據以Json的字元串的形式保存在資料庫中,並且在讀取時自動還原成之前的字元數組。

為此,我們為之前的User對象添加一個hobbies(愛好)屬性:

public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @Type(type = "String[]")
    private String[] hobbies;

    public User() {
    }

    public User(String name, String[] hobbies) {
        this.name = name;
        this.hobbies = hobbies;
    }
    // 省略setter、getter、toString方法
}

註意,我們為String[] hobbies加上了org.hibernate.annotations.Type註解,並且指明瞭需要使用名叫“String[]”的Hibernate基本類型去處理。這個"String[]"命名是隨意的,但要與我們定義時的名字一致。下麵來定義這個basic type:

public class StringArrayType extends AbstractSingleColumnStandardBasicType<String[]>
        implements DiscriminatorType<String[]> {

    public static final StringArrayType INSTANCE = new StringArrayType();

    public StringArrayType() {
        super(VarcharTypeDescriptor.INSTANCE, StringArrayTypeDescriptor.INSTANCE);
    }

    @Override
    public String[] stringToObject(String xml) throws Exception {
        return fromString( xml );
    }

    @Override
    public String objectToSQLString(String[] value, Dialect dialect) throws Exception {
        return toString(value);
    }

    @Override
    public String getName() {
        return "String[]";
    }
}

觀察以上代碼,我們定義了一個StringArrayType類,它繼承自AbstractSingleColumnStandardBasicType,實現了DiscriminatorType介面,泛型參數類型為我們要處理的Java類型String[],並且該類還採用了單例模式(餓漢式),這是自定義基本類型的“標準做法”。在構造方法中,調用了父類的構造方法,兩個參數分別是SqlTypeDescriptor(Sql類型描述符)和JavaTypeDescriptor(描述符),代表需要在Sql的varchar類型和Java的String[]類型間做轉換。接下去實現了三個方法,其中stringToObject方法是用來指明如何將discriminator-value(這個以後會說明)的值轉換為我們的需要處理的Java類型;objectToSQLString方法用來指明如何將我們需要處理的類型的對象轉化為Sql語句(片段)。這兩個方法都是直接調用了父類的fromStringtoString方法,事實上在父類中,這兩個方法調用了我們之前在構造函數中給定的 JavaTypeDescriptor的fromStringtoString方法;最後還實現的一個方法是getName,該方法用來定義該類型的名稱,即之前我們在Type註釋中使用的。

接下來需要實現上面使用的JavaTypeDescriptor——StringArrayTypeDescriptor:

public class StringArrayTypeDescriptor extends AbstractTypeDescriptor<String[]> {

    public static final StringArrayTypeDescriptor INSTANCE = new StringArrayTypeDescriptor();

    protected StringArrayTypeDescriptor() {
        super(String[].class);
    }

    @Override
    public String toString(String[] value) {
        return JSON.toJSONString(value);
    }

    @Override
    public String[] fromString(String string) {
        JSONArray jsonArray = JSON.parseArray(string);
        String[] result = new String[jsonArray.size()];
        for (int i = 0; i < result.length; i++) {
            result[i] = jsonArray.getString(i);
        }
        return result;
    }

    @Override
    @SuppressWarnings({"unchecked"})
    public <X> X unwrap(String[] value, Class<X> type, WrapperOptions options) {
        if (value == null) {
            return null;
        }
        if (type != String.class) {
            throw unknownUnwrap(type);
        }
        return (X) toString(value);
    }

    @Override
    public <X> String[] wrap(X value, WrapperOptions options) {
        if (!(value instanceof String)) {
            throw unknownWrap(value.getClass());
        }
        return fromString((String) value);
    }
}

該類也採用單例模式,需要實現toStringfromStringunwrapwrap方法。toStringfromString方法指定我們需要處理的類與String之間如何轉換,這也是上面提到的被調用的JavaTypeDescriptor的toStringfromString方法;unwrap方法在為String[]類型欄位指定PreparedStatement的參數時調用;wrap方法相反,是表明如何將從資料庫中取出的值轉換為需要處理的Java類型。

在其中,我們用到了阿裡的fastjson庫,需要在Maven的pom.xml中添加依賴:

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.24</version>
</dependency>

最後還需要將我們定義的這個基本類型“告訴”hibernate。“告訴”的方式官方給出了兩種,但都是用Hibernate原生的API調用的,我暫時還沒有找到用JPA怎麼添加基本類型的,如果你知道,請告訴我。

這裡就採用原生的API來舉例吧。我們需要添加新的配置文件hibernate.cfg.xml,該文件位於resources目錄或類的根路徑下,內容和之前JPA使用的persistence.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="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost/test?useUnicode=true&amp;characterEncoding=UTF-8
        </property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <property name="connection.pool_size">5</property>
        <property name="dialect">org.hibernate.dialect.MySQL57InnoDBDialect</property>
        <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
        <property name="show_sql">true</property>
        <property name="hbm2ddl.auto">update</property>
        <property name="hibernate.physical_naming_strategy">cn.derker.strategy.MyNamingStrategy</property>
        
        <mapping class="cn.derker.model.User"/>
    </session-factory>
</hibernate-configuration>

之後在Java代碼中我們這樣將定義的StringArrayType添加到hibernate中:

@Before
public void init() {
  final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                    .configure() // configures settings from hibernate.cfg.xml
                    .build();
  MetadataSources sources = new MetadataSources(registry);
  MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
  metadataBuilder.applyBasicType(StringArrayType.INSTANCE);
  sessionFactory = metadataBuilder.build().buildSessionFactory();
}

hibernate原生API的sessionFactory與JPA的EntityManager類似,有了它之後,可以這樣調用添加一個用戶:

@Test
public void testSave() throws Exception {
    Session session = sessionFactory.openSession();
    session.getTransaction().begin();
    User user = new User("張三", new String[]{"吃飯", "睡覺", "打豆豆"});
    session.persist(user);
    session.getTransaction().commit();
    session.close();
}

這樣就可以將一個Java字元數組類型的欄位以json字元的形式(sql中是varchar類型)保存在資料庫中。其他更新、查找、刪除方法就不一一給出了,總之都是通過sessionFactory.openSession()得到一個Session對象,然後調用session對想的相關方法。Session對象與JPA中的EntityManager是類似的。

3. 小結

以上內容除了對hibernate的一些基本概念的介紹外,主要給出了三個問題的解答:

  1. 如何使用Hibernate對一個對象進行最基本的CURD操作;
  2. 如何自定義表名,列名;
  3. 如何自定義Hibernate基本類型處理“非一般”Java類型,如處理String[]類型;
    

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

-Advertisement-
Play Games
更多相關文章
  • 一, java多線程 線程與進程 進程: 程式(任務)的執行過程,擁有資源(共用記憶體,共用資源)和線程(一個或者多個,至少一個)。 例如:打開任務管理器,qq,chrome,都屬於進程。 線程: 系統中最小的執行單元,同一進程中有多個線程,線程共用進程的資源。 例如:qq的聊天發送消息。 單線程:程 ...
  • 1. MyBaits基本概念:略 2. 案例:需求:建立資料庫查詢,一對一 3.開發步驟: (1).建立資料庫+表 (2).加包 (3).定義實體類 (4).編寫db.properties,將資料庫信息寫入其中;編寫MyBatis的配置文件conf.xml文件,引入db.properties;引入別 ...
  • 嗯,這篇講可用的多線程記憶體池。 零、上期彩蛋:不要重載全局new 或許,是一次很不愉快的經歷,所以在會有這麼一個“認識”。反正,大概就是:即使你足夠聰明,也不要自作聰明;在這就是不要重載全局new,無論你有著怎樣的目的和智商。因為: 這是一個不對稱的介面:只告訴了我們如何創建一個【堆】對象,但是釋放 ...
  • 1 package cn.temptation; 2 3 public class Sample01 { 4 public static void main(String[] args) { 5 System.out.println("第一個例子"); 6 } 7 } 1 package cn.te... ...
  • Java泛型是JDK1.5加入的新特性。泛型是指參數化的能力。可以定義帶泛型的類型的類或者方法,編譯時期編譯器會用具體的類型來代替它。Java泛型有泛型類、泛型介面和泛型方法。泛型的主要優點是能夠在編譯時期而不是在運行時期就檢測出錯誤。 泛型的出現 在JDK1.5之前,java.lang.Compa ...
  • 引入線程池的原因 通常我們需要使用線程去完成某項任務的時候都會去創建一個線程,一般都會這麼寫: 這樣操作直接且簡單,當然是沒有錯的,但是卻存在這一些問題。在應付一些線程併發不多的情況時是完全夠用的,但是如果併發的線程數量很多,就會造成系統的效率降低。主要會造成如下影響: 頻繁創建和銷毀線程占用大量不 ...
  • 運算符優先順序: siwtch語句註意事項: for迴圈 continue :跳過迴圈體中剩餘的語句執行下一次迴圈 break:退出指定的迴圈,直接執行迴圈後面的代碼 使用Scanner工具類來獲取用戶輸入的信息 示例:接收3個班級各4名學生的成績,計算每個班級的學生的平均分並列印出來。 ...
  • 隱藏細節 現實生活中有很多隱藏細節的案例,比如我們平時用的電腦,當我們按電源按鈕後電腦就自動開始啟動了,對用戶來講很簡單隻需要知道按按鈕就行。但電腦內部的工作原理其實是很複雜的一個流程,這裡不多說。 如果不隱藏細節會怎樣? 我想可能的結果就是電腦只能是特別特別的專業人員才能操作,永遠無法像現在一樣成 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...