Hibernate 延迟加载 分析
出处:http://www.ibm.com/developerworks/cn/java/j-lo-hibernatelazy/#icomments
Hibernate 的延迟加载(lazy load)是一个被广泛使用的技术。这种延迟加载保证了应用只有在需要时才去数据库中抓取相应的记录。通过延迟加载技术可以避免过多、过早地加载数据表里 的数据,从而降低应用的内存开销。Hibernate 的延迟加载本质上就是代理模式的应用,当程序通过 Hibernate 装载一个实体时,默认情况下,Hibernate 并不会立即抓取它的集合属性、关联实体所以对应的记录,而是通过生成一个代理来表示这些集合属性、关联实体,这就是代理模式应用带来的优势。
Hibernate 延迟加载剖析与代理模式应用
Hibernate 的延迟加载(lazy load)是一个被广泛使用的技术。这种延迟加载保证了应用只有在需要时才去数据库中抓取相应的记录。通过延迟加载技术可以避免过多、过早地加载数据表里 的数据,从而降低应用的内存开销。Hibernate 的延迟加载本质上就是代理模式的应用,当程序通过 Hibernate 装载一个实体时,默认情况下,Hibernate 并不会立即抓取它的集合属性、关联实体所以对应的记录,而是通过生成一个代理来表示这些集合属性、关联实体,这就是代理模式应用带来的优势。Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载。Hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hibernate 的运行性能。
下面先来剖析 Hibernate 延迟加载的“秘密”。
集合属性的延迟加载
当 Hibernate 从数据库中初始化某个持久化实体时,该实体的集合属性是否随持久化类一起初始化呢?如果集合属性里包含十万,甚至百万的记录,在初始化持久化实体的同时, 完成所有集合属性的抓取,将导致性能急剧下降。完全有可能系统只需要使用持久化类集合属性中的部分记录,而完全不是集合属性的全部,这样,没有必要一次加 载所有的集合属性。
对于集合属性,通常推荐使用延迟加载策略。所谓延迟加载就是等系统需要使用集合属性时才从数据库装载关联的数据。
例如下面 Person 类持有一个集合属性,该集合属性里的元素的类型为 Address,该 Person 类的代码片段如下:
清单 1. Person.java
public class Person
{
// 标识属性
private Integer id;
// Person 的 name 属性
private String name;
// 保留 Person 的 age 属性
private int age;
// 使用 Set 来保存集合属性
private Set<Address> addresses = new HashSet<Address>();
// 下面省略了各属性的 setter 和 getter 方法
...
}
为了让 Hibernate 能管理该持久化类的集合属性,程序为该持久化类提供如下映射文件:
清单 2. Person.hbm.xml
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
<!-- 映射 Person 持久化类 -->
<class name="Person" table="person_inf">
<!-- 映射标识属性 id -->
<id name="id" column="person_id">
<!-- 定义主键生成器策略 -->
<generator class="identity"/>
</id>
<!-- 用于映射普通属性 -->
<property name="name" type="string"/>
<property name="age" type="int"/>
<!-- 映射集合属性 -->
<set name="addresses" table="person_address" lazy="true">
<!-- 指定关联的外键列 -->
<key column="person_id"/>
<composite-element class="Address">
<!-- 映射普通属性 detail -->
<property name="detail"/>
<!-- 映射普通属性 zip -->
<property name="zip"/>
</composite-element>
</set>
</class>
</hibernate-mapping>
从上面映射文件的代码可以看出,Person 的集合属性中的 Address 类只是一个普通的 POJO。该 Address 类里包含 detail、zip 两个属性。由于 Address 类代码非常简单,故此处不再给出该类的代码。
上面映射文件中 <set.../> 元素里的代码指定了 lazy="true"(对于 <set.../> 元素来说,lazy="true"是默认值),它指定 Hibernate 会延迟加载集合属性里 Address 对象。
例如通过如下代码来加载 ID 为 1 的 Person 实体:
Session session = sf.getCurrentSession();
Transaction tx = session.beginTransaction();
Person p = (Person) session.get(Person.class, 1); //<1>
System.out.println(p.getName());
上面代码只是需要访问 ID 为 1 的 Person 实体,并不想访问这个 Person 实体所关联的 Address 对象。此时有两种情况:
- 如果不延迟加载,Hibernate 就会在加载 Person 实体对应的数据记录时立即抓取它关联的 Address 对象。
- 如果采用延迟加载,Hibernate 就只加载 Person 实体对应的数据记录。
很明显,第二种做法既能减少与数据库的交互,而且避免了装载 Address 实体带来的内存开销——这也是 Hibernate 默认启用延迟加载的原因。
现在的问题是,延迟加载到底是如何实现的呢? Hibernate 在加载 Person 实体时,Person 实体的 addresses 属性值是什么呢?
为了解决这个问题,我们在 <1>
号代码处设置一个断点,在 Eclipse 中进行 Debug,此时可以看到 Eclipse 的 Console 窗口有如图 1 所示的输出:
图 1. 延迟加载集合属性的 Console 输出
正如图 1 输出所看到的,此时 Hibernate 只从 Person 实体对应的数据表中抓取数据,并未从 Address 对象对应的数据表中抓取数据,这就是延迟加载。
那么 Person 实体的 addresses 属性是什么呢?此时可以从 Eclipse 的 Variables 窗口看到如图 2 所示的结果:
图 2. 延迟加载的集合属性值
从图 2 的方框里的内容可以看出,这个 addresses 属性并不是我们熟悉的 HashSet、TreeSet 等实现类,而是一个 PersistentSet 实现类,这是 Hibernate 为 Set 接口提供的一个实现类。
PersistentSet 集合对象并未真正抓取底层数据表的数据,因此自然也无法真正去初始化集合里的 Address 对象。不过 PersistentSet 集合里持有一个 session 属性,这个 session 属性就是 Hibernate Session,当程序需要访问 PersistentSet 集合元素时,PersistentSet 就会利用这个 session 属性去抓取实际的 Address 对象对应的数据记录。
那么到底抓取那些 Address 实体对应的数据记录呢?这也难不倒 PersistentSet,因为 PersistentSet 集合里还有一个 owner 属性,该属性就说明了 Address 对象所属的 Person 实体,Hibernate 就会去查找 Address 对应数据表中外键值参照到该 Person 实体的数据。
例如我们单击图 2 所示窗口中 addresses 行,也就是告诉 Eclipse 要调试、输出 addresses 属性,这就是要访问 addresses 属性了,此时就可以在 Eclipse 的 Console 窗口看到输出如下 SQL 语句:
select
addresses0_.person_id as person1_0_0_,
addresses0_.detail as detail0_,
addresses0_.zip as zip0_
from
person_address addresses0_
where
addresses0_.person_id=?
这就是 PersistentSet 集合跟据 owner 属性去抓取特定 Address 记录的 SQL 语句。此时可以从 Eclipse 的 Variables 窗口看到图 3 所示的输出:
图 3. 已加载的集合属性值
从图 3 可以看出,此时的 addresses 属性已经被初始化了,集合里包含了 2 个 Address 对象,这正是 Person 实体所关联的两个 Address 对象。
通 过上面介绍可以看出,Hibernate 对于 Set 属性延迟加载关键就在于 PersistentSet 实现类。在延迟加载时,开始 PersistentSet 集合里并不持有任何元素。但 PersistentSet 会持有一个 Hibernate Session,它可以保证当程序需要访问该集合时“立即”去加载数据记录,并装入集合元素。
与 PersistentSet 实现类类似的是,Hibernate 还提供了 PersistentList、PersistentMap、PersistentSortedMap、PersistentSortedSet 等实现类,它们的功能与 PersistentSet 的功能大致类似。
熟悉 Hibernate 集合属性读者应该记得:Hibernate 要求声明集合属性只能用 Set、List、Map、SortedSet、SortedMap 等接口,而不能用 HashSet、ArrayList、HashMap、TreeSet、TreeMap 等实现类,其原因就是因为 Hibernate 需要对集合属性进行延迟加载,而 Hibernate 的延迟加载是依靠 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、 PersistentSortedSet 来完成的——也就是说,Hibernate 底层需要使用自己的集合实现类来完成延迟加载,因此它要求开发者必须用集合接口、而不是集合实现类来声明集合属性。
Hibernate 对集合属性默认采用延迟加载,在某些特殊的情况下,为 <set.../>、<list.../>、<map.../> 等元素设置 lazy="false"属性来取消延迟加载。
关联实体的延迟加载
默认情况下,Hibernate 也会采用延迟加载来加载关联实体,不管是一对多关联、还是一对一关联、多对多关联,Hibernate 默认都会采用延迟加载。
对于关联实体,可以将其分为两种情况:
- 关联实体是多个实体时(包括一对多、多对多):此时关联实体将以集合的形式存在,Hibernate 将使用 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、 PersistentSortedSet 等集合来管理延迟加载的实体。这就是前面所介绍的情形。
- 关联实体是单个实体时(包括一对一、多对一):当 Hibernate 加载某个实体时,延迟的关联实体将是一个动态生成代理对象。
当关联实体是单个实体时,也就是使用 <many-to-one.../> 或 <one-to-one.../> 映射关联实体的情形,这两个元素也可通过 lazy 属性来指定延迟加载。
下面例子把 Address 类也映射成持久化类,此时 Address 类也变成实体类,Person 实体与 Address 实体形成一对多的双向关联。此时的映射文件代码如下:
清单 3. Person.hbm.xml
<?xml version="1.0" encoding="GBK"?>
<!-- 指定 Hibernate 的 DTD 信息 -->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
<!-- 映射 Person 持久化类 -->
<class name="Person" table="person_inf">
<!-- 映射标识属性 id -->
<id name="id" column="person_id">
<!-- 定义主键生成器策略 -->
<generator class="identity"/>
</id>
<!-- 用于映射普通属性 -->
<property name="name" type="string"/>
<property name="age" type="int"/>
<!-- 映射集合属性,集合元素是其他持久化实体
没有指定 cascade 属性,指定不控制关联关系 -->
<set name="addresses" inverse="true">
<!-- 指定关联的外键列 -->
<key column="person_id"/>
<!-- 用以映射到关联类属性 -->
<one-to-many class="Address"/>
</set>
</class> <!-- 映射 Address 持久化类 -->
<class name="Address" table="address_inf">
<!-- 映射标识属性 addressId -->
<id name="addressId" column="address_id">
<!-- 指定主键生成器策略 -->
<generator class="identity"/>
</id>
<!-- 映射普通属性 detail -->
<property name="detail"/>
<!-- 映射普通属性 zip -->
<property name="zip"/>
<!-- 必须指定列名为 person_id,
与关联实体中 key 元素的 column 属性值相同 -->
<many-to-one name="person" class="Person"
column="person_id" not-null="true"/>
</class>
</hibernate-mapping>
接下来程序通过如下代码片段来加载 ID 为 1 的 Person 实体:
// 打开上下文相关的 Session
Session session = sf.getCurrentSession();
Transaction tx = session.beginTransaction();
Address address = (Address) session.get(Address.class , 1); //<1>
System.out.println(address.getDetail());
为了看到 Hibernate 加载 Address 实体时对其关联实体的处理,我们在 <1>
号代码处设置一个断点,在 Eclipse 中进行 Debug,此时可以看到 Eclipse 的 Console 窗口输出如下 SQL 语句:
select
address0_.address_id as address1_1_0_,
address0_.detail as detail1_0_,
address0_.zip as zip1_0_,
address0_.person_id as person4_1_0_
from
address_inf address0_
where
address0_.address_id=?
从这条 SQL 语句不难看出,Hibernate 加载 Address 实体对应的数据表抓取记录,并未从 Person 实体对应的数据表中抓取记录,这是延迟加载发挥了作用。
从 Eclipse 的 Variables 窗口看到如图 4 所示的输出:
图 4. 延迟加载的实体
从 图 4 可以清楚地看到,此时 Address 实体所关联的 Person 实体并不是 Person 对象,而是一个 Person_$$_javassist_0 类的实例,这个类是 Hibernate 使用 Javassist 项目动态生成的代理类——当 Hibernate 延迟加载关联实体时,将会采用 Javassist 生成一个动态代理对象,这个代理对象将负责代理“暂未加载”的关联实体。
只要应用程序需要使用“暂未加载”的关联实体,Person_$$_javassist_0 代理对象会负责去加载真正的关联实体,并返回实际的关联实体——这就是最典型的代理模式。
单击图 4 所示 Variables 窗口中的 person 属性(也就是在调试模式下强行使用 person 属性),此时看到 Eclipse 的 Console 窗口输出如下的 SQL 语句:
select
person0_.person_id as person1_0_0_,
person0_.name as name0_0_,
person0_.age as age0_0_
from
person_inf person0_
where
person0_.person_id=?
上面 SQL 语句就是去抓取“延迟加载”的关联实体的语句。此时可以看到 Variables 窗口输出图 5 所示的结果:
图 5. 已加载的实体
Hibernate 采用“延迟加载”管理关联实体的模式,其实就在加载主实体时,并未真正去抓取关联实体对应数据,而只是动态地生成一个对象作为关联实体的代理。当应用程序真正需要使用关联实体时,代理对象会负责从底层数据库抓取记录,并初始化真正的关联实体。
在 Hibernate 的延迟加载中,客户端程序开始获取的只是一个动态生成的代理对象,而真正的实体则委托给代理对象来管理——这就是典型的代理模式。
代理模式
代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上也不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理(Proxy)。
在这种设计方式下,系统会为某个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个 Java 对象代表另一个 Java 对象来采取行动。在某些情况下,客户端代码不想或不能够直接调用被调用者,代理对象可以在客户和目标对象之间起到中介的作用。
对客户端而言,它不能分辨出代理对象与真实对象的区别,它也无须分辨代理对象和真实对象的区别。客户端代码并不知道真正的被代理对象,客户端代码面向接口编程,它仅仅持有一个被代理对象的接口。
总而言之,只要客户端代码不能或不想直接访问被调用对象——这种情况有很多原因,比如需要创建一个系统开销很大的对象,或者被调用对象在远程主机上,或者目标对象的功能还不足以满足需求……,而是额外创建一个代理对象返回给客户端使用,那么这种设计方式就是代理模式。
下面示范一个简单的代理模式,程序首先提供了一个 Image 接口,代表大图片对象所实现的接口,该接口代码如下:
清单 3. Image.java
public interface Image
{
void show();
}
该接口提供了一个实现类,该实现类模拟了一个大图片对象,该实现类的构造器使用 Thread.sleep() 方法来暂停 3s。下面是该 BigImage 的程序代码。
清单 4. BigImage.java
// 使用该 BigImage 模拟一个很大图片
public class BigImage implements Image
{
public BigImage()
{
try
{
// 程序暂停 3s 模式模拟系统开销
Thread.sleep(3000);
System.out.println("图片装载成功 ...");
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
// 实现 Image 里的 show() 方法
public void show()
{
System.out.println("绘制实际的大图片");
}
}
上面的程序代码暂停了 3s,这表明创建一个 BigImage 对象需要 3s 的时间开销——程序使用这种延迟来模拟装载此图片所导致的系统开销。如果不采用代理模式,当程序中创建 BigImage 时,系统将会产生 3s 的延迟。为了避免这种延迟,程序为 BigImage 对象提供一个代理对象,BigImage 类的代理类如下所示。
清单 5. ImageProxy.java
public class ImageProxy implements Image
{
// 组合一个 image 实例,作为被代理的对象
private Image image;
// 使用抽象实体来初始化代理对象
public ImageProxy(Image image)
{
this.image = image;
}
/**
* 重写 Image 接口的 show() 方法
* 该方法用于控制对被代理对象的访问,
* 并根据需要负责创建和删除被代理对象
*/
public void show()
{
// 只有当真正需要调用 image 的 show 方法时才创建被代理对象
if (image == null)
{
image = new BigImage();
}
image.show();
}
}
上面的 ImageProxy 代理类实现了与 BigImage 相同的 show() 方法,这使得客户端代码获取到该代理对象之后,可以将该代理对象当成 BigImage 来使用。
在 ImageProxy 类的 show() 方法中增加了控制逻辑,这段控制逻辑用于控制当系统真正调用 image 的 show() 时,才会真正创建被代理的 BigImage 对象。下面程序需要使用 BigImage 对象,但程序并不是直接返回 BigImage 实例,而是先返回 BigImage 的代理对象,如下面程序所示。
清单 6. BigImageTest.java
public class BigImageTest
{
public static void main(String[] args)
{
long start = System.currentTimeMillis();
// 程序返回一个 Image 对象,该对象只是 BigImage 的代理对象
Image image = new ImageProxy(null);
System.out.println("系统得到 Image 对象的时间开销 :" +
(System.currentTimeMillis() - start));
// 只有当实际调用 image 代理的 show() 方法时,程序才会真正创建被代理对象。
image.show();
}
}
上面程序初始化 image 非常快,因为程序并未真正创建 BigImage 对象,只是得到了 ImageProxy 代理对象——直到程序调用 image.show() 方法时,程序需要真正调用 BigImage 对象的 show() 方法,程序此时才真正创建 BigImage 对象。运行上面程序,看到如图 6 所示的结果。
图 6. 使用代理模式提高性能
看 到如图 6 所示的运行结果,读者应该能认同:使用代理模式提高了获取 Image 对象的系统性能。但可能有读者会提出疑问:程序调用 ImageProxy 对象的 show() 方法时一样需要创建 BigImage 对象啊,系统开销并未真正减少啊?只是这种系统开销延迟了而已啊?
我们可以从如下两个角度来回答这个问题:
- 把创建 BigImage 推迟到真正需要它时才创建,这样能保证前面程序运行的流畅性,而且能减少 BigImage 在内存中的存活时间,从宏观上节省了系统的内存开销。
- 有些情况下,也许程序永远不会真正调用 ImageProxy 对象的 show() 方法——意味着系统根本无须创建 BigImage 对象。在这种情形下,使用代理模式可以显著地提高系统运行性能。
与此完全类似的是,Hibernate 也是通过代理模式来“推迟”加载关联实体的时间,如果程序并不需要访问关联实体,那程序就不会去抓取关联实体了,这样既可以节省系统的内存开销,也可以缩短 Hibernate 加载实体的时间。
小结
Hibernate 的延迟加载(lazy load)本质上就是代理模式的应用,我们在过去的岁月里就经常通过代理模式来降低系统的内存开销、提升应用的运行性能。Hibernate 充分利用了代理模式的这种优势,并结合了 Javassist 或 CGLIB 来动态地生成代理对象,这更加增加了代理模式的灵活性,Hibernate 给这种用法一个新名称:延迟加载。无论怎样,充分分析、了解这些开源框架的实现可以更好的感受经典设计模式的优势所在。
参考资料
学习
- 李刚编著 《轻量级 Java EE 企业应用实战 ( 第三版 ) 》,本书全面介绍了 Struts 2.2、Spring 3.0、Hibernate 3.6 整合开发的知识,并通过实际案例介绍了 3 大框架在实际项目中应用。
- HIBERNATE - Relational Persistence for Idiomatic Java,全面介绍 Hibernate 框架功能和用法的参考手册。
- Hibernate JavaDoc:介绍 Hibernate 3.6 所有 API 功能和用法的参考文档。
- “评论专栏:真的,为什么要用 Hibernate ?” (developerWorks,2010 年 10 月):IBM WebSphere Application Server 提供一个基于 Apache OpenJPA 项目的完整 JPA 解决方案。尽管使用其他 JPA 提供者(比如 Hibernate JPA)也是可行的,但问题是:“为什么要选择 Hibernate JPA ?”本文解释为何使用 WebSphere JPA 解决方案总是最明智的选择。
- “针对 SaaS 应用程序的基于 Hibernate 框架的数据库分片”(developerWorks,2010 年 11 月):扩展共享数据库的一个最简单的技术是基于租户 ID 通过数据分片启用多租户数据架构。在本文中,我们探究如何使用 Hibernate 分片来解决 SaaS 应用程序的数据库伸缩问题。
- “Struts2、Spring、Hibernate 高效开发的最佳实践” (developerWorks,2011 年 8 月):Struts2、Spring、Hibernate(SSH)是最常用的 Java EE Web 组件层的开发技术搭配,网络中和许多 IT 技术书籍中都有它们的开发教程,但是通常的教程都会让很多程序员陷入痛苦的配置与修改配置的过程。本文利用 SSH 中的技术特性,利用 Java 反射技术,按照规约优于配置的原理,基于 SSH 设定编写了一个通用开发框架,这使得开发者可以专注于业务逻辑的开发,而不用随着业务增加而添加或修改任何配置,并且对于权限控制和日志记录也提供了方便 的接口。
- “Hibernate 3 深度解析” (developerWorks,2011 年 8 月):Hibernate 的重要性,我想各位一定很清楚 . 那么 Hibernate 的一些重要功能项大家是否真的很了解呢,是否明白它的执行逻辑呢?本文章将会带领大家深入到 Hibernate 的内部,深度解析下重要功能项或是关键属性的工作原理。
- developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。
Hibernate 延迟加载 分析的更多相关文章
- 【转】hibernate 延迟加载
Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hiberna ...
- hibernate 延迟加载深入分析(persistentSet的延迟加载)
Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.Hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hiberna ...
- [转]Hibernate延迟加载与opensessioninviewFilter
原文地址:http://blog.csdn.net/a19881029/article/details/7916702 hibernate延迟加载: 一个person对应多个school,使用hibe ...
- Hibernate延迟加载Lazy
Hibernate延迟加载Lazy 延迟加载(lazy load)又称为懒加载,延迟加载的机制是为了避免一些无谓性能的开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作 如 ...
- Hibernate学习--hibernate延迟加载原理(动态代理)
在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...
- Hibernate学习--hibernate延迟加载原理-动态代理(阿里电面)
在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...
- Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式
Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...
- SSH项目web.xml文件的常用配置【struts2的过滤器、spring监听器、解决Hibernate延迟加载问题的过滤器、解决中文乱码的过滤器】
配置web.xml(struts2的过滤器.spring监听器.解决Hibernate延迟加载问题的过滤器.解决中文乱码的过滤器) <!-- 解决中文乱码问题 --> <filter ...
- Hibernate延迟加载与opensessioninviewFilter
转自:https://blog.csdn.net/skk_7/article/details/17917339 hibernate延迟加载: 一个person对应多个school,使用hibernat ...
随机推荐
- python3爬虫-下载网易云音乐,评论
# -*- coding: utf-8 -*- ''' 16位随机字符的字符串 参数一 获取歌曲下载地址 "{"ids":"[1361348080]" ...
- Detect Changes in Network Connectivity
Some times you will need a mechanism to check whether changes to network occurring during running yo ...
- springboot快速入门(二)——项目属性配置(日志详解)
一.概述 application.properties就是springboot的属性配置文件 在使用spring boot过程中,可以发现项目中只需要极少的配置就能完成相应的功能,这归功于spring ...
- 二级管工作原理(PN结原理)学习
0.小叙闲言 前面已经写了两篇介绍放大器应用和MOSFET作驱动的文章:常规放大电路和差分放大电路和MOSFET使用与H桥驱动问题.但是对它们的工作原理并没有进一步研究一下,今天写下这篇文章,主要是介 ...
- PostgreSQL内存结构图示
磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面:PostgreSQL内部结构与源代码研究索引页 回到顶级页面:PostgreSQL索引页 作者:高健@博客园 luckyjackgao@ ...
- c#总结:datatable的方法大全
最近在做一个数据处理,保存到datatable中,在过程中了解了datatable一些用法,总结一下: //定义 DataTable dt = new DataTable(); //写入列名: htT ...
- java四舍五入
package com.clzhang.sample; import java.math.BigDecimal; import java.math.RoundingMode; import java. ...
- paramiko 简单的使用
感觉自己操作服务器还要用xshell,麻烦很多,于是呢就去google,找到了paramiko. 使用这个模块还是很简单的, 我们链接服务器,只需要导入 SSHClient,AutoAddPolicy ...
- TortoiseGit版本库中某个文件显示问号或叹号的问题解决办法
这是一个怪问题,原因就是文件名大小写与版本库管理的大小写不一致. 解决办法: 1.先把文件夹中的物理文件名改为版本库浏览器中显示的文件名(版本库浏览器中的文件名不知道怎么改),改了以后这个文件图标就变 ...
- Ubuntu根目录下各文件夹的作用
Ubuntu上常识和常用命令: 1.Ubuntu文件结构 在ubuntu上硬盘的目录和Windows上不同,Windows可以根据自己的需求分不同的盘符,但ubuntu上只有一个盘,从根目录开始每个目 ...