day16-聲明式事務-02

来源:https://www.cnblogs.com/liyuelian/archive/2023/02/01/17084525.html
-Advertisement-
Play Games

聲明式事務-02 3.事務的傳播機制 事務的傳播機制說明: 當有多個事務處理並存時,如何控制? 比如用戶去購買兩次商品(使用不同的方法),每個方法都是一個事務,那麼如何控制呢? 也就是說,某個方法本身是一個事務,然後該方法中又調用了其他一些方法,這些方法也是被@Transactional 修飾的,同 ...


聲明式事務-02

3.事務的傳播機制

事務的傳播機制說明:

  1. 當有多個事務處理並存時,如何控制?

  2. 比如用戶去購買兩次商品(使用不同的方法),每個方法都是一個事務,那麼如何控制呢?

    image-20230131224401793

    也就是說,某個方法本身是一個事務,然後該方法中又調用了其他一些方法,這些方法也是被@Transactional 修飾的,同樣是事務。

  3. 問題在於:裡層方法的事務是被外層方法事務管理?還是它本身作為一個獨立的事務呢?這就涉及到事務的傳播機制問題。

3.1事務傳播機制種類

  • 事務傳播的屬性 / 種類:
傳播屬性 說明
REQUIRED (預設)如果有事務在運行,當前的方法就在這個事務內運行,否則,就啟動一個新的事務,並且在自己的事務內運行
REQUIRES_NEW 當前的方法必須啟動新事務,併在它自己的事務內運行,如果有事務正在運行,應該將它掛起
SUPPORTS 如果有事務在運行,當前的方法就在這個事務內運行,否則它可以不運行在事務中
NOT_SUPPORTED 當前的方法不應該運行在事務中,如果有運行的事務,將它掛起
MANDATORY 當前的方法必須運行在事務內部,如果沒有正在運行的事務,就拋出異常
NEVER 當前的方法不應該運行在事務中,如果有運行的事務,就拋出異常
NESTED 如果有事務在運行,當前的方法就應該在這個事務的嵌套事務內運行,否則,就啟動一個新的事務,併在它自己的事務內運行

常用的只有前面兩種:(1)REQUIRED,(2)REQUIRES_NEWREQUIRES_NEW


  • 事務傳播的屬性/種類機制分析

重點分析 REQUIRED 和 REQUIRES_NEW 兩種事務傳播屬性,其他知道即可。

如下,有一個multiTxTest()方法,該方法中又有f1(),f2() 方法。所有方法都分別開啟了聲明式事務。

@Transactional
public void multiTxTest() {
    f1(); //含事務
    
    f2(); //含事務
}
  1. 如果f1(),f2() 的傳播屬性都是 REQUIRED,那麼它們實際上是被Tx()的事務統一管理的。所有方法是一個整體,只要有一個方法的事務錯誤,那麼兩個方法都不會執行成功。

    image-20230201182508199
  2. 如果f1(),f2() 的傳播屬性都是 REQUIRES_NEW,那麼f1(),f2()實際上是獨立的事務,不會受到Tx()事務的影響。如果f1()錯誤,不會影響到f2(),反之亦然。

    image-20230201182408268

3.2應用實例

需求說明:

  1. 用戶要去購買兩次商品(使用不同的方法),每個方法都是一個事務,那麼如何控制呢?
  2. 看一個具體的案例(用 required 和 requires_new 測試)

代碼實現

1.GoodsDao.java

分別有6個方法:queryPriceById,queryPriceById2,updateBalance,updateBalance2,updateAmount,updateAmount2。

package com.li.tx.dao;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

/**
 * @author 李
 * @version 1.0
 */
@Repository //將GoodsDao對象 註入到 spring 容器
public class GoodsDao {
    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 根據商品id,查詢對應的商品價格
     * @param id
     * @return
     */
    public Float queryPriceById(Integer id) {
        String sql = "select price from goods where goods_id = ?";
        Float price = jdbcTemplate.queryForObject(sql, Float.class, id);
        return price;
    }

    /**
     * 修改用戶餘額 [減少用戶餘額]
     * @param user_id
     * @param money
     */
    public void updateBalance(Integer user_id, Float money) {
        String sql = "update user_account set money=money-? where user_id=? ";
        jdbcTemplate.update(sql, money, user_id);
    }

    /**
     * 修改商品庫存量
     * @param goods_id
     * @param amount
     */
    public void updateAmount(Integer goods_id, int amount) {
        String sql = "update goods_amount set goods_num=goods_num-? where goods_id=? ";
        jdbcTemplate.update(sql, amount, goods_id);
    }

    //和queryPriceById的操作是一樣的
    public Float queryPriceById2(Integer id) {
        String sql = "select price from goods where goods_id = ?";
        Float price = jdbcTemplate.queryForObject(sql, Float.class, id);
        return price;
    }

    //和updateBalance的操作是一樣的
    public void updateBalance2(Integer user_id, Float money) {
        String sql = "update user_account set money=money-? where user_id=? ";
        jdbcTemplate.update(sql, money, user_id);
    }

    //和updateAmount的操作是一樣的
    public void updateAmount2(Integer goods_id, int amount) {
        String sql = "update goods_amount set goods_num=goods_num-? where goods_id=? ";
        jdbcTemplate.update(sql, amount, goods_id);
    }
}

2.GoodsService.java,分別有兩個方法buyGoodsByTx,buyGoodsByTx02

package com.li.tx.service;

import com.li.tx.dao.GoodsDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.beans.Transient;

/**
 * @author 李
 * @version 1.0
 */
@Service //將GoodsService對象註入到容器中
public class GoodsService {
    @Resource
    private GoodsDao goodsDao;

    /**
     * 進行商品購買的方法
     * @param userId
     * @param goodsId
     * @param amount
     */
    @Transactional
    public void buyGoodsByTx(int userId, int goodsId, int amount) {
        //輸出購買的相關信息
        System.out.println("用戶購買信息 userId=" + userId
                + " goodsId=" + goodsId + " 購買數量=" + amount);

        //1.得到商品價格
        Float price = goodsDao.queryPriceById(goodsId);
        //2.減少用戶餘額
        goodsDao.updateBalance(userId, price * amount);
        //3.減少商品庫存量
        goodsDao.updateAmount(goodsId, amount);

        System.out.println("用戶購買成功...");
    }

    /**
     * 進行商品購買的方法02,調用的是GoodsDao的2尾碼的方法
     * @param userId
     * @param goodsId
     * @param amount
     */
    @Transactional
    public void buyGoodsByTx02(int userId, int goodsId, int amount) {
        //輸出購買的相關信息
        System.out.println("用戶購買信息 userId=" + userId
                + " goodsId=" + goodsId + " 購買數量=" + amount);

        //1.得到商品價格
        Float price = goodsDao.queryPriceById2(goodsId);
        //2.減少用戶餘額
        goodsDao.updateBalance2(userId, price * amount);
        //3.減少商品庫存量
        goodsDao.updateAmount2(goodsId, amount);

        System.out.println("用戶購買成功...");
    }
}

3.MultiplyService.java

package com.li.tx.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @author 李
 * @version 1.0
 */
@Service
public class MultiplyService {
    @Resource
    private GoodsService goodsService;

    /**
     * 說明
     * 1.multiBuyGoodsByTx() 方法中,有兩次購商品的操作
     * 2.buyGoodsByTx 和 buyGoodsByTx02 都是聲明式事務
     * 3.並且buyGoodsByTx 和 buyGoodsByTx02使用的傳播屬性為預設的 REQUIRED,
     * 即會當做一個整體事務來處理
     */
    @Transactional
    public void multiBuyGoodsByTx() {
        goodsService.buyGoodsByTx(1, 1, 1);
        goodsService.buyGoodsByTx02(1, 1, 1);
    }
}

4.測試

//測試事務的傳播機制
@Test
public void multiBuyGoodsByTx(){
    ApplicationContext ioc =
            new ClassPathXmlApplicationContext("tx.xml");
    MultiplyService multiplyService = ioc.getBean(MultiplyService.class);

    multiplyService.multiBuyGoodsByTx();
}

測試結果:購買成功

image-20230201190434933

測試前數據:

表結構詳見上一篇

image-20230201190524624 image-20230201190535746

測試後數據:

image-20230201190550986 image-20230201190607334

5.在GoodsDao的updateAmount2()方法中添加錯誤字元,使其不能成功執行:

image-20230201190942010

因為 buyGoodsByTx() 和buyGoodsByTx02() 的事務傳播屬性都是required,且都在multiBuyGoodsByTx()方法內部,因此它們被視為一個整體。當 buyGoodsByTx02() 執行出現錯誤,兩個方法將會一起回滾。

執行4.的測試代碼,測試結果:出現異常。

image-20230201191836373

測試後數據:

image-20230201191709589 image-20230201191801446

仍然是之前的數據,說明兩個方法一起進行了事務回滾。

6.將GoodsService 的 buyGoodsByTx() / buyGoodsByTx02() 方法的事務傳播屬性改為REQUIRES_NEW。

image-20230201192043623 image-20230201192348370

這時兩個方法的事務是獨立的,buyGoodsByTx02() 失敗不會造成 buyGoodsByTx() 的回滾。

7.再執行4.測試方法,結果如下:仍然出現異常

image-20230201192838075

但是只有 buyGoodsByTx() 方法操作改變了數據。

測試前數據:

image-20230201191709589 image-20230201191801446

測試後數據:

image-20230201192942765 image-20230201193005177

說明只有 buyGoodsByTx02() 方法進行了回滾。

4.事務的隔離機制

4.1事務隔離級別說明

MySQL 隔離級別定義了事務與事務之間的隔離程度

MySQL隔離級別(4種) 臟讀 不可重覆讀 幻讀 加鎖讀
讀未提交(Read uncommitted) v v v 不加鎖
讀已提交(Read committed) x v v 不加鎖
可重覆讀(Repeatable read) x x x 不加鎖
可串列化(Serializable) x x x 加鎖

關於可重覆讀會不會發生幻讀問題:

SQL92標準有,mysql資料庫改進了,解決了這個級別的幻讀問題。

  • 事務隔離級別說明
  1. Spring聲明式事務的預設隔離級別,就是 mysql 資料庫預設的隔離級別,一般為 REPREATABLE_READ

    查看源碼可知:Use the default isolation level of the underlying datastore. All other levels correspond to the JDBC isolation levels.

  2. 查看資料庫的隔離級別 SELECT @@global.tx_isolation

4.2事務隔離級別的設置和測試

整體思路如下:

在開啟了聲明式事務的某方法中,查詢兩次數據。在第一次查詢後,先在控制臺中修改該數據(在終端中預設為自動提交),方法再進行第二次的查詢。查看兩次查詢的數據是否相同。通過這樣的方法來模擬兩個客戶端,測試聲明式事務的隔離級別。


1.修改GoodsService.java,先測試預設隔離級別,增加方法 buyGoodsByTxISOLATION()

/**
 * 在預設下,聲明式事務使用的隔離界別為 可重覆讀-Repeatable read
 */
@Transactional
public void buyGoodsByTxISOLATION() {
    //查詢兩次商品的價格
    Float price = goodsDao.queryPriceById(1);
    System.out.println("第一次查詢的價格=" + price);
   
    Float price2 = goodsDao.queryPriceById(1);
    System.out.println("第二次查詢的價格=" + price2);

}

併在方法如下位置打上斷點

image-20230201202807525

2.測試方法

//測試聲明式事務的隔離級別
@Test
public void buyGoodsByTxISOLATIONTest() {
    ApplicationContext ioc =
            new ClassPathXmlApplicationContext("tx.xml");
    GoodsService goodsService = ioc.getBean(GoodsService.class);

    goodsService.buyGoodsByTxISOLATION();
}

3.點擊debug,當游標跳轉到斷點時,可以看到第一次查詢的 price=10

image-20230201200745112

4.這時我們在控制台修改該數據為 15

image-20230201201026362

5.然後點擊Step Over,發現第二次查詢的價格仍然為 10

image-20230201201304711

這說明Spring的聲明是事務的預設隔離級別為 可重覆讀。

6.將方法buyGoodsByTxISOLATION() 的事務隔離級別改為 讀已提交

讀已提交表示只要是提交的數據,在當前事務中都可以讀取到最新數據

image-20230201202900352

同時和之前一樣打上斷點。

7.測試方法不變,點擊debug,游標跳轉到斷點時,可以看到第一次查詢時 price=15

image-20230201203026741

8.此時在控制台將該數據改為 20

image-20230201202623349

9.點擊Step Over,可以看到第二次查詢的數據已經變成了 20

image-20230201203229314

說明當前事務的隔離級別為 讀已提交。

4.3事務的超時回滾

  • 基本介紹
  1. 如果一個事務執行的時間超過某個時間限制,就讓該事務回滾。
  2. 可以通過設置事務超時回滾來實現
  • 基本語法
image-20230201203229314

例子:超時回滾代碼實現

1.GoodsService 中增加方法 buyGoodsByTxTimeout(),並設置事務超時時間為2s。為了模擬超時效果,在方法中休眠4s。

/**
 * 1.timeout = 2,表示該方法如果執行時間超過了兩秒,就進行回滾
 * 2.如果沒有設置 timeout,則預設該值為 -1,表示使用預設超時時間,
 *  一般為連接的資料庫的預設超時時間
 */
@Transactional(timeout = 2)
public void buyGoodsByTxTimeout(int userId, int goodsId, int amount){
    //輸出購買的相關信息
    System.out.println("用戶購買信息 userId=" + userId
            + " goodsId=" + goodsId + " 購買數量=" + amount);
    //1.得到商品價格
    Float price = goodsDao.queryPriceById2(goodsId);
    //2.減少用戶餘額
    goodsDao.updateBalance2(userId, price * amount);
    //模擬超時
    System.out.println("==========超時開始4s=========");
    try {
        Thread.sleep(4000);//休眠4s
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("==========超時結束4s=========");
    
    //3.減少商品庫存量
    goodsDao.updateAmount2(goodsId, amount);
    System.out.println("用戶購買成功...");
}

2.測試方法

//測試超時 timeout 屬性
@Test
public void buyGoodsByTxTimeoutTest() {
    ApplicationContext ioc =
            new ClassPathXmlApplicationContext("tx.xml");
    GoodsService goodsService = ioc.getBean(GoodsService.class);
    goodsService.buyGoodsByTxTimeout(1, 1, 1);
}

測試結果:出現異常,顯示事務超時。

image-20230201210010309

測試前數據:

image-20230201210047908 image-20230201210057742

測試後數據:

image-20230201210138486 image-20230201210226451

數據沒有進行改變,說明事務超時,併進行了回滾。

5.練習

要求:模擬一個用戶,進行銀行轉賬,購買淘寶商品的業務。數據表,dao層,service層自己設置,要求保證數據一致性。

  1. seller [賣家表]
  2. buyer [買家表]
  3. goods [商品表[有庫存量屬性]]
  4. taoBao [taoBao表,提取入賬成交額的 10%]
  5. 要求簡單實現,使用聲明式事務完成
  6. 要求創建新的spring容器文件 shopping_ioc.xml,完成測試

實現

1.創建表格,並插入初始數據

-- buyer表
CREATE TABLE `buyer`(
buyer_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
buyer_name VARCHAR(32) NOT NULL DEFAULT '',
buyer_money DOUBLE NOT NULL DEFAULT 0.0
)CHARSET=utf8;

INSERT INTO `buyer` VALUES(NULL,'張三', 1000);
INSERT INTO `buyer` VALUES(NULL,'李四', 2000);

-- seller表
CREATE TABLE `seller`(
seller_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
seller_name VARCHAR(32) NOT NULL DEFAULT '',
seller_money DOUBLE NOT NULL DEFAULT 0.0
)CHARSET=utf8 ;

INSERT INTO `seller` VALUES(NULL,'賣家1', 0);
INSERT INTO `seller` VALUES(NULL,'賣家2', 0);

-- goods表
CREATE TABLE `goods`(
goods_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
goods_name VARCHAR(32) NOT NULL DEFAULT '',
price DOUBLE NOT NULL DEFAULT 0.0,
seller_id INT UNSIGNED,
goods_num INT UNSIGNED DEFAULT 0
)CHARSET=utf8 ;

INSERT INTO `goods` VALUES(NULL,'小風扇', 10.00, 1, 100);
INSERT INTO `goods` VALUES(NULL,'小臺燈', 12.00, 1, 100);
INSERT INTO `goods` VALUES(NULL,'可口可樂', 3.00, 2, 100);

-- taoBao表
CREATE TABLE `taoBao`(
taoBao_money DOUBLE NOT NULL DEFAULT 0.0
)CHARSET=utf8 ;

INSERT INTO `taoBao` VALUES(0);

image-20230201213831975 image-20230201213852126

image-20230201214739346 image-20230201213910704

2.ShopDao

package com.li.tx.hw.dao;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

/**
 * @author 李
 * @version 1.0
 */
@Repository
public class ShopDao {
    @Resource
    private JdbcTemplate jdbcTemplate;

    //通過商品id,查詢商品價格
    public Double queryGoodsPrice(int goodsId) {
        String sql = "SELECT price FROM goods WHERE goods_id=?";
        return jdbcTemplate.queryForObject(sql, Double.class, goodsId);
    }

    //通過商品id,查詢商品所屬的賣家id
    public Integer queryGoodsOwner(int goodsId) {
        String sql = "SELECT seller_id FROM goods WHERE goods_id=?";
        return jdbcTemplate.queryForObject(sql, Integer.class, goodsId);
    }

    //通過商品id,修改商品庫存量
    public void updateGoodsNum(int goodsId, int shopNum) {
        String sql = "UPDATE goods SET goods_num=goods_num-? WHERE goods_id=?";
        jdbcTemplate.update(sql, shopNum, goodsId);
    }

    //通過買家id,修改買家餘額
    public void updateBuyerMoney(Integer buyerId, Double money) {
        String sql = "UPDATE buyer SET buyer_money=buyer_money-? WHERE buyer_id=?";
        jdbcTemplate.update(sql, money, buyerId);
    }

    //通過賣家id,修改賣家餘額
    public void updateSellerMoney(Integer sellerId, Double money) {
        String sql = "UPDATE seller SET seller_money=seller_money+? WHERE seller_id=?";
        jdbcTemplate.update(sql, money, sellerId);
    }

    //修改 taoBao餘額
    public void updateTaobaoMoney(Double money) {
        String sql = "UPDATE taoBao SET taoBao_money=taoBao_money+?";
        jdbcTemplate.update(sql, money);
    }
}

3.ShopService

package com.li.tx.hw.service;

import com.li.tx.hw.dao.ShopDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @author 李
 * @version 1.0
 */
@Service
public class ShopService {
    @Resource
    private ShopDao shopDao;

    @Transactional
    public void shopping(int buyerId, int goodsId, int goodsNum) {
        System.out.println("用戶購買信息 buyerId=" + buyerId
                + " goodsId=" + goodsId + " 購買數量=" + goodsNum);

        //查詢商品價格
        Double goodsPrice = shopDao.queryGoodsPrice(goodsId);
        System.out.println("商品價格=" + goodsPrice);

        //查詢商品賣家
        Integer sellerId = shopDao.queryGoodsOwner(goodsId);
        System.out.println("商品所屬賣家=" + sellerId);

        //減少商品庫存量
        shopDao.updateGoodsNum(goodsId, goodsNum);
        System.out.println("商品庫存-" + goodsNum);

        //修改買家餘額
        shopDao.updateBuyerMoney(buyerId, goodsPrice * goodsNum);
        System.out.println("買家餘額-" + goodsPrice * goodsNum);

        //將成交額的 90% 轉入賣家餘額
        shopDao.updateSellerMoney(sellerId, goodsPrice * goodsNum * 0.9);
        System.out.println("賣家餘額+" + goodsPrice * goodsNum * 0.9);

        //將成交額的 10% 轉入taoBao餘額
        shopDao.updateTaobaoMoney(goodsPrice * goodsNum * 0.1);
        System.out.println("taoBao餘額+" + goodsPrice * goodsNum * 0.1);

        System.out.println("購買成功...");
    }
}

4.配置容器文件

<!--配置要掃描的包-->
<context:component-scan base-package="com.li.tx.hw"/>

<!--引入外部的屬性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!--配置數據源對象-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSources">
    <property name="user" value="${jdbc.user}"/>
    <property name="password" value="${jdbc.pwd}"/>
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
</bean>

<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
    <property name="dataSource" ref="dataSources"/>
</bean>

<!--配置事務管理器對象
    1.DataSourceTransactionManager 這個對象是進行事務管理的
    2.一定要配置數據源屬性,即指定該事務管理器 是對哪個數據源進行事務控制
-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
      id="dataSourceTransactionManager">
    <property name="dataSource" ref="dataSources"/>
</bean>

<!--配置:啟用基於註解的聲明式事務管理功能-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

5.jdbc.properties

jdbc.user=root
jdbc.pwd=123456
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring

6.測試

@Test
public void shoppingTest() {
    ApplicationContext ioc =
            new ClassPathXmlApplicationContext("shopping_ioc.xml");
    ShopService shopService = ioc.getBean(ShopService.class);
    shopService.shopping(1, 1, 10);
}

測試結果:

image-20230201230806612

測試後的數據:

image-20230201231001496 image-20230201231023707

image-20230201231045358 image-20230201231105899

7.測試數據一致性:

修改sql,使其無法執行:

image-20230201232310918

測試結果:出現異常。

image-20230201232222624

查看資料庫表,數據沒有改變。說明事務進行了回滾。


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

-Advertisement-
Play Games
更多相關文章
  • 鑒於阮一峰老師的技術文章,在此做一個轉載記錄。轉戰react技術一年,希望在技術上可以不斷精進,日後成為一位大牛! 引子:《準備工作》 知識準備 環境準備 第一講:《前端開發的歷史和趨勢》 前端開發的歷史演變 前端MVC框架的興起 前後端分離 全棧工程師 前端開發的未來 第二講:《React 技術棧 ...
  • 前言 先說幾句廢話,本人是一名 web 後端開發,主語言是 java,在學 Electron 之前,只會一點點 HTML和 JavaScript。本文講的也是我學習 Electron 的過程,而非教程,請酌情參考。 Electron是什麼 Electron是一個使用 JavaScript、HTML ...
  • 隨著業務變化的速度越來越快各類IT系統的建設也越來越複雜大規模研發團隊的管理問題日益突出如何提升研發效能成為時下各類技術團隊面臨的重要挑戰 京東雲DevOps專家將帶您深入研發一線揭秘支撐京東集團萬人級研發管理的行雲DevOps平臺 分享企業應該如何規劃DevOps落地與演進 嘉賓介紹 孫長虹 京東 ...
  • 物流合約中心是京東物流合同管理的唯一入口。為商家提供合同的創建,蓋章等能力,為不同業務條線提供合同的定製,歸檔,查詢等功能。由於各個業務條線眾多,為各個業務條線提供高可用查詢能力是物流合約中心重中之重。同時計費系統在每個物流單結算時,都需要查詢合約中心,確保商家簽署的合同內容來保證計費的準確性。 ...
  • 名單服務是風控架構中重要子域,對風險決策的性能、用戶體驗、成本管控、風險治理沉澱都有重要影響,本文將詳細介紹名單服務設計思路和實現。 ...
  • 深度學習在很大程度上影響了遙感影像分析領域的研究。然而,大多數現有的遙感深度模型都是用ImageNet預訓練權重初始化的,其中自然圖像不可避免地與航拍圖像相比存在較大的域差距,這可能會限制下游遙感場景任務上的微調性能。 ...
  • 【前置內容】Spring 學習筆記全系列傳送門: Spring學習筆記 - 第一章 - IoC(控制反轉)、IoC容器、Bean的實例化與生命周期、DI(依賴註入) Spring學習筆記 - 第二章 - 註解開發、配置管理第三方Bean、註解管理第三方Bean、Spring 整合 MyBatis 和 ...
  • 簡介 在文章《Apache Beam入門及Java SDK開發初體驗》中大概講了Apapche Beam的簡單概念和本地運行,本文將講解如何把代碼運行在GCP Cloud Dataflow上。 本地運行 通過maven命令來創建項目: mvn archetype:generate \ -Darche ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...