当前位置: 首页 > news >正文

unity console日志双击响应事件扩展

1 对于项目中一些比较长的日志,比如前后端交互协议具体数据等,这些日志内容可能会比较长,在unity控制面板上查看不是十分方便,我们可以对双击事件进行扩展,将日志保存到一个文本中,然后用系统默认的文本查看工具查看这个日志
2 项目中如果用到了lua,当我们在控制台输出lua文件的时候,能不能双击日志用我们的代码编辑器软件打开日志输出位置

console日志双击然后用记事本打开显示

我们先用 OnOpenAsset 特性,重写双击回调方法,这个方法返回false,会继续执行系统默认日志双击打开方法,返回true则会中断。
我们想要的效果是用记事本打开这个特殊日志,但是不需要跳转到对应的代码输出位置
首先我们先在lua里面把日志输出方法重写一下,在日志输出的时候,添加一个标签 “”,当我们检测到双击的日志信息包含这个标签,则用记事本打开这一段日志(我们也可以把一些特殊的日志先在代码中缓存,在这个时候调用缓存读取那些特殊的日志进行输出显示)

function printLongLog(fmt, ...)local logText = string.format(fmt, ...)local debugInfo = debug.getinfo(2)local str = string.format("%s\n<open in file>", logText, debugInfo.short_src, debugInfo.currentline)local logMessage = concatStrs(str, debug.traceback("", 2))UnityEngine.Debug.Log(logMessage)
end

在这里插入图片描述

然后再C#代码中监听这一段日志

 [OnOpenAsset(0)]public static bool OnAsset(int instanceID, int line){string assetPath = AssetDatabase.GetAssetPath(instanceID);string fileExtension = Path.GetExtension(assetPath);string stackTrace = GetStackTrace();if (string.IsNullOrEmpty(stackTrace)){return false;}if (stackTrace.Contains("<open in file>")) //对于一些比较长的日志,添加标签<open in file>,双击的时候再文本中打开{openLog(stackTrace);return true;}return false;}

获取console里面的日志

private static string GetStackTrace(){var consoleWindowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");var fieldInfo = consoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);var consoleWindowInstance = fieldInfo.GetValue(null);if (null != consoleWindowInstance && (object)EditorWindow.focusedWindow == consoleWindowInstance){fieldInfo = consoleWindowType.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);string activeText = fieldInfo.GetValue(consoleWindowInstance).ToString();return activeText;}return "";}

用记事本打开日志文件,我们先把日志信息保存在本地,然后调用系统方法打开这个文件
这里用到了一个方法 System.Diagnostics.Process.Start(fileName),参数是文件的完整路径,调用系统默认方式打开改文件

private static void openLog(string stackTrace){string fileName = GetFileSaveName();string saveDirPath = Application.dataPath + "/../Debug";if (!Directory.Exists(saveDirPath)){Directory.CreateDirectory(saveDirPath);}string savePath = saveDirPath + "/" + fileName;File.WriteAllText(savePath, stackTrace);System.Diagnostics.Process.Start(savePath);}private static string GetFileSaveName(){DateTime currentTime = DateTime.Now;string customFormat = currentTime.ToString("yyyy_MM_dd_HH_mm_ss");string fileName = $"debug_{customFormat}.txt";return fileName;}

双击打开lua日志然后用lua编辑器定位到日志输出位置

对于lua输出的日志,我们为了便于打开可以用类似上面的方法重新封装一个lua日志输出方法,然后通关特殊标签的进行确定lua文件的名称和对应的行号。也可以进行拆分lua堆栈数据取到lua代码名称和对应堆栈。
添加标签方式的lua日志输出

function print(fmt, ...)local logText = string.format(fmt, ...)local debugInfo = debug.getinfo(2)local str = string.format("%s\n<filePath>%s</filePath><line>%s</line>", logText, debugInfo.short_src, debugInfo.currentline)local logMessage = concatStrs(str, debug.traceback("", 2))UnityEngine.Debug.Log(logMessage)
end

在这里插入图片描述

获取文件名称和行号

static bool OpenLua(string logText){//获取lua文件路径Regex regex = new Regex(@"<filePath>.*<\/filePath>");Match match = regex.Match(logText);if (!match.Success){return false;}string filePath = match.Groups[0].Value.Trim();int length = filePath.Length - 10 - 11; //去掉开头和结尾的字符串 <filePath>  </filePath>filePath = filePath.Substring(10, length);filePath = filePath.Replace(".", "/");if (!filePath.EndsWith(".lua")){filePath = filePath + ".lua";}//获取日志行号Regex lineRegex = new Regex(@"<line>.*<\/line>");match = lineRegex.Match(logText);if (!match.Success){return false;}string luaLineString = match.Groups[0].Value;luaLineString.Trim();length = luaLineString.Length - 6 - 7;luaLineString = luaLineString.Substring(6, length);int luaLine = int.Parse(luaLineString.Trim());return OpenFileAtLineExternal(filePath, luaLine);}

在这里插入图片描述

对于系统默认输出日志,我们通关拆分字符串方式获取

 static bool OpenLuaDefault(string logText){int index = logText.IndexOf("stack traceback:");string temp = logText.Substring(index, logText.Length - index);string[] arr = temp.Split(':');if (arr.Length < 3){return false;}string filePath = arr[1].Trim();int luaLine = int.Parse(arr[2]);filePath = filePath + ".lua";return OpenFileAtLineExternal(filePath, luaLine);}

有了文件名和行号,我们就可以通关调用VScode或者Idea编辑器软件,打开对应的文件并定位到指定的行

static void OpenFileWith(string fileName, int line){string editorPath = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);System.Diagnostics.Process proc = new System.Diagnostics.Process();proc.StartInfo.FileName = editorPath;string projectRootPath = EditorUserSettings.GetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY);if (string.IsNullOrEmpty(projectRootPath)){SetLuaProjectRoot();return;}string procArgument = "";if (editorPath.IndexOf("idea") != -1) //idea{procArgument = string.Format("{0} --line {1} {2}", projectRootPath, line, fileName);}else if (editorPath.IndexOf("Code.exe") != -1) // VSCode{string filePath = Path.Combine(projectRootPath, fileName);procArgument = string.Format("-g {0}:{1}:0", filePath, line);}else{procArgument = string.Format("{0}:{1}:0", fileName, line);}proc.StartInfo.Arguments = procArgument;proc.Start(); }

完整代码

lua 日志方法

function print(fmt, ...)local logText = string.format(fmt, ...)local debugInfo = debug.getinfo(2)local str = string.format("%s\n<filePath>%s</filePath><line>%s</line>", logText, debugInfo.short_src, debugInfo.currentline)local logMessage = concatStrs(str, debug.traceback("", 2))UnityEngine.Debug.Log(logMessage)
endfunction printLongLog(fmt, ...)local logText = string.format(fmt, ...)local debugInfo = debug.getinfo(2)local str = string.format("%s\n<open in file>", logText, debugInfo.short_src, debugInfo.currentline)local logMessage = concatStrs(str, debug.traceback("", 2))UnityEngine.Debug.Log(logMessage)
end

C#方法

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;public class ConsoleTools : MonoBehaviour
{public const string EXTERNAL_EDITOR_PATH_KEY = "mTv8";public const string LUA_PROJECT_ROOT_FOLDER_PATH_KEY = "obUd";[OnOpenAsset(0)]public static bool OnAsset(int instanceID, int line){string assetPath = AssetDatabase.GetAssetPath(instanceID);string fileExtension = Path.GetExtension(assetPath);// string fileName = Path.GetFileNameWithoutExtension(assetPath);if(fileExtension != ".cs" && fileExtension != ".lua"){return false; }string stackTrace = GetStackTrace();if (string.IsNullOrEmpty(stackTrace)){return false;}if (stackTrace.Contains("<open in file>")) //对于一些比较长的日志,添加标签<open in file>,双击的时候再文本中打开{openLog(stackTrace);return true;}if (stackTrace.Contains("stack traceback:")) //lua输出堆栈信息,会自带 “"stack traceback:” 这描述,这里用来筛选lua日志{bool isOpenSuccess = false;if (stackTrace.Contains("<filePath>")){isOpenSuccess = OpenLua(stackTrace);}else{isOpenSuccess = OpenLuaDefault(stackTrace);}return isOpenSuccess;}return false;}/// <summary>/// 通关日志堆栈获取日志信息/// </summary>/// <returns></returns>private static string GetStackTrace(){var consoleWindowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");var fieldInfo = consoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);var consoleWindowInstance = fieldInfo.GetValue(null);if (null != consoleWindowInstance && (object)EditorWindow.focusedWindow == consoleWindowInstance){fieldInfo = consoleWindowType.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);string activeText = fieldInfo.GetValue(consoleWindowInstance).ToString();return activeText;}return "";}/// <summary>/// 以文件个格式打开日志/// </summary>/// <param name="stackTrace"></param>private static void openLog(string stackTrace){string fileName = GetFileSaveName();string saveDirPath = Application.dataPath + "/../Debug";if (!Directory.Exists(saveDirPath)){Directory.CreateDirectory(saveDirPath);}string savePath = saveDirPath + "/" + fileName;File.WriteAllText(savePath, stackTrace);System.Diagnostics.Process.Start(savePath);}private static string GetFileSaveName(){DateTime currentTime = DateTime.Now;string customFormat = currentTime.ToString("yyyy_MM_dd_HH_mm_ss");string fileName = $"debug_{customFormat}.txt";return fileName;}static void SetExternalEditorPath(){string path = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);path = EditorUtility.OpenFilePanel("设置lua文件默认打开的编辑器软件路径(选择exe文件)",path,"exe");if (path != ""){EditorUserSettings.SetConfigValue(EXTERNAL_EDITOR_PATH_KEY, path);Debug.Log("Set Editor Path: " + path);}} static void SetLuaProjectRoot(){string path = EditorUserSettings.GetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY);path = EditorUtility.OpenFolderPanel("设置lua项目根目录位置",path,"");if (path != ""){EditorUserSettings.SetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY, path);Debug.Log("Set Editor Path: " + path);}}static bool OpenLua(string logText){//获取lua文件路径Regex regex = new Regex(@"<filePath>.*<\/filePath>");Match match = regex.Match(logText);if (!match.Success){return false;}string filePath = match.Groups[0].Value.Trim();int length = filePath.Length - 10 - 11; //去掉开头和结尾的字符串 <filePath>  </filePath>filePath = filePath.Substring(10, length);filePath = filePath.Replace(".", "/");if (!filePath.EndsWith(".lua")){filePath = filePath + ".lua";}//获取日志行号Regex lineRegex = new Regex(@"<line>.*<\/line>");match = lineRegex.Match(logText);if (!match.Success){return false;}string luaLineString = match.Groups[0].Value;luaLineString.Trim();length = luaLineString.Length - 6 - 7;luaLineString = luaLineString.Substring(6, length);int luaLine = int.Parse(luaLineString.Trim());return OpenFileAtLineExternal(filePath, luaLine);}/// <summary>/// 打开没有标签的日志对应的脚本/// </summary>/// <param name="logText"></param>/// <returns></returns>static bool OpenLuaDefault(string logText){int index = logText.IndexOf("stack traceback:");string temp = logText.Substring(index, logText.Length - index);string[] arr = temp.Split(':');if (arr.Length < 3){return false;}string filePath = arr[1].Trim();int luaLine = int.Parse(arr[2]);filePath = filePath + ".lua";return OpenFileAtLineExternal(filePath, luaLine);}/// <summary>/// 打开指定的文件/// </summary>/// <param name="fileName">文件名</param>/// <param name="line">行号</param>/// <returns></returns>static bool OpenFileAtLineExternal(string fileName, int line){string editorPath = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);if (string.IsNullOrEmpty(editorPath) || !File.Exists(editorPath)){   // 没有path就弹出面板设置SetExternalEditorPath();}OpenFileWith(fileName, line);return true;}static void OpenFileWith(string fileName, int line){string editorPath = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);System.Diagnostics.Process proc = new System.Diagnostics.Process();proc.StartInfo.FileName = editorPath;string projectRootPath = EditorUserSettings.GetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY);if (string.IsNullOrEmpty(projectRootPath)){SetLuaProjectRoot();return;}string procArgument = "";if (editorPath.IndexOf("idea") != -1) //idea{procArgument = string.Format("{0} --line {1} {2}", projectRootPath, line, fileName);}else if (editorPath.IndexOf("Code.exe") != -1) // VSCode{string filePath = Path.Combine(projectRootPath, fileName);procArgument = string.Format("-g {0}:{1}:0", filePath, line);}else{procArgument = string.Format("{0}:{1}:0", fileName, line);}proc.StartInfo.Arguments = procArgument;proc.Start(); }}

相关文章:

unity console日志双击响应事件扩展

1 对于项目中一些比较长的日志&#xff0c;比如前后端交互协议具体数据等&#xff0c;这些日志内容可能会比较长&#xff0c;在unity控制面板上查看不是十分方便&#xff0c;我们可以对双击事件进行扩展&#xff0c;将日志保存到一个文本中&#xff0c;然后用系统默认的文本查看…...

维度建模维度表技术基础解析(以电商场景为例)

维度建模维度表技术基础解析(以电商场景为例) 维度表是维度建模的核心组成部分,其设计直接影响数据仓库的查询效率、分析灵活性和业务价值。本文将从维度表的定义、结构、设计方法及典型技术要点展开,结合电商场景案例,深入解析其技术基础。 1. 维度表的定义与作用 定义…...

Leetcode 264-丑数/LCR 168/剑指 Offer 49

题目描述 我们把只包含质因子 2、3 和 5 的数称作丑数&#xff08;Ugly Number&#xff09;。求按从小到大的顺序的第 n 个丑数。 示例: 说明: 1 是丑数。 n 不超过1690。 题解 动态规划法 根据题意&#xff0c;每个丑数都可以由其他较小的丑数通过乘以 2 或 3 或 5 得到…...

阿里云MaxCompute面试题汇总及参考答案

目录 简述 MaxCompute 的核心功能及适用场景,与传统数据仓库的区别 解释 MaxCompute 分层架构设计原则,与传统数仓分层有何异同 MaxCompute 的存储架构如何实现高可用与扩展性 解析伏羲(Fuxi)分布式调度系统工作原理 盘古(Pangu)分布式存储系统数据分片策略 计算与存…...

笔记:Directory.Build.targets和Directory.Build.props的区别

一、目的&#xff1a;分享Directory.Build.targets和Directory.Build.props的区别 Directory.Build.targets 和 Directory.Build.props 是 MSBuild 的两个功能&#xff0c;用于在特定目录及其子目录中的所有项目中应用共享的构建设置。它们的主要区别在于应用的时机和用途。 二…...

istio入门到精通-2

上部分讲到了hosts[*] 匹配所有的微服务&#xff0c;这部分细化一下 在 Istio 的 VirtualService 配置中&#xff0c;hosts 字段用于指定该虚拟服务适用的 目标主机或域名。如果使用具体的域名&#xff08;如 example.com&#xff09;&#xff0c;则只有请求的主机 域名与 exa…...

第5章:vuex

第5章&#xff1a;vuex 1 求和案例 纯vue版2 vuex工作原理图3 vuex案例3.1 搭建vuex环境错误写法正确写法 3.2 求和案例vuex版细节分析源代码 4 getters配置项4.1 细节4.2 源代码 5 mapState与mapGetters5.1 总结5.2 细节分析5.3 源代码 6 mapActions与mapMutations6.1 总结6.2…...

[Python入门学习记录(小甲鱼)]第5章 列表 元组 字符串

第5章 列表 元组 字符串 5.1 列表 一个类似数组的东西 5.1.1 创建列表 一个中括号[ ] 把数据包起来就是创建了 number [1,2,3,4,5] print(type(number)) #返回 list 类型 for each in number:print(each) #输出 1 2 3 4 5#列表里不要求都是一个数据类型 mix [213,"…...

Docker 学习(四)——Dockerfile 创建镜像

Dockerfile是一个文本格式的配置文件&#xff0c;其内包含了一条条的指令(Instruction)&#xff0c;每一条指令构建一层&#xff0c;因此每一条指令的内容&#xff0c;就是描述该层应当如何构建。有了Dockerfile&#xff0c;当我们需要定制自己额外的需求时&#xff0c;只需在D…...

Java多线程与高并发专题——为什么 Map 桶中超过 8 个才转为红黑树?

引入 JDK 1.8 的 HashMap 和 ConcurrentHashMap 都有这样一个特点&#xff1a;最开始的 Map 是空的&#xff0c;因为里面没有任何元素&#xff0c;往里放元素时会计算 hash 值&#xff0c;计算之后&#xff0c;第 1 个 value 会首先占用一个桶&#xff08;也称为槽点&#xff…...

LeetCode hot 100—二叉树的中序遍历

题目 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#xff1a;root […...

代码随想录算法训练营第35天 | 01背包问题二维、01背包问题一维、416. 分割等和子集

一、01背包问题二维 二维数组&#xff0c;一维为物品&#xff0c;二维为背包重量 import java.util.Scanner;public class Main{public static void main(String[] args){Scanner scanner new Scanner(System.in);int n scanner.nextInt();int bag scanner.nextInt();int[…...

与中国联通技术共建:通过obdiag分析OceanBase DDL中的报错场景

中国联通软件研究院&#xff08;简称联通软研院&#xff09;在全面评估与广泛调研后&#xff0c;在 2021年底决定采用OceanBase 作为基础&#xff0c;自研分布式数据库产品CUDB&#xff08;即China Unicom Database&#xff0c;中国联通数据库&#xff09;。目前&#xff0c;该…...

IDEA 接入 Deepseek

在本篇文章中&#xff0c;我们将详细介绍如何在 JetBrains IDEA 中使用 Continue 插件接入 DeepSeek&#xff0c;让你的 AI 编程助手更智能&#xff0c;提高开发效率。 一、前置准备 在开始之前&#xff0c;请确保你已经具备以下条件&#xff1a; 安装了 JetBrains IDEA&…...

斗地主小游戏

<!DOCTYPE html> <html><head><meta charset="utf-8"><title>斗地主</title><style>.game-container {width: 1000px;height: 700px;margin: 0 auto;position: relative;background: #35654d;border-radius: 10px;padding…...

如何改变怂怂懦弱的气质(2)

你是否曾经因为害怕失败而逃避选择&#xff1f;是否因为不敢拒绝别人而让自己陷入困境&#xff1f;是否因为过于友善而被人轻视&#xff1f;如果你也曾为这些问题困扰&#xff0c;那么今天的博客就是为你准备的。我们将从行动、拒绝、自我认知、实力提升等多个角度&#xff0c;…...

C# OnnxRuntime部署DAMO-YOLO人头检测

目录 说明 效果 模型信息 项目 代码 下载 参考 说明 效果 模型信息 Model Properties ------------------------- --------------------------------------------------------------- Inputs ------------------------- name&#xff1a;input tensor&#xff1a;Floa…...

基于GeoTools的GIS专题图自适应边界及高宽等比例生成实践

目录 前言 一、原来的生成方案问题 1、无法自动读取数据的Bounds 2、专题图高宽比例不协调 二、专题图生成优化 1、直接读取矢量数据的Bounds 2、专题图成果抗锯齿 3、专题成果高宽比例自动调节 三、总结 前言 在当今数字化浪潮中&#xff0c;地理信息系统&#xff08;…...

各种DCC软件使用Datasmith导入UE教程

3Dmax: 先安装插件 https://www.unrealengine.com/zh-CN/datasmith/plugins 左上角导出即可 虚幻中勾选3个插件,重启引擎 左上角选择文件导入即可 Blender导入Datasmith进UE 需要两个插件, 文章最下方链接进去下载安装即可 一样的,直接导出,然后UE导入即可 C4D 直接保存成…...

尚硅谷爬虫note15

一、当当网 1. 保存数据 数据交给pipelines保存 items中的类名&#xff1a; DemoNddwItem class DemoNddwItem(scrapy.Item): 变量名 类名&#xff08;&#xff09; book DemoNddwItem(src src, name name, price price)导入&#xff1a; from 项目名.items import 类…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...