Spring中的事務

来源:http://www.cnblogs.com/zhangzongle/archive/2016/10/19/5978301.html
-Advertisement-
Play Games

Spring配置文件中關於事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分。 DataSource、 TransactionManager這兩部分只是會根據數據訪問方式有所變化,比如使用Hi ...


Spring配置文件中關於事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分。 DataSource、 TransactionManager這兩部分只是會根據數據訪問方式有所變化,比如使用Hibernate進行數據訪問時,DataSource實際為 SessionFactory,TransactionManager的實現為HibernateTransactionManager。

一.事務的4個特性:

原子性:一個事務中所有對資料庫的操作是一個不可分割的操作序列,要麼全做,要麼全部做。 一致性:數據不會因為事務的執行而遭到破壞。
隔離性:一個事務的執行,不受其他事務(進程)的干擾。既併發執行的個事務之間互不幹擾。
持久性:一個事務一旦提交,它對資料庫的改變將是永久的。

二:Spring事務的隔離級別 

1. ISOLATION_DEFAULT: 這是一個PlatfromTransactionManager預設的隔離級別,使用資料庫預設的事務隔離級別.
      另外四個與JDBC的隔離級別相對應
 2. ISOLATION_READ_UNCOMMITTED: 這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的數據。
      這種隔離級別會產生臟讀,不可重覆讀和幻像讀。
 3. ISOLATION_READ_COMMITTED: 保證一個事務修改的數據提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據
 4. ISOLATION_REPEATABLE_READ: 這種事務隔離級別可以防止臟讀,不可重覆讀。但是可能出現幻像讀。
      它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下麵的情況產生(不可重覆讀)。
 5. ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。
      除了防止臟讀,不可重覆讀外,還避免了幻像讀。

什麼是臟數據,臟讀,不可重覆讀,幻覺讀?
 臟讀: 指當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交到資料庫中,這時,
         另外一個事務也訪問這個數據,然後使用了這個數據。因為這個數據是還沒有提交的數據, 那麼另外一
         個事務讀到的這個數據是臟數據,依據臟數據所做的操作可能是不正確的。
    
 不可重覆讀: 指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。
         那麼,在第一個事務中的兩次讀數據之間,由於第二個事務的修改,那麼第一個事務兩次讀到的數據
         可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱為是不可重覆讀。
            
 幻覺讀: 指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及
         到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,
         以後就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。

三:Spring事務類型詳解:

PROPAGATION_REQUIRED--支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。

PROPAGATION_SUPPORTS--支持當前事務,如果當前沒有事務,就以非事務方式執行。

PROPAGATION_MANDATORY--支持當前事務,如果當前沒有事務,就拋出異常。

PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。

PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則拋出異常。

PROPAGATION_NESTED--如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。

四.事務的實現方式:

實現方式共有兩種:編碼方式;聲明式事務管理方式。
基於AOP技術實現的聲明式事務管理,實質就是:在方法執行前後進行攔截,然後在目標方法開始之前創建並加入事務,執行完目標方法後根據執行情況提交或回滾事務。
聲明式事務管理又有兩種方式:基於XML配置文件的方式;另一個是在業務方法上進行@Transactional註解,將事務規則應用到業務邏輯中。

五.實現事務的案例

下麵模擬一個用戶(Account)用錢來買股票(Stock),當用戶出錢買股要是錯誤,就需要我們的事務了。

源碼介紹(Spring框架基本jar包全的情況下):

1.首先我們要用到事務的jar包,我用的是:

   spring-tx-4.2.0.RELEASE.jar

2.Account.java

package cn.tx.entity;
/**
 * 賬戶
 * @author zhangzong
 *
 */
public class Account {
    
    private Integer aid;//賬戶編號
    
    private String aname;//用戶姓名
    
    private int balance;//賬戶金額

    public Integer getAid() {
        return aid;
    }

    public void setAid(Integer aid) {
        this.aid = aid;
    }

    public String getAname() {
        return aname;
    }

    public void setAname(String aname) {
        this.aname = aname;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

}
View Code

3.Stock.java

package cn.tx.entity;
/**
 * 股票類
 * @author zhangzong
 *
 */
public class Stock {
    
    private Integer sid;//股票編號
    
    private String sname;//股票名稱
    
    private int count;//股數

    public Integer getSid() {
        return sid;
    }

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

    public String getSname() {
        return sname;
    }

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

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

}
View Code

4.AccountDao.java

package cn.tx.dao;
//賬戶介面
import cn.tx.entity.Account;

public interface AccountDao {
    
    /**
     * 新增賬戶
     * @param account
     * @return
     */
    public int createAccount(Account account);
    
    /**
     * 對賬戶的操作(錢買股,股收錢)
     * @param id 賬戶的編號
     * @param money 發費的錢財
     * @param isYesOrNo 是買股,還是收錢
     * @return 受影響的行數
     */
    public int updateBalance(int id,int money,boolean isYesOrNo);
    
    /**
     * 根據編號查詢餘額
     * @param id 編號
     * @return 餘額
     */
    public int selectOfBalance(int id);

}
View Code

5.StockDao.java

package cn.tx.dao;
//股票介面
import cn.tx.entity.Stock;

public interface StockDao {
    
    /**
     * 創建股票
     * @param stock 股票對象
     * @return 受影響行數
     */
    public int createStock(Stock stock);
    
    /**
     * 對股票的操作(錢買股,股收錢)
     * @param id 股票編號
     * @param num 變化的數量
     * @param isYesOrNo 是買股,還是收錢
     * @return 受影響的行數
     */
    public int updateCount(int id,int num,boolean isYesOrNo);

}
View Code

6.AccountDaoImpl.java

package cn.tx.dao.impl;
//實現了AccountDao介面的實現類
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import cn.tx.dao.AccountDao;
import cn.tx.entity.Account;
//JdbcDaoSupport是JDBC數據訪問對象的超類
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    @Override
    public int createAccount(Account account) {
        String sql = "insert into account(aname,balance) values(?,?)";
        int count = this.getJdbcTemplate().update(sql, account.getAname(),
                account.getBalance());
        return count;
    }

    @Override
    public int updateBalance(int id, int money, boolean isBuyOrNot) {
        String sql = null;
        // isBuyOrNot 為真,金額減少
        if (isBuyOrNot) {
            sql = "update account set balance=balance-? where aid=?";
        } else {
            sql = "update account set balance=balance+? where aid=?";
        }
        int count = this.getJdbcTemplate().update(sql, money, id);
        return count;
    }

    @Override
    public int selectOfBalance(int id) {
        String sql = "select balance from account where aid=?";
        Double money = this.getJdbcTemplate().queryForObject(sql,
                new Object[] { id }, Double.class);
        return money.intValue();

    }

}
View Code

7.StockDaoImpl.java

package cn.tx.dao.impl;
//實現了StockDao介面的實現類
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import cn.tx.dao.StockDao;
import cn.tx.entity.Stock;
//JdbcDaoSupport是JDBC數據訪問對象的超類
public class StockDaoImpl extends JdbcDaoSupport implements StockDao {

    @Override
    public int createStock(Stock stock) {
        String sql="insert into Stock(sname,count) values(?,?)";
        int count = this.getJdbcTemplate().update(sql,stock.getSname(),stock.getCount());
        return count;
    }

    @Override
    public int updateCount(int id, int num, boolean isYesOrNo) {
        String sql=null;
        if (isYesOrNo) {
            sql="update Stock set count+=? where sid=?";
        }else {
            sql="update Stock set count-=? where sid=?";
        }
        int count = this.getJdbcTemplate().update(sql,num,id);
        return count;
    }

}
View Code

8.AccountService.java

package cn.tx.service;
//用戶操作業務介面(用錢買股)
import cn.tx.entity.Account;
import cn.tx.entity.Stock;
import cn.tx.util.StockException;

public interface AccountService {
    /**
     * 新增賬戶
     * @param account
     * @return
     */
    public int createAccount(Account account);
    
    /**
     * 對賬戶的操作(錢買股,股收錢)
     * @param id 賬戶的編號
     * @param money 發費的錢財
     * @param isYesOrNo 是買股,還是收錢
     * @return 收影響的行數
     */
    public int updateBalance(int id,int money,boolean isYesOrNo);
    /**
     * 創建股票
     * @param stock 股票對象
     * @return 受影響行數
     */
    public int createStock(Stock stock);
    /**
     * 對股票的操作(錢買股,股收錢)
     * @param id 股票編號
     * @param num 變化的數量
     * @param isYesOrNo 是買股,還是收錢
     * @return 受影響的行數
     */
    public int updateCount(int id,int num,boolean isYesOrNo);
    /**
     * 購買股票的方法
     * @param aid 賬戶編號
     * @param money 發費的金額
     * @param sid 股票的編號
     * @param num 所買股數
     * @throws StockException 
     */
    public void buyStock(int aid,int money,int sid,int num) throws StockException;
    /**
     * 根據編號查詢餘額
     * @param id 編號
     * @return 餘額
     */
    public int selectOfBalance(int id);
}
View Code

9.AccountServiceImpl.java

package cn.tx.service.impl;
//用戶操作實現類

import cn.tx.dao.AccountDao;
import cn.tx.dao.StockDao;
import cn.tx.entity.Account;
import cn.tx.entity.Stock;
import cn.tx.service.AccountService;
import cn.tx.util.StockException;

public class AccountServiceImpl implements AccountService {

    //植入賬戶介面
    private AccountDao adao;
    //植入股票介面
    private StockDao sdao;
    @Override
    public int createAccount(Account account) {
        // TODO Auto-generated method stub
        return adao.createAccount(account);
    }

    @Override
    public int updateBalance(int id, int money, boolean isYesOrNo) {
        // TODO Auto-generated method stub
        return adao.updateBalance(id, money, isYesOrNo);
    }
    @Override
    public int createStock(Stock stock) {
        // TODO Auto-generated method stub
        return sdao.createStock(stock);
    }
    @Override
    public int updateCount(int id, int num, boolean isYesOrNo) {
        // TODO Auto-generated method stub
        return sdao.updateCount(id, num, isYesOrNo);
    }
    @Override
    public int selectOfBalance(int id) {
        // TODO Auto-generated method stub
        return adao.selectOfBalance(id);
    }

    //@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,rollbackFor=StockException.class)
    public void buyStock(int aid,int money,int sid,int num) throws StockException{
         boolean isBuy=true;//預設為錢買股
        //更改賬戶信息
        adao.updateBalance(aid, money, isBuy);
        //模擬異常,如果沒錢或錢為負數
        if(adao.selectOfBalance(aid)<=0){
            throw new StockException("異常發生了。。。。。");
        }
        //更改股票信息
        sdao.updateCount(sid, num, isBuy);
    }

    public AccountDao getAdao() {
        return adao;
    }

    public void setAdao(AccountDao adao) {
        this.adao = adao;
    }

    public StockDao getSdao() {
        return sdao;
    }

    public void setSdao(StockDao sdao) {
        this.sdao = sdao;
    }    

}
View Code

10.StockException.java(製造的一個異常類)

package cn.tx.util;
/**
 * 構造一個檢查異常
 * @author zhangzong
 *
 */
public class StockException extends Exception {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public StockException() {
        super();
    }

    public StockException(String message) {
        super(message);
    }
    
}
View Code

11.applicationContext.xml(Spring的配置文件)---幾種事務的實現都在其中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 1.dao -->
    <bean id="accountDao" class="cn.tx.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="stockDao" class="cn.tx.dao.impl.StockDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 2.service -->
    <bean id="accountService" class="cn.tx.service.impl.AccountServiceImpl">
        <property name="adao" ref="accountDao"></property>
        <property name="sdao" ref="stockDao"></property>
    </bean>

    <!-- 3.c3p0數據源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!-- 4.註冊jdbc屬性 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!--******************************事務配置 ********************************* -->

    <!-- 註冊事務管理器 -->
    <bean id="mytxManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- *****************獲得事務代理************** -->

    <!--方法一: 事務自動代理 :此方法要結合註解使用,在AccountServiceImpl的buyStock上 -->    
    <!--<tx:annotation-driven transaction-manager="mytxManager"/> -->



    <!-- 方法二:TransactionProxyFactoryBean 生成事務代理 -->
    <!-- <bean id="stockServiceProxy"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="target" ref="accountService"></property>
        <property name="transactionManager" ref="mytxManager"></property>
        <property name="transactionAttributes">
            <props>  四種隔離級別 傳播屬性 required
                <prop key="create*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop>
                <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-StockException
                </prop>
            </props>
        </property>
    </bean> -->



    <!-- 方法三 : aop**************** -->
    <tx:advice id="txAdvice" transaction-manager="mytxManager">
        <tx:attributes>
            <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" />
            <tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED"
                rollback-for="StockException" />
        </tx:attributes>
    </tx:advice>
    <!-- aop配置 -->
    <aop:config>
        <aop:pointcut expression="execution(* *..service.*.*(..))"
            id="stockPointcut" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="stockPointcut"/>
    </aop:config>

</beans>
View Code

12.jdbc.properties(連接資料庫的配置)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\:///mybook
jdbc.username=root
jdbc.password=1234
View Code

13.log4j.properties

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c\:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=info, stdout
View Code

14.MyTest.java

package cn.tx.test;
//測試類
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.tx.entity.Account;
import cn.tx.entity.Stock;
import cn.tx.service.AccountService;
import cn.tx.util.StockException;

public class MyTest {
    //測試餘額
    @Test
    public void selectOfbalance(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        AccountService service = (AccountService) ctx.getBean("accountService");
        int balance = service.selectOfBalance(1);
        System.out.println(balance);
    }    
    @Test
    // 購買股票測試
    public void buyStockTest() throws StockException {

        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        AccountService service = (AccountService) ctx.getBean("accountService");
        // 花200 塊 買 2股
        service.buyStock(1, 200, 1, 2);
    }

    @Test
    // 開戶測試
    public void addData() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        AccountService service = (AccountService) ctx.getBean("accountService");
        // 銀行卡賬戶
        Account account = new Account();
        account.setAname("傻瞿亮");
        account.setBalance(1000);

        service.createAccount(account);

        Stock stock = new Stock();
        stock.setSname("腦殘教育");
        stock.setCount(5);

        service.createStock(stock);
    }

}
View Code

 


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

-Advertisement-
Play Games
更多相關文章
  • 一、 hibernate是什麼 (一)hibernate 是一個orm框架,orm (object relation mapping) 對象關係映射框架 o object -> 業務層(只對對象操作) r relation-> 關係資料庫 m mapping 對象關係映射文件 Hibernate有六 ...
  • 一、插入排序 1 #-*- coding:utf-8 -*- 2 ''' 3 描述 4 插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,演算法適用於少量數據的排序,時間複雜度為O(n^2)。 5 是穩定的排序方法。插入演算法把要排序的數組分成兩部分:第 ...
  • 轉載於:http://www.cnblogs.com/767355675hutaishi/p/3873770.html 題目大意:眾所周知冒泡排序演算法多數情況下不能只掃描一遍就結束排序,而是要掃描好幾遍。現在你的任務是求1~N的排列中,需要掃描K遍才能排好序的數列的個數模20100713。註意,不同 ...
  • 大家好啊,今天為大家帶來的是自己實現的用C++編寫的簡單進位轉換器,用於10進位數和8進位數,16進位數,2進位數的相互轉換. 首先,說明一下什麼是進位.n進位就是一種用來表示數值的方法,n進位,顧名思義,逢n進1.我們日常生活中使用的基本都是10進位數,逢10進1;現代電腦處理器所能處理的只能是 ...
  • 本章重點介紹java.lang.reflect包下的介面和類 當程式使用某個類時,如果該類還沒有被載入到記憶體中,那麼系統會通過載入,連接,初始化三個步驟來對該類進行初始化. 類的載入時指將類的class文件讀入記憶體,併為之創建一個java.lang.class對象,也就是說,當程式中使用任何類時,系 ...
  • 一、什麼是指針? 指針在百度的解釋:是編程語言中的一個對象,利用地址,它的值直接指向(points to)存在電腦存儲器中另一個地方的值。 也就是說,指針是用於指向某一記憶體單元。 簡而化之,指針便是地址。 二、聲明指針 1.方法:數據類型 *名稱 初始化 2.例如: 3.註意:指針聲明完一定要初始化 ...
  • JAVA Collections工具類sort()排序方法,對Comparable介面 Comparator介面簡述 ...
  • JAVA WEB 作用域 1、page屬性範圍(pageContext) a、在一個頁面設置的屬性,跳轉到其他頁面就無法訪問了(包括重定向和)。 示例:pageScopeDemo01.jsp <%@ page import="java.util.*" language="java" contentT ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...