现在开始实现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映射实现的更多相关文章

  1. 2018/2/13 ElasticSearch学习笔记三 自动映射以及创建自动映射模版,ElasticSearch聚合查询

    终于把这些命令全敲了一遍,话说ELK技术栈L和K我今天花了一下午全部搞定,学完后还都是花式玩那种...E却学了四天(当然主要是因为之前上班一直没时间学,还有安装服务时出现的各种error真是让我扎心了 ...

  2. JSP学习笔记(三):简单的Tomcat Web服务器

    注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...

  3. 学习笔记(三)--->《Java 8编程官方参考教程(第9版).pdf》:第十章到十二章学习笔记

    回到顶部 注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法 ...

  4. JAVA WEB学习笔记(三):简单的基于Tomcat的Web页面

    注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...

  5. Java IO学习笔记三:MMAP与RandomAccessFile

    作者:Grey 原文地址:Java IO学习笔记三:MMAP与RandomAccessFile 关于RandomAccessFile 相较于前面提到的BufferedReader/Writer和Fil ...

  6. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  7. [Firefly引擎][学习笔记三][已完结]所需模块封装

    原地址:http://www.9miao.com/question-15-54671.html 学习笔记一传送门学习笔记二传送门 学习笔记三导读:        笔记三主要就是各个模块的封装了,这里贴 ...

  8. java之jvm学习笔记三(Class文件检验器)

    java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...

  9. VSTO学习笔记(三) 开发Office 2010 64位COM加载项

    原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...

随机推荐

  1. oracle for update和for update nowait 的区别

    原文地址:http://www.cnblogs.com/quanweiru/archive/2012/11/09/2762223.html 1.for update 和 for update nowa ...

  2. [BZOJ3312][USACO]不找零(状压DP)

    Description 约翰带着 N 头奶牛在超市买东西,现在他们正在排队付钱,排在第 i 个位置的奶牛需要支付 Ci元.今天说好所有东西都是约翰请客的,但直到付账的时候,约翰才意识到自己没带钱,身上 ...

  3. 笔记-python-build-in-types

    笔记-python-build-in-types 注:文档内容来源为Python 3.6.5 documentation 1.      built-in types 1.1.    真值测试 所有对 ...

  4. IQueryable与IEnumerable区别

    前者可以延迟加载,即执行完后不马上执行数据库语句,用到再加载.

  5. 微信公众开发api接口

      简介 微信公众平台消息接口为开发者提供了一种新的消息处理方式.微信公众平台消息接口为开发者提供与用户进行消息交互的能力.对于成功接入消息接口的微信公众账号,当用户发消息给公众号,微信公众平台服务器 ...

  6. thulac安装问题

    目标:在anaconda里面安装thulac. 1.打开Anaconda Prompt 2.输入pip install thulac 结果:报错! 报错画面如下: 解决方案: 1.到https://p ...

  7. js跨域post请求

    function funPostBack(srvMethod){ /* var contentNR=$(document.getElementById("reportFrame") ...

  8. android TranslateAnimation动画执行时的坐标获取。

    android 的Tween动画并不会改变控件的属性值,比如以下测试片段: 定义一个从屏幕右边进入,滚动到屏幕左边消失的一个TranslateAnimation动画: <?xml version ...

  9. IIS是怎么处理同时到来的多个请求的?

        假设有一台服务器,它的IIS上部署有一个Web应用程序-S,可以通过浏览器或其他方式进行访问.     假设有A.B.C三台电脑同时访问网站S,IIS接收到3个HTTP请求,然后分别为三个请求 ...

  10. java中的读/写锁

    读写锁接口:ReadWriteLock,它的具体实现类为:ReentrantReadWriteLock 使用场景: 对于一个资源,读读能共存,读写不能共存,写写不能共存. 锁降级:从写锁变成读锁: 锁 ...