關於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了,雖然目前看來對於現工程的改動還不是太迫切,也希望輪子越來越多,越來越好,當然自己沒事也會繼續鼓搗,學無止境,路漫漫。