转:C#制作ORM映射学习笔记三 ORM映射实现
现在开始实现ORM的主体模块,首先需要在项目中新建一个类,命名为DbAccess,然后在项目的引用中添加两个dll,分别是MySql.Data.dll和System.Data.SQLite.dll,这两个dll都可以在对应的数据库官网上下载到,为了方便我这里也提供一个下载地址。添加好dll后需要在DbAccess中添加几个名空间,具体代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.Reflection;
using MySql.Data;
using MySql.Data.MySqlClient;
下面开始实现ORM,首先需要实现对数据库的访问,具体代码如下:
private DbConnection dbConnection; private DbCommand dbCommand; private DbDataReader reader; //打开数据库连接
public void OpenDB()
{
try
{
switch (DbConfig.Type)
{
case DbType.Sqlite: dbConnection = new SQLiteConnection("data source = " + DbConfig.Host); break;
case DbType.Mysql: dbConnection = new MySqlConnection(DbConfig.Host); break;
default: break;
}
dbConnection.Open();
}
catch (Exception e)
{
throw e;
}
} //关闭数据库连接
public void CloseSqlConnection()
{
if (dbCommand != null)
{
dbCommand.Dispose();
}
dbCommand = null;
if (reader != null)
{
reader.Dispose();
}
reader = null;
if (dbConnection != null && dbConnection.State == ConnectionState.Open)
{
dbConnection.Close();
dbConnection.Dispose();
}
dbConnection = null;
} //执行Sql命令
public int ExecuteQuery(string sql)
{
OpenDB();
dbCommand = dbConnection.CreateCommand();
dbCommand.CommandText = sql;
reader = dbCommand.ExecuteReader();
return reader.RecordsAffected;
}
实现了对数据库的连接访问后就可以开始具体实现ORM了,首先实现两个查询方法:FirstOrDefault和Fetch,分别实现查询第一个满足条件的记录和查询所有满足条件的记录,代码如下:
//查询符合条件的第一个记录
public T FirstOrDefault<T>(Sql sql)
{
try
{
ExecuteQuery(sql.GetSql());
T result = default(T);
if (reader.Read())
{
Type type = typeof(T);
if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type.IsEnum)
{
if (type.IsEnum)
{
result = (T)Enum.ToObject(type, reader.GetValue());
}
else
{
result = (T)Convert.ChangeType(reader.GetValue(), type);
}
}
else
{
result = Activator.CreateInstance<T>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
string columName = AttributeProcess.GetColumnName(property);
if (property.PropertyType.IsEnum)
{
property.SetValue(result, Enum.ToObject(property.PropertyType, reader.GetValue(reader.GetOrdinal(columName))), null);
}
else
{
property.SetValue(result, Convert.ChangeType(reader.GetValue(reader.GetOrdinal(columName)), property.PropertyType), null);
}
}
}
}
return result;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
} //查询所有符合条件的记录
public List<T> Fetch<T>(Sql sql)
{
try
{
ExecuteQuery(sql.GetSql());
List<T> list = new List<T>();
Type type = typeof(T);
if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type.IsEnum)
{
while (reader.Read())
{
if (type.IsEnum)
{
list.Add((T)Enum.ToObject(type, reader.GetValue()));
}
else
{
list.Add((T)Convert.ChangeType(reader.GetValue(), type));
}
}
}
else
{
while (reader.Read())
{
T result = Activator.CreateInstance<T>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
string columName = AttributeProcess.GetColumnName(property);
if (property.PropertyType.IsEnum)
{
property.SetValue(result, Enum.ToObject(property.PropertyType, reader.GetValue(reader.GetOrdinal(columName))), null);
}
else
{
property.SetValue(result, Convert.ChangeType(reader.GetValue(reader.GetOrdinal(columName)), property.PropertyType), null);
}
}
list.Add(result);
}
}
return list;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
}
这里有两点需要注意,第一、一定要再finally中执行CloseSqlConnection,确保每次查询结束后都会关闭连接,哪怕是查询时出现异常。第二、对于只查询一列的情况要特殊处理。
下面来实现增删改三个方法,代码如下:
/// <summary>
/// 更新指定的列
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="columns"></param>
/// <returns></returns>
public bool Update<T>(T data, IEnumerable<string> columns)
{
try
{
if (columns == null || columns.Count() == )
{
Update<T>(data);
}
Type type = data.GetType();
string table = AttributeProcess.GetTableName(type);
string sql = "Update " + table + " Set ";
string where = " Where ";
List<string> sets = new List<string>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
string column = AttributeProcess.GetColumnName(property);
if (!AttributeProcess.IsPrimary(type, property))
{
if (columns.Any(a => a == column))
{
if (property.PropertyType == typeof(bool))
{
bool value = bool.Parse(property.GetValue(data, null).ToString());
sets.Add(column + "=" + (value ? "" : ""));
}
else if (property.PropertyType.IsPrimitive)
{
sets.Add(column + "=" + property.GetValue(data, null));
}
else if (property.PropertyType.IsEnum)
{
int intValue = (int)property.GetValue(data, null);
sets.Add(column + "=" + intValue);
}
else
{
if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))
{
sets.Add(column + "=\'" + property.GetValue(data, null) + "\'");
}
}
}
}
else
{
if (property.PropertyType.IsPrimitive)
{
where += column + "=" + property.GetValue(data, null);
}
else
{
where += column + "=\'" + property.GetValue(data, null) + "\'";
}
}
}
sql += (string.Join(",", sets) + where);
ExecuteQuery(sql);
return true;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
} //更新指定的记录
public bool Update<T>(T data)
{
try
{
Type type = data.GetType();
string table = AttributeProcess.GetTableName(type);
string sql = "Update " + table + " Set ";
List<string> sets = new List<string>();
string where = " Where ";
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
string column = AttributeProcess.GetColumnName(property);
if (!AttributeProcess.IsPrimary(type, property))
{
if (property.PropertyType == typeof(bool))
{
bool value = bool.Parse(property.GetValue(data, null).ToString());
sets.Add(column + "=" + (value ? "" : ""));
}
else if (property.PropertyType.IsPrimitive)
{
sets.Add(column + "=" + property.GetValue(data, null));
}
else if (property.PropertyType.IsEnum)
{
int intValue = (int)property.GetValue(data, null);
sets.Add(column + "=" + intValue);
}
else
{
if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))
{
sets.Add(column + "=\'" + property.GetValue(data, null) + "\'");
}
}
}
else
{
if (property.PropertyType.IsPrimitive)
{
where += column + "=" + property.GetValue(data, null);
}
else
{
where += column + "=\'" + property.GetValue(data, null) + "\'";
}
}
}
sql += (string.Join(",", sets) + where);
ExecuteQuery(sql);
return true;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
} //插入新数据
public bool Insert<T>(T data)
{
try
{
Type type = data.GetType();
string table = AttributeProcess.GetTableName(type);
List<string> columns = new List<string>();
List<string> values = new List<string>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (!(AttributeProcess.IsPrimary(type, property) && AttributeProcess.IsIncrement(type)))
{
if (property.GetValue(data, null) != null)
{
columns.Add(AttributeProcess.GetColumnName(property));
if (property.PropertyType == typeof(bool))
{
bool value = bool.Parse(property.GetValue(data, null).ToString());
values.Add((value ? "" : ""));
}
else if (property.PropertyType.IsPrimitive)
{
values.Add(property.GetValue(data, null).ToString());
}
else if (property.PropertyType.IsEnum)
{
int intValue = (int)property.GetValue(data, null);
values.Add(intValue.ToString());
}
else
{
if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))
{
values.Add("\'" + property.GetValue(data, null) + "\'");
}
}
}
}
}
string sql = "INSERT INTO " + table + "(" + string.Join(",", columns) + ")" + "VALUES" + "(" + string.Join(",", values) + ")";
ExecuteQuery(sql);
return true;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
} //删除数据
public bool Delete<T>(object id)
{
try
{
Type type = typeof(T);
string table = AttributeProcess.GetTableName(type);
string sql = "DELETE FROM " + table + " WHERE ";
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (AttributeProcess.IsPrimary(type, property))
{
sql += (AttributeProcess.GetColumnName(property) + "=");
if (property.PropertyType.IsPrimitive)
{
sql += (id.ToString() + ";");
}
else
{
sql += ("\'" + id.ToString() + "\';");
}
}
}
ExecuteQuery(sql);
return true;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
}
上面我实现了两个update,因为如果只实现一个对一行数据的所有列的update的话,那么在实现一些如更是状态等只更新某几列数据的功能时数据更新会变慢。此外还有两点需要注意,第一、每个方法最后都要关闭数据库访问连接;第二update和insert中设置数据值的地方如果数据为string类型需要进行防sql注入的操作。
下面是完整代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.Reflection;
using MySql.Data;
using MySql.Data.MySqlClient; namespace ORM
{
public class DbAccess
{
private DbConnection dbConnection; private DbCommand dbCommand; private DbDataReader reader; //打开数据库连接
public void OpenDB()
{
try
{
switch (DbConfig.Type)
{
case DbType.Sqlite: dbConnection = new SQLiteConnection("data source = " + DbConfig.Host); break;
case DbType.Mysql: dbConnection = new MySqlConnection(DbConfig.Host); break;
default: break;
}
dbConnection.Open();
}
catch (Exception e)
{
throw e;
}
} //关闭数据库连接
public void CloseSqlConnection()
{
if (dbCommand != null)
{
dbCommand.Dispose();
}
dbCommand = null;
if (reader != null)
{
reader.Dispose();
}
reader = null;
if (dbConnection != null && dbConnection.State == ConnectionState.Open)
{
dbConnection.Close();
dbConnection.Dispose();
}
dbConnection = null;
} //执行Sql命令
public int ExecuteQuery(string sql)
{
OpenDB();
dbCommand = dbConnection.CreateCommand();
dbCommand.CommandText = sql;
reader = dbCommand.ExecuteReader();
return reader.RecordsAffected;
} //查询符合条件的第一个记录
public T FirstOrDefault<T>(Sql sql)
{
try
{
ExecuteQuery(sql.GetSql());
T result = default(T);
if (reader.Read())
{
Type type = typeof(T);
if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type.IsEnum)
{
if (type.IsEnum)
{
result = (T)Enum.ToObject(type, reader.GetValue());
}
else
{
result = (T)Convert.ChangeType(reader.GetValue(), type);
}
}
else
{
result = Activator.CreateInstance<T>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
string columName = AttributeProcess.GetColumnName(property);
if (property.PropertyType.IsEnum)
{
property.SetValue(result, Enum.ToObject(property.PropertyType, reader.GetValue(reader.GetOrdinal(columName))), null);
}
else
{
property.SetValue(result, Convert.ChangeType(reader.GetValue(reader.GetOrdinal(columName)), property.PropertyType), null);
}
}
}
}
return result;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
} //查询所有符合条件的记录
public List<T> Fetch<T>(Sql sql)
{
try
{
ExecuteQuery(sql.GetSql());
List<T> list = new List<T>();
Type type = typeof(T);
if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type.IsEnum)
{
while (reader.Read())
{
if (type.IsEnum)
{
list.Add((T)Enum.ToObject(type, reader.GetValue()));
}
else
{
list.Add((T)Convert.ChangeType(reader.GetValue(), type));
}
}
}
else
{
while (reader.Read())
{
T result = Activator.CreateInstance<T>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
string columName = AttributeProcess.GetColumnName(property);
if (property.PropertyType.IsEnum)
{
property.SetValue(result, Enum.ToObject(property.PropertyType, reader.GetValue(reader.GetOrdinal(columName))), null);
}
else
{
property.SetValue(result, Convert.ChangeType(reader.GetValue(reader.GetOrdinal(columName)), property.PropertyType), null);
}
}
list.Add(result);
}
}
return list;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
} /// <summary>
/// 更新指定的列
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="columns"></param>
/// <returns></returns>
public bool Update<T>(T data, IEnumerable<string> columns)
{
try
{
if (columns == null || columns.Count() == )
{
Update<T>(data);
}
Type type = data.GetType();
string table = AttributeProcess.GetTableName(type);
string sql = "Update " + table + " Set ";
string where = " Where ";
List<string> sets = new List<string>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
string column = AttributeProcess.GetColumnName(property);
if (!AttributeProcess.IsPrimary(type, property))
{
if (columns.Any(a => a == column))
{
if (property.PropertyType == typeof(bool))
{
bool value = bool.Parse(property.GetValue(data, null).ToString());
sets.Add(column + "=" + (value ? "" : ""));
}
else if (property.PropertyType.IsPrimitive)
{
sets.Add(column + "=" + property.GetValue(data, null));
}
else if (property.PropertyType.IsEnum)
{
int intValue = (int)property.GetValue(data, null);
sets.Add(column + "=" + intValue);
}
else
{
if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))
{
sets.Add(column + "=\'" + property.GetValue(data, null) + "\'");
}
}
}
}
else
{
if (property.PropertyType.IsPrimitive)
{
where += column + "=" + property.GetValue(data, null);
}
else
{
where += column + "=\'" + property.GetValue(data, null) + "\'";
}
}
}
sql += (string.Join(",", sets) + where);
ExecuteQuery(sql);
return true;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
} //更新指定的记录
public bool Update<T>(T data)
{
try
{
Type type = data.GetType();
string table = AttributeProcess.GetTableName(type);
string sql = "Update " + table + " Set ";
List<string> sets = new List<string>();
string where = " Where ";
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
string column = AttributeProcess.GetColumnName(property);
if (!AttributeProcess.IsPrimary(type, property))
{
if (property.PropertyType == typeof(bool))
{
bool value = bool.Parse(property.GetValue(data, null).ToString());
sets.Add(column + "=" + (value ? "" : ""));
}
else if (property.PropertyType.IsPrimitive)
{
sets.Add(column + "=" + property.GetValue(data, null));
}
else if (property.PropertyType.IsEnum)
{
int intValue = (int)property.GetValue(data, null);
sets.Add(column + "=" + intValue);
}
else
{
if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))
{
sets.Add(column + "=\'" + property.GetValue(data, null) + "\'");
}
}
}
else
{
if (property.PropertyType.IsPrimitive)
{
where += column + "=" + property.GetValue(data, null);
}
else
{
where += column + "=\'" + property.GetValue(data, null) + "\'";
}
}
}
sql += (string.Join(",", sets) + where);
ExecuteQuery(sql);
return true;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
} //插入新数据
public bool Insert<T>(T data)
{
try
{
Type type = data.GetType();
string table = AttributeProcess.GetTableName(type);
List<string> columns = new List<string>();
List<string> values = new List<string>();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (!(AttributeProcess.IsPrimary(type, property) && AttributeProcess.IsIncrement(type)))
{
if (property.GetValue(data, null) != null)
{
columns.Add(AttributeProcess.GetColumnName(property));
if (property.PropertyType == typeof(bool))
{
bool value = bool.Parse(property.GetValue(data, null).ToString());
values.Add((value ? "" : ""));
}
else if (property.PropertyType.IsPrimitive)
{
values.Add(property.GetValue(data, null).ToString());
}
else if (property.PropertyType.IsEnum)
{
int intValue = (int)property.GetValue(data, null);
values.Add(intValue.ToString());
}
else
{
if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))
{
values.Add("\'" + property.GetValue(data, null) + "\'");
}
}
}
}
}
string sql = "INSERT INTO " + table + "(" + string.Join(",", columns) + ")" + "VALUES" + "(" + string.Join(",", values) + ")";
ExecuteQuery(sql);
return true;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
} //删除数据
public bool Delete<T>(object id)
{
try
{
Type type = typeof(T);
string table = AttributeProcess.GetTableName(type);
string sql = "DELETE FROM " + table + " WHERE ";
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (AttributeProcess.IsPrimary(type, property))
{
sql += (AttributeProcess.GetColumnName(property) + "=");
if (property.PropertyType.IsPrimitive)
{
sql += (id.ToString() + ";");
}
else
{
sql += ("\'" + id.ToString() + "\';");
}
}
}
ExecuteQuery(sql);
return true;
}
catch (Exception e)
{
throw e;
}
finally
{
CloseSqlConnection();
}
}
}
}
在之前建立的userinfo表中插入一条数据用于测试,如下图:
然后在main函数中添加如下代码进行测试:
static void Main(string[] args)
{
DbAccess dao = new DbAccess();
Sql sql = new Sql();
sql.Select("*").From("userinfo");
sql.Where("Id=@0", );
User user = dao.FirstOrDefault<User>(sql);
Console.WriteLine(user.UserName);
user.UserName = "tczhoulan";
Console.WriteLine(dao.Update<User>(user, new string[] { "UserName" }));
}
执行结果如下图:
再看数据库中的数据,UserName中的值已经被修改了,如下图所示:
到这里一个简单的ORM映射框架就基本完成了,当然这只是一个最简单的ORM框架,其中还有许多不完善的地方,如有需要可以自己在上面进行扩充,我也会慢慢的进行完善。
转:C#制作ORM映射学习笔记三 ORM映射实现的更多相关文章
- 2018/2/13 ElasticSearch学习笔记三 自动映射以及创建自动映射模版,ElasticSearch聚合查询
终于把这些命令全敲了一遍,话说ELK技术栈L和K我今天花了一下午全部搞定,学完后还都是花式玩那种...E却学了四天(当然主要是因为之前上班一直没时间学,还有安装服务时出现的各种error真是让我扎心了 ...
- JSP学习笔记(三):简单的Tomcat Web服务器
注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...
- 学习笔记(三)--->《Java 8编程官方参考教程(第9版).pdf》:第十章到十二章学习笔记
回到顶部 注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法 ...
- JAVA WEB学习笔记(三):简单的基于Tomcat的Web页面
注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...
- Java IO学习笔记三:MMAP与RandomAccessFile
作者:Grey 原文地址:Java IO学习笔记三:MMAP与RandomAccessFile 关于RandomAccessFile 相较于前面提到的BufferedReader/Writer和Fil ...
- Oracle学习笔记三 SQL命令
SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)
- [Firefly引擎][学习笔记三][已完结]所需模块封装
原地址:http://www.9miao.com/question-15-54671.html 学习笔记一传送门学习笔记二传送门 学习笔记三导读: 笔记三主要就是各个模块的封装了,这里贴 ...
- java之jvm学习笔记三(Class文件检验器)
java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...
- VSTO学习笔记(三) 开发Office 2010 64位COM加载项
原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...
随机推荐
- ZendFramework-2.4 源代码 - 关于MVC - View层 - 在模板内渲染子模板
<?php // 方式一: // 1.在模板内直接编写如下内容即可 $viewModel = new ViewModel(); $viewModel->setTemplate('album ...
- mysql索引详细描述与应用场景
索引的数据结构: (1)一般是B+tree:MySql使用最频繁的一个索引数据结构,数据结构以平衡树的形式来组织,因为是树型结构,所以更适合用来处理排序,范围查找等功能. (2)Hash:Hsah索引 ...
- Python知识点进阶——生成器
生成器 为什么要将列表转化为迭代器? 因为列表太大的话用内存太大,做成迭代器可以节省空间,用的时候再拿出部分. 生成器是不会把结果保存在一个系列中,而是保存生成器的状态,在每次进行迭代时返回一个值,知 ...
- 三次样条插值matlab实现
三次样条插值matlab实现 %三次样条差值-matlab通用程序 - zhangxiaolu2015的专栏 - CSDN博客 https://blog.csdn.net/zhangxiaolu201 ...
- 使用Hbase快照将数据输出到互联网区测试环境的临时Hbase集群
通过snapshot对内网测试环境Hbase生产集群的全量数据(包括原始数据和治理后数据)复制到互联网Hbase临时集群.工具及原理: 1) Hbase自带镜像导出工具(snapsho ...
- requests中文页面乱码解决方案【转】
requests中文页面乱码解决方案! 请给作者点赞 --> 原文链接 Python中文乱码,是一个很大的坑,自己不知道在这里遇到多少问题了.还好通过自己不断的总结,现在遇到乱码的情况越来越 ...
- Kali 中文家目录改英文目录
中文版Kali装好之后,家目录会中文显示,不便操作 root@kali:~# ls -l drwxr-xr-x root root .0K 7月 : 公共 drwxr-xr-x root root . ...
- MySQL之索引(一)
创建高性能索引 索引是存储引擎用于快速找到记录的一种数据结构.这是索引的基本功能.索引对于良好的性能非常关键.尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要.在数据量较小且负载较低时,不恰当 ...
- [netty4][netty-transpot]Channel体系分析
Channel体系分析 接口与类结构体系 -- [I]AttributeMap, ChannelOutboundInvoker, Comparable -- [I]AttributeMap ---- ...
- Python-S9-Day125-Web微信&爬虫框架之scrapy
01 今日内容概要 02 内容回顾:爬虫 03 内容回顾:网络和并发编程 04 Web微信之获取联系人列表 05 Web微信之发送消息 06 为什么request.POST拿不到数据 07 到底使用j ...