- 浏览: 1364114 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (551)
- 计划 (4)
- java (115)
- oracle (60)
- ajax (3)
- javascript (64)
- 计算机操作技巧集 (11)
- 近期关注话题 (10)
- 随想 (13)
- html (6)
- struts (15)
- hibernate (16)
- spring (2)
- game (0)
- Eglish (10)
- DisplayTag (6)
- jsp (18)
- css (3)
- eclipse (3)
- 其他知识 (8)
- 备用1 (12)
- 备用2 (1)
- 笑话-放松心情 (9)
- 设计 (1)
- 设计模式 (1)
- 数据结构 (0)
- office办公软件 (5)
- webwork (0)
- tomcat (2)
- MySql (1)
- 我的链接资源 (5)
- xml (2)
- servlet (0)
- PHP (13)
- DOM (0)
- 网页画图vml,canvas (1)
- 协议 (2)
- 健康 (3)
- 书籍下载 (1)
- jbpm (1)
- EXT (1)
- 自考 (2)
- 报表 (4)
- 生活 (64)
- 操作系统基础知识 (2)
- 测试 (2)
- guice (1)
- google学习 (2)
- Erlang (1)
- LOG4J (2)
- wicket (1)
- 考研 (1)
- 法律 (1)
- 地震 (1)
- 易学-等等相关 (1)
- 音乐 (1)
- 建站 (4)
- 分享说 (3)
- 购物省钱 (0)
- linux (1)
最新评论
-
zenmshuo:
如果使用SpreadJS这一类的表格工具,应该能更好的实现这些 ...
js中excel的用法 -
hjhj2991708:
第一个已经使用不了
jar包查询网站 非常好用! -
jiangmeiwei:
...
中文乱码 我的总结 不断更新 -
gary_bu:
...
response.sendRedirect 中文乱码问题解决 -
hnez:
多谢指点,怎么调试也不通,原来我在<body>&l ...
ExtJs IE ownerDocument.createRange() 错误解决方案
http://www.javalobby.org/articles/hibernate-query-101/
Hibernate Querying 101 : tips and tricks
Table of Contents
* Introduction
* Background
* Hibernate Query Methods
* More advanced techniques
* Sidenote : Unit testing
* Conclusion
This article presents a few real-life tips and tricks for Hibernate querying. It is designed to be a 'from-the-trenches' account of the methods and best-practices we use and that we have found to work.
Background
First a little background. We are currently working on a project using a JSTL / Struts / Hibernate / PostgreSQL stack. The application requires many PDF reports. For the PDF (and Excel) report generation, we use JasperReports. Maybe I'll write another article on JasperReports some other time. But for now, I want to talk about the Hibernate queries.
The reports tend to be complicated. So are the queries. The database is big enough to be slow if you don't make your HQL queries slick and efficient. Why don't we use plain old SQL queries for the reports? Well, if we did that, we'd loose the abstraction level we get with Hibernate, and the queries would be much longer and more complicated. Nevertheless, in practice, you do have to fine-tune each query individally in order to get the best performance, just as you would a plain old SQL query.
Hibernate Query Methods
The queries we general use can be divided up into three main categories :
1. Simple HQL queries using one class (where no joins are required)
2. HQL queries which require joins over several classes
3. HQL queries which cannot be efficiently done using joins
Simple HQL queries
The first and simplest query is the plain old HQL query on one object, as you can find in all the Hibernate introductory examples :
from Sale sale where sale.date > :startDate
This type of query is not really the subject of this article. If it works, fine. However, there are often cases where this type of query will pose problems for inexperienced Hiberate programmers.
Queries using joins
Sometimes, a Hibernate query will take an excessive amount of time to execute. A Hibernate query which takes more than a few hundred milliseconds to execute should be immediately considered with suspicion. Under the hood (looking at the log files), the typical symptom will often be an excessive number of SQL queries (potentially as many as Kn + 1, where n is the number of objects returned). This means that for each object that Hibernate loads, it needs to do one or more extra SQL queries to load associated objects. This is generally regarded as a bad thing.
Consider the following (simplified) Hibernate mappings :
Now lets go back to the previous query. This query will generate (at least) one SQL query for the initial select,
select sale_ch_id, sale_d_date, sale_n_price...from t_sale...
and then a sequence of selects from the associated tables :
select prod_ch_id...from t_product...
select prod_ch_id...from t_product...
select prod_ch_id...from t_product...
...
This problem is well-known in Hibernate circles, and the solution is straight-forward once you know it. The associated objects must be loaded in the initial query, for example by using the 'left join fetch' construction :
from Sale sale
where sale.date > :startDate
left join fetch sale.product
This usually works well for classes with only a handful of associated objects. In practice, there may be an issue in cases where there are a lot of associated tables, and where many of the associated tables contain only limited amounts of static data.
...
Notice how we may need to perform left joins on second-level associations (eg. sale.product.color). But do we really need to do left joins on the whole object tree from Sale down ? What are the pros and cons ?
In practice, if you want to be sure of having a unique SQL query, you will potentially need left joins on all directly and indirectly associated objects (I'm not talking about collections, which should use lazy-loading, and which are not the subject of this discussion). However, if you have a lot of objects to load, you may have to be more selective about what you fetch, and about how big your SQL query gets.
For example, if your Color table contains 10 lines, omitting the join on sale.product.color will result in (at most) 10 extra SQL queries, bringing the total to 11. Once all the Color instances are loaded into the Hibernate session cache, you can trust Hibernate not to do any extra queries.
Bigger queries return more data, and when large data sets are involved, it is quite possible that the overhead of the weighty SQL query outweighs the overhead of reading a few small tables into memory and caching the results. In practice, these smaller tables should probably be cached using the second-level hibernate caching anyway. The bottom line is that the query structure should be designed and optimised via unit testing and concrete metrics (System.currentTimeMillis() will do fine).
Cases where joined queries aren't enough
Now suppose we have complex, multiple, bi-directional relations between two table. For example, suppose we have two classes : Company and City. A company is located in a city. A city outsources different types of work (water installation management, electricity management, etc.) to different companies. Each company is in turn located in a city.
In this situation, the presence of many-to-one relations means that Hibernate will potentially generate an uncontrollable number of SQL queries, and left join fetching is helpless to resolve the problem : you would end up with a query looking something like this :
etc...
In this case, you basically have two main options :
1. Let the client wait half an hour for his reports
2. Use an optimised HQL query to return only the columns you really need.
Generally, the first option isn't greatly appreciated by the client. So lets look at the second option. This approach involves determining exactly which columns you really need, and instanciating data-transfer JavaBean objects containing exactly those columns.
select new CityItem(city.id, city.name, city.electrityCompany.name)
from City city
...
This technique is fast and efficient, and avoids the overheads of handling associated objects and of loading large numbers of persistent objects into the Hibernate session cache. The only downside is the need to create a dedicated data-transfer class for each query.
More advanced techniques
Finally, I will present a variation on the previous technique, which avoids the need to create a new data-transfer class for each query. The idea is to retrieve a list of instances of a Hibernate-persisted business class, using a given HQL query, but with only a specified set of columns being instanciated. This allows fast, light-weight queries which return only the minimum necessary information, and avoid complex joins in the HQL queries. For example :
This query will return a list of Community objects, but with only the 4 specified fields instanciated. Note that this is a powerful technique, as the presentation layer may use ordinary business classes instead of data transfer objects, without having to know the details of the querying techniques being used. This method should be reserved for pure read-only/display actions, however, as data manipulation with incompletely-instanciated objects is generally a very bad idea.
The implementation of such a method is fairly simple, and based on introspection and the Apache BeanUtils classes. For the curious, here is a simplified version of the implementation of the first method (error handling and optimised caching code has been removed for simplicity) :
The implementation of the version using parameters is left as an exercise to the reader.
Sidenote : Unit testing
There are plenty of good articles on the merits of unit testing and of test-driven development, so I won't write much about this here. But, a word for the wise : in my experience, you cannot efficently code Hibernate queries without using a test-driven approach. OK, maybe I exaggerate a little. But the fact is that it's a lot easier to fine-tune your Hibernate queries against a local database, using timers and/or by studying the generated SQL queries, than if you wait until the solution is deployed in a WAR and the page takes 5 minutes to appear.
There is also much debate on how to test the Hibernate layers (hibernate mappings and DAOs). In our case, we are lucky enough to have a test database containing legacy data. We use a dedicated test database, which is also used for the test server.
1. Unit tests must not change the state of the database (so any created objects should be removed in the tearDown() method)
2. Unit tests may use existing test data (useful to test real cases), but only for read-only query operations (existing data must not be modified). It is the developers responsibility to only use stable existing test data, and to update the test cases if necessary.
Conclusion
Hibernate is a powerful object/relational persistence library, and it has an equally powerful query mechanism. However, the power of Hibernate does not dispense the developer from optimising the HQL queries. Indeed, the very simplicity of HQL can sometimes hide serious performance issues for the unwary. However, when well tuned, and using appropriate querying techniques, Hibernate can result in cleaner, simpler code as well as performance equaling that of an optimised SQL query via JDBC.
Hibernate Querying 101 : tips and tricks
Table of Contents
* Introduction
* Background
* Hibernate Query Methods
* More advanced techniques
* Sidenote : Unit testing
* Conclusion
This article presents a few real-life tips and tricks for Hibernate querying. It is designed to be a 'from-the-trenches' account of the methods and best-practices we use and that we have found to work.
Background
First a little background. We are currently working on a project using a JSTL / Struts / Hibernate / PostgreSQL stack. The application requires many PDF reports. For the PDF (and Excel) report generation, we use JasperReports. Maybe I'll write another article on JasperReports some other time. But for now, I want to talk about the Hibernate queries.
The reports tend to be complicated. So are the queries. The database is big enough to be slow if you don't make your HQL queries slick and efficient. Why don't we use plain old SQL queries for the reports? Well, if we did that, we'd loose the abstraction level we get with Hibernate, and the queries would be much longer and more complicated. Nevertheless, in practice, you do have to fine-tune each query individally in order to get the best performance, just as you would a plain old SQL query.
Hibernate Query Methods
The queries we general use can be divided up into three main categories :
1. Simple HQL queries using one class (where no joins are required)
2. HQL queries which require joins over several classes
3. HQL queries which cannot be efficiently done using joins
Simple HQL queries
The first and simplest query is the plain old HQL query on one object, as you can find in all the Hibernate introductory examples :
from Sale sale where sale.date > :startDate
This type of query is not really the subject of this article. If it works, fine. However, there are often cases where this type of query will pose problems for inexperienced Hiberate programmers.
Queries using joins
Sometimes, a Hibernate query will take an excessive amount of time to execute. A Hibernate query which takes more than a few hundred milliseconds to execute should be immediately considered with suspicion. Under the hood (looking at the log files), the typical symptom will often be an excessive number of SQL queries (potentially as many as Kn + 1, where n is the number of objects returned). This means that for each object that Hibernate loads, it needs to do one or more extra SQL queries to load associated objects. This is generally regarded as a bad thing.
Consider the following (simplified) Hibernate mappings :
<class name="Sale" dynamic-update="true" table="t_sale" > <id column="sale_ch_id" name="id" unsaved-value="null" type="string"> <generator class="uuid.hex"/> </id> <property column="sale_d_date" name="date" type="date" not-null="true"/> <property column="sale_n_price" name="price" type="big_decimal" not-null="true"/> <many-to-one name="product" class="Product" column="prod_ch_id".../> ... </class>
Now lets go back to the previous query. This query will generate (at least) one SQL query for the initial select,
select sale_ch_id, sale_d_date, sale_n_price...from t_sale...
and then a sequence of selects from the associated tables :
select prod_ch_id...from t_product...
select prod_ch_id...from t_product...
select prod_ch_id...from t_product...
...
This problem is well-known in Hibernate circles, and the solution is straight-forward once you know it. The associated objects must be loaded in the initial query, for example by using the 'left join fetch' construction :
from Sale sale
where sale.date > :startDate
left join fetch sale.product
This usually works well for classes with only a handful of associated objects. In practice, there may be an issue in cases where there are a lot of associated tables, and where many of the associated tables contain only limited amounts of static data.
from Sale sale where sale.date > :startDate left join fetch sale.salesman left join fetch sale.product left join fetch sale.product.color left join fetch sale.product.category
...
Notice how we may need to perform left joins on second-level associations (eg. sale.product.color). But do we really need to do left joins on the whole object tree from Sale down ? What are the pros and cons ?
In practice, if you want to be sure of having a unique SQL query, you will potentially need left joins on all directly and indirectly associated objects (I'm not talking about collections, which should use lazy-loading, and which are not the subject of this discussion). However, if you have a lot of objects to load, you may have to be more selective about what you fetch, and about how big your SQL query gets.
For example, if your Color table contains 10 lines, omitting the join on sale.product.color will result in (at most) 10 extra SQL queries, bringing the total to 11. Once all the Color instances are loaded into the Hibernate session cache, you can trust Hibernate not to do any extra queries.
Bigger queries return more data, and when large data sets are involved, it is quite possible that the overhead of the weighty SQL query outweighs the overhead of reading a few small tables into memory and caching the results. In practice, these smaller tables should probably be cached using the second-level hibernate caching anyway. The bottom line is that the query structure should be designed and optimised via unit testing and concrete metrics (System.currentTimeMillis() will do fine).
Cases where joined queries aren't enough
Now suppose we have complex, multiple, bi-directional relations between two table. For example, suppose we have two classes : Company and City. A company is located in a city. A city outsources different types of work (water installation management, electricity management, etc.) to different companies. Each company is in turn located in a city.
In this situation, the presence of many-to-one relations means that Hibernate will potentially generate an uncontrollable number of SQL queries, and left join fetching is helpless to resolve the problem : you would end up with a query looking something like this :
from company c left join fetch c.city left join fetch c.city.waterCompany left join fetch c.city.electrityCompany left join fetch c.city.waterCompany.city left join fetch c.city.electrityCompany.city
etc...
In this case, you basically have two main options :
1. Let the client wait half an hour for his reports
2. Use an optimised HQL query to return only the columns you really need.
Generally, the first option isn't greatly appreciated by the client. So lets look at the second option. This approach involves determining exactly which columns you really need, and instanciating data-transfer JavaBean objects containing exactly those columns.
select new CityItem(city.id, city.name, city.electrityCompany.name)
from City city
...
This technique is fast and efficient, and avoids the overheads of handling associated objects and of loading large numbers of persistent objects into the Hibernate session cache. The only downside is the need to create a dedicated data-transfer class for each query.
More advanced techniques
Finally, I will present a variation on the previous technique, which avoids the need to create a new data-transfer class for each query. The idea is to retrieve a list of instances of a Hibernate-persisted business class, using a given HQL query, but with only a specified set of columns being instanciated. This allows fast, light-weight queries which return only the minimum necessary information, and avoid complex joins in the HQL queries. For example :
String query = "select com.id, com.label, com.postCode, com.mayor.name from Community as com left join com.mayor order by com.label "; results = HibernateUtils.find(query); or String query = "select com.id, com.label, com.postCode, com.mayor.name from Community as com left join com.mayor where com.label like ? order by com.label "; results = HibernateUtils.find(query, new Object[] {name + "%"}, new Type [] {Hibernate.STRING});
This query will return a list of Community objects, but with only the 4 specified fields instanciated. Note that this is a powerful technique, as the presentation layer may use ordinary business classes instead of data transfer objects, without having to know the details of the querying techniques being used. This method should be reserved for pure read-only/display actions, however, as data manipulation with incompletely-instanciated objects is generally a very bad idea.
The implementation of such a method is fairly simple, and based on introspection and the Apache BeanUtils classes. For the curious, here is a simplified version of the implementation of the first method (error handling and optimised caching code has been removed for simplicity) :
public List find(final String hqlQuery) throws Exception { List results = new ArrayList(); // // Prepare a Hibernate query // Query query = SessionManager.currentSession().createQuery(hqlQuery); // // Determine the return type for this query // Type beanType = query.getReturnTypes()[0]; Class beanClass = beanType.getReturnedClass(); // // Extract the list of columns returned by this query // String[] columns = extractColumns(hqlQuery); // // Pre-process bean attribute names, stripping spaces 'as' clauses // String[] attributeNames = getAttributeFieldNames(columns); // // Pre-process result field names, stripping spaces and retaining // alias field names instead of the original column names where necessary // String[] resultFieldNames = getResultFieldNames(columns); // // Execute query and build result list // Iterator iter = query.iterate(); while(iter.hasNext()) { Object[] row = (Object[]) iter.next(); Object bean = beanClass.newInstance(); for (int j = 0; j < row.length; j++) { if (row[j] != null) { initialisePath(bean, attributeNames[j]); PropertyUtils.setProperty(bean, attributeNames[j], row[j]); } } results.add(bean); } return results; } private static void initialisePath(final Object bean, final String fieldName) throws Exception { int dot = fieldName.indexOf('.'); while (dot >= 0) { String attributeName = fieldName.substring(0, dot); Class attributeClass = PropertyUtils.getPropertyType(bean, attributeName); if (PropertyUtils.getProperty(bean, attributeName) == null) { PropertyUtils.setProperty(bean, attributeName, attributeClass.newInstance()); } dot = fieldName.indexOf('.', dot + 1); } }
The implementation of the version using parameters is left as an exercise to the reader.
Sidenote : Unit testing
There are plenty of good articles on the merits of unit testing and of test-driven development, so I won't write much about this here. But, a word for the wise : in my experience, you cannot efficently code Hibernate queries without using a test-driven approach. OK, maybe I exaggerate a little. But the fact is that it's a lot easier to fine-tune your Hibernate queries against a local database, using timers and/or by studying the generated SQL queries, than if you wait until the solution is deployed in a WAR and the page takes 5 minutes to appear.
There is also much debate on how to test the Hibernate layers (hibernate mappings and DAOs). In our case, we are lucky enough to have a test database containing legacy data. We use a dedicated test database, which is also used for the test server.
1. Unit tests must not change the state of the database (so any created objects should be removed in the tearDown() method)
2. Unit tests may use existing test data (useful to test real cases), but only for read-only query operations (existing data must not be modified). It is the developers responsibility to only use stable existing test data, and to update the test cases if necessary.
Conclusion
Hibernate is a powerful object/relational persistence library, and it has an equally powerful query mechanism. However, the power of Hibernate does not dispense the developer from optimising the HQL queries. Indeed, the very simplicity of HQL can sometimes hide serious performance issues for the unwary. However, when well tuned, and using appropriate querying techniques, Hibernate can result in cleaner, simpler code as well as performance equaling that of an optimised SQL query via JDBC.
发表评论
-
hibernate技巧 封装查询结果 初学者必看
2009-11-26 09:47 2733简单说明:本文章内容与我的网站 天天成长的博客的文章是同步写作 ... -
Hibernate, Oracle, Sequences, Triggers
2009-06-29 16:49 2425有困难,找猪八戒 请看原文: http://www.julie ... -
使用hibernate 一个表 映射 两个类
2008-04-25 21:51 2005可以将一个表映射成为两个类,方法就是写针对此表写两个映射文件, ... -
hiberante 查询问题 格式化日期
2008-04-23 11:13 6463如果数据库中的日期字段格式是 年月日 时分秒,那么在用hibe ... -
hibernate 查询时 对日期的比较
2008-04-22 14:17 15992http://www.roseindia.net/hibern ... -
hibernate资源
2008-04-07 10:41 1165myeclipse应用hibernate,包括视频教程 htt ... -
实现hibernate单表的查询 参数传递
2008-03-12 14:40 1937public void testHibernate(){ ... -
hibernate分页
2007-10-24 21:20 1145http://displaytag.sourceforge.n ... -
hibernate 支持 to_char 方法 不用本地SQL
2007-10-19 20:05 4133from YourPOJO a where to_char(a ... -
hibernate 映射 视图 view
2007-10-18 18:28 3850摘自http://www.hibernate.org/hib_ ... -
[转]在Hibernate中处理批量更新和批量删除 hibernate batch update and insert
2007-10-16 10:19 5460转:http://java.ccidnet.com/art/3 ... -
Hibernate commit() 和flush() 的区别
2007-10-15 17:24 2739Hibernate commit() 和flush() 的区别 ... -
createSQLQuery,addScalar列名用大写
2007-09-28 18:17 6371public List findByGroupByMateri ... -
hibernate 批量插入
2007-09-27 18:58 2105StringBuffer sql = new StringBu ... -
Hibernate条件查询(Criteria Query)
2007-08-25 15:18 2037Hibernate条件查询(Criteria Query) - ...
相关推荐
10. 扩展与高级主题:涉及Hibernate的事件监听、拦截器、动态模型、批量操作和CGLIB/AOP集成等内容,帮助读者掌握更高级的Hibernate技巧。 11. 实战案例:书中可能会提供一些实际项目案例,让读者将所学知识应用于...
【标题】"Hibernate使用技巧共2页.pdf.zip" 提供的文档主要聚焦于Hibernate这一流行的Java持久化框架的使用技巧。Hibernate是一个对象关系映射(ORM)工具,它简化了数据库与Java对象之间的交互,使得开发者可以使用...
### Hibernate使用技巧详解 #### 一、两种配置文件选择及使用场景 - **A. `hibernate.cfg.xml`** - **特点**: 包含数据库连接信息、可用的映射文件等配置。 - **示例**: `Configuration config = new ...
2. **实用性**:本书不仅仅局限于理论介绍,更注重实践应用,通过大量示例帮助读者掌握Hibernate的实际操作技巧。 3. **全面性**:从基础概念到高级技术,本书内容全面覆盖了Hibernate的各个方面,适合不同层次的...
只是一份非常珍贵的hibernatePPT教程。从第一个hibernate的简介到hibernate的完全实践都非常详细,按照课件一步步的操作。你就可以快速掌握真正的企业级hibernate开发实践。是非常珍贵的学习资料
这份史上最全的Hibernate教程,旨在帮助新手快速入门并掌握Hibernate的核心概念和使用技巧,同时也为有经验的开发者提供了深入研究的素材。通过系统学习,开发者不仅能提升数据库操作的效率,还能更好地理解和应用...
根据提供的文件信息,我们可以深入探讨Hibernate中的...在实际项目中,合理运用这些技巧可以极大地提高开发效率和代码质量。需要注意的是,在编写连表查询时要充分考虑性能问题,避免因查询复杂度过高而导致性能瓶颈。
《Hibernate DOC中文文档》是学习Hibernate框架的重要参考资料,它详细阐述了Hibernate的核心概念、配置、对象关系映射(ORM)以及各种操作技巧。对于初学者和有经验的开发者来说,这份文档都是掌握Hibernate不可或...
在实际项目中,了解并熟练掌握这些Hibernate核心概念和使用技巧,将极大提升Java开发的效率和代码质量。通过深入学习和实践,开发者可以充分利用Hibernate的优势,减少数据库相关的繁琐工作,专注于业务逻辑的实现。
通过《Hibernate4.1.11中文手册》,开发者不仅可以深入理解Hibernate的工作原理,还能掌握其在实际项目中的应用技巧,从而提高开发效率和软件质量。这份资源对于任何使用Java进行数据库开发的人来说,都是一份宝贵的...
### Hibernate电子书(全)- 关键知识点解析 #### Hibernate简介 Hibernate是一种“对象/关系映射”...通过深入理解上述知识点,初学者可以快速掌握Hibernate的核心概念和使用技巧,为进一步的开发打下坚实的基础。
通过深入学习Hibernate 4.1.4.Final的源码,我们可以更好地理解其设计思想,提升我们的编程技巧,同时也能为日常开发中遇到的问题提供解决方案。这是一份宝贵的资源,无论是对于初学者还是经验丰富的开发者,都值得...
### Hibernate中Example的用法详解 #### 一、前言 ...通过本文的介绍,相信你已经对`Example`的使用有了全面的认识,接下来不妨在实际项目中尝试运用这些技巧,以提高你的Hibernate应用开发水平。
理解并合理运用这些技巧,可以显著提升Hibernate应用的性能。 通过《Hibernate5实例程序》中的代码示例,读者可以亲手实践上述各个知识点,加深理解,并掌握在实际项目中如何有效利用Hibernate5进行数据库操作。...
### Hibernate使用技巧详解 #### 一、应用程序的分层结构及其优化 在现代软件开发中,采用分层架构是常见的设计模式,它有助于提高代码的可维护性和扩展性。分层架构通常包括以下三个主要层次: 1. **表述层**:...
本集合包含了三个关键的学习文档,分别关注于Hibernate的操作基础、对象关系映射和高级查询技巧,是深入理解Hibernate不可或缺的资料。 一、《Hiberhate的增、删、改、查、动态、查询、分页.doc》 这份文档详细阐述...
再者,我们要了解Hibernate的二级缓存使用技巧。在处理列表数据时,可以选择iterator()和list()。list()方法在首次执行时会将结果存入二级缓存,并记录SQL,如果后续请求相同的SQL且数据未变动,可以直接从缓存中...
通过观看“反向工程视频”,可以深入理解这些概念并掌握实际操作技巧。在学习过程中,不仅要理解理论知识,还要动手实践,将所学应用于实际项目,这样才能真正实现从菜鸟到高手的蜕变。分享这些知识,可以帮助更多的...