ORM(Object Relational Mapping):对象关系映射;
笔记中演示的开发环境:
VS2019 Core 3.1;
NHibernate5.3.12;
MySql.Data8.0.29;
NHibernate.Mapping.Attributes;
MariaDB 5.5.68;
测试使用postman;
数据库可视化工具使用Navicat Premium
一、创建项目
笔记中使用一个Core框架的Web API项目为例,部分内容跟Nhibernate配置没有关系,不感兴趣可自行创建其他类型项目;
1)、创建项目
F5运行项目,
看到浏览器中的Json字符串表示项目创建成功;
2)、删除不必要的系统自建Demo文件,
WeatherForecastController.cs;
WeatherForecast.cs;
3)、添加控制器
4)、修改API调试状态下的配置文件lanchSettings.json
这个文件中的配置跟发布后的文件没有关系,只是VS调试的时候会起作用;
原来的文件内容如下:
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:62978",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"NhibernateDemol": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
修改后如下:
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"NhibernateDemol": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "NHibernateTest/gt",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
webapi的调试配置文件这么改的原因在之前关于webapi的笔记中已经提过,不在重复;
5)、修改控制器代码
文件:NHibernateTestController.cs
using Microsoft.AspNetCore.Mvc;
namespace NhibernateDemo.Controllers
{
[Route("[controller]")]
[ApiController]
public class NHibernateTestController : ControllerBase
{
[Route("gt")]
[HttpGet]
public ContentResult a()
{
return Content("This is a WebApiTest with GetMethod.");
}
}
}
添加一个测试webapi的get方法,测试项目创建成功;
6)、运行代码,postman测试服务正常如下图
以上工作,创建项目完成。
二、安装依赖包环境
1)、vs2019直接官网下载、安装后注册用户,登录就可以了
2)、MariaDB安装可以看之前的笔记,或者网上自行搜索;
3)、MySql.Data安装,鼠标右键点击项目,弹出选项中点管理NuGet程序包,检查一安装中是否已经存在历史版本,如果没有在浏览标签页中的输入框中输入mysql搜索,选中MySql.Data,一般搜索出来的第一个就是,然后右侧选择与你.Net框架兼容的版本,我用的是Core3.1,选择了8.0.29的版本安装;
4)、同上面一样安装NHibernate;
5)、上面一样安装NHibernate.Mapping.Attributes;
6)、检查是否都安装成功
三、确定数据库连接正常(检查MySql.Data的兼容没有问题)
先使用原生的MySql.Data.Dll组件,连接数据库,以确保我们带入的程序包MySql.Data是兼容的;
1)、在项目中添加文件夹Models
2)、在这个文件夹中添加Emp类;
using System;
namespace NhibernateDemo.Models
{
public class Emp
{
public int EmpId { get; set; }
public string EmpName { get; set; }
public DateTime Birthday { get; set; }
}
}
3)、使用Navicat Premium连接MySql(或者MariaDB)数据库,并添加Emp表;sql如下
CREATE TABLE `Emp` (
`empid` int(255) NOT NULL AUTO_INCREMENT,
`empname` varchar(255) NULL,
`birthday` date NULL,
PRIMARY KEY (`empid`)
);
我们看到,我们的表名和实体类名是一致的,字段名和属性名是一致的;虽然对于NHibernate这样做不是必须的,但是为了少些映射配置,而且阅读方便,我建议这样做,但是这样做也要求我们在设计表名的时候就要考虑到避开编程语言关键字;我认为这是可以接受的,还有就是,我的数据库是不区分大小写的,字符集使用的是gbk;连接数据库的时候要注意在连接字符串是限制CharSet属性为gbk,类的属性避开仅相差字母大小写的命名;
4)、使用MySql.Data,原生连接数据库;以确保版本的兼容性没有问题
首先确认表中没有数据;
然后在控制器类中加入一个方法,代码如下
[Route("AddEmpWithMySqlDataDll")]
[HttpGet]
public ContentResult AddEmpWithMySqlDataDll()
{
var connectStr = "server=xxx.xxx.xxx.xxx;port=xxxx;database=test;user=xxxxx;password=xxxxx;CharSet=gbk";
MySqlConnection conn = new MySqlConnection(connectStr);
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand("insert into emp (empname,birthday) values ('张三','2000-01-01')", conn);
var count = cmd.ExecuteNonQuery();//查询
return Content("成功向Emp表中插入一条数据!");
}
catch (Exception ex)
{
return Content(ex.Message);
}
}
控制器添加命名空间引用
using MySql.Data.MySqlClient;
连接字符串自己根据实际情况配置
然后运行程序,使用postman测试
去数据库中验证,数据正确插入;
以上测试通过证明MySql.Data程序包依赖包连接数据库正常;
四、使用XML配置文件配置数据库连接
项目根目录下添加hibernate.cfg.xml文件
文件内容如下
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="show_sql">true</property>
<property name="dialect">NHibernate.Dialect.MySQL5Dialect</property>
<property name="connection.connection_string">Server=xxx.xxx.xxx.xxx;port=3306;database=test;user=xxx;password=xxx;CharSet=gbk;</property>
</session-factory>
</hibernate-configuration>
上面的xml是配置连接我的MySql或者MariaDB,如果你用的是oracle、SQL Server等其他数据库;属性值需要做相应的修改;
文件属性设置成【始终复制】
这个属性配置很重要
五、配置表的映射
1)、项目添加Mappings文件夹
2)、Mappings文件夹中添加Emp.hbm.xml文件
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NhibernateDemo" namespace="NhibernateDemo.Models">
<class name="Emp" table="emp">
<id name="EmpId" column="empid" type="int">
<generator class="native" />
</id>
<property name="EmpName" column="empname" type="string"/>
<property name="Birthday" column="birthday" type="DateTime"/>
</class>
</hibernate-mapping>
assembly="NhibernateDemo"设置的是当前程序集的名称,
namespace="NhibernateDemo.Models"设置的是映射类的namespace;
文件施行设置成【嵌入的资源】
这个文件属性配置很重要
六、控制器添加测试方法
[Route("AddEmpWithHibernateXml")]
[HttpGet]
public ContentResult AddEmpWithHibernateXml()
{
var cfg = new Configuration();
cfg.Configure(); //装载hibernate.cfg.xml
cfg.AddAssembly("NhibernateDemo"); //装载NhibernateDemo程序集中的*.hbm.xml
ISessionFactory sessionFactory = null;
ISession session = null;
ITransaction transaction = null;
try
{
sessionFactory = cfg.BuildSessionFactory();
session = sessionFactory.OpenSession();
transaction = session.BeginTransaction();
Emp emp = new Emp() { EmpName = "李四", Birthday = DateTime.Now };
session.Save(emp);
transaction.Commit();
}
catch (Exception ex)
{
if(transaction!=null)
transaction.Rollback();
Console.WriteLine(ex);
return Content(ex.Message);
}
finally
{
if (transaction != null)
transaction.Dispose();
if (session != null)
session.Close();
if (sessionFactory != null)
sessionFactory.Close();
}
return Content("Emp 添加成功!");
}
控制器引用命名空间为
using System;
using Microsoft.AspNetCore.Mvc;
using MySql.Data.MySqlClient;
using NHibernate;
using NHibernate.Cfg;
using NhibernateDemo.Models;
这是我们应该可以用程序将向Emp插入李四的信息了;
数据库中查看数据如下:
注意,以上代码经常会遇到两个错误,如果 “cfg.Configure();”报错,说明第一个XML(hibernate.cfg.xml)文件有问题;如果cfg.AddAssembly("NhibernateDemo");报错,说明第二个XML(Emp.hbm.xml)文件有问题;
以上就实现了NHibernate的xml配置映射Emp对象;
七、使用C#的特性代替xml配置文件
上面的配置,虽然让我们实现了没有任何sql语句,就可以操作数据库;看起来很酷,但是配置文件的编写,也是很大的工作量,而且是很无趣的重复工作,还容易出现编写错误,虽然可以用一些代码生成工具生成配置文件,但是依然存在不好调试查找配置文件出现错误的位置,而有一定C#开发经验的人,很容易想到为什么不能用C#的Attribute来实现映射配置呢,幸运的是NHibernate.Mapping.Attributes的作者也是这么想的,并且很好的实现了这个想法,他的实现原理是既然NHibernate使用的是xml配置,我们就使用C#的Attribute,生成类的映射xml文件,然后使用NHibernate开放出来的API加载这个xml实现映射关系绑定;废话不多说,开始干!
1)、项目中创建一个DataAccess文件夹,存放数据库操作文件;
2)、在上面文件夹中添加一个类:NHibernateHelper.cs;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Connection;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Mapping.Attributes;
using System.IO;
using System.Reflection;
namespace NhibernateDemo.DataAccess
{
public class NhibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get {
if (_sessionFactory == null)
{
var cfg = new Configuration()
.DataBaseIntegration(dbi =>
{
dbi.Dialect<MySQL5Dialect>();
dbi.Driver<MySqlDataDriver>();
dbi.ConnectionProvider<DriverConnectionProvider>();
dbi.ConnectionString = "server=xxx.xxx.xxx.xxx;database=test;uid=xxxx;pwd=xxxx;Charset=gbk;port=xxxx";
dbi.LogSqlInConsole = dbi.LogFormattedSql = true;
});
using (MemoryStream stream = new MemoryStream())
{
HbmSerializer.Default.Serialize(stream, Assembly.GetExecutingAssembly());
stream.Position = 0;
cfg.AddInputStream(stream);
}
_sessionFactory = cfg.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
}
其中
var cfg = new Configuration()
.DataBaseIntegration(dbi =>
{
dbi.Dialect<MySQL5Dialect>();
dbi.Driver<MySqlDataDriver>();
dbi.ConnectionProvider<DriverConnectionProvider>();
dbi.ConnectionString = "server=xxx.xxx.xxx.xxx;database=test;uid=xxxx;pwd=xxxx;Charset=gbk;port=xxxx";
dbi.LogSqlInConsole = dbi.LogFormattedSql = true;
});
很容易看出来是代替数据库配置文件hibernate.cfg.xml的;
其中
using (MemoryStream stream = new MemoryStream())
{
HbmSerializer.Default.Serialize(stream, Assembly.GetExecutingAssembly());
stream.Position = 0;
cfg.AddInputStream(stream);
}
这小段代码是用数据流的方式,通过反射类的Attribute标签,生成xml,并装载到cfg中;
3)、为Emp类添加Attribute
using NHibernate.Mapping.Attributes;
using System;
namespace NhibernateDemo.Models
{
[Class(Lazy = true)]
public class Emp
{
[Id(TypeType = typeof(long))]
[Generator(2, Class = "native")]
public virtual long EmpID { get; set; }
[Property(TypeType = typeof(string))]
public virtual string EmpName { get; set; }
[Property(TypeType = typeof(DateTime))]
public virtual DateTime Birthday { get; set; }
}
}
此时需要导入命名空间NHibernate.Mapping.Attributes;
using NHibernate.Mapping.Attributes;
4)控制器中添加测试方法,代码如下:
[Route("NHibernateAttributeTest")]
[HttpGet]
public ContentResult NHibernateAttributeTest()
{
ITransaction transaction = null;
try
{
var session = NhibernateHelper.OpenSession();
Emp emp = new Emp() { EmpName = "王五", Birthday = DateTime.Now };
transaction = session.BeginTransaction();
session.Save(emp);
transaction.Commit();
}
catch (Exception ex)
{
if (transaction != null)
transaction.Rollback();
Console.WriteLine(ex.ToString());
}
finally
{
transaction.Dispose();
}
return Content("王五添加成功!");
}
控制器引入命名空间:
using NhibernateDemo.DataAccess;
这个时候,我们就可以把之前的两个配置文件的扩展名改掉(让NHibernate找不到xml文件,所以建议改扩展名) 然后运行程序,是用postman测试,如果不想在研究使用xml配置数据库和表映射,可以直接删除这两个xml和之前控制器中的测试代码,只保留最后这个测试代码;
至此,我们就实现类使用Attribute完成配置Nhibernate映射,这看起来比之前酷多了,不用写繁琐的配置文件,奖励的错误,并且应为使用了Attribute,映射错误也变得很容易被发现;
所有配置的属性,及映射特性里面的数据类型等知识点,可以到NHibernate的文档里查找;
以上内容是我的学习总结,仅供参考,因为内容较长,如有错漏,欢迎留言指正。