分享
分销 收藏 举报 申诉 / 33
播放页_导航下方通栏广告

类型Nhibernate 全面分析.doc

  • 上传人:二***
  • 文档编号:4734865
  • 上传时间:2024-10-11
  • 格式:DOC
  • 页数:33
  • 大小:162KB
  • 下载积分:5 金币
  • 播放页_非在线预览资源立即下载上方广告
    配套讲稿:

    如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。

    特殊限制:

    部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。

    关 键  词:
    Nhibernate 全面分析 全面 分析
    资源描述:
    Nhibernate 全面分析 NHibernate 是一个基于.Net 的针对关系型数据库的对象持久化类库.Nhibernate 来源于非常优秀的基于Java的Hibernate 关系型持久化工具. NHibernate源码分析之开篇: 计划和安排只从使用NHibernate以来,请被其强大的功能和使用的简洁所吸引. 为了进一步研究NHibernate,决定分析其源代码,如有感兴趣者,欢迎一起研究. 这里列出了将要分析的部分: 官方源码下载地址: NHibernate配置和持久对象映射文件 NHibernate配置有三种方式来存放nhibernate的配置 1, 作为单独的一节放在相应程序的配置文件中,对于执行文件或类库为文件名称后加.config,对于则是放在web.config中.这种方式必须在配置文件的configSetions中声明nhibernate的配置节, 配置内容由Cfg.Environment类来读取,该类所有成员均为静态的,另外它还定义了配置中key值的常数. 2. 放在一个单独的配置文件中,默认为hibernate.cfg.xml,使用时必须调用Cfg.Configuration.Config().如不是默认的配置文件名,还必须指明配置文件名称.这种方式最适合多数据库的情况,可以为每个数据库建立一个配置文件. 3. 手工在程序中加入,配置内容最后将加入到Cfg.Configuration.Properties属性中,此属性为一IDictionary对象,并且为public,其余的就不用多话了吧. 下面对几个重要的key值说明一下: hibernate.connection.provider 连接提供者,取值必须是实现了IConnectionProvider接口的类的全名,当前版本只能取值NHibernate.Connection.DriverConnectionProvider; hibernate.connection.driver_class 数据驱动类,取值必须是实现了IDriver接口的类的全名,常用的选择有NHibernate.Driver.SqlClientDriver, NHibernate.Driver.OleDbDriver等; hibernate.dialect 数据库方言,取值必须是继承之Dialect的类的全名,最常用的就是NHibernate.Dialect.MsSql2000Dialect了, 其它的没用过,不清楚能不能正常使用; hibernate.connection.connection_string 连接字符串,取值与driver_class对应即可; hibernate.show_sql 指明是否在log4net日志中显示sql语句,主要用于调试,取值为true或false; 完整的配置key列表请查看Cfg.Environment类中的常数声明. 持久对象映射文件 nhibernate为我们提供了很多方式将持久对象映射文件加入到Cfg.Configuration类,下面将其一一列出: AddXmlFile:加入包括对象映射信息的文件; AddXmlString:加入包含映射信息的字符串; AddDocument:加入包含映射信息的Xmldocument; AddInputStream:加入包含映射信息的输入流; AddXmlReader:加入包含映射信息的XmlReader; AddResource:加入指定程序集的映射信息资源; AddClass:加入以类名指定的映射信息资源,映射文件必须为classname.hbm.xml; AddAssembly:加入指定程序集名称的映射信息资源注意:如果映射信息为文件形式,包括加入到程序集资源的文件,那么文件名必须以.hbm.xml结尾. NHibernate架构分析uml图从图中可以看到,Session和SessionFactory是NHibernate的核心部分. SessionFactory维护到持久机制(数据库)的连接并对它们进行管理,同时还保存着所有持久对象的映射信息. SessionFactory由Configuration.BuildSessionFactory创建,这个对象一般使用Singleton模式. Session用于将对象持久化,支持数据库事务,另外Session还提供了强大的数据加载功能. Session由SessionFactory创建. 其它对象说明: IConnectionProvider: 连接提供者接口,负责与数据进行连接; Dialect: 数据库方言; CollectionPersister: 集合持久化类; IClassPersister: 类持久化接口,定义了基本的CRUD操作; TransactionFactory: 数据库事务工厂; IInterceptor: 拦截器接口,用于在操作执行时进行一些处理,典型的就是记录操作日志; NHibernate源码分析之一: 配置信息配置信息用于指定NH以何种方式访问数据库, 根据这些配置信息, NH动态的创建数据访问对象并与数据库进行交互. 除了.net类库自带的Odbc, OleDb, OracleClient和SqlClient访问方式外, 在0.2版中, NH增加了用于访问MySQL和Firebird的访问方式, 这两种访问方式由第三方组件提供, mono的用户应该高兴了. :) NH的配置有两种存放方式存放在应用程序集的配置文件中, 对于Web应用程序则存放在Web.config中. 这种方式必须指定配置节的处理程序(类); 存放在一个单独的xml文件中, 使用这种方式我们必须在程序中显式的加载配置文件, 本文后面有详细说明. 此方式有一个优点, 就是在多数据库的情况下, 可以用不同的配置文件与各个数据库进行对应. 配置内容先来看看配置内容, 下列是一个简单的配置例子: 连接提供者,取值必须是实现了IConnectionProvider接口的类的全名,当前版本只能取值NHibernate.Connection.DriverConnectionProvider. 数据库方言,取值必须是继承之Dialect的类的全名,最常用的就是NHibernate.Dialect.MsSql2000Dialect了吧, 谁让它是M$的了. 数据驱动类,取值必须是实现了IDriver接口的类的全名,常用的选择有NHibernate.Driver.SqlClientDriver, NHibernate.Driver.OleDbDriver等, 不过现在又多了ByteFXDataDriver(访问MySQL). 连接字符串,取值要与driver_class指定的数据驱动类对应. 配置节处理程序因为NH的配置信息为自定义配置节, 所以必须指定配置节处理程序, NH的配置内容采用key/value形式,这和预定义配置节appSettings是一样的,我们只要用.net内置的配置节处理程序就可以处理NH的配置内容了, 这个处理key/value形式的类就是NameValueSetionHandler. nhibernate配置节的声明如下: 注意Version的值对于不同的.net framework版本取值也可能不一样. 在nh中, Environment类用于读取配置信息, 代码如下 //*** Environment.cs - 65行 *** static Environment() { NameValueCollection props = System.Configuration.ConfigurationSettings.GetConfig("nhibernate") as NameValueCollection; if (props==null) { return; } foreach(string key in props.Keys) { properties[key] = props[key]; } } 这是一个静态构造函数, 在静态成员首次调用时执行. 配置信息放在properties集合中. //*** Environment.cs - 90行 *** public static IDictionary Properties { get { IDictionary copy = new Hashtable(properties.Count); foreach(DictionaryEntry de in properties) { copy[de.Key] = de.Value; } return copy; } } Properties属性用于访问配置信息, 注意这里并没有直接返回properties, 而是复制了一个集合用于返回. 曾有网友问为什么不是直接返回properties 可能的原因是如果返回properties(即引用)话, 那么配置信息将是共享的, 如果在程序中修改了properties, 那么将影响到其它地方. 另外Environment类中还定义了一些属性名称常数. 在程序中操作配置信息除了在配置文件中指定nh的配置信息外, nh还允许我们在程序中操作配置信息, 这对于一些敏感的数据,如数据库连接串,提供了一种安全的操作方法(可以在程序中加入连接串属性,而不用将其存储在配置文件中). Configuration类提供提供两个方法和一个属性用于操作配置信息. //*** Configuration.cs - 637行 *** public Configuration AddProperties(IDictionary properties) { foreach(DictionaryEntry de in properties) { this.properties.Add(de.Key, de.Value); } return this; } 将一个数据字典对象加入到配置属性中. //*** Configuration.cs - 646行 *** public Configuration SetProperty(string name, string value) { properties[name] = value; return this; } 设置指定的属性的值, name应使用Environment类中定义的那些属性名称常数. //*** Configuration.cs - 625行 *** public IDictionary Properties { get { return properties; } set { this.properties = value; } } 这个就不用多说的吧, 用dotNet的人都知道.有了Properties, 想干啥就干啥吧 :-) NHibernate源码分析之一续: 对象映射 1. 持久对象映射文件关于持久对象映射文件,这里就不多说了,可参考nhibernate的例子和文档. 在nhibernate源代码的根目录里有一个nhibernate-mapping-2.0.xsd文档,这个文档是nhibernate用来对映射文件进行验证的,我们也可以借助相关软件用这个文档来验证映射文件的有效性. 2. 映射信息的读取通过Configuration类,可以用多种方式读取映射信息,一些以Add开头的方法就是用来加入映射信息的,这些方法最终将调用Add(XmlDocument doc). //** Configuration.cs ** private Hashtable classes = new Hashtable(); classes集合用于存放所有的持久对象映射信息, 它的Key为持久类的类型;Value为PermissionClass类的子类. private void Add(XmlDocument doc) { try { Binder.dialect = Dialect.Dialect.GetDialect(properties); Binder.BindRoot( doc, CreateMappings()); } catch (MappingException me) { log.Error("Could not compile the mapping document", me); throw me; } // end try/catch } AddDocument方法调用Binder的静态方法BindRoot来绑定持久类映射信息.CreateMappings返回一个Mappings对象,此对象是一个简单封装了所有映射信息集合的类. 3. 建立对象映射信息 Binder类的BindRoot用于绑定映射信息中的所有映射内容. //** Binder.cs ** public static void BindRoot(XmlDocument doc, Mappings model) { // ... foreach(XmlNode n in hmNode.SelectNodes(nsPrefix + ":class", nsmgr) ) { RootClass rootclass = new RootClass(); Binder.BindRootClass(n, rootclass, model); model.AddClass(rootclass); } // ... } 遍历所有的类映射节点,然后调用BindRootClass来绑定类映射信息,最后将类映射信息加到集合中. 其中RootClass为PermissionClass的子类. public static void BindRootClass(XmlNode node, RootClass model, Mappings mappings) { BindClass(node, model, mappings); //TABLENAME XmlAttribute tableNameNode = node.Attributes["table"]; string tableName = (tableNameNode==null) StringHelper.Unqualify( model.PersistentClazz.Name ) : tableNameNode.Value; XmlAttribute schemaNode = node.Attributes["schema"]; string schema = schemaNode==null mappings.SchemaName : schemaNode.Value; Table table = mappings.AddTable(schema, tableName); model.Table = table; // ... PropertiesFromXML(node, model, mappings); } BindRootClass首先调用BindClass绑定持久类映射信息,然后调用PropertiesFromXML来绑定类属性. public static void BindClass(XmlNode node, PersistentClass model, Mappings mapping) { string className = node.Attributes["name"] == null null : node.Attributes["name"].Value; // class try { model.PersistentClazz = ReflectHelper.ClassForName(className); } catch ( Exception cnfe ) { throw new MappingException( "persistent class not found", cnfe); } // ... } BindClass通过反射来取得持久对象的类型. protected static void PropertiesFromXML(XmlNode node, PersistentClass model, Mappings mappings) { string path = model.Name; Table table = model.Table; foreach(XmlNode subnode in node.ChildNodes) { CollectionType collectType = CollectionType.CollectionTypeFromString(name); Value value = null; if (collectType!=null) { value = new Value(table); BindValue(subnode, value, true); } else if ( "many-to-one".Equals(name) ) { value = new ManyToOne(table); BindManyToOne(subnode, (ManyToOne) value, propertyName, true); } else if ( "any".Equals(name) ) { value = new Any(table); BindAny(subnode, (Any) value, true); } else if ( "one-to-one".Equals(name) ) { value = new OneToOne(table, model.Identifier ); BindOneToOne(subnode, (OneToOne) value, true); } else if ( "property".Equals(name) ) { value = new Value(table); BindValue(subnode, value, true, propertyName); } else if ( "component".Equals(name) ) { value = new Component(model); BindComponent(subnode, (Component) value, reflectedClass, subpath, true, mappings); } else if ( "subclass".Equals(name) ) { Subclass subclass = new Subclass(model); BindSubclass( subnode, subclass, mappings ); } else if ( "joined-subclass".Equals(name) ) { Subclass subclass = new Subclass(model); BindJoinedSubclass( subnode, subclass, mappings); } if ( value!=null) { Property prop = new Property(value); BindProperty(subnode, prop, mappings); } } } 遍历所有子节点,然后根据节点类型对进行绑定.(注: 部分内容已删除) 关于属性的映射以后有空再详细研究,只需要知道属性已加入到RootClass的Properties属性就行了. NHibernate源码分析之二: 会话工厂会话工厂是NHibernate中的关键类,它与数据库连接,数据库事务等进行交互,还存储着与所有持久对象类型关联的持久化对象,持久化类是持久化的关键,它实现基本的CRUD操作. 当用户需要持久操作时,由会话工厂创建一个会话供用户进行持久操作. 1. 会话工厂的创建会话工厂由ISessionFactory接口实现,由Configuration的BuildSessionFactory方法创建,会话工厂应该使用Singleton模式. 如果要访问多个数据库,应建立多个会话工厂. //*** Configuration.cs *** public ISessionFactory BuildSessionFactory() { // ... Hashtable copy = new Hashtable(); foreach(DictionaryEntry de in properties) { copy.Add(de.Key, de.Value); } return new SessionFactoryImpl(this, copy, interceptor); } 其中SessionFactoryImpl为实现ISessionFactory的类,这个类的修饰为Internal. 2. 持久化类的创建持久化类用于对持久对象进行持久化操作,每一个持久对象类型都有一个与之关联的持久化对象. 持久化类继承自IClassPersister接口,这个接口定义了用于持久对象的CRUD操作. //*** SessionFactoryImpl *** private IDictionary classPersisters; 持久化对象集合,Key为持久对象的类型; private IDictionary classPersistersByName; 持久化对象集合,Key为持久对象的类名; public SessionFactoryImpl(Configuration cfg, IDictionary properties, IInterceptor interceptor) { // ... foreach(PersistentClass model in cfg.ClassMappings) { System.Type persisterClass = model.Persister; IClassPersister cp; //TODO: H2.0.3 created a PersisterFactory if (persisterClass==null || persisterClass==typeof(EntityPersister)) { cp = new EntityPersister(model, this); } else if (persisterClass==typeof(NormalizedEntityPersister)) { cp = new NormalizedEntityPersister(model, this); } else { cp = InstantiatePersister(persisterClass, model); } classPersisters[model.PersistentClazz] = cp; classPersistersByName[model.Name] = cp ; } // ... } 在构造函数中遍历所有持久类映射信息,然后根据持久类的持久类型建立一个持久化对象,并将此对象加入到集合中. InstantiatePersister用于创建一个自定义的持久化对象,类名称由映射文件中Class节点的Persister属性指定,自定义持久化类必须实现IClassPersister接口. 3. 连接提供者连接提供者由IConnectionProvider接口实现,会话工厂通过连接提供者与持久机制(数据库等)进行交互,例如取得数据库连接等. //*** SessionFactoryImpl.cs *** public SessionFactoryImpl(Configuration cfg, IDictionary properties, IInterceptor interceptor) { // ... connectionProvider = ConnectionProviderFactory.NewConnectionProvider(properties); // ... } 还是在构造函数中,连接提供者由连接提供者工厂根据配置属性来创建. //*** ConnectionProviderFactory *** public static IConnectionProvider NewConnectionProvider(IDictionary settings) { IConnectionProvider connections = null; string providerClass = settings[Cfg.Environment.ConnectionProvider] as string; if (providerClass != null) { try { connections = (IConnectionProvider) Activator.CreateInstance(System.Type.GetType(providerClass)); } catch (Exception e) { throw new HibernateException("Could not instantiate connection provider: " + providerClass); } } else { throw new NotImplementedException("We have not implemented user supplied connections yet."); } connections.Configure(settings); return connections; } NewConnectionProvider方法通过配置中ConnectionProvider的值来创建连接提供者.当前版本(v0.0.5)唯一可用的提供者只有DriverConnectionProvider类. // 部分内容等有空再补充. NHibernate源码分析之三: 会话与持久化操作会话是nhibernate中的主要接口,也是我们进行持久化操作和数据加载的主要接口,ISession在IClassPersister,ITransaction,ICriteria和IQuery之间起着协调者的作用. 会话对象通过调用会话工厂的OpenSession方法获得,OpenSession方法有一个参数interceptor,这是一个拦截器,由实现了IInterceptor接口的对象来完成,比较典型的是对会话的操作进行日志记录. 1. 持久对象的状态持久对象的状态由EntityEntry类来维护. sealed internal class EntityEntry { private LockMode _lockMode; private Status _status; private object _id; private object[] _loadedState; private object[] _deletedState; private bool _existsInDatabase; private object _version; // for convenience to save some lookups [NonSerialized] private IClassPersister _persister; private string _className; // ... } private IDictionary entitiesByKey; //key=Key, value=Object entitiesByKey集合保存当前会话中的所有持久对象. [NonSerialized] private IdentityMap entries;//key=Object, value=Entry entries集合维护当前会话中所有持久对象的状态,entries中的项目和entitiesByKey中的项目是一一对应的. 2. 持久化操作当执行持久化操作时(Save/Update/Delete),除了少数情况外,持久化操作并没有立即执行(更新数据源),而是被记录下来,直到会话 Flush时才会实际更新到数据源,这样做的原因很容易理解,就是为了避免频烦的数据库连接操作.如果没有调用Flush而关闭了会话,当前会话中的持久对象将不会持久化! SessionImpl.cs中有三个集合用来记录要持久化的计划对象: [NonSerialized] private ArrayList insertions; 记录所有的ScheduledInsertion对象,ScheduledInsertion对象是通过要Save的持久对象创建的,如果对象的标识必须从数据库获得(如Identity标识),那么并不会创建ScheduledInsertion对象,而是立即执行Save操作,原因很简单,因为必须取得 Identity标识; [NonSerialized] private ArrayList updates; 记录所有的ScheduledUpdate对象,ScheduledUpdate对象是通过要Update的持久对象创建的; [NonSerialized] private ArrayList deletions; 记录所有的ScheduledDeletion对象,ScheduledDeletion对象是通过要Delete的持久对象创建的; 以上三个计划对象都从ScheduledEntityAction对象继承,而此对象实现了IExecutable接口,IExecutable接口的Execute方法用于执行执久化操作,此操作由Flush间接调用. 下面来看看Flush的代码: public void Flush() { if (cascading>0) throw new HibernateException( "..." ); FlushEverything(); Execute(); PostFlush(); } Execute执行所有的计划对象. private void Execute() { log.Debug("executing flush"); try { ExecuteAll( insertions ); insertions.Clear(); ExecuteAll( updates ); updates.Clear(); //... ExecuteAll( deletions ); deletions.Clear(); } catch (Exception e) { throw new ADOException("...", e); } } 分别执行insert/update/delete计划. private void ExecuteAll(ICollection coll) { foreach(IExecutable e in coll) { executions.Add(e); e.Execute(); } if ( batcher!=null ) batcher.ExecuteBatch(); } 3. Save ISession有两种保存持久对象的方法,区别在于有没有指定对象Id(标识符). public object Save(object obj) { if (obj==null) throw new NullReferenceException("attempted to save null"); if ( !NHibernate.IsInitialized(obj) ) throw new PersistentObjectException("uninitialized proxy passed to save()"); object theObj = UnproxyAndReassociate(obj); EntityEntry e = GetEntry(theObj); if ( e!=null ) { if ( e.Status==Status.Deleted) { Flush(); } else { log.Debug( "object already associated with session" ); return e.Id; } } object id; try { id = GetPersister(theObj).IdentifierGenerator.Generate(this, theObj); if( id == (object) IdentifierGeneratorFactory.ShortCircuitIndicator) return GetIdentifier(theObj); //TODO: yick! } catch (Exception ex) { throw new ADOException("Could not save object", ex); } return DoSave(theObj, id); } 先取得持久对象的状态,如为删除则flush;然后取得持久对象的id(标识符),最后调用DoSave方法. 有关持久对象的标识符请参考我的下一篇文章 《持久对象标识符》. public void Save(object obj, object id) { if (obj==null) throw new NullReferenceException("attemted to insert null"); if (id==null) throw new NullReferenceException("null identifier passed to insert()"); if ( !NHibernate.IsInitialized(obj) ) throw new PersistentObjectException("uninitialized proxy passed to save()"); object theObj = UnproxyAndReassociate(obj); EntityEntry e = GetEntry(theObj); if ( e!=null ) { if ( e.Status==Status.Deleted ) { Flush(); } else { if ( !id.Equals(e.Id) ) throw new PersistentObjectException("..."); } } DoSave(theObj, id); } 与前一个Save方法不同的是,不用取得持久对象的id,显然这个方法适用于对象标识符已知的情况,这样会提高一些性能. private object DoSave(object obj, object id) { IClassPersister persister = GetPersister(obj); Key key = null; bool identityCol; if (id==null) { if ( persister.IsIdentifierAssignedByInsert ) { identityCol = true; } else { throw new AssertionFailure("null id"); } } else { identityCol = false; } if (!identityCol) { // if the id is generated by the db, we assi
    展开阅读全文
    提示  咨信网温馨提示:
    1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
    2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
    3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
    4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
    5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
    6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。

    开通VIP折扣优惠下载文档

    自信AI创作助手
    关于本文
    本文标题:Nhibernate 全面分析.doc
    链接地址:https://www.zixin.com.cn/doc/4734865.html
    页脚通栏广告

    Copyright ©2010-2026   All Rights Reserved  宁波自信网络信息技术有限公司 版权所有   |  客服电话:0574-28810668    微信客服:咨信网客服    投诉电话:18658249818   

    违法和不良信息举报邮箱:help@zixin.com.cn    文档合作和网站合作邮箱:fuwu@zixin.com.cn    意见反馈和侵权处理邮箱:1219186828@qq.com   | 证照中心

    12321jubao.png12321网络举报中心 电话:010-12321  jubao.png中国互联网举报中心 电话:12377   gongan.png浙公网安备33021202000488号  icp.png浙ICP备2021020529号-1 浙B2-20240490   


    关注我们 :微信公众号  抖音  微博  LOFTER               

    自信网络  |  ZixinNetwork