Django中事务的基本使用

1. Django事务处理

handlebars 复制代码
事务(Transaction): 是一种将多个数据库操作组合成一个单一工作单元的机制.
如果事务中的所有操作都成功完成, 则这些更改将永久保存到数据库中.
如果事务中的某个操作失败, 则整个事务将回滚到事务开始前的状态, 所有的更改都不会被保存到数据库中.
这对于保持数据的一致性和完整性非常重要.
handlebars 复制代码
大多数数据库系统默认处于自动提交模式下, 
这意味着每个单独的数据库操作(如: INSERT, UPDATE, DELETE)都会立即被提交到数据库, 成为永久更改.
在这种模式下, 不需要显式地开始和提交事务, 因为每个操作都被视为一个独立的事务.

自增ID与回滚: 
当事务中包含插入操作, 并且这些插入操作分配了自增ID, 如果事务被回滚, 则这些已分配的自增ID会被撤销, 它们会'重置'回之前的值.
这意味着, 事务回滚, 数据库的自增计数器(或称为序列)也会回退到之前的状态.
handlebars 复制代码
Django默认情况下会开启事务, 但这里的'开启事务'指的是Django在模型(Model)层面对数据库操作的自动管理.
Django的默认事务行为是自动提交(autocommit)模式, 即每当执行数据库写操作(如save(), delete()等)时,
Django会自动将这些操作提交到数据库, 这与很多数据库默认的自动提交事务设置相似.

这意味着, 如果在一个视图中执行了: MyModel.objects.create(name='object 1'), 那么这条记录会立即被写入数据库,
并且不会因为这个视图中的后续操作失败而自动回滚.
handlebars 复制代码
以下是一个完整的示例, 展示了Django ORM操作数据库的默认行为:
handlebars 复制代码
* 1. 在的Django应用的models.py文件中定义一个简单的模型.
python 复制代码
# index/models.py  
from django.db import models  
  
class MyModel(models.Model):  
    name = models.CharField(max_length=100)  
  
    def __str__(self):  
        return self.name
    
handlebars 复制代码
* 2. 运行: python manage.py makemigrations 和 python manage.py migrate 来创建数据库表.
handlebars 复制代码
* 3. 可以在配置文件开启ORM日志查看对数据库的操作命令.
python 复制代码
# settings.py

# ORM日志配置
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,

    # 处理程序
    'handlers': {
        # 定义一个日志处理程序, 日志级别为DEBUG, 信息在终端展示
        'console': {
            'level': 'DEBUG',  # 日志级别
            'class': 'logging.StreamHandler',  # 消息发送到标准输出
        },
    },

    # 日志记录器
    'loggers': {
        # Django数据库后端的日志记录
        'django.db.backends': {
            'handlers': ['console'],  # 使用的日志处理程序(前面定义的console)
            'propagate': True,
            'level': 'DEBUG',
        },
    }
}
handlebars 复制代码
* 4. 在urls.py文件中添加一个URL配置:
python 复制代码
# MyDjango/urls.py  
from django.urls import path  
from index import views  
  
urlpatterns = [  
    path('', views.my_view, name='my_view'),  
]
handlebars 复制代码
* 5. 编写一个视图函数, 创建一个数据实例, 然后抛出异常.
python 复制代码
# index/urls.py
from django.shortcuts import HttpResponse
from .models import MyModel  
  
def my_view(request):  
    try:  
        MyModel.objects.create(name='object 1')  
        # 手动触发异常  
        raise ValueError("数据库出错了!")  
        
    # 异常捕获
    except ValueError as e:  
        return HttpResponse(f"{e}")
handlebars 复制代码
* 6. 启动项目, 访问: 127.0.0.1:8000 , 查看index_mymodel表数据.
sql 复制代码
# ORM日志(Django 4.2.14):
(0.016) 
INSERT INTO "index_mymodel" ("name") VALUES ('object 1') RETURNING "index_mymodel"."id"; 
args=('object 1',); alias=default

# 事务将自动自动提交, 这是数据库的默认行为.
handlebars 复制代码
SQL语句是用于向数据库中插入一条新记录, 并希望立即获取该记录的自增主键(ID).
这个语句特别适用于支持RETURNING子句的数据库系统, 如PostgreSQL.
在SQLite中, 从版本3.35开始也支持RETURNING子句, 但在早期版本中则不支持.
MySQL则使用不同的方法(如LAST_INSERT_ID()函数)来获取最后插入记录的自增ID.

2. transaction模块

2.1 模块介绍

handlebars 复制代码
在Django框架中, transaction模块是用于管理数据库事务的关键组件.
事务是确保数据库操作原子性, 一致性, 隔离性和持久性(ACID特性)的重要机制.
Django通过transaction模块提供了灵活的事务管理功能, 允许开发者在应用程序中根据需要手动控制事务的边界.

注意: 虽然Django ORM提供了对事务的支持,但具体的事务行为(如隔离级别, 持久性等)会根据使用的数据库后端而有所不同.
handlebars 复制代码
Django的ORM通过django.db.transaction模块提供了对数据库事务的细粒度控制.
默认情况下, Django在每个HTTP请求结束时自动提交事务, 这意味着在单个视图函数或中间件中执行的所有数据库操作(如模型的保存, 删除等)都将作为单个事务处理, 除非在代码中显式地进行了事务管理.

transaction模块的主要功能和用法包括:
* 1. 手动提交和回滚.
     尽管Django鼓励使用transaction.atomic()进行事务管理. 
     但在某些特殊情况下, 开发者可能需要手动控制事务的提交和回滚.
     这可以通过调用transaction.commit()和transaction.rollback()函数来实现, 
     但通常不推荐这样做, 因为这样做可能会破坏Django的自动事务管理机制.
* 2. 手动事务管理.
     适用于需要更细粒度控制事务的场景.
     Django提供了transaction.atomic()上下文管理器.
     当代码块被transaction.atomic()装饰或包裹时, Django会确保该代码块内的所有数据库操作都在同一个事务中执行.
     如果代码块内发生异常, 事务将自动回滚; 否则, 事务将自动提交.
* 3. 自动事务管理.
     Django的TransactionMiddleware中间件默认在每个HTTP请求开始时自动开启一个事务, 并在请求结束时提交该事务.
     如果请求处理过程中发生异常, 则事务会被回滚. 这种机制简化了大多数Web应用中的事务管理.
     在Django 1.9及之前版本中是自动加载的, 但在后续版被移除. 不过可以通过数据库配置的ATOMIC_REQUESTS属性开启这个功能.
     如果想要为某个特定的视图消这种自动事务行为, 可以使用@transaction.non_atomic_requests装饰视图.
* 4. 事务钩子: Django还提供transaction.on_commit()函数, 允许开发者注册在事务提交时执行的回调函数.
     这对于在事务提交后执行某些清理工作或异步任务非常有用.
* 5. 保存点(Savepoints): 在一些复杂的场景中, 开发者可能需要在事务中设置保存点(avepoints),
     以便在发生错误时能够回滚到事务中的某个特定点, 而不是整个事务.
     然而, Django的transaction模块并不直接支持保存点操作, 这通常需要通过数据库底层的API来实现.

2.2 手动提交或回滚

handlebars 复制代码
transaction.set_autocommit()方法: 用于设置自动提交模式的开启与关闭.
transaction.rollback()方法: 用于回滚当前事务.

注意: 
* Django的默认事务行为是自动提交(autocommit)模式, 可以使用transaction.set_autocommit(False)关闭自动提交模式.
* 一旦提交事务, 数据将永久保存到数据库中, 并且不能通过transaction.rollback()来回滚这些更改.
* Django请求结束transaction.set_autocommit会被设置为True, 恢复自动提交.
handlebars 复制代码
以下是一个使用MyModel模型手动控制事务提交和回滚的例子:
* 1. 修改views.py文件, 在视图任何适当的地方, 可以手动控制事务:
python 复制代码
# index/views.py
from django.shortcuts import HttpResponse
from django.db import transaction
from .models import MyModel


def my_view(request):
    try:
        # 关闭自动提交行为
        transaction.set_autocommit(False)

        # 往数据中插入数据
        MyModel.objects.create(name='object 2')

        # 如果满足某些条件则提交事务
        # transaction.commit()

        # 这里手动抛出异常
        raise Exception("未知错误!")

    except Exception as e:
        # 回滚事务, 撤销插入数据的操作
        transaction.rollback()

        # 处理异常或记录错误
        return HttpResponse("由于错误, 事务回滚: {}".format(e))

    finally:
        # 恢复自动提交(这通常在Django请求结束时自动发生, 但在这里显式调用以保持一致性)
        transaction.set_autocommit(True)
handlebars 复制代码
* 2. 启动项目, 访问: 127.0.0.1:8000 , 查看index_mymodel表数据.
sql 复制代码
# ORM日志:
(0.015)
INSERT INTO "index_mymodel" ("name") VALUES ('object 2') RETURNING "index_mymodel"."id"; 
args=('object 2',); alias=default
# 回滚
(0.000)
ROLLBACK; args=None; alias=default

2.3 手动事务管理

handlebars 复制代码
transaction.atomic()是Django ORM提供的一个上下文管理器(context manager).
它用于确保数据库操作的原子性, 当将数据库操作(如插入, 更新, 删除等)包裹在transaction.atomic()块中时,
这些操作要么全部成功, 要么在发生错误时全部回滚, 从而保持数据的一致性.
总结: transaction.atomic块如果程序正常退出, 则自动提交事务, 否则回滚.

手动事务控制的两种使用方式如下:
python 复制代码
# with语句用法(推荐):
from django.db import transaction


def my_view(request):
    try:
        # 以下数据库操作将在同一个事务中执行  
        with transaction.atomic():
            # 执行一些数据库操作  
            pass
    except Exception as e:
        # 如果发生异常, 事务将自动回滚自动调用transaction.rollback()  
        raise

    # 事务已经提交或回滚, 这里可以安全地继续执行其他操作
    # 返回响应  
    return render(request, 'my_template.html')
python 复制代码
# 装饰器用法(不适用于代码块或单独的语句, 如果函数内部有多个独立的操作需要单独控制事务, 使用装饰器可能会变得不够灵活): 
from django.db import transaction  

  
@transaction.atomic  
def my_view(request):  
    # 以下所有数据库操作将在同一个事务中执行, 如果所有操作都成功, 则事务自动提交  
    # transaction.atomic()装饰器会在遇到异常时自动回滚事务  
  
    # 返回响应  
    return render(request, 'my_template.html')
handlebars 复制代码
* 1. 修改views.py文件, 使用transaction.atomic()在视图中定义一个事务的边界,
     在边界内创建一个模型实例, 并在发生异常时自动回滚事务.
python 复制代码
# index/views.py  
from django.db import transaction
from .models import MyModel


def my_view(request):
    # 创建一个事务的边界
    with transaction.atomic():
        # 执行数据库操作
        MyModel.objects.create(name='object 2')
        # 抛出一个异常来测试回滚, 会自动捕获异常, 执行事务回滚
        raise Exception("测试事务回滚!")
handlebars 复制代码
* 2. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
sql 复制代码
# ORM日志:
(0.016)
INSERT INTO "index_mymodel" ("name") VALUES ('object 2') RETURNING "index_mymodel"."id"; 
args=('object 2',); alias=default

-- 在这期间尝试通过另一个数据库连接看数据库, 是看不到object 2, 除非提交了这个事务, 不然会破坏事务的原子性.

# 回滚
(0.000)
ROLLBACK; args=None; alias=default
handlebars 复制代码
隔离级别: 数据库事务的隔离级别决定了事务之间如何相互影响.
不同的隔离级别(如: 读未提交, 读已提交, 可重复读, 串行化)会影响你看到的数据状态.
例如, 在高隔离级别下(如可重复读或串行化), 当插入数据时, 其他用户可能暂时看不到这些新插入的数据, 直到当前事务提交.
handlebars 复制代码
* 3. 如果想要优雅的停止程序不出现异常, 可以使用try语句捕获transaction.atomic()块中的异常.
python 复制代码
# index/views.py  
from django.shortcuts import HttpResponse
from django.db import transaction
from .models import MyModel


def my_view(request):
    try:
        # 创建一个事务的边界
        with transaction.atomic():
            # 执行数据库操作
            MyModel.objects.create(name='object 2')
            # 抛出一个异常来测试回滚, 会自动捕获异常, 执行事务回滚
            raise Exception("测试事务回滚!")

    except Exception as e:
        # 现在可以安全地返回一个包含错误信息的响应
        return HttpResponse(f"出现错误, 原因: {e}")
handlebars 复制代码
* 4. 启动项目, 访问: http://127.0.0.1:8000 , transaction.atomic()块中的异常被捕获.
sql 复制代码
# ORM日志:
(0.000) I
NSERT INTO "index_mymodel" ("name") VALUES ('object 2') RETURNING "index_mymodel"."id"; 
args=('object 2',); alias=default
# 回滚
(0.000) 
ROLLBACK; args=None; alias=default

2.4 注意事项

handlebars 复制代码
try...except与with transaction.atomic()语句块一起工作的详细解释: 
在with transaction.atomic(): 语句块中, 如果发生异常会导致Python退出该块, 这时Django会自动回滚事务.
如果try...except块定义在with transaction.atomic块中, 而数据库操作实是在try块中执行的.
如果try中捕获了异常并且没有重新抛出, 那么事务将不会被回滚, 因为从Python的角度来看, 异常已经被except块'处理'了.
handlebars 复制代码
* 1. 修改views.py, 代码如下:
python 复制代码
# index/views.py
from django.db import transaction
from .models import MyModel


def my_view(request):
    with transaction.atomic():
        try:
            # 执行数据库操作
            MyModel.objects.create(name='object 2')
            # 抛出一个异常来测试回滚
            raise Exception("测试事务回滚!")
        except Exception as e:
            print(f'异常信息{e}')
            # try中的异常被except捕获, 也应该在except中处理.
            # 这里不可以手动执行transaction.rollback() 会报错:事务管理错误
handlebars 复制代码
* 2. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
     如果try中捕获了异常并且没有重新抛出, 那么事务将不会被回滚.
     transaction.atomic块没捕获到异常会在结束时会自动提交事务.
sql 复制代码
# ORM日志:
(0.000) 
INSERT INTO "index_mymodel" ("name") VALUES ('object 2') RETURNING "index_mymodel"."id"; 
args=('object 2',); alias=default
#  transaction.atomic()语句结束时提交事务
(0.000)
COMMIT; args=None; alias=default
handlebars 复制代码
* 3. 修改views.py, 在except子句中抛出异常.
     想要继续触发回滚, 只能继续抛出异常, 被with transaction.atomic()语句捕获异常, 从而触发回滚.
python 复制代码
# index/views.py
from django.db import transaction
from .models import MyModel


def my_view(request):
    with transaction.atomic():
        try:
            # 执行数据库操作
            MyModel.objects.create(name='object 3')
            # 抛出一个异常来测试回滚
            raise Exception("测试事务回滚!")
        except Exception as e:
            print(f'异常信息{e}')
            # try中的异常被except捕获, 也应该在except中处理.
            # 这里不可以手动执行transaction.rollback() 会报错:事务管理错误

            # 想要继续触发回滚, 只能继续抛出异常, 被with transaction.atomic()捕获
            raise Exception(e)
        
handlebars 复制代码
* 4. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
     如果不想程序报错, 可在with transaction.atomic()块外层在套一个try, 让程序优雅的结束.
sql 复制代码
# ORM日志:
(0.000) INSERT INTO "index_mymodel" ("name") VALUES ('object 3') RETURNING "index_mymodel"."id";
args=('object 3',); alias=default
# 回滚
(0.000) ROLLBACK; args=None; alias=default

2.5 自动事务管理

handlebars 复制代码
在Django中, ATOMIC_REQUESTS(原子性请求)是一个数据库设置选项,
它会在每个HTTP请求开始时自动开启一个数据库事务, 并在请求结束时提交或回滚该事务.
handlebars 复制代码
* 1. 在配置文件的添加ATOMIC_REQUESTS(原子性请求)属性:
python 复制代码
# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
        'ATOMIC_REQUESTS': True
    }
}
handlebars 复制代码
* 2. 修改上面示例的视图函数:
python 复制代码
# index的views.py
from index.models import MyModel


def my_view(request):
    # 执行数据库操作
    MyModel.objects.create(name='object 3')
    # 抛出一个异常测试回滚
    raise Exception("操作出错")
handlebars 复制代码
* 3. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
sql 复制代码
# ORM日志:
(0.000)
INSERT INTO "index_mymodel" ("name") VALUES ('object 3') RETURNING "index_mymodel"."id"; 
args=('object 3',); alias=default
# 回滚
(0.000) ROLLBACK; args=None; alias=default
handlebars 复制代码
* 4. 如果想要为某个特定的视图消这种自动事务行为, 可以使用@transaction.non_atomic_requests装饰视图.
python 复制代码
# index的views.py
from index.models import MyModel
from django.db import transaction


@transaction.non_atomic_requests
def my_view(request):
    # 执行数据库操作
    MyModel.objects.create(name='object 4')
    # 抛出一个异常测试回滚
    raise Exception("操作出错")
handlebars 复制代码
* 5. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
sql 复制代码
# ORM日志:
(0.000)
INSERT INTO "index_mymodel" ("name") VALUES ('object 4') RETURNING "index_mymodel"."id"; 
args=('object 3',); alias=default

# 事务将自动自动提交, 这是数据库的默认行为.

2.6 事务钩子

handlebars 复制代码
transaction.on_commit()是Django提供的一个非常有用的函数, 
它允许注册一个回调函数, 该回调函数将在当前数据库事务成功提交后执行.
这对于在事务完成后执行清理工作, 发送通知, 触发异步任务等场景特别有用.
handlebars 复制代码
下面是一个使用transaction.on_commit()的例子:
* 1. 在视图或任何数据库操作函数中, 可以使用transaction.on_commit()来注册一个任务, 它会在事务提交后执行:
python 复制代码
# index/views.py
from django.http import HttpResponse
from django.db import transaction
from .models import MyModel


def my_view(request):
    # 在数据库事务中创建一个新的 MyModel 实例
    with transaction.atomic():
        MyModel.objects.create(name="object 5")

        # 使用 @transaction.on_commit 装饰器来注册一个回调函数
        # 该回调函数将在当前事务提交后执行
        @transaction.on_commit
        def send_email_after_commit():
            # 这里发送一封电子邮件
            # 注意:在实际应用中,你应该使用更安全的方式来处理敏感信息,比如使用 Django 的 EmailBackend
            print("交易提交后发送的电子邮件.")

        # 手动异常
        # raise ValueError("数据库出错了!")

        # send_email_after_commit 函数会在当前数据库事务提交后被自动调用

    return HttpResponse("记录已创建, 交易提交后将发送电子邮件.")
handlebars 复制代码
* 2. 启动项目, 访问: http://127.0.0.1:8000 , 查看事务钩子.
sql 复制代码
# ORM日志:
(0.000) INSERT INTO "index_mymodel" ("name") VALUES ('object 5') RETURNING "index_mymodel"."id";
args=('object 5',); alias=default
# 提交事务
(0.016) COMMIT; args=None; alias=default
handlebars 复制代码
* 3. 修改代码, 在with transaction.atomic()块中手动抛出异常, 事务边界块捕获到异常后会执行回滚操作.
python 复制代码
# index/views.py
from django.db import transaction
from .models import MyModel


def my_view(request):
    # 在数据库事务中创建一个新的 MyModel 实例
    with transaction.atomic():
        MyModel.objects.create(name="object 6")

        # 使用 @transaction.on_commit 装饰器来注册一个回调函数
        # 该回调函数将在当前事务提交后执行
        @transaction.on_commit
        def send_email_after_commit():
            # 这里发送一封电子邮件
            # 注意:在实际应用中,你应该使用更安全的方式来处理敏感信息,比如使用 Django 的 EmailBackend
            print("交易提交后发送的电子邮件.")

        # 触发异常, 事务回滚, 事务钩子函数不会被触发
        raise ValueError("数据库出错了!")
handlebars 复制代码
* 4. 重新启动项目, 访问: http://127.0.0.1:8000 , 查看事务钩子的执行情况.
     在没有提交事务的时候不会被触发钩子函数.
sql 复制代码
# ORM日志
(0.016) INSERT INTO "index_mymodel" ("name") VALUES ('object 6') RETURNING "index_mymodel"."id";
args=('object 6',); alias=default
# 回滚事务
(0.000) ROLLBACK; args=None; alias=default

2.7 保存点

handlebars 复制代码
在Django的数据库事务管理中, transaction.savepoint()是一个用于在事务中创建保存点的函数.
保存点允许在事务中设置一个点, 之后可以回滚到这个点, 而不是回滚整个事务.
这对于复杂的数据库操作非常有用, 尤其是当需要在事务的某个特定部分失败时能够恢复状态, 而不是放弃整个事务.
handlebars 复制代码
下面是一个使用ransaction.savepoint的例子, 演示保存点的使用.
* 1. 修改views.py文件中使用transaction.atomic()装饰器来装饰一个视图函数, 为其开启事务功能.
     在该视图函数中创建一个保存点, 并创建两个模型实例, 最后在发生异常时能够回滚事务.
python 复制代码
# index/views.py
from django.shortcuts import HttpResponse
from index.models import MyModel
# 导入事务
from django.db import transaction


# 以装饰器形式开启事务
@transaction.atomic
def my_view(request):
    # 开启事务(创建一个保存点), 调用 transaction.savepoint() 时, 会返回一个表示该保存点的对象或标识符
    sid = transaction.savepoint()
    try:
        # 执行一些数据库操作
        MyModel.objects.create(name='object 6')
        # 假设这里需要基于某些条件来决定是否回滚到保存点
        raise Exception("需要回滚到保存点")
    except Exception as e:
        # 事务回滚
        transaction.savepoint_rollback(sid)
        print(f'操作失败, 原因为: {e}')

    return HttpResponse('保存点测试!')
handlebars 复制代码
* 2. 启动项目, 访问: http://127.0.0.1:8000 , 查看保存点效果.
sql 复制代码
# ORM日志:
(0.000) BEGIN; args=None; alias=default  # 标记一个事务的开始
(0.000) SAVEPOINT "s5148_x1"; args=None; alias=default  # 创建保存点
(0.016) INSERT INTO "index_mymodel" ("name") VALUES ('object 6') RETURNING "index_mymodel"."id"; ...
(0.000) ROLLBACK TO SAVEPOINT "s5148_x1"; args=None; alias=default  # 恢复保存点
(0.000) COMMIT; args=None; alias=default  # 提交事务
handlebars 复制代码
注意事项: 在一个全局事务的上下文中工作时, 再使用@transaction.atomic装饰器来包裹特定的代码块,
Django的ORM会在这个@transaction.atomic块开始时自动创建一个保存点(savepoint).

不管代码块正常还是异常结果都是释放保存点提交事务...
sql 复制代码
# ORM日志:
(0.000) BEGIN; args=None; alias=default  # 标记一个事务的开始
(0.000) SAVEPOINT "s8456_x1"; args=None; alias=default  # 创建保存点
(0.000) INSERT INTO "index_mymodel" ("name") VALUES ('object 6') RETURNING "index_mymodel"."id"; ...
(0.000) RELEASE SAVEPOINT "s8456_x1"; args=None; alias=default  # 释放保存点
(0.000) COMMIT; args=None; alias=default  # 提交事务
handlebars 复制代码
拿不到保存点, 觉得好玩就去修改源码, 想办法在视图中获取这个保存点,,,  
相关推荐
小汤猿人类7 分钟前
open Feign 连接池(性能提升)
数据库
阳冬园28 分钟前
mysql数据库 主从同步
数据库·主从同步
Mr.132 小时前
数据库的三范式是什么?
数据库
Cachel wood2 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Python之栈2 小时前
【无标题】
数据库·python·mysql
风_流沙2 小时前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch
亽仒凣凣2 小时前
Windows安装Redis图文教程
数据库·windows·redis
亦世凡华、2 小时前
MySQL--》如何在MySQL中打造高效优化索引
数据库·经验分享·mysql·索引·性能分析
YashanDB2 小时前
【YashanDB知识库】Mybatis-Plus调用YashanDB怎么设置分页
数据库·yashandb·崖山数据库
ProtonBase3 小时前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构