1. 项目概述
作为一名长期从事Unity开发的程序员,我经常遇到需要在游戏运行时直接操作数据库的需求。传统方式需要编写大量SQL语句,既繁琐又容易出错。最近在项目中尝试了FreeSql这款ORM工具,发现它完美解决了Unity与MySQL数据库交互的痛点。
FreeSql是一个功能强大的.NET ORM框架,支持多种数据库类型。它最大的优势在于语法简洁、性能优异,特别适合Unity这种对性能敏感的环境。通过FreeSql,我们可以用面向对象的方式操作数据库,无需关心底层SQL细节,大大提升了开发效率。
2. 环境准备与工具安装
2.1 安装NuGetForUnity
FreeSql需要通过NuGet包管理器安装到Unity项目中。Unity本身不支持直接使用NuGet,我们需要先安装NuGetForUnity插件:
- 打开Unity项目,选择Window > Package Manager
- 点击左上角的"+"按钮,选择"Add package from git URL"
- 输入NuGetForUnity的Git地址:
https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity - 等待安装完成后,Unity菜单栏会出现"NuGet"选项
注意:如果遇到安装失败,可能是网络问题导致。可以尝试手动下载源码包,然后导入到项目的Packages目录下。
2.2 安装FreeSql核心包
安装好NuGetForUnity后,我们就可以通过它来安装FreeSql了:
- 点击菜单栏的NuGet > Manage NuGet Packages
- 在搜索框中输入"FreeSql.Provider.MySqlConnector"
- 选择最新稳定版本,点击Install按钮
- 等待安装完成后,项目中会出现FreeSql相关的程序集
提示:FreeSql有多个数据库提供程序包,针对MySQL我们需要安装MySqlConnector版本,而不是官方MySQL驱动,因为它在Unity中的兼容性更好。
3. 数据库连接配置
3.1 创建数据库连接单例
在项目中创建一个DB.cs脚本,用于管理数据库连接:
csharp复制public class DB
{
/// <summary>
/// 可配置的数据库参数
/// </summary>
static string host = "127.0.0.1"; // IP
static int port = 3306; // 端口
static string user = "root"; // 用户名
static string passwd = "123456"; // 密码
static string dbname = "game"; // 数据库名称
static string connectionString =
$"Data Source={host};Port={port};User ID={user};Password={passwd};" +
$"Initial Catalog={dbname};Charset=utf8;SslMode=none;Max pool size=10";
public static IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.MySql, connectionString)
.UseAutoSyncStructure(true) // 自动同步实体结构到数据库
.Build(); // 务必定义成单例模式
}
这里有几个关键点需要注意:
UseAutoSyncStructure(true):这个选项允许FreeSql自动创建或修改表结构,开发阶段非常方便- 连接字符串中的
Max pool size=10:设置连接池大小,根据项目需求调整 IFreeSql实例应该作为单例使用,避免频繁创建销毁
3.2 数据库实体类定义
定义一个Player实体类,对应数据库中的player表:
csharp复制[Table(Name = "player")]
public class DbPlayer
{
[Column(IsIdentity = true, IsPrimary = true)]
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
实体类注解说明:
[Table]:指定表名,如果不加则默认使用类名[Column]:配置列属性,如主键、自增等- 属性名默认映射为列名(驼峰转下划线),也可以通过
[Column(Name="xxx")]自定义
4. 基本CRUD操作
4.1 插入数据
创建一个测试脚本FreeSqlTest.cs:
csharp复制public class FreeSqlTest : MonoBehaviour
{
void Start()
{
// 插入一条玩家记录
var affectedRows = DB.fsql.Insert<DbPlayer>()
.AppendData(new DbPlayer()
{
Username = "testuser",
Password = "test123"
})
.ExecuteAffrows();
Debug.Log($"插入了{affectedRows}条数据");
}
}
执行后可以在MySQL客户端中看到数据已经插入,同时FreeSql会自动创建player表(如果不存在)。
4.2 删除数据
删除指定用户名的玩家记录:
csharp复制var affectedRows = DB.fsql.Delete<DbPlayer>()
.Where(t => t.Username == "testuser")
.ExecuteAffrows();
Debug.Log($"删除了{affectedRows}条数据");
注意:Where条件支持Lambda表达式,FreeSql会将其转换为对应的SQL条件。
4.3 更新数据
修改指定用户的密码:
csharp复制var affectedRows = DB.fsql.Update<DbPlayer>()
.Set(a => a.Password, "newpassword")
.Where(p => p.Username == "testuser")
.ExecuteAffrows();
Debug.Log($"更新了{affectedRows}条数据");
4.4 查询数据
查询单个玩家信息:
csharp复制var player = DB.fsql.Select<DbPlayer>()
.Where(a => a.Username == "testuser")
.First();
if (player != null)
{
Debug.Log($"找到玩家: {player.Username}, 密码: {player.Password}");
}
5. 高级功能与性能优化
5.1 批量操作
FreeSql提供了高效的批量操作方法:
csharp复制// 批量插入
var players = new List<DbPlayer>();
for (int i = 0; i < 100; i++)
{
players.Add(new DbPlayer
{
Username = $"user_{i}",
Password = $"pwd_{i}"
});
}
DB.fsql.Insert<DbPlayer>()
.AppendData(players)
.ExecuteAffrows();
批量操作相比单条操作可以大幅提升性能,特别是在需要插入大量数据时。
5.2 事务处理
使用事务确保多个操作的原子性:
csharp复制using (var uow = DB.fsql.CreateUnitOfWork())
{
try
{
var repo = uow.GetRepository<DbPlayer>();
// 操作1
repo.Insert(new DbPlayer { Username = "user1", Password = "pwd1" });
// 操作2
repo.Update(new DbPlayer { Id = 1, Username = "updated" });
uow.Commit();
}
catch
{
uow.Rollback();
throw;
}
}
5.3 性能优化建议
- 连接池配置:适当增大连接池大小(Max Pool Size),但不要过大
- 异步操作:FreeSql支持异步API,在UI线程中推荐使用
- 延迟加载:对于关联实体,可以使用
Navigate属性实现延迟加载 - SQL监控:开发阶段可以开启SQL日志,方便调试
csharp复制DB.fsql.Aop.CurdAfter += (s, e) =>
{
Debug.Log($"SQL: {e.Sql}\n耗时: {e.ElapsedMilliseconds}ms");
};
6. 常见问题与解决方案
6.1 连接失败问题
问题现象:连接MySQL数据库失败,报错"Unable to connect to any of the specified MySQL hosts"
解决方案:
- 检查MySQL服务是否启动
- 确认连接字符串中的IP、端口、用户名密码是否正确
- 如果是远程连接,检查MySQL是否允许远程访问
6.2 表结构同步问题
问题现象:修改实体类后,表结构没有自动更新
解决方案:
- 确保
UseAutoSyncStructure(true)已启用 - 检查实体类属性是否有正确的
[Column]注解 - 对于已有表的结构修改,可能需要手动执行迁移
6.3 性能问题
问题现象:批量操作时速度较慢
优化建议:
- 使用
InsertBatch替代多次单条插入 - 适当增大命令超时时间
- 考虑使用原生SQL执行大批量操作
csharp复制DB.fsql.Ado.ExecuteNonQuery("INSERT INTO player(...) VALUES(...)");
7. 实际项目中的应用技巧
7.1 数据验证
在执行数据库操作前进行数据验证:
csharp复制public class DbPlayer
{
[Column(IsIdentity = true, IsPrimary = true)]
public int Id { get; set; }
[Required, MaxLength(50)]
public string Username { get; set; }
[Required, MinLength(6)]
public string Password { get; set; }
}
// 插入前验证
var player = new DbPlayer();
var isValid = DB.fsql.GetRepository<DbPlayer>()
.DataFilter
.ValidateOnSave(true)
.Insert(player);
7.2 软删除实现
通过重写Delete方法实现软删除:
csharp复制public interface ISoftDelete
{
bool IsDeleted { get; set; }
}
public class DbPlayer : ISoftDelete
{
// ...其他属性
public bool IsDeleted { get; set; }
}
// 配置全局过滤器
DB.fsql.GlobalFilter.Apply<ISoftDelete>("soft_delete", a => a.IsDeleted == false);
// 删除操作会自动变为更新IsDeleted字段
DB.fsql.Delete<DbPlayer>().Where(a => a.Id == 1).ExecuteAffrows();
7.3 多数据库支持
FreeSql支持多种数据库,可以通过配置轻松切换:
csharp复制// 根据环境变量使用不同数据库
var dbType = Environment.GetEnvironmentVariable("DB_TYPE") ?? "MySQL";
var fsql = new FreeSqlBuilder()
.UseConnectionString(GetDataType(dbType), GetConnectionString(dbType))
.Build();
private static DataType GetDataType(string dbType)
{
return dbType switch
{
"MySQL" => DataType.MySql,
"SQLite" => DataType.Sqlite,
_ => DataType.MySql
};
}
在实际项目中使用FreeSql后,我发现它显著减少了数据库相关的代码量,同时提供了足够的灵活性来处理复杂场景。特别是在Unity这种环境下,FreeSql的性能表现非常出色,没有出现明显的GC压力。