关于orm的个人测试——SqlSugar与FreeSql
前言
转眼已经过了金九,光阴真的是似箭啊,周六日常加班,忙里抽闲就想鼓捣个啥看看,刚好最近想着有没有必要换个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包FreeSql,sqlSugarCore,先来个实体吧。
这里注意下,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了,虽然目前看来对于现工程的改动还不是太迫切,也希望轮子越来越多,越来越好,当然自己没事也会继续鼓捣,学无止境,路漫漫。
文连接,否则保留追究法律责任的权利。