[Nhibernate]二级缓存(二)

目录

写在前面

文档与系列文章

更新数据

二级缓存管理

总结

写在前面

本篇文章也算nhibernate入门系列的结尾了,在总结nhibernate系列的过程中,遇到了很多问题,学习的过程也是解决bug的过程,在学习nhibernate的过程中还学习了单元测试的使用,这个是附属产品,我也没有想到,算是意外收获吧。这个系列学完了,正好公司有个项目,马上就要立项,正好能将学到的东西运用到实际的项目中,想想就让人激动。当然,我相信在实际的项目中,肯定还会遇到问题,但是通过本系列的学习,我已经不怕遇到bug了,解决bug的过程也是成长。废话不多说了,进入本篇学习的内容吧。这篇继续上一篇NHibernate二级缓存剩下的内容,比如你修改、删除数据时,二级缓存是什么策略呢?我们如果使用缓存查询呢?如何管理NHibernate二级缓存呢?

文档与系列文章

[Nhibernate]体系结构

[NHibernate]ISessionFactory配置

[NHibernate]持久化类(Persistent Classes)

[NHibernate]O/R Mapping基础

[NHibernate]集合类(Collections)映射 

[NHibernate]关联映射

[NHibernate]Parent/Child

[NHibernate]缓存(NHibernate.Caches)

[NHibernate]NHibernate.Tool.hbm2net

[NHibernate]Nullables

[NHibernate]Nhibernate如何映射sqlserver中image字段

[NHibernate]基本配置与测试 

[NHibernate]HQL查询 

[NHibernate]条件查询Criteria Query

[NHibernate]增删改操作

[NHibernate]事务

[NHibernate]并发控制

[NHibernate]组件之依赖对象

[NHibernate]一对多关系(级联删除,级联添加)

[NHibernate]一对多关系(关联查询)

[NHibernate]多对多关系(关联查询)

[NHibernate]延迟加载

[NHibernate]立即加载

[NHibernate]视图处理

[NHibernate]N+1 Select查询问题分析

[NHibernate]存储过程的使用(一)

[NHibernate]存储过程的使用(二)

[NHibernate]存储过程的使用(三)

[Nhibernate]SchemaExport工具的使用(一)——通过映射文件修改数据表

[Nhibernate]SchemaExport工具的使用(二)——创建表及其约束、存储过程、视图

[Nhibernate]对象状态

[Nhibernate]一级缓存

[Nhibernate]二级缓存(一)

更新数据

在启用二级缓存时候,我们将数据查询出来后,如果对数据进行了修改,再查看二级缓存中的数据,此时的数据是什么呢?

一个例子

修改CustomerData类,添加如下三个方法用于测试

 1         /// <summary>
 2         /// 开启二级缓存的情况下,修改customer对象
 3         /// </summary>
 4         /// <param name="customer"></param>
 5         public void ISessionFactoryCacheUpdate(Customer customer)
 6         {
 7             ISession session = NHibernateHelper.GetSession();
 8             using (var trans=session.BeginTransaction())
 9             {
10                 try
11                 {
12                     session.SaveOrUpdate(customer);
13                     session.Flush();
14                     trans.Commit();
15                 }
16                 catch (Exception)
17                 {
18                     trans.Rollback();
19                     throw;
20                 }
21             }
22         }
23         /// <summary>
24         /// 根据客户id查询
25         /// </summary>
26         /// <param name="customerID"></param>
27         /// <returns></returns>
28         public Customer GetCustomerById(Guid customerID)
29         {
30             ISession session = NHibernateHelper.GetSession();
31             return session.Get<Customer>(customerID);
32         }
33         /// <summary>
34         /// 开启二级缓存情况下,重置ISession然后,根据客户id查询
35         /// </summary>
36         /// <param name="customerID"></param>
37         /// <returns></returns>
38         public Customer GetCustomerById2(Guid customerID)
39         {
40             //重置Session
41             ISession session = NHibernateHelper.ResetSession();
42             return session.Get<Customer>(customerID);
43         }

单元测试

 1         [TestMethod]
 2         public void ISessionFactoryCacheUpdateTest()
 3         {
 4             string strCustomerName = "zhangsan2222";
 5             Console.WriteLine("第一次读取Customer数据");
 6             Customer customer = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
 7             Console.WriteLine("修改读取的customer对象的名字");
 8             customer.NameAddress.CustomerName = strCustomerName;
 9             Console.WriteLine("更新数据库");
10             _customerData.ISessionFactoryCacheUpdate(customer);
11             Console.WriteLine("重置Session,第二次读取Customer数据");
12             Customer c2 = _customerData.GetCustomerById2(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
13             Assert.AreEqual(c2.NameAddress.CustomerName, strCustomerName);
14         }

测试结果

在第一次查询数据时,由于一级、二级缓存中都不存在需要的数据,这时NHibernate从数据库中查询数据。我们修改这条数据并提交到数据库中,NHibernate执行一条更新语句,由于我们设置了读写缓存策略,NHibernate更新了二级缓存中的数据内容,第二次读取这条数据,NHibernate首先从内置缓存(一级缓存)中查找是否存在所需要数据,由于不是在同一个ISession中,所以内置ISession缓存(一级缓存)中不存在所需数据,NHibernate则查询二级缓存,这时由于第一次查询了这条数据,所以在二级缓存中存在所需数据,则直接使用缓存中数据。这时缓存中的数据也是最新的。

至于删除、插入数据我想也是类似的。这里我就不写测试了。

上篇文章多少接触了缓存查询的东西,这里在系统的说一下它吧

在NHibernate中,除了缓存持久化类和集合外,查询结果集也可以缓存。如果程序中经常使用同样的条件查询数据,则可以使用查询缓存。在配置文件中可以指定启动查询缓存。

1    <!--启用查询缓存-->
2     <property name ="cache.use_query_cache">true</property>

查询缓存后,NHibernate将创建两个缓存区域:

一个用于保存查询结果集,由NHibernate.Cache.StandardQueryCache实现。

一个用来保存最近更新的查询表的时间截,由NHibernate.Cache.UpdateTimestampsCache实现。

查询缓存中的结果集并不是永久有效的。当缓存的查询语句对应的数据库发生改变时,该缓存结果随之失效。因而对大多数查询而言,查询缓存的益处不是很大,所以NHibernate在默认情况下不对查询进行缓存。

如果需要对查询缓存,还需要显式的使用IQuery.SetCacheable(true)方法。IQuery调用这个方法后,NHibernate将根据查询语句、查询参数、结果集起始范围等信息组成一个IQueryKey。接着根据这个IQueryKey到查询缓存中查找相应数据,查询成功则直接返回查找结果。否则,查询数据库,获取结果集,并把结果集根据IQueryKey放入查询缓存。如果IQueryKey数据发生改变(增加、删除、修改等),这些IQueryKey及其对象的结果集将从缓存中删除。

显式启用缓存查询

一个例子:显式使用IQuery.SetCacheable(true)方法缓存查询结果,第二次查询相同条件时,直接从缓存查询中读取。

 1         /// <summary>
 2         /// 根据客户姓名进行模糊查询
 3         /// </summary>
 4         /// <param name="strName">查询条件</param>
 5         /// <returns>满足条件的客户信息</returns>
 6         public IList<Customer> SearchByName(string strName)
 7         {
 8             ISession session = NHibernateHelper.GetSession();
 9             //from后面跟的是持久化类Customer而不是数据表名TB_Customer
10             return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
11                 //显示启用缓存查询
12                 .SetCacheable(true)
13                 .List<Customer>();
14         }

 1         /// <summary>
 2         /// 重置Session根据客户姓名进行模糊查询
 3         /// </summary>
 4         /// <param name="strName">查询条件</param>
 5         /// <returns>满足条件的客户信息</returns>
 6         public IList<Customer> SearchByName2(string strName)
 7         {
 8             //重置session
 9             ISession session = NHibernateHelper.ResetSession();
10             //from后面跟的是持久化类Customer而不是数据表名TB_Customer
11             return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
12                 //显示启用缓存查询
13                 .SetCacheable(true)
14                 .List<Customer>();
15         }

单元测试

 1         [TestMethod]
 2         public void SearchByNameTest()
 3         {
 4             Console.WriteLine("第一次查询某数据,显式缓存查询结果");
 5             IList<Customer> customers = _customerData.SearchByName("z");
 6             Assert.AreEqual(2, customers.Count);
 7             Console.WriteLine("重置session");
 8             Console.WriteLine("第二次查询某数据,显式缓存查询结果");
 9             IList<Customer> customers2 = _customerData.SearchByName2("z");
10             Assert.AreEqual(2, customers.Count);
11         }

数据库中数据

测试结果

在第一次查询时,显示的缓存了查询的结果集,所以第二次查询的时候直接使用二级缓存中的数据了。

指定命名缓存区域

我们还可以使用.SetCacheRegion("cacheRegion")给查询缓存指定了特定的命名缓存区域,该查询缓存的缓存策略将由二级缓存的命名区域负责:

 1         /// <summary>
 2         /// 根据客户姓名进行模糊查询
 3         /// </summary>
 4         /// <param name="strName">查询条件</param>
 5         /// <returns>满足条件的客户信息</returns>
 6         public IList<Customer> SearchByName(string strName)
 7         {
 8             ISession session = NHibernateHelper.GetSession();
 9             //from后面跟的是持久化类Customer而不是数据表名TB_Customer
10             return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
11                 //显示启用缓存查询
12                 .SetCacheable(true)
13                 //指定缓存区域
14                 .SetCacheRegion("querycache")
15                 .List<Customer>();
16         }
17         /// <summary>
18         /// 重置Session根据客户姓名进行模糊查询
19         /// </summary>
20         /// <param name="strName">查询条件</param>
21         /// <returns>满足条件的客户信息</returns>
22         public IList<Customer> SearchByName2(string strName)
23         {
24             //重置session
25             ISession session = NHibernateHelper.ResetSession();
26             //from后面跟的是持久化类Customer而不是数据表名TB_Customer
27             return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
28                 //显示启用缓存查询
29                 .SetCacheable(true)
30                 //指定缓存区域
31                 .SetCacheRegion("querycache")
32                 .List<Customer>();
33         }

第一次查询出来的结果集被存储在名为queryCache的缓存区域,第二次同样在这个缓存区域里寻找需要数据,如果第二次没有指定或者指定别的缓存区域则没有需要的数据,就要到数据库中查询了。(测试结果与上面的相同,就不再贴图)。

命名查询

可以在映射文件中定义命名查询,<query>元素提供了很多属性,可以用于缓存结果,在Customer.hbm.xml映射文件中定义名为selectCustomer的查询并查询所有Customer并启用缓存查询。配置如下

1  <!--query节点位于class节点外,hibernate-mapping节点内,不然会出现MappingException: Named query not known异常-->
2   <query cacheable="true" cache-mode="normal" name="selectCustomer">
3     <!--此时使用Customer是持久化类,而不是数据库表名称-->
4     from Customer
5   </query>

CustomerData类中添加如下的方法,用于测试:

 1         /// <summary>
 2         /// 使用命名查询的方式
 3         /// </summary>
 4         /// <returns></returns>
 5         public IList<Customer> NamedQueryCache()
 6         {
 7             ISession session = NHibernateHelper.GetSession();
 8             return session.GetNamedQuery("selectCustomer").List<Customer>();
 9         }
10         /// <summary>
11         /// 重置session后使用命名查询的方式
12         /// </summary>
13         /// <returns></returns>
14         public IList<Customer> NamedQueryCache2()
15         {
16             ISession session = NHibernateHelper.ResetSession();
17             return session.GetNamedQuery("selectCustomer").List<Customer>();
18         }

单元测试

 1         [TestMethod]
 2         public void NamedQueryCache()
 3         {
 4             Console.WriteLine("--->第一次使用命名查询");
 5             IList<Customer> c1 = _customerData.NamedQueryCache();
 6             Assert.AreEqual(2, c1.Count);
 7             Console.WriteLine("--->第二次使用命名查询");
 8             IList<Customer> c2 = _customerData.NamedQueryCache2();
 9             Assert.AreEqual(2, c2.Count);
10         }

运行测试,测试结果

NHibernate提供的查询(HQL、条件查询、原生SQL查询)类似,不再重复举例。

二级缓存管理

NHibernate二级缓存由ISessionFactory创建并由ISessionFactory自行维护。我们使用NHibernate操作数据时,ISessionFactory能够自动同步缓存,保证缓存的有效性。但是当我们批量操作数据时,往往NHibernate不能维护缓存持久有效。ISessionFactory提供了可编程方式的缓存管理方法。

ISessionFactory提供了一系列的EvictXXX()方法可以方便的从二级缓存中删除一个实例、删除一个集合、一个命名缓存等操作

  • Evict(persistentClass):从二级缓存中删除persistentClass类所有实例
  • Evict(persistentClass, id):从二级缓存中删除指定的持久化实例
  • EvictEntity(entityName):从二级缓存中删除命名实例
  • EvictCollection(roleName):从二级缓存中删除集合
  • EvictCollection(roleName, id):从二级缓存中删除指定的集合
  • EvictQueries():从二级缓存中刷新全部查询结果集
  • EvictQueries(cacheRegion):从二级缓存中刷新指定查询结果集

ISession内置缓存可以共享ISessionFactory缓存,通过指定ISession的CacheMode可以控制ISession和ISessionFactory的交互方式。ISession可以通过以下五种方式和ISessionFactory交互:

  • Ignore:更新数据时将二级缓存失效,其它时间不和二级缓存交互
  • Put:向二级缓存写数据,但不从二级缓存读数据
  • Get:从二级缓存读数据,仅在数据更新时向二级缓存写数据
  • Normal:默认方式。从二级缓存读/写数据
  • Refresh:向二级缓存写数据,想不从二级缓存读数据,可以通过在nhibernate配置文件设置cache.use_minimal_puts从数据库中读取数据时,强制二级缓存刷新

ISessionFactory提供了一系列的EvictXXX()方法从二级缓存中删除一个实例。

一个例子:在第一次读取持久化实例时,结果集保存在二级缓存中,使用Evict方法从二级缓存中删除所有持久化实例,第二次查询相同数据,二级缓存中不存在则重新从数据库中查询。

在CustomerData类中添加如下方法

1         /// <summary>
2         /// 使用SessionFactory的Evict方法管理二级缓存
3         /// </summary>
4         public void SessionFactoryEvict(Type type)
5         {
6             ISessionFactory sessionFactoty = NHibernateHelper.GetSessionFactory();
7             sessionFactoty.Evict(type);
8             //也可以sessionFactoty.EvictEntity("Wolfy.Shop.Domain.Entities.Customer");
9         }

单元测试

 1         [TestMethod]
 2         public void ISessionFactoryCacheManageTest()
 3         {
 4             Console.WriteLine("第一次加载两个customer实例");
 5             Customer customer1 = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
 6             Customer customer2 = _customerData.GetCustomerById(new Guid("095659B0-8D3F-4DC3-8861-9D7D8A9BA570"));
 7             Console.WriteLine("从二级缓存中删除Customer类所有实例");
 8             _customerData.SessionFactoryEvict(typeof(Customer));
 9             Console.WriteLine("重置session并进行第二次读取持久化实例");
10             Customer customer3 = _customerData.GetCustomerById2(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
11             Assert.IsNotNull(customer3);
12         }

运行测试,测试结果

因为二级缓存是共享的,将ISession重置后,一级缓存也将跟ISession重置,第二次查询的时候,缓存中已经没有对象了,又从数据库中查询了一次。

强制刷新缓存区域

使用ISession提供的.SetCacheMode(CacheMode.Refresh) 方法可以强制刷新缓存区域,这样可以避免数据不一致问题。

 1         /// <summary>
 2         /// 根据客户姓名进行模糊查询
 3         /// </summary>
 4         /// <param name="strName">查询条件</param>
 5         /// <returns>满足条件的客户信息</returns>
 6         public IList<Customer> SearchByName(string strName)
 7         {
 8             ISession session = NHibernateHelper.GetSession();
 9             //from后面跟的是持久化类Customer而不是数据表名TB_Customer
10             return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
11                 //显示启用缓存查询
12                 .SetCacheable(true)
13                 //指定缓存区域
14                 .SetCacheRegion("querycache")
15                 //强制刷新缓存区域
16                 .SetCacheMode(CacheMode.Refresh)
17                 .List<Customer>();
18         }
19         /// <summary>
20         /// 重置Session根据客户姓名进行模糊查询
21         /// </summary>
22         /// <param name="strName">查询条件</param>
23         /// <returns>满足条件的客户信息</returns>
24         public IList<Customer> SearchByName2(string strName)
25         {
26             //重置session
27             ISession session = NHibernateHelper.ResetSession();
28             //from后面跟的是持久化类Customer而不是数据表名TB_Customer
29             return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
30                 //显示启用缓存查询
31                 .SetCacheable(true)
32                 //指定缓存区域
33                 .SetCacheRegion("querycache")
34                 //强制刷新缓存区域
35                 .SetCacheMode(CacheMode.Refresh)
36                 .List<Customer>();
37         }

测试

 1         [TestMethod]
 2         public void SearchByNameTest2()
 3         {
 4             Console.WriteLine("第一次查询某数据,显式缓存查询结果");
 5             IList<Customer> customers = _customerData.SearchByName("z");
 6             Assert.AreEqual(2, customers.Count);
 7             Console.WriteLine("重置session");
 8             Console.WriteLine("第二次查询某数据,显式缓存查询结果");
 9             Console.WriteLine("----指定特定的命名缓存区域并强制刷新缓存区域----");
10             IList<Customer> customers2 = _customerData.SearchByName2("z");
11             Assert.AreEqual(2, customers.Count);
12         }

测试结果

通过测试结果,也看出在第二次查询的时候,因为强制刷新了缓存区域,所以又重新访问数据库加载了一次数据。

总结

关于二级缓存的使用方法就介绍到这里,本篇文章也算是nhibernate入门系列的结尾篇了,关于nhibernate的高级功能,只能在遇到的时候总结,然后加入本系列了。

系列文章所有demo下载连接:链接:http://pan.baidu.com/s/1sjJja6x 密码:r9vh

参考文章

http://www.cnblogs.com/lyj/archive/2008/11/28/1343418.html

博客地址: http://www.cnblogs.com/wolf-sun/
博客版权: 本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。
如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步!
再次感谢您耐心的读完本篇文章。http://www.cnblogs.com/wolf-sun/p/4133353.html
时间: 2025-01-11 14:40:54

[Nhibernate]二级缓存(二)的相关文章

[Nhibernate]二级缓存(一)

目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级缓存过期时间和ISession对象的生命周期相同,并且不同的Session不能共享缓存,一级缓存也可以成为ISession缓存.那么现在我们就学一下nhibernate中的二级缓存,即ISessionFactory级别缓存,可被所有的ISession所共享.二级缓存是可扩展的,在http://sourceforge.net/proj

NHibernate之旅(24):探索NHibernate二级缓存(下)

本节内容 引入 使用NHibernate二级缓存 启用缓存查询 管理NHibernate二级缓存 结语 引入 呵呵,今天收到了微软"最有影响力开发者"礼包,很高兴自己荣获"微软最有影响力开发者"称号(详情请进),这篇我还继续上一篇的话题聊聊NHibernate二级缓存剩下的内容,比如你修改.删除数据时,二级缓存是什么策略呢?我们如果使用缓存查询呢?如何管理NHibernate二级缓存呢? 使用NHibernate二级缓存 不知道具体配置的请转到NHibernate之

NHibernate之旅(23):探索NHibernate二级缓存(上)

本节内容 引入 介绍NHibernate二级缓存 NHibernate二级缓存提供程序 实现NHibernate二级缓存 结语 引入 上一篇我介绍了NHibernate内置的一级缓存即ISession缓存.这篇我们来了解下NHibernate二级缓存即ISessionFactory级别缓存.二级缓存是可扩展的,在NHibernate Contrib上提供了第三方NHibernate二级缓存提供程序. 介绍NHibernate二级缓存 NHibernate二级缓存由ISessionFactory创

hibernate3学习笔记(二十一)|二级缓存

二级缓存及外部缓存,它能够越过数个Session,通过同一个SessionFactory进行管理和维护. 接上例的代码:http://blog.csdn.net/kunshan_shenbin/archive/2008/09/03/2874375.aspx 要使用二级缓存,首先需要对hibernate.cfg.xml文件进行修改: 1.<?xml version="1.0" encoding="utf-8"?>2.<!DOCTYPE hiberna

[Nhibernate]一级缓存

目录 写在前面 文档与系列文章 一级缓存 一个例子 一级缓存管理 总结 写在前面 上篇文章介绍了nhibernate中对象的三种状态,通过对象的三种状态,很容易想到缓存. 什麽是缓存? 有时候,某些数据是会经常需要访问的,像硬盘内部的缓存(暂存器的一种)会将读取比较频繁的一些数据存储在缓存中,再次读取时就可以直接从缓存中直接传输.说白了,缓存是用空间换取时间的一种技术. 文档与系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibern

Hibernate中二级缓存的配置和使用

(一)Hibernate的二级缓存策略的一般过程如下: 1) 条件查询的时候,总是发出一条select * from table_name where -. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象. 2) 把获得的所有数据对象根据ID放入到第二级缓存中. 3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查:查不到,如果配置了二级缓存,那么从二级缓存中查:查不到,再查询数据库,把结果按照ID放入到缓存. 4) 删除.更新.增加数据的时

二级缓存和三级缓存的区别

先来了解教电脑缓存的工作原理? 电脑缓存是当cpu在读取数据的时候,先是从缓存文件中查找,然后找到之后会自动读取在输入到cpu进行处理,当然如果没有在缓存中找到对应的缓存文件的话,那么就会从内存中读取并且传输给cpu来处理.当然这样的话需要一定的时间所以会很慢.等cpu处理之后,就很贵把这个暑假所在的数据块保存在缓存文件中,这样的话在以后读取这项数据的时候就直接在缓存中进行,这样就不要重复在内存中调用并读取数据了. 了解了电脑缓存的作用,对于电脑缓存又分为一级.二级.三级缓存.那么小编就对缓存来

xp系统如何开启cpu二级缓存?

  xp系统如何开启cpu二级缓存?          方法一: Windows优化大师打开二级缓存的操作步骤: 1.启动Windows优化大师,选择"系统性能优化"; 软件名称: Windows优化大师 V7.99 Build 13.0604 绿色去广告特别版 软件大小: 5.64MB 更新时间: 2013-06-07 2.在"文件系统优化"中,最上面就是关于CPU二级缓存的设置项. 3.拖动滑块到相应的位置后,保存设置并重新启动计算机. 方法二: 注册表法打开二

安卓listview加载网络图片时,当时用了软引用之后,是不是就不用在使用二级缓存机制了

问题描述 安卓listview加载网络图片时,当时用了软引用之后,是不是就不用在使用二级缓存机制了 软引用不就是避免OOM的吗,和二级缓存机制的作用是一样的吧,可能二级缓存会更打程度的减少访问网络的次数吧,,但是如果在一个listview加载网络图片的项目中,是不是这二者选择一个用就行了? 解决方案 首先,ListView加载图片,使用什么软引用什么的,其实都是避免oom,那么你可以做到一下几点 1.使用缓存,至于你说的两种,任意一种都是可以的,起码起到了作用 2.当ListView去网络读取图