MyBatis保姆級理解與使用,動態SQL(核心)

来源:https://www.cnblogs.com/lijili/archive/2022/08/27/16596849.html
-Advertisement-
Play Games

1. 動態SQL(核心) 1.1 簡介 Mybatis框架的動態SQL技術是一種根據特定條件動態拼裝SQL語句的功能,它存在的意義是為瞭解決拼接SQL語句字元串時的難點問題。 比如: 我們在多條件查詢的時候會寫這樣的語句: select * from sys_user where 1=1 and 再 ...


1. 動態SQL(核心)

1.1 簡介

Mybatis框架的動態SQL技術是一種根據特定條件動態拼裝SQL語句的功能,它存在的意義是為瞭解決拼接SQL語句字元串時的難點問題。

比如: 我們在多條件查詢的時候會寫這樣的語句:

select * from sys_user where 1=1 and

 

再比如:做更新的時候,我們沒有修改的數據列也執行了更新操作。

1.2 ifwhere標簽

<!-- List<Emp> select ByCondition(Emp emp); -->

 

<select id="selectByCondition" resultType="com.hy.bean.Emp">

 

    select emp_id,emp_name,emp_salary from sys_emp

    <!-- where標簽會自動去掉標簽體內前面、後面多餘的and/or” -->

    <where>

        <!-- 使用if標簽,讓我們可以有選擇的加入SQL語句的片段。這個SQL語句片段是否要加入整個SQL語句,就看if標簽判斷的結果是否為true -->

        <!-- if標簽的test屬性中,可以訪問實體類的屬性,不可以訪問資料庫表的欄位 -->

        <if test="empName != null">

            <!-- if標簽內部,需要訪問介面的參數時還是正常寫#{} -->

 

            or emp_name=#{empName}

        </if>

        <if test="empSalary > 2000">

            or emp_salary>#{empSalary}

        </if>

 

        <!--

         第一種情況:所有條件都滿足 WHERE emp_name=? or emp_salary>?

         第二種情況:部分條件滿足 WHERE emp_salary>?

         第三種情況:所有條件都不滿足 沒有where子句

         -->

    </where>

</select>

 

 

1.3 set標簽

需求:實際開發時,對一個實體類對象進行更新。往往不是更新所有欄位,而是更新一部分欄位。此時頁面上的表單往往不會給不修改的欄位提供表單項 

 

例如上面的表單,如果伺服器端接收表單時,使用的是User這個實體類,那麼userNameuserBalanceuserGrade接收到的數據就是null

如果不加判斷,直接用User對象去更新資料庫,在Mapper配置文件中又是每一個欄位都更新,那就會把userNameuserBalanceuserGrade設置為null值,從而造成資料庫表中對應數據被破壞。

此時需要我們在Mapper配置文件中,對update語句的set子句進行定製,此時就可以使用動態SQLset標簽。

 


<!-- void updateeDynamic(Emp emp) -->

 

<update id="updateEmployeeDynamic">

 

    update sys_emp

 

    <!-- set emp_name=#{empName},emp_salary=#{empSalary} -->

 

    <!-- 使用set標簽動態管理set子句,並且動態去掉兩端多餘的逗號 -->

 

    <set>

 

        <if test="empName != null">

 

            emp_name=#{empName},

 

        </if>

 

        <if test="empSalary < 3000">

 

            emp_salary=#{empSalary},

 

        </if>

 

    </set>

 

    where emp_id=#{empId}

 

   </update>

         第一種情況:所有條件都滿足 SET emp_name=?, emp_salary=?

         第二種情況:部分條件滿足 SET emp_salary=?

         第三種情況:所有條件都不滿足 update t_emp where emp_id=?

                沒有set子句的update語句會導致SQL語法錯誤

 

 

1.4 trim標簽

使用trim標簽控制條件部分兩端是否包含某些字元

prefix屬性:指定要動態添加的首碼內容

suffix屬性:指定要動態添加的尾碼

prefixOverrides屬性:指定要動態去掉的首碼,使用“|”分隔有可能的多個值

suffixOverrides屬性:指定要動態去掉的尾碼,使用“|”分隔有可能的多個值

 

下麵例子中,trim標簽內部如果有條件,則where會出現,否則where不出現

suffixOverrides=and|or”:整個條件部分,如果在後面有多出來的“andor”會被自動去掉。

 

<!-- List<Emp> selectByConditionByTrim(Emp emp) -->

<select id="selectByConditionByTrim" resultType="com.hy.bean.Emp">

    select emp_id,emp_name,emp_age,emp_salary,emp_gender

    from sys_emp

   <!-- prefix屬性指定要動態添加的首碼 -->

    <!-- suffix屬性指定要動態添加的尾碼 -->

    <!-- prefixOverrides屬性指定要動態去掉的首碼,使用“|”分隔有可能的多個值 -->

    <!-- suffixOverrides屬性指定要動態去掉的尾碼,使用“|”分隔有可能的多個值 -->

    <!-- 當前例子用where標簽實現更簡潔,但是trim標簽更靈活,可以用在任何有需要的地方 -->

    <trim prefix="where" suffixOverrides="and|or">

        <if test="empName != null">

            emp_name=#{empName} and

        </if>

        <if test="empSalary > 3000">

            emp_salary > #{empSalary} and

        </if>

        <if test="empAge < 20">

            emp_age=#{empAge} or

        </if>

        <if test="empGender==’ m’ ">

            emp_gender=#{empGender}

        </if>

    </trim>

</select>

 

 

 

1.5 choose/when/otherwise標簽

在多個分支條件中,僅執行一個。

<!-- List<Emp> selectByConditionByChoose(Emp emp) -->

<select id="selectEmployeeByConditionByChoose" resultType="com.hy.bean.Emp">

    select emp_id,emp_name,emp_salary from sys_emp

    where

    <choose>

        <when test="empName != null">emp_name=#{empName}</when>

        <when test="empSalary > 3000">emp_salary > 3000</when>

        <otherwise>1=1</otherwise>

    </choose>

    

    <!--

     第一種情況:第一個when滿足條件 where emp_name=?

     第二種情況:第二個when滿足條件 where emp_salary < 3000

     第三種情況:兩個when都不滿足 where 1=1 執行了otherwise

     -->

</select>

 

 

1.6 foreach標簽

1.6.1 基本用法

比如:批量插入

abstract public void insertBatch(@Param(“empList”) List<Emp> empList));

<!--  

INSERT INTO `sys_emp` VALUES (null, '李冰冰', 'lbb', 'f', 300,default,2),

(null, '張彬彬', 'zbb', 'm', 599,default,3),

(null, '萬茜', 'wq', 'm', 4000,default,1),

(null, '李若彤', 'lrt', 'm', 5000.8,default,1)

-->

<insert id="insertBatch">

INSERT INTO `sys_emp`(emp_id,emp_name,emp_gender)

<foreach collection="empList" item="emp" separator="," open="VALUES" >

(null,#{emp.empName},#{emp.empGender})

</foreach>

</insert>

 

 

我們這裡寫的批量插入的例子本質上是一條SQL語句

    collection屬性:要遍歷的集合,如果介面中方法中使用了@Param collection屬性中要用這個名字。

    item屬性:遍歷集合的過程中能得到每一個具體對象,在item屬性中設置一個名字,將來通過這個名字引用遍歷出來的對象

    separator屬性:指定當foreach標簽的標簽體重覆拼接字元串時,各個標簽體字元串之間的分隔符

    open屬性:指定整個foreach迴圈把字元串拼好後,這個字元串整體的前面要添加的字元串

    close屬性:指定整個foreach迴圈把字元串拼好後,這個字元串整體的後面要添加的字元串

    index屬性:這裡起一個名字,便於後面引用

        遍歷List集合,這裡能夠得到List集合的索引值

        遍歷Map集合,這裡能夠得到Map集合的key

 

如果介面形參位置沒有使用@Param註解,而且foreach標簽也沒有使用預設的名稱,則會拋出異常

 

Caused by:org.apache.ibatis.binding.BindingException: Parameter ‘empList’ not found. Available parameters are[arg0,collection, list]

1.6.2 批量更新

而實現批量更新則需要多條update SQL語句拼起來,並且用分號分開。也就是一次性發送多條SQL語句讓資料庫執行

此時需要在資料庫連接信息的URL地址中設置

?allowMultiQueries=true

 

對應的foreach標簽如下:

<!-- int updateBatch(@Param("empList") List<Emp> empList) -->

<update id="updateBatch">

    <foreach collection="empList" item="emp" separator=";">

        update sys_emp set emp_name=#{emp.empName} where emp_id=#{emp.empId}

    </foreach>

</update>

 

 

1.6.3 關於foreach標簽的collection屬性

如果沒有給介面中List類型的參數使用@Param註解指定一個具體的名字,那麼在collection屬性中預設可以使用collectionlist來引用這個list集合。這一點可以通過異常信息看出來:

Parameter 'empList' not found. Available parameters are [collection, list]

 

在實際開發中,為了避免隱晦的表達造成一定的誤會,建議使用@Param註解明確聲明變數的名稱,然後在foreach標簽的collection屬性中按照@Param註解指定的名稱來引用傳入的參數。

 

 

1.7 sql標簽

 

1.7.1 抽取重覆的SQL片段

 

 <!-- 使用sql標簽抽取重覆出現的SQL片段 -->

    <sql id="mySelectSql">

        select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp

    </sql>

 

1.7.2 引用已抽取的SQL片段

 <!-- 使用include標簽引用聲明的SQL片段 -->

 <include refid="mySelectSql"/>

 

 

2.緩存

2.1 緩存機制

 

 

 

 

比如:以前農村裝水的水缸

 

2.2 一級緩存和二級緩存

 

 

 

 

二級緩存被所有的SqlSession共用,也就是能被訪問的。

一級緩存是SqlSession級別的緩存。在操作資料庫時需要構造 sqlSession對象,在對象中有一個數據結構(HashMap)用於存儲緩存數據。不同的sqlSession之間的緩存數據區域(HashMap)是互相不影響的。

二級緩存是Mappernamespace)級別的緩存。多個SqlSession去操作同一個Mappersql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。

 

第一次查詢,先將結果放入到一級緩存,SqlSession提交事務後,再將數據放入到二級緩存。

 

2.2.1 一級緩存和二級緩存的使用順序

 

查詢的順序是:

1先查詢二級緩存,因為二級緩存中可能會有其他程式已經查出來的數據,可以拿來直接使用。

2如果二級緩存沒有命中,再查詢一級緩存

3如果一級緩存也沒有命中,則查詢資料庫

SqlSession關閉之前,會將一級緩存中的數據會寫入二級緩存

 

2.2.2 效用範圍

一級緩存:SqlSession級別

二級緩存:SqlSessionFactory級別

 

 

 

 

 

 

 

 

它們之間範圍的大小參考下麵圖:

 

 

 

 

一個請求中可能包含多個事務  ------  一個service方法(一個SqlSession)對應一個事務

註意:緩存是用再查詢過程中的,增刪改,反而會破壞緩存。造成緩存和資料庫不一致,所以很多情況,執行了一個增刪改操作,他會把緩存清空。

 

3. 一級緩存

Mybatis預設開啟了一級緩存

3.1 案例1:測試一級緩存是否存在

為了測試緩存失效,我們需要修改測試類中的代碼。需要關閉SqlSessioin對象,然後重新開,再重新開的話,需要操作SqlSessionFactory

 

如何測試一級緩存是否存在???同一個數據查兩次,但是只發了一條SQL語句[給資料庫]。

3.1.1 證明一級緩存存在

 

package com.hy.mybatis.test;

 

import java.io.IOException;

 

import org.apache.ibatis.io.Resources;

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import org.junit.Before;

import org.junit.Test;

 

import com.hy.bean.Emp;

import com.hy.mapper.EmpMapper;

 

public class TestLevelOneCache1 {

private SqlSessionFactory sqlSessionFactory;

private Logger logger = null;

@Before

public void init() throws IOException {

logger = LoggerFactory.getLogger(this.getClass());

sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));

}

 

@Test

public void testLevelOneCache1_01() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

Emp emp2 = empMapper.selectById(1); // 第二次是從緩存中查詢出來的。

 

logger.info("是否相等:"+ (emp1 == emp2)); //true

 

session.commit();

session.close();

}

}

 

只發一條SQL語句。一級緩存預設開啟,不需要做額外的配置。二級緩存需要開啟才有,現在沒有開,所以只有一級緩存。

 

 

 

3.2 案例2一級緩存失效的情況

1同一個SqlSession兩次查詢期間提交了事務

2同一個SqlSession兩次查詢期間執行了任何一次增刪改操作,不論是夠提交事務,都清空一級緩存

3同一個SqlSession但是查詢條件發生了變化

4同一個SqlSession兩次查詢期間手動清空了緩存

5不是同一個SqlSession

 

總結:在執行commitrollback時會清空一級緩存

一級緩存的清除還有以下兩個地方:

 

1、就是獲取緩存之前會先進行判斷用戶是否配置了flushCache=true屬性(參考一級緩存的創建代碼截圖),如果配置了則會清除一級緩存。

2MyBatis全局配置屬性localCacheScope配置為Statement時,那麼完成一次查詢就會清除緩存。

 

3.2.1 同一個SqlSession兩次查詢期間提交了事務

public class TestLevelCache1 {

private SqlSessionFactory sqlSessionFactory;

private Logger logger = null;

@Before

public void init() throws IOException {

logger = LoggerFactory.getLogger(this.getClass());

sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));

}

 

//1) 同一個SqlSession兩次查詢期間提交了事務

//1.1 沒有提交事務,使用一級緩存

@Test

public void testLevelOneCache1_01 () throws IOException {

SqlSession session = sqlSessionFactory.openSession();

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

Emp emp2 = empMapper.selectById(1); // 第二次是從緩存中查詢出來的。

 

logger.info("是否相等:"+ (emp1 == emp2)); //true

 

session.commit();

session.close();

}

 

 

//1.2 兩次查詢中間,提交了事務,清空一級緩存

@Test

public void testLevelOneCache1_02() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

session.commit();

Emp emp2 = empMapper.selectById(1); // 由於進行了事務的提交,所以又發送了一條查詢語句。

 

logger.info("是否相等:"+ (emp1 == emp2)); //false

session.close();

}

 

 

 

 

}

 

 

3.2.2 同一個SqlSession兩次查詢期間執行了任何一次增刪改操作,並且提交了事務

 

 

 

 

 

@Test

public void testLevelOneCache01_4() {

SqlSession sqlSession = sqlSessionFactory.openSession();

 

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

 

//Emp newEmp = new Emp(2L, "範冰冰ILoveYou", null, null, null, null);

//empMapper.updateById(newEmp);  //更新但是不提交事務

 

empMapper.deleteById(8);  //執行了刪除沒有,提交事務也,重新一遍

 

Emp emp2 = empMapper.selectById(1);

//logger.debug(emp2.toString());

logger.debug("是否相等:" + (emp1 == emp2));

 

sqlSession.commit();

sqlSession.close();

}

 

 

// 2.2

@Test

public void testLevelOneCache2_02() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

 

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1); //查詢後,將對象放入一級緩存

 

empMapper.deleteById(7); //執行了delete操作,但是並沒有提交

session.commit();

 

Emp emp11 = empMapper.selectById(1); //提交事務,一級緩存清空

logger.info("是否相等:"+ (emp1 == emp11)); //false

 

session.commit();

session.close();

}

 

 

 

 

 

 

第一次發起查詢用戶id1的用戶信息,先去找緩存中是否有id1的用戶信息,如果沒有,從資料庫查詢用戶信息,將查詢到的用戶信息存儲到一級緩存中。

如果中間sqlSession去執行插入、更新、刪除,並且commit,清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。

第二次發起查詢用戶id1的用戶信息,先去找緩存中是否有id1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。如果沒有,則重新發出一條查詢語句。

 

3.2.3 同一個SqlSession但是查詢條件發生了變化

// 3. 同一個SqlSession但是查詢條件發生了變化

@Test

public void testLevelOneCache3_01() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

 

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1); //查詢後,將對象放入一級緩存

//查詢的是emp_id2的員工信息,一級緩存中並沒有所以重新發出一條SQL語句

Emp emp2 = empMapper.selectById(2);

session.commit();

session.close();

}

 

 

 

 

3.2.4 同一個SqlSession兩次查詢期間手動清空了緩存

// 4)同一個SqlSession兩次查詢期間手動清空了緩存

@Test

public void testLevelOneCache4() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

 

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

session.clearCache();

Emp emp2 = empMapper.selectById(1); // 由於兩次查詢中間清空了一級緩存,所以又發送了一條查詢語句。

logger.info("是否相等:"+ (emp1 == emp2)); //false

 

session.commit();

session.close();

}

 

 

3.2.5 不是同一個SqlSession

 

// 5)不是同一個SqlSession

@Test

public void testLevelOneCache1_05() throws IOException {

SqlSession session01 = sqlSessionFactory.openSession();

SqlSession session02 = sqlSessionFactory.openSession();

EmpMapper empMapper01 = session01.getMapper(EmpMapper.class);

EmpMapper empMapper02 = session02.getMapper(EmpMapper.class);

Emp emp1 = empMapper01.selectById(1);

// 由於是不同的SqlSession,所以又發送了一條查詢語句。各自有各自的一級緩存。

Emp emp2 = empMapper02.selectById(1);

 

logger.info("是否相等:"+ (emp1 == emp2)); //false

session01.commit();

session02.commit();

session01.close();

session02.close();

}

 

 

 

 

 

4. 二級緩存[mybatis自帶的二級緩存]

 

 

 

 

 

4.1 使用二級緩存步驟

1在想要使用二級緩存的EmpMapper映射文件中加入cache標簽

開啟全局二級緩存配置:<setting name="cacheEnabled" value="true"></setting> 

 

2讓實體類支持序列化

public class Emp implements Serializable{

private static final long serialVersionUID = 1L;

 

這裡我們使用mybaits自帶的二級緩存,後面我們使用第三方的二級緩存EHCache

 

3SqlSession提交事務時才會將查詢到的數據存入二級緩存

@Test

public void testLevelTwoCache() {

// 測試二級緩存存在:使用兩個不同SqlSession執行查詢

// 說明:SqlSession提交事務時才會將查詢到的數據存入二級緩存

// 所以本例並沒有能夠成功從二級緩存獲取到數據

SqlSession sqlSession01 = sqlSessionFactory.openSession();

SqlSession sqlSession02 = sqlSessionFactory.openSession();

 

EmpMapper empMapper01 = sqlSession01.getMapper(EmpMapper.class);

EmpMapper empMapper02 = sqlSession02.getMapper(EmpMapper.class);

 

//[00:01:07.699] [DEBUG] [main] [com.hy.mapper.EmpMapper] [Cache Hit Ratio [com.hy.mapper.EmpMapper]: 0.0]

Emp emp01 = empMapper01.selectById(1);

//[00:01:07.746] [DEBUG] [main] [com.hy.mapper.EmpMapper] [Cache Hit Ratio [com.hy.mapper.EmpMapper]: 0.0]

Emp emp02 = empMapper02.selectById(1);

 

logger.info("是否相等:"+ (emp1 == emp2)); //false

sqlSession01.commit();

sqlSession01.close();

 

sqlSession02.commit();

sqlSession02.close();

}

 

 

修改代碼;

@Test

public void testSecondLevelCache2() {

SqlSession sqlSession01 = sqlSessionFactory.openSession();

SqlSession sqlSession02 = sqlSessionFactory.openSession();

 

EmpMapper empMapper01 = sqlSession01.getMapper(EmpMapper.class);

EmpMapper empMapper02 = sqlSession02.getMapper(EmpMapper.class);

 

Emp emp01 = empMapper01.selectById(1);

 

sqlSession01.commit();

sqlSession01.close();

Emp emp02 = empMapper02.selectById(1);

 

//false 註意,從二級緩存中new了一個新的對象,所以emp01emp02不是同一個對象

logger.info("是否相等:"+ (emp1 == emp2));

sqlSession02.commit();

sqlSession02.close();

}

 

 

 

 

雖然兩個對象不相等,但是註意:只發出了一條查詢語句。

 

日誌中列印的Cache Hit Ratio叫做緩存命中率

緩存命中率=命中緩存的次數/查詢的總次數

 

4mybatis序列化的特點

mybatis的二級緩存是屬於序列化,序列化的意思就是從記憶體中的數據傳到硬碟中,這個過程就是序列化;

反序列化意思就是相反而已;

也就是說,mybatis的二級緩存,實際上就是將數據放進了硬碟文件中去了;

 

現在呢,你僅僅的將Emp類給序列化了,如果有父類Person、級聯屬性,它們是不會跟著被序列化的,所以光這些是不夠的;

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

-Advertisement-
Play Games
更多相關文章
  • 摘要:今天給大家帶來一篇MySQL資料庫崩潰的修複案例 本文分享自華為雲社區《記一次MySQL崩潰修複案例,再也不用刪庫跑路了》,作者: 冰 河。 問題描述 研究MySQL源代碼,調試並壓測MySQL源代碼時,MySQL崩潰了!問題是它竟然崩潰了!而且還損壞了InnoDB文件!!還好是在調試環境下發 ...
  • 如何去掉vue的url地址中的#號?及其原理? 點擊打開視頻講解更加詳細 如何去掉vue的url地址中的#號? import Vue from 'vue'; import VueRouter from 'vue-router'; Vue.use(VueRouter) // 1. 定義一些路由 // ...
  • ##Query 是一個 JavaScript 函數庫。 jQuery 是一個輕量級的"寫的少,做的多"的 JavaScript 庫。 jQuery 庫包含以下功能: HTML 元素選取 HTML 元素操作 CSS 操作 HTML 事件函數 JavaScript 特效和動畫 HTML DOM 遍歷和修 ...
  • 1、axios的二次封裝 BiliBili作者原地址,多多支持 npm i axios //下載axios 首先創建兩個文件夾在src目錄下;api和config 先在config文件夾下建立一個index.js;具體代碼如下 export default{ baseUrl:{ dev: "http ...
  • 對象及日期定時器 Date日期 日期對象的定義(使用new關鍵詞) 1.獲取當前的時間(本地的時間) var date = new Date() //不傳參就是獲取當前時間 2.獲取指定的時間 var date = new Date(123456) //一個參數毫秒值 將這個毫秒值去加上對應的197 ...
  • 字元串 字元串 字元串也是一個數據結構,將同樣的內容串在一塊。因為在對應的js裡面字元串屬於一個值類型(值類型是常量 常量是不能變)。字元串是不能改變的。結合數據結構裡面串也是一個存儲結構,作為存儲結構增刪改查的方法(字元串的增刪改查 不能針對於本身 而是返回一個新的字元串) 字元串的聲明 1.值類 ...
  • 數組 數據結構 數據的結構 (邏輯結構 存儲結構 演算法) 存儲結構 (數據存儲的結構方式) 線性結構 數組(順序表) 隊列 棧 堆 鏈表 非線性結構 樹 圖hash (散列表) 只要是能存數據的容器 就必須具備增刪改查的方法 數組 數組概述:數組固定一類數據的組合(一般情況下我們數組裡面的數據類型一 ...
  • 淺拷貝 只拷貝第一層的值,其他後面拷貝的是地址。 示例 使用u盤在一臺電腦上拷貝文件,使用淺拷貝拷貝的相當於快捷方式。 第一層兩個內容不一樣,其他每層都是指向同一個文件 實現淺拷貝的方法 Object.assign (實現淺拷貝) let obj = { user:{ name: " jack" } ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...