.NET8关于ORM的一次思考

文章目录

  • 前言
  • 一、思路
  • 二、实现ODBC=>SqlHelper.cs
  • 三、数据对象实体化
  • 四、SQL生成SqlBuilder.cs
  • 五、参数注入 SqlParameters.cs
  • 六、反射 SqlOrm.cs
  • 七、自定义数据查询
  • 八、总结

前言

琢磨着在.NET8找一个ORM,对比了最新的框架和性能。

框架批量操作性能SQL控制粒度学习成本扩展性
Dapper★★★★☆完全自主依赖扩展库
SqlSugar★★★★☆半自动内置优化
EF Core★★☆☆☆自动生成高度可扩展
ODBC★★☆☆☆完全自主依赖驱动

Dapper在1000条以内和10万以上的数据都是最快的,且粒度小。
毕竟Dapper够轻量:仅26个核心类,无复杂映射配置。

但当我看到它的驱动依旧是System.Data.SqlClient时,我思索,为什么我不自己写一个呢。
毕竟我的逻辑大多在存储过程里。框架也是人家重构出来的嘛,人家的不一定是适合自己的。
尝试着在.net8里重构了一下并记录下来

一、思路

1、泛型调用实体,缩减定义代码
2、语句参数化,保障性能和安全(防注入)
3、以dataset为主、model的二次操作为辅,实现数据的快速Josn化,方便接口调用、数据IO
4、事务可选化设置,因为主业务在存储过程,我可以另外单独写事务。
5、异步提升效率。
6、使用Lamada精简代码
7、复杂语句在存储过程分离,降低ODBC的维护成本。
8、using实现数据的快速回收。

二、实现ODBC=>SqlHelper.cs

Nuget 安装Microsoft.Data.SqlClient包。
主要方法
1、ExecuteNonQuery 返回执行条数,作为编辑用。
2、ExecuteScalar返回执行Object ,作为查询用。
这区分开了就很好实现了,增、改、删我用1、查我用2。思路清晰。

 using (SqlConnection connection = new SqlConnection(connectionString)){await connection.OpenAsync();using (SqlCommand cmd = new SqlCommand(sql, connection)){return await cmd.ExecuteNonQueryAsync();//ExecuteScalar()}
}

主要连接就这个了,使用using 实现资源释放。

加上我们提到的重构一下:

using Microsoft.Data.SqlClient;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Transactions;namespace DataProcess
{/// <summary>/// ODBC 辅助类/// </summary>public class SqlHelper{public static string ConnectionString = "";private static int timeout = 30;private static string _empty = "null";/// <summary>/// 返回查询结果/// </summary>/// <param name="SqlStr"></param>/// <returns></returns>public static async Task<object> MyTran(List<CmdData> list, string type, bool IsTran){try{using (SqlConnection connection = new SqlConnection(ConnectionString)){await connection.OpenAsync();if (type == "Edit")// 执行SQL 返回影响条数return await Edit(connection, CommandType.Text, list, IsTran);if (type == "QueryDs")// 执行SQL 返回查询结果DataSetreturn await QueryDs(connection, CommandType.Text, list);if (type == "QueryInt")// 执行SQL 返回查询结果 第一行第一列的int return await QueryInt(connection, CommandType.Text, list);elsereturn _empty;}}catch (SqlException ex){Console.WriteLine($"数据库错误:{ex.Message}");return _empty;}}/// <summary>/// 非查询  =>条数  --存储过程 Istran=0/// </summary>/// <returns></returns>public static async Task<object> Edit(SqlConnection connection, CommandType cmdType, List<CmdData> list, bool IsTran){SqlTransaction trans = null;if (IsTran) //是否开启事务trans = connection.BeginTransaction();try{int result = 0;for (int i = 0; i < list.Count; i++)//批量删除{using (SqlCommand cmd = new SqlCommand(list[i].sql, connection)){if (IsTran) cmd.Transaction = trans;cmd.CommandType = cmdType;if (list[i].parameter != null) cmd.Parameters.AddRange(list[i].parameter);cmd.CommandTimeout = timeout; // 超时定义int rst = await cmd.ExecuteNonQueryAsync();if (rst == 0){  //没执行成功if (IsTran) await trans.RollbackAsync();else break;}else result += rst;}if (IsTran) await (trans).CommitAsync();}return result;}catch (Exception e){if (IsTran) await trans.RollbackAsync();return _empty;}}/// <summary>/// 查询  =>数据 Int/// </summary>public static async Task<object> QueryInt(SqlConnection connection, CommandType cmdType, List<CmdData> list){using (SqlCommand cmd = new SqlCommand(list[0].sql, connection)){cmd.CommandType = cmdType;if (list[0].parameter != null) cmd.Parameters.AddRange(list[0].parameter);cmd.CommandTimeout = timeout; // 超时定义try{var rst = await cmd.ExecuteScalarAsync() ;return rst;}catch (Exception e){return _empty;}}}/// <summary>/// 查询  =>数据 DataSet /// </summary>public static async Task<object> QueryDs(SqlConnection connection, CommandType cmdType, List<CmdData> list){using (SqlCommand cmd = new SqlCommand(list[0].sql, connection)){cmd.CommandType = cmdType;if (list[0].parameter != null) cmd.Parameters.AddRange(list[0].parameter);cmd.CommandTimeout = timeout; // 超时定义try{using (DbDataReader reader = await cmd.ExecuteReaderAsync()){DataSet rst = ConvertDataReaderToDataSet(reader);return rst;}}catch (Exception e){return _empty;}}}public static DataSet ConvertDataReaderToDataSet(DbDataReader reader){DataSet dataSet = new DataSet();// 处理第一个结果集DataTable schemaTable = reader.GetSchemaTable();DataTable dataTable = new DataTable();// 自动构建列结构(根据DataReader的元数据)dataTable.Load(reader);  // 此方法自动映射列并填充数据[^2]dataSet.Tables.Add(dataTable);// 处理后续结果集(如果存在)while (!reader.IsClosed && reader.NextResult()){DataTable nextTable = new DataTable();nextTable.Load(reader);dataSet.Tables.Add(nextTable);}return dataSet;}}//存储过程对象public class StoredProc{public string Name { get; set; }public List<ProcParam> param { get; set; }}public class ProcParam {public ProcParam(string Name, object value){this.Name = Name;this.value = value;}public string Name { get; set; }public object value { get; set; }}

webapi 直接给 sqlhelp.connectionString 赋值,就能实现数据连接了。

三、数据对象实体化

要实现泛型,那肯定要先拿到数据实体的model
数据实体手敲那肯定不和谐,现在实现一键转换的工具还是很多的。我使用EF Core Power Tools,

在这里插入图片描述
在这里插入图片描述
连接数据库,并选中所有表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这样就能自动生成跟数据库字段对应的表对象实体了。

四、SQL生成SqlBuilder.cs

SQL是生成分两部分,主体、参数。增删查改还是有迹可循的。

using Microsoft.Data.SqlClient;
using Model;
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;namespace DataProcess
{/// <summary>/// T-SQL DIY/// </summary>public class SqlBuilder{#region  编辑Edit/// <summary>/// 实体 泛型 Insert 语句/// </summary>public static string InsertSQL<T>(T entity){Type type = typeof(T);string  columns = "";string values = "";foreach (PropertyInfo prop in type.GetProperties()){if (prop.Name == "Id") continue; // 排除自增主键columns+=$"{prop.Name},";values+=$"@{prop.Name},";}return $"INSERT INTO {type.Name} ({columns.TrimEnd(',')}) VALUES ({values.TrimEnd(',')});";}public static string InsertSQL_id<T>(T entity){return InsertSQL(entity) + "Select @@IDENTITY AS LastID;";}public static string UpdateSQL<T>(T entity){Type type = typeof(T);string temp = "";foreach (PropertyInfo prop in type.GetProperties()){if (prop.Name == "Id") { continue; } // 排除自增主键temp += string.Format("{0}=@{0},", prop.Name);}return $"UPDATE  {type.Name} SET ({temp.TrimEnd(',')} WHERE  @id=id";}public static string DeleteSQL<T>(T entity) {string objectName = entity.GetType().Name;string sql = string.Format("delete from  {0}  where id=@id;", objectName);return sql;}#endregion#region 查询 querypublic static string SelectSQL<T>(string id) where T: new(){string ModelName = getName<T>();string sql = string.Format("Select * from  {0}  where id={1};", ModelName, id);return sql;}public static string  ByPageSQL<T>(string strWhere, string orderby, int startIndex, int endIndex) where T : new(){string ModelName = getName<T>();StringBuilder strSql = new StringBuilder();strSql.Append("Select * FROM ( ");strSql.Append(" Select ROW_NUMBER() OVER (");if (!string.IsNullOrEmpty(orderby.Trim())){strSql.Append("order by T." + orderby);}else{strSql.Append("order by T.id desc");}strSql.Append(")AS Row, T.*  from " + ModelName + " T ");if (!string.IsNullOrEmpty(strWhere.Trim())){strSql.Append(" WHERE " + strWhere);}strSql.Append(" ) TT");strSql.AppendFormat(" WHERE TT.Row between {0} and {1}", startIndex, endIndex);return strSql.ToString();}public static string  ListSQL<T>(string strWhere) where T : new(){string ModelName = getName<T>();if (string.IsNullOrEmpty(strWhere)) strWhere = "1=1";string sql = string.Format("Select * from  {0}  where {1};", ModelName, strWhere);return sql;}public static string CountSQL<T>(string strWhere) where T : new(){string ModelName = getName<T>();if (string.IsNullOrEmpty(strWhere)) strWhere = "1=1";string sql = string.Format("Select count(1) from  {0}  where {1};", ModelName, strWhere);return sql;}public static string ProcSQL(StoredProcs T){string columns = "";foreach (ProcParam item in T.param){columns += $"@{item.Name}=\"{item.value}\",";}return $"EXEC {T.Name} {columns.TrimEnd(',')};";}#endregion/// <summary>/// 实例以取表名/// </summary>public static string getName<T>() where T : new(){T t = new T();string name = t.GetType().Name;return name;}}
}

五、参数注入 SqlParameters.cs

using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;namespace DataProcess
{/// <summary>/// 实体注入/// </summary>public class SqlParameters{/// <summary>/// for add/// </summary>public static SqlParameter[] NoID(object entity){return entity.GetType().GetProperties().Where(p => p.Name != "Id").Select(p => new SqlParameter($"@{p.Name}", p.GetValue(entity) ?? DBNull.Value)).ToArray();}/// <summary>/// for delete/// </summary>public static SqlParameter[] OnlyID(object entity){return entity.GetType().GetProperties().Where(p => p.Name == "Id").Select(p => new SqlParameter($"@{p.Name}", p.GetValue(entity) ?? DBNull.Value)).ToArray();}/// <summary>/// for update/// </summary>public static SqlParameter[] HaveId(object entity){SqlParameter[] rst = NoID(entity);SqlParameter[] rst2 = OnlyID(entity);rst.Append(rst2[0]);return rst;}}
}

根据加、删、改,分别循环实体的value注入进去。当然我的每个表都是自增id作为主键的。

六、反射 SqlOrm.cs

增、删、改的Edit部分完成了,现在实现 查询部分。
通过循环实体属性,将DataSet转换为model,并完成赋值

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace DataProcess
{/// <summary>///  DataRow to Model/// </summary>public class SqlOrm{/// <summary>/// 反射/// </summary>public static T ToEntity<T>(DataRow row) where T : new(){//初始化T t = new T();//得到类型Type type = t.GetType();//属性集合PropertyInfo[] ps = type.GetProperties();//赋值、格式转换ps.ToList().ForEach(p =>{var _data = row[p.Name];//值if (_data != null && _data != DBNull.Value)//非空{if (p.PropertyType == typeof(System.Nullable<System.DateTime>))//空时间p.SetValue(t, DateTime.Parse(_data.ToString() ?? "1970-01-01"), null);else if (p.PropertyType == typeof(System.Nullable<System.Decimal>))//空Decimalp.SetValue(t, Decimal.Parse(_data.ToString() ?? "0"), null);else if (p.PropertyType == typeof(System.Nullable<System.Int32>))//Int32p.SetValue(t, Int32.Parse(_data.ToString() ?? "0"), null);elsep.SetValue(t, Convert.ChangeType(_data, p.PropertyType), null);}elsep.SetValue(t, Convert.ChangeType(_data, p.PropertyType), null);});return t;}}
}

七、自定义数据查询

using Microsoft.Data.SqlClient;
using Model;
using System;
using System.Data;
using System.Data.Common;
using System.Text;
using System.Threading.Tasks;namespace DataProcess
{public class Access{#region 基础basic/// <summary>/// 单增/// </summary>public static async Task<bool> Add<T>(T entity){string SqlStr = SqlBuilder.InsertSQL<T>(entity);//语句SqlParameter[] parameter = SqlParameters.NoID(entity);//参数List<CmdData> list = new List<CmdData>();list.Add(new CmdData(SqlStr, parameter));int rst = (int)await SqlHelper.MyTran(list, "Edit", true);return rst > 0;}/// <summary>/// 单改/// </summary>public static async Task<bool> Update<T>(T entity){string SqlStr = SqlBuilder.UpdateSQL<T>(entity);SqlParameter[] parameters = SqlParameters.HaveId(entity);List<CmdData> list = new List<CmdData>();list.Add(new CmdData(SqlStr, parameters));int rst = (int)await SqlHelper.MyTran(list, "Edit", true);return rst > 0;}/// <summary>/// 单删/// </summary>public static async Task<bool> Delete<T>(T entity){string SqlStr = SqlBuilder.DeleteSQL<T>(entity);SqlParameter[] parameters = SqlParameters.OnlyID(entity);List<CmdData> list = new List<CmdData>();list.Add(new CmdData(SqlStr, parameters));int rst = (int)await SqlHelper.MyTran(list, "Edit", true);return rst > 0;}/// <summary>/// 单查=>Entity/// </summary>public static async Task<T> GetModel<T>(int id) where T : new(){string SqlStr = SqlBuilder.SelectSQL<T>(id.ToString());List<CmdData> list = new List<CmdData>();list.Add(new CmdData(SqlStr, null));DataSet ds = (DataSet)await SqlHelper.MyTran(list, "QueryDs", true);if (ds.Tables[0].Rows.Count > 0)return SqlOrm.ToEntity<T>(ds.Tables[0].Rows[0]);else return new T();}#endregion#region  扩展 Extend/// <summary>/// 批量增/// </summary>public static async Task<bool> Adds<T>(List<T> listT){List<CmdData> list = new List<CmdData>();foreach (var entity in listT){string SqlStr = SqlBuilder.InsertSQL<T>(entity);//语句SqlParameter[] parameter = SqlParameters.NoID(entity);//参数list.Add(new CmdData(SqlStr, parameter));}int rst = (int)await SqlHelper.MyTran(list, "Edit", true);return rst > 0;}/// <summary>/// 批改/// </summary>public static async Task<bool> Updates<T>(List<T> listT){List<CmdData> list = new List<CmdData>();foreach (var entity in listT){string SqlStr = SqlBuilder.UpdateSQL<T>(entity);SqlParameter[] parameter = SqlParameters.HaveId(entity);list.Add(new CmdData(SqlStr, parameter));}int rst = (int)await SqlHelper.MyTran(list, "Edit", true);return rst > 0;}/// <summary>/// 批删/// </summary>public static async Task<bool> Deletes<T>(List<T> listT){List<CmdData> list = new List<CmdData>();foreach (var entity in listT){string SqlStr = SqlBuilder.DeleteSQL<T>(entity);SqlParameter[] parameters = SqlParameters.OnlyID(entity);list.Add(new CmdData(SqlStr, parameters));}int rst = (int)await SqlHelper.MyTran(list, "Edit", true);return rst > 0;}/// <summary>/// 单增 返回ID/// </summary>public static async Task<int> Add_ID<T>(T entity){string SqlStr = SqlBuilder.InsertSQL_id<T>(entity);//语句SqlParameter[] parameters = SqlParameters.NoID(entity);//参数List<CmdData> list = new List<CmdData>();list.Add(new CmdData(SqlStr, parameters));return (int)await SqlHelper.MyTran(list, "QueryInt", true);}/// <summary>/// 条件查询/// </summary>/// <returns></returns>public static async Task<DataSet> GetList<T>(string strWhere) where T : new(){string SqlStr = SqlBuilder.ListSQL<T>(strWhere);List<CmdData> list = new List<CmdData>();list.Add(new CmdData(SqlStr, null));return (DataSet)await SqlHelper.MyTran(list, "QueryDs", true);}/// <summary>/// 条件查询=》多Entity/// </summary>public static async Task<List<T>> GetModelList<T>(string strWhere) where T : new(){DataSet ds = await GetList<T>(strWhere);List<T> rst = new List<T>();if (ds.Tables[0].Rows.Count > 0){ds.Tables[0].Rows.Cast<DataRow>().ToList().ForEach(p =>{rst.Add(SqlOrm.ToEntity<T>(p));});}return rst;}/// <summary>/// 条件查询=》条数/// </summary>public static async Task<int> RecordCount<T>(string strWhere) where T : new(){string SqlStr = SqlBuilder.ListSQL<T>(strWhere);List<CmdData> list = new List<CmdData>();list.Add(new CmdData(SqlStr, null));return (int)await SqlHelper.MyTran(list, "QueryInt", true);}/// <summary>/// 分页/// </summary>public static async Task<DataSet> GetListByPage<T>(string strWhere, string orderby, int startIndex, int endIndex) where T : new(){string SqlStr = SqlBuilder.ByPageSQL<T>(strWhere, orderby, startIndex, endIndex);List<CmdData> list = new List<CmdData>();list.Add(new CmdData(SqlStr, null));return (DataSet)await SqlHelper.MyTran(list, "QueryDs", true);}/// <summary>/// 存储过程/// </summary>public static async Task<DataSet> ExecProc(StoredProcs proc){// return (DataSet)await SqlHelper.MyTran(SqlStr, null, "Query", 1);string SqlStr = SqlBuilder.ProcSQL(proc);//语句List<CmdData> list = new List<CmdData>();list.Add(new CmdData(SqlStr, null));return (DataSet)await SqlHelper.MyTran(list, "QueryDs", false);//不启动事务}#endregion}
}

八、总结

5个类完成了自己在 .net8 中的ORM. 不是太复杂。
我的想法是前端传回json到API ,解析成modelList 完成批量的增删改,联表这种数据量大的就丢到存储过程里去把。

//调用存储过程示例
public static async Task<DataSet> MyProc()
{StoredProc proc = new StoredProc();proc.Name = "SP_Test";proc.param = new List<ProcParam>();proc.param.Add(new ProcParam("createtime", "2025-0-13"));proc.param.Add(new ProcParam("name", "张三"));proc.param.Add(new ProcParam("class", "2年纪"));return await Access.ExecProc(proc);
}
//批量修改示例public static async Task<bool> Updates<Users>(string JsonStr){List<Users> List = JsonConvert.DeserializeObject<List<Users>>(JsonStr);return await Access.Updates<Users>(List);}
//分页查询示例public static async Task<string> GetListByPage<User>(){DataSet ds= await Access.GetListByPage<User> ("name like '%张%'"," createtme desc",  101,  200);string JsonStr=JsonConvert.SerializeObject(ds.tables[0]);return JsonStr;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/79777.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CVE-2025-31258 macOS远程视图服务沙箱逃逸漏洞PoC已公开

苹果公司近日针对macOS系统中新披露的CVE-2025-31258漏洞发布补丁&#xff0c;该漏洞可能允许恶意应用程序突破沙箱限制&#xff0c;获取未授权的系统资源访问权限。在安全研究员Seo Hyun-gyu公开概念验证&#xff08;PoC&#xff09;利用代码后&#xff0c;该漏洞已在macOS Se…

21.第二阶段x64游戏实战-分析采集物偏移

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;20.第二阶段x64游戏实战-代码实现遍历周围 上一个内容里把遍历周围的npc和玩家…

2025年全新 GPT 4.5 AI 大模型 国内免费调用

一、中转账号注册 第一步&#xff1a;打开宙流AI中转站&#xff0c;网站地址如下&#xff1a; 宙流AI中转站 按照上图中的操作步骤&#xff0c;通过邮箱进行账号注册&#xff0c;注册完毕后&#xff0c;网站初始会分配0.4刀的免费额度&#xff0c;获取额度后&#xff0c;即可…

基于事件驱动和策略模式的差异化处理方案

一、支付成功后事件驱动 1、支付成功事件 /*** 支付成功事件** author ronshi* date 2025/5/12 14:40*/ Getter Setter public class PaymentSuccessEvent extends ApplicationEvent {private static final long serialVersionUID 1L;private ProductOrderDO productOrderDO;…

简述Web和HTTP

目录 HTTP概述 非持续连接和持续连接 非持续连接 持续连接 HTTP报文格式 HTTP请求报文 HTTP响应报文 用户与服务器的交互&#xff1a;cookie Web缓存 条件GET方法 Web 即万维网&#xff0c;是一个基于超文本和 HTTP 协议的全球性信息系统&#xff0c;通过浏览器访问…

基于STM32的LCD信号波形和FFT频谱显示

一、项目准备 主要利用LCD驱动中的画点和画连线函数&#xff0c;驱动是正点原子给我写好了的画点和画线的函数等些相关函数 void LCD_Draw_Circle(u16 x0,u16 y0,u8 r); //画圆 void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2); //画线 二、画波形图函数实…

Go 语言即时通讯系统开发日志-day1:从简单消息收发 Demo 起步

Go语言即时通讯系统开发日志day1&#xff0c;主要模拟实现的一个简单的发送消息和接受消息的小demo&#xff0c;因为也才刚学习go语言的语法&#xff0c;对go的json、net/http库了解不多&#xff0c;所以了解了一下go语言的encoding/json库和net/http库&#xff0c;以及websock…

基于vllm-ascend的华为atlas大模型部署

vllm-ascend介绍&#xff1a; vLLM 昇腾插件 (vllm-ascend) 是一个让vLLM在Ascend NPU无缝运行的后端插件。 此插件是 vLLM 社区中支持昇腾后端的推荐方式。它遵循[RFC]: Hardware pluggable所述原则&#xff1a;通过解耦的方式提供了vLLM对Ascend NPU的支持。 使用 vLLM 昇腾…

贝叶斯优化Transformer融合支持向量机多变量时间序列预测,Matlab实现

贝叶斯优化Transformer融合支持向量机多变量时间序列预测&#xff0c;Matlab实现 目录 贝叶斯优化Transformer融合支持向量机多变量时间序列预测&#xff0c;Matlab实现效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.BO-TransformerSVM多变量时间序列预测&#xff0c…

状压DP总结

前言 一般来讲 n n n 数据范围在 10 ~ 25 之间都是可以进行状态压缩的 -> 2 n 2^n 2n 状压 The 2024 Shanghai Collegiate Programming Contest Problem G.象棋大师 知识点&#xff1a;线性DP&#xff0c;状压DP&#xff0c;预处理 辅助转移的技巧 首先看到 n*n 的方格…

SQLite 转换为 MySQL 数据库

一、导出 SQLite 数据库 1. 使用 SQLite 命令行工具 • 打开终端&#xff08;在 Linux 或 macOS 上&#xff09;或命令提示符&#xff08;在 Windows 上&#xff09;。 • 输入sqlite3 your_database_name.db&#xff08;将 your_database_name.db 替换为你的 SQLite 数据库…

【技巧】使用UV创建python项目的开发环境

回到目录 【技巧】使用UV创建python项目的开发环境 0. 为什么用UV 下载速度快、虚拟环境、多版本python支持、清晰的依赖关系 1. 安装基础软件 1.1. 安装python 下载地址&#xff1a;https://www.python.org/downloads/windows/ 1.2. 安装UV > pip install uv -i ht…

Java SpringMVC 和 MyBatis 整合项目的事务管理配置详解

目录 一、事务管理的基本概念二、在 SpringMVC 和 MyBatis 整合项目中配置事务管理1. 配置数据源2. 配置事务管理器3. 使用事务注解4. 配置 MyBatis 的事务支持5. 测试事务管理三、总结在企业级应用开发中,事务管理是确保数据一致性和完整性的重要机制。特别是在整合了 Spring…

Nakama:让游戏与应用更具互动性和即时性

在现代游戏和应用程序开发中,实现社交互动和实时功能已成为用户体验的核心需求。为满足这种需求,许多开发者正转向分布式服务器技术,在这些技术中,Nakama 构建起了一座桥梁。Nakama 是一个开源的分布式服务器,专门为社交和实时游戏及应用程序设计,为开发者提供了强大的工…

项目中会出现的css样式

1.重复渐变边框 思路&#xff1a; 主要是用重复的背景渐变实现的 如图&#xff1a; <div class"card"><div class"container">全面收集中医癌毒临床医案&#xff0c;建立医案共享机制&#xff0c;构建癌毒病机知识图谱&#xff0c;便于医疗人…

数组和切片的区别

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

Jenkins企业级实战

目标 在Windows操作系统上使用Jenkins完成代码的自动拉取、编译、打包、发布工作。 实施 1.安装Java开发工具包&#xff08;JDK&#xff09; Jenkins是基于Java的应用程序&#xff0c;因此需要先安装JDK。可以从Oracle官网或OpenJDK下载适合的JDK版本。推荐java17版本&#x…

C++ 异常捕获 try 和 __try的区别笔记

最近碰到了try 和 __try的区别的问题&#xff0c;经过实测与验证&#xff0c;发现在vs2019下&#xff0c;确实存在try无法捕获特定异常的问题&#xff0c;比如下面的代码&#xff1a; //以空格作为分割符的符号个数 //内存复制功能 // test1.cpp : 定义控制台应用程序的入口点…

Spark基础介绍

1. Spark 核心概念 1.1 RDD&#xff08;弹性分布式数据集&#xff09; 定义&#xff1a;RDD&#xff08;Resilient Distributed Dataset&#xff09;是 Spark 的核心抽象&#xff0c;是不可变、可分区、容错的分布式数据集合。特性&#xff1a; 弹性&#xff1a;自动进行内存…

采用SqlSugarClient创建数据库实例引发的异步调用问题

基于SqlSugar编写的多个WebApi接口&#xff0c;项目初始化时采用单例模式注册SqlSugarClient实例对象&#xff0c;前端页面采用layui布局&#xff0c;并在一个按钮事件中通过Ajax连续调用多个WebApi接口获取数据。实际运行时点击按钮会随机报下面几种错误&#xff1a; Execute…