关于orm的个人测试——SqlSugar与FreeSql
阅读原文时间:2024年11月20日阅读:1

前言

转眼已经过了金九,光阴真的是似箭啊,周六日常加班,忙里抽闲就想鼓捣个啥看看,刚好最近想着有没有必要换个orm,从当时原生到Dapper,又到现在的Sqlsugar,因为经常听到几个不错的orm,就是今天想测试的Freesql,其实对于造轮子这种事,个人觉得其实是件好事,只有轮子多了,才会有车,虽然参差不齐,但开车的心情还是挺嗨皮的,就算磕磕绊绊,那也是体验过才知道,当然毕竟是开源的自己也可以扩展改造嘛。

开始

因为电脑上只有mysql,这里就单对mysql做下对比测试了,针对增删改查这些常规操作看下性能对比(这里暂时只对sqlsugar与freesql对比,至于其他的,可以加可以加)。

测试环境

  • net core 3.1
  • FreeSql 1.8.1
  • sqlSugarCore 5.0.0.15
  • mysql 5.5.53(wc,这么低)

准备工作

新建个控制台工程,引入两个nuget包FreeSqlsqlSugarCore,先来个实体吧。

这里注意下,freesql这个库没有对应的扩展包,例如mysql还需要引入FreeSql.Provider.MySql,具体可看对应文档

    [Table(Name = "student_free")]
    [SugarTable("student_sugar")]
    public class StudentEntity
    {
        [Column(IsIdentity = true, IsPrimary = true)]
        [SugarColumn(IsIdentity = true,IsPrimaryKey = true)]
        public int ID { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Number { get; set; }
        public int Sex { get; set; }
        public string Location { get; set; }
        [Column(Name = "class_id")]
        [SugarColumn(ColumnName = "class_id")]
        public int ClassID { get; set; }
    }

接下来对应实现两个类库的方法类。

freesql
    public class FreeSqlUtil
    {
        private static readonly string CONNECTION_STRING = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root; Initial Catalog=test;Charset=utf8;SslMode=none;Min Pool Size=20;Max Pool Size=20";

        private static IFreeSql _fsql;

        private static readonly object locker = new object();

        public static IFreeSql GetInstance()
        {
            if (_fsql == null)
            {
                lock (locker)
                {
                    if (_fsql == null)
                    {
                        _fsql = new FreeSqlBuilder()
                                .UseConnectionString(DataType.MySql, CONNECTION_STRING)
                                // .UseAutoSyncStructure(true)
                                .UseLazyLoading(true)
                                .Build();
                    }
                }
            }
            return _fsql;
        }
    }

当然这里的sql连接串记得替换成自己的。

    public class FreeSqlTest
    {

        public static List<StudentEntity> GetList(int index, int limit)
        {
            return FreeSqlUtil.GetInstance()
                .Select<StudentEntity>()
                .Page(index, limit)
                .ToList();
        }

        public static List<StudentEntity> GetList(int limit)
        {
            return FreeSqlUtil.GetInstance()
                .Select<StudentEntity>()
                .Limit(limit)
                .ToList();
        }

        public static long Insert(StudentEntity entity)
        {
            long result = FreeSqlUtil.GetInstance()
                                         .Insert(entity)
                                         .ExecuteIdentity();
            return result;
        }

        public static void InsertList(IEnumerable<StudentEntity> entities)
        {
            FreeSqlUtil.GetInstance()
                        .Insert(entities)
                        .ExecuteAffrows();
        }

        public static bool Update(StudentEntity entity)
        {
            int result = FreeSqlUtil.GetInstance()
                .Update<StudentEntity>()
                .SetSource(entity)
                .ExecuteAffrows();
            return result > 0;
        }

        public static void UpdateList(IEnumerable<StudentEntity> entities)
        {
            FreeSqlUtil.GetInstance()
                .Update<StudentEntity>()
                .SetSource(entities)
                .ExecuteAffrows();
        }
    }
sqlsugar

这里可以参考我之前文章中专门介绍sqlsugar时整理的util,当然也可以参考我的demo工程April.WebApi

    public class BaseDbContext
    {
        public SqlSugarClient Db;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="connStr">数据库连接串</param>
        /// <param name="sqlType">数据库类型</param>
        public BaseDbContext(string connStr, int sqlType = 1)
        {
            InitDataBase(connStr, sqlType);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="serverIp">服务器IP</param>
        /// <param name="user">用户名</param>
        /// <param name="pass">密码</param>
        /// <param name="dataBase">数据库</param>
        public BaseDbContext(string serverIp, string user, string pass, string dataBase)
        {
            string connStr = $"server={serverIp};user id={user};password={pass};persistsecurityinfo=True;database={dataBase}";
            InitDataBase(connStr);
        }

        /// <summary>
        /// 初始化数据库连接
        /// </summary>
        /// <param name="listConn">连接字符串</param>
        private void InitDataBase(string connStr, int sqlType = 1)
        {
            Db = new SqlSugarClient(new ConnectionConfig()
            {
                ConnectionString = connStr,
                DbType = (DbType)sqlType,
                IsAutoCloseConnection = true,
                //SlaveConnectionConfigs = slaveConnectionConfigs
            });
            Db.Ado.CommandTimeOut = 30000;//设置超时时间
            Db.Aop.OnLogExecuted = (sql, pars) => //SQL执行完事件
            {
                //这里可以查看执行的sql语句跟参数
            };
            Db.Aop.OnLogExecuting = (sql, pars) => //SQL执行前事件
            {
                //这里可以查看执行的sql语句跟参数
            };
            Db.Aop.OnError = (exp) =>//执行SQL 错误事件
            {
                //这里可以查看执行的sql语句跟参数
            };
            Db.Aop.OnExecutingChangeSql = (sql, pars) => //SQL执行前 可以修改SQL
            {
                return new KeyValuePair<string, SugarParameter[]>(sql, pars);
            };
        }
        /// <summary>
        /// 开启事务
        /// </summary>
        public void BeginTran()
        {
            Db.Ado.BeginTran();
        }
        /// <summary>
        /// 提交事务
        /// </summary>
        public void CommitTran()
        {
            Db.Ado.CommitTran();
        }
        /// <summary>
        /// 回滚事务
        /// </summary>
        public void RollbackTran()
        {
            Db.Ado.RollbackTran();
        }
    }

下面是对应的测试方法实现,其中强调一点,对于mysql中有个max_allow_packet这个配置,批量操作的时候会抛出这个异常,当然这里可以通过修改mysql的配置,不过个人建议还是尽可能通过拆解分次,应该也没有动不动甩个万八数据吧(有了轻喷),所以这里调整了插入方法的实现。

    public class SqlSugarTest
    {
        private static readonly string CONNECTION_STRING = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root; Initial Catalog=test;Charset=utf8;SslMode=none;Min Pool Size=20;Max Pool Size=20";
        private static BaseDbContext baseDb;
        private static SqlSugarClient db;

        static SqlSugarTest()
        {
            baseDb = new BaseDbContext(CONNECTION_STRING, 0);
            db = baseDb.Db;
        }

        public static List<StudentEntity> GetList(int index,int limit)
        {
            return db.Queryable<StudentEntity>().ToPageList(index, limit);
        }
        public static List<StudentEntity> GetList(int limit)
        {
            return db.Queryable<StudentEntity>().Take(limit).ToList();
        }

        public static int Insert(StudentEntity entity)
        {
            int result = db.Insertable(entity).ExecuteReturnIdentity();
            return result;
        }

        public static void InsertList(IEnumerable<StudentEntity> entities)
        {
            if (entities.Count() >= 10000)
            {
                int count = entities.Count();
                int index = 0;
                Console.WriteLine($"批量插入{count}数据");
                while (count / 5000 > 0)
                {
                    var data = entities.Skip(5000 * index).Take(5000);
                    db.Insertable(data.ToArray())
                .ExecuteCommand();
                    count -= 5000;
                    index++;
                }
                if (count % 5000 > 0)
                {
                    var data = entities.Skip(5000 * index).Take(5000);
                    db.Insertable(data.ToArray())
                .ExecuteCommand();
                    index++;
                }
                Console.WriteLine($"拆解执行{index}次");
            }
            else
            {
                db.Insertable(entities.ToArray())
                .ExecuteCommand();
            }
        }

        public static bool Update(StudentEntity entity)
        {
            int result = db.Updateable(entity).ExecuteCommand();
            return result > 0;
        }

        public static void UpdateList(IEnumerable<StudentEntity> entities)
        {
            if (entities.Count() >= 10000)
            {
                int count = entities.Count();
                int index = 0;
                Console.WriteLine($"批量修改{count}数据");
                while (count / 5000 > 0)
                {
                    var data = entities.Skip(5000 * index).Take(5000);
                    db.Updateable(data.ToArray())
                .ExecuteCommand();
                    count -= 5000;
                    index++;
                }
                if (count % 5000 > 0)
                {
                    var data = entities.Skip(5000 * index).Take(5000);
                    db.Updateable(data.ToArray())
                .ExecuteCommand();
                    index++;
                }
                Console.WriteLine($"拆解执行{index}次");
            }
            else
            {
                db.Updateable(entities.ToArray())
                .ExecuteCommand();
            }
        }
    }

开始测试

测试部分的代码就没啥发的,一个图。

新增(下面有补充)

新增总共测试3次吧,基本时间都是保持一个稳定的。

查询(下面有补充)

查询这里用的是分页方法,库里数据就是单次执行插入测试的数据,总共163006条。

修改(下面有补充)

补充测试

看了下freesql入门文章,发现配置这块儿这样写,不使用命令参数化,在新增和修改的速度就很明显了,当然实际使用的时候还是要注意,毕竟注入还是可怕的。

新增

查询

修改

小结

虽然不知道这个测试结果有没有什么问题(毕竟没咋用过),不过通过时间上来看 ,FreeSql在速度上确实是有优势,当然这里只是基础的方法测试,没有什么各种功能的尝试,不过毕竟常用的还是增删改查嘛,再鼓捣什么demo的时候可以考虑下尝试freesql了,虽然目前看来对于现工程的改动还不是太迫切,也希望轮子越来越多,越来越好,当然自己没事也会继续鼓捣,学无止境,路漫漫