`
nlslzf
  • 浏览: 1049264 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

『转姚博文』提高hibernate性能 很经典

阅读更多
在一个拥有单独业务层的应用中,业务层必须在返回之前,为web层“准备”好其所需的数据集合。这就意味着 业务层应该载入所有表现层/web层所需的数据,并将这些已实例化完毕的数据返回。通常,应用程序应该 为web层所需的每个集合调用Hibernate.initialize()(这个调用必须发生咱session关闭之前); 或者使用带有FETCH从句,或FetchMode.JOIN的Hibernate查询, 事先取得所有的数据集合。如果你在应用中使用了Command模式,代替Session Facade , 那么这项任务将会变得简单的多。



你也可以通过merge()或lock()方法,在访问未实例化的集合(或代理)之前, 为先前载入的对象绑定一个新的Session。 显然,Hibernate将不会,也不应该自动完成这些任务,因为这将引入一个特殊的事务语义。




有时候,你并不需要完全实例化整个大的集合,仅需要了解它的部分信息(例如其大小)、或者集合的部分内容。

你可以使用集合过滤器得到其集合的大小,而不必实例化整个集合:

( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue() 


这里的createFilter()方法也可以被用来有效的抓取集合的部分内容,而无需实例化整个集合:
s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list(); 



20.1.5. 使用批量抓取(Using batch fetching)



Hibernate可以充分有效的使用批量抓取,也就是说,如果仅一个访问代理(或集合),那么Hibernate将不载入其他未实例化的代理。 批量抓取是延迟查询抓取的优化方案,你可以在两种批量抓取方案之间进行选择:在类级别和集合级别。

类/实体级别的批量抓取很容易理解。假设你在运行时将需要面对下面的问题:你在一个Session中载入了25个 Cat实例,每个Cat实例都拥有一个引用成员owner, 其指向Person,而Person类是代理,同时lazy="true"。 如果你必须遍历整个cats集合,对每个元素调用getOwner()方法,Hibernate将会默认的执行25次SELECT查询, 得到其owner的代理对象。这时,你可以通过在映射文件的Person属性,显式声明batch-size,改变其行为:

<class name="Person" batch-size="10">...</class> 


随之,Hibernate将只需要执行三次查询,分别为10、10、 5。

你也可以在集合级别定义批量抓取。例如,如果每个Person都拥有一个延迟载入的Cats集合, 现在,Sesssion中载入了10个person对象,遍历person集合将会引起10次SELECT查询, 每次查询都会调用getCats()方法。如果你在Person的映射定义部分,允许对cats批量抓取, 那么,Hibernate将可以预先抓取整个集合。请看例子:

<class name="Person"> <set name="cats" batch-size="3"> ... </set></class> 


如果整个的batch-size是3(笔误?),那么Hibernate将会分四次执行SELECT查询, 按照3、3、3、1的大小分别载入数据。这里的每次载入的数据量还具体依赖于当前Session中未实例化集合的个数。

如果你的模型中有嵌套的树状结构,例如典型的帐单-原料结构(bill-of-materials pattern),集合的批量抓取是非常有用的。 (尽管在更多情况下对树进行读取时,嵌套集合(nested set)或原料路径(materialized path)(××) 是更好的解决方法。)



20.1.6. 使用子查询抓取(Using subselect fetching)



假若一个延迟集合或单值代理需要抓取,Hibernate会使用一个subselect重新运行原来的查询,一次性读入所有的实例。这和批量抓取的实现方法是一样的,不会有破碎的加载。



20.1.7. 使用延迟属性抓取(Using lazy property fetching)



Hibernate3对单独的属性支持延迟抓取,这项优化技术也被称为组抓取(fetch groups)。 请注意,该技术更多的属于市场特性。在实际应用中,优化行读取比优化列读取更重要。但是,仅载入类的部分属性在某些特定情况下会有用,例如在原有表中拥有几百列数据、数据模型无法改动的情况下。

可以在映射文件中对特定的属性设置lazy,定义该属性为延迟载入。

<class name="Document"> <id name="id"> <generator class="native"/> </id> <property name="name" not-null="true" length="50"/> <property name="summary" not-null="true" length="200" lazy="true"/> <property name="text" not-null="true" length="2000" lazy="true"/></class> 


属性的延迟载入要求在其代码构建时加入二进制指示指令(bytecode instrumentation),如果你的持久类代码中未含有这些指令, Hibernate将会忽略这些属性的延迟设置,仍然将其直接载入。

你可以在Ant的Task中,进行如下定义,对持久类代码加入“二进制指令。”

<target name="instrument" depends="compile"> <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask"> <classpath path="${jar.path}"/> <classpath path="${classes.dir}"/> <classpath refid="lib.class.path"/> </taskdef> <instrument verbose="true"> <fileset dir="${testclasses.dir}/org/hibernate/auction/model"> <include name="*.class"/> </fileset> </instrument></target> 


还有一种可以优化的方法,它使用HQL或条件查询的投影(projection)特性,可以避免读取非必要的列, 这一点至少对只读事务是非常有用的。它无需在代码构建时“二进制指令”处理,因此是一个更加值得选择的解决方法。

有时你需要在HQL中通过抓取所有属性,强行抓取所有内容。



20.2. 二级缓存(The Second Level Cache)



Hibernate的Session在事务级别进行持久化数据的缓存操作。 当然,也有可能分别为每个类(或集合),配置集群、或JVM级别(SessionFactory级别)的缓存。 你甚至可以为之插入一个集群的缓存。注意,缓存永远不知道其他应用程序对持久化仓库(数据库)可能进行的修改 (即使可以将缓存数据设定为定期失效)。

默认情况下,Hibernate使用EHCache进行JVM级别的缓存(目前,Hibernate已经废弃了对JCS的支持,未来版本中将会去掉它)。 你可以通过设置hibernate.cache.provider_class属性,指定其他的缓存策略, 该缓存策略必须实现org.hibernate.cache.CacheProvider接口。


表 20.1.  缓存策略提供商(Cache Providers) Cache Provider class Type Cluster Safe Query Cache Supported
Hashtable (not intended for production use) org.hibernate.cache.HashtableCacheProvider memory   yes 
EHCache org.hibernate.cache.EhCacheProvider memory, disk   yes 
OSCache org.hibernate.cache.OSCacheProvider memory, disk   yes 
SwarmCache org.hibernate.cache.SwarmCacheProvider clustered (ip multicast) yes (clustered invalidation)   
JBoss TreeCache org.hibernate.cache.TreeCacheProvider clustered (ip multicast), transactional yes (replication) yes (clock sync req.) 





20.2.1. 缓存映射(Cache mappings)



类或者集合映射的“<cache>元素”可以有下列形式:

<cache usage="transactional|read-write|nonstrict-read-write|read-only" (1)/>

(1) 

usage说明了缓存的策略: transactional、 read-write、 nonstrict-read-write或 read-only。



另外(首选?), 你可以在hibernate.cfg.xml中指定<class-cache>和 <collection-cache> 元素。

这里的usage 属性指明了缓存并发策略(cache concurrency strategy)。



20.2.2. 策略:只读缓存(Strategy: read only)



如果你的应用程序只需读取一个持久化类的实例,而无需对其修改, 那么就可以对其进行只读 缓存。这是最简单,也是实用性最好的方法。甚至在集群中,它也能完美地运作。

<class name="eg.Immutable" mutable="false"> <cache usage="read-only"/> ....</class>



20.2.3.  策略:读/写缓存(Strategy: read/write)



如果应用程序需要更新数据,那么使用读/写缓存 比较合适。 如果应用程序要求“序列化事务”的隔离级别(serializable transaction isolation level),那么就决不能使用这种缓存策略。 如果在JTA环境中使用缓存,你必须指定hibernate.transaction.manager_lookup_class属性的值, 通过它,Hibernate才能知道该应用程序中JTA的TransactionManager的具体策略。 在其它环境中,你必须保证在Session.close()、或Session.disconnect()调用前, 整个事务已经结束。 如果你想在集群环境中使用此策略,你必须保证底层的缓存实现支持锁定(locking)。Hibernate内置的缓存策略并不支持锁定功能。

<class name="eg.Cat" .... > <cache usage="read-write"/> .... <set name="kittens" ... > <cache usage="read-write"/> .... </set></class>



20.2.4.  策略:非严格读/写缓存(Strategy: nonstrict read/write)



如果应用程序只偶尔需要更新数据(也就是说,两个事务同时更新同一记录的情况很不常见),也不需要十分严格的事务隔离, 那么比较适合使用非严格读/写缓存策略。如果在JTA环境中使用该策略, 你必须为其指定hibernate.transaction.manager_lookup_class属性的值, 在其它环境中,你必须保证在Session.close()、或Session.disconnect()调用前, 整个事务已经结束。



20.2.5.  策略:事务缓存(transactional)



Hibernate的事务缓存策略提供了全事务的缓存支持, 例如对JBoss TreeCache的支持。这样的缓存只能用于JTA环境中,你必须指定 为其hibernate.transaction.manager_lookup_class属性。

没有一种缓存提供商能够支持上列的所有缓存并发策略。下表中列出了各种提供器、及其各自适用的并发策略。


表 20.2.  各种缓存提供商对缓存并发策略的支持情况(Cache Concurrency Strategy Support) Cache read-only nonstrict-read-write read-write transactional
Hashtable (not intended for production use) yes yes yes  
EHCache yes yes yes  
OSCache yes yes yes  
SwarmCache yes yes    
JBoss TreeCache yes     yes




20.3.  管理缓存(Managing the caches)



无论何时,当你给save()、update()或 saveOrUpdate()方法传递一个对象时,或使用load()、 get()、list()、iterate() 或scroll()方法获得一个对象时, 该对象都将被加入到Session的内部缓存中。

当随后flush()方法被调用时,对象的状态会和数据库取得同步。 如果你不希望此同步操作发生,或者你正处理大量对象、需要对有效管理内存时,你可以调用evict() 方法,从一级缓存中去掉这些对象及其集合。

ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result setwhile ( cats.next() ) { Cat cat = (Cat) cats.get(0); doSomethingWithACat(cat); sess.evict(cat);}

Session还提供了一个contains()方法,用来判断某个实例是否处于当前session的缓存中。

如若要把所有的对象从session缓存中彻底清除,则需要调用Session.clear()。

对于二级缓存来说,在SessionFactory中定义了许多方法, 清除缓存中实例、整个类、集合实例或者整个集合。

sessionFactory.evict(Cat.class, catId); //evict a particular CatsessionFactory.evict(Cat.class); //evict all CatssessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittenssessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections

CacheMode参数用于控制具体的Session如何与二级缓存进行交互。





CacheMode.NORMAL - 从二级缓存中读、写数据。



CacheMode.GET - 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。



CacheMode.PUT - 仅向二级缓存写数据,但不从二级缓存中读数据。



CacheMode.REFRESH - 仅向二级缓存写数据,但不从二级缓存中读数据。通过 hibernate.cache.use_minimal_puts的设置,强制二级缓存从数据库中读取数据,刷新缓存内容。



如若需要查看二级缓存或查询缓存区域的内容,你可以使用统计(Statistics) API。

Map cacheEntries = sessionFactory.getStatistics() .getSecondLevelCacheStatistics(regionName) .getEntries();

此时,你必须手工打开统计选项。可选的,你可以让Hibernate更人工可读的方式维护缓存内容。

hibernate.generate_statistics truehibernate.cache.use_structured_entries true



20.4. 查询缓存(The Query Cache)



查询的结果集也可以被缓存。只有当经常使用同样的参数进行查询时,这才会有些用处。 要使用查询缓存,首先你必须打开它:

hibernate.cache.use_query_cache true

该设置将会创建两个缓存区域 - 一个用于保存查询结果集(org.hibernate.cache.StandardQueryCache); 另一个则用于保存最近查询的一系列表的时间戳(org.hibernate.cache.UpdateTimestampsCache)。 请注意:在查询缓存中,它并不缓存结果集中所包含的实体的确切状态;它只缓存这些实体的标识符属性的值、以及各值类型的结果。 所以查询缓存通常会和二级缓存一起使用。

绝大多数的查询并不能从查询缓存中受益,所以Hibernate默认是不进行查询缓存的。如若需要进行缓存,请调用 Query.setCacheable(true)方法。这个调用会让查询在执行过程中时先从缓存中查找结果, 并将自己的结果集放到缓存中去。

如果你要对查询缓存的失效政策进行精确的控制,你必须调用Query.setCacheRegion()方法, 为每个查询指定其命名的缓存区域。

List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger") .setEntity("blogger", blogger) .setMaxResults(15) .setCacheable(true) .setCacheRegion("frontpages") .list();

如果查询需要强行刷新其查询缓存区域,那么你应该调用Query.setCacheMode(CacheMode.REFRESH)方法。 这对在其他进程中修改底层数据(例如,不通过Hibernate修改数据),或对那些需要选择性更新特定查询结果集的情况特别有用。 这是对SessionFactory.evictQueries()的更为有效的替代方案,同样可以清除查询缓存区域。



20.5.  理解集合性能(Understanding Collection performance)



前面我们已经对集合进行了足够的讨论。本段中,我们将着重讲述集合在运行时的事宜。



20.5.1.  分类(Taxonomy)



Hibernate定义了三种基本类型的集合:





值数据集合



一对多关联



多对多关联



这个分类是区分了不同的表和外键关系类型,但是它没有告诉我们关系模型的所有内容。 要完全理解他们的关系结构和性能特点,我们必须同时考虑“用于Hibernate更新或删除集合行数据的主键的结构”。 因此得到了如下的分类:





有序集合类



集合(sets)



包(bags)



所有的有序集合类(maps, lists, arrays)都拥有一个由<key>和 <index>组成的主键。 这种情况下集合类的更新是非常高效的——主键已经被有效的索引,因此当Hibernate试图更新或删除一行时,可以迅速找到该行数据。

集合(sets)的主键由<key>和其他元素字段构成。 对于有些元素类型来说,这很低效,特别是组合元素或者大文本、大二进制字段; 数据库可能无法有效的对复杂的主键进行索引。 另一方面,对于一对多、多对多关联,特别是合成的标识符来说,集合也可以达到同样的高效性能。( 附注:如果你希望SchemaExport为你的<set>创建主键, 你必须把所有的字段都声明为not-null="true"。)

<idbag>映射定义了代理键,因此它总是可以很高效的被更新。事实上, <idbag>拥有着最好的性能表现。

Bag是最差的。因为bag允许重复的元素值,也没有索引字段,因此不可能定义主键。 Hibernate无法判断出重复的行。当这种集合被更改时,Hibernate将会先完整地移除 (通过一个(in a single DELETE))整个集合,然后再重新创建整个集合。 因此Bag是非常低效的。

请注意:对于一对多关联来说,“主键”很可能并不是数据库表的物理主键。 但就算在此情况下,上面的分类仍然是有用的。(它仍然反映了Hibernate在集合的各数据行中是如何进行“定位”的。)



20.5.2.  Lists, maps 和sets用于更新效率最高



根据我们上面的讨论,显然有序集合类型和大多数set都可以在增加、删除、修改元素中拥有最好的性能。

可论证的是对于多对多关联、值数据集合而言,有序集合类比集合(set)有一个好处。因为Set的内在结构, 如果“改变”了一个元素,Hibernate并不会更新(UPDATE)这一行。 对于Set来说,只有在插入(INSERT)和删除(DELETE) 操作时“改变”才有效。再次强调:这段讨论对“一对多关联”并不适用。

注意到数组无法延迟载入,我们可以得出结论,list, map和idbags是最高效的(非反向)集合类型,set则紧随其后。 在Hibernate中,set应该时最通用的集合类型,这时因为“set”的语义在关系模型中是最自然的。

但是,在设计良好的Hibernate领域模型中,我们通常可以看到更多的集合事实上是带有inverse="true" 的一对多的关联。对于这些关联,更新操作将会在多对一的这一端进行处理。因此对于此类情况,无需考虑其集合的更新性能。



20.5.3.  Bag和list是反向集合类中效率最高的



在把bag扔进水沟之前,你必须了解,在一种情况下,bag的性能(包括list)要比set高得多: 对于指明了inverse="true"的集合类(比如说,标准的双向的一对多关联), 我们可以在未初始化(fetch)包元素的情况下直接向bag或list添加新元素! 这是因为Collection.add())或者Collection.addAll() 方法 对bag或者List总是返回true(这点与与Set不同)。因此对于下面的相同代码来说,速度会快得多。

Parent p = (Parent) sess.load(Parent.class, id); Child c = new Child(); c.setParent(p); p.getChildren().add(c); //no need to fetch the collection! sess.flush();



20.5.4.  一次性删除(One shot delete)



偶尔的,逐个删除集合类中的元素是相当低效的。Hibernate并没那么笨, 如果你想要把整个集合都删除(比如说调用list.clear()),Hibernate只需要一个DELETE就搞定了。

假设我们在一个长度为20的集合类中新增加了一个元素,然后再删除两个。 Hibernate会安排一条INSERT语句和两条DELETE语句(除非集合类是一个bag)。 这当然是显而易见的。

但是,假设我们删除了18个数据,只剩下2个,然后新增3个。则有两种处理方式:





逐一的删除这18个数据,再新增三个;



删除整个集合类(只用一句DELETE语句),然后增加5个数据。



Hibernate还没那么聪明,知道第二种选择可能会比较快。 (也许让Hibernate不这么聪明也是好事,否则可能会引发意外的“数据库触发器”之类的问题。)

幸运的是,你可以强制使用第二种策略。你需要取消原来的整个集合类(解除其引用), 然后再返回一个新的实例化的集合类,只包含需要的元素。有些时候这是非常有用的。

显然,一次性删除并不适用于被映射为inverse="true"的集合。



20.6.  监测性能(Monitoring performance)



没有监测和性能参数而进行优化是毫无意义的。Hibernate为其内部操作提供了一系列的示意图,因此可以从 每个SessionFactory抓取其统计数据。



20.6.1.  监测SessionFactory



你可以有两种方式访问SessionFactory的数据记录,第一种就是自己直接调用 sessionFactory.getStatistics()方法读取、显示统计数据。

此外,如果你打开StatisticsService MBean选项,那么Hibernate则可以使用JMX技术 发布其数据记录。你可以让应用中所有的SessionFactory同时共享一个MBean,也可以每个 SessionFactory分配一个MBean。下面的代码即是其演示代码:

// MBean service registration for a specific SessionFactoryHashtable tb = new Hashtable();tb.put("type", "statistics");tb.put("sessionFactory", "myFinancialApp");ObjectName on = new ObjectName("hibernate", tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationstats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactoryserver.registerMBean(stats, on); // Register the Mbean on the server

// MBean service registration for all SessionFactory'sHashtable tb = new Hashtable();tb.put("type", "statistics");tb.put("sessionFactory", "all");ObjectName on = new ObjectName("hibernate", tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationserver.registerMBean(stats, on); // Register the MBean on the server

TODO:仍需要说明的是:在第一个例子中,我们直接得到和使用MBean;而在第二个例子中,在使用MBean之前 我们则需要给出SessionFactory的JNDI名,使用hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name") 得到SessionFactory,然后将MBean保存于其中。

你可以通过以下方法打开或关闭SessionFactory的监测功能:





在配置期间,将hibernate.generate_statistics设置为true或false;







在运行期间,则可以可以通过sf.getStatistics().setStatisticsEnabled(true) 或hibernateStatsBean.setStatisticsEnabled(true)



你也可以在程序中调用clear()方法重置统计数据,调用logSummary() 在日志中记录(info级别)其总结。



20.6.2.  数据记录(Metrics)



Hibernate提供了一系列数据记录,其记录的内容包括从最基本的信息到与具体场景的特殊信息。所有的测量值都可以由 Statistics接口进行访问,主要分为三类:





使用Session的普通数据记录,例如打开的Session的个数、取得的JDBC的连接数等;



实体、集合、查询、缓存等内容的统一数据记录



和具体实体、集合、查询、缓存相关的详细数据记录



例如:你可以检查缓存的命中成功次数,缓存的命中失败次数,实体、集合和查询的使用概率,查询的平均时间等。请注意 Java中时间的近似精度是毫秒。Hibernate的数据精度和具体的JVM有关,在有些平台上其精度甚至只能精确到10秒。

你可以直接使用getter方法得到全局数据记录(例如,和具体的实体、集合、缓存区无关的数据),你也可以在具体查询中通过标记实体名、 或HQL、SQL语句得到某实体的数据记录。请参考Statistics、EntityStatistics、 CollectionStatistics、SecondLevelCacheStatistics、 和QueryStatistics的API文档以抓取更多信息。下面的代码则是个简单的例子:

Statistics stats = HibernateUtil.sessionFactory.getStatistics();double queryCacheHitCount = stats.getQueryCacheHitCount();double queryCacheMissCount = stats.getQueryCacheMissCount();double queryCacheHitRatio = queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);log.info("Query Hit ratio:" + queryCacheHitRatio);EntityStatistics entityStats = stats.getEntityStatistics( Cat.class.getName() );long changes = entityStats.getInsertCount() + entityStats.getUpdateCount() + entityStats.getDeleteCount();log.info(Cat.class.getName() + " changed " + changes + "times" );

如果你想得到所有实体、集合、查询和缓存区的数据,你可以通过以下方法获得实体、集合、查询和缓存区列表: getQueries()、getEntityNames()、 getCollectionRoleNames()和 getSecondLevelCacheRegionNames()。
分享到:
评论

相关推荐

    java文集

    映射标记 Java的“静态库链接” 姚博文 集成ACEGI 进行权限控制 SWT可交互式Browser控件 JDK配置(注意) RIA简介(第一部分) 在 Eclipse 中嵌入 NASA World Wind Java SDK, 用3DES加密解密 ...

    图像去雾基于基于Matlab界面的(多方法对比,PSNR,信息熵,GUI界面).rar

    MATLAB设计

    c语言打字母游戏源码.zip

    c语言打字母游戏源码

    c语言做的一个任务管理器.zip

    c语言做的一个任务管理器

    JetBra-2021.1.x-重置.mp4.zip

    JetBra-2021.1.x-重置.mp4.zip

    小学班主任与家长沟通现状及改进策略研究

    内容概要:本文围绕小学班主任与家长沟通的现状进行了详尽分析,揭示了沟通方式不当、频率低、内容片面及理念不一致等问题,并基于访谈、文献研究及案例分析,提出了多元化的沟通方式、丰富沟通内容、讲究沟通艺术、转变家长观念和完善制度等多项策略,旨在提高家校合作的效能。 适合人群:从事小学教育教学的班主任、教师以及对家校合作感兴趣的教育工作者。 使用场景及目标:①通过本文提出的多种策略,改善小学班主任与家长之间的沟通;②促进家校互动,助力学生健康成长和发展;③推动教育领域的研究与发展。 阅读建议:本文详细阐述了沟通现状及具体问题,适合系统阅读。读者可根据实际情况,挑选适用于自身的沟通策略实施,并结合实例进行反思与改进。

    WSL批量压缩MP4文件对应Shell脚本文件

    WSL批量压缩MP4文件对应Shell脚本文件

    Java源码ssm框架的社区疫情防控管理系统-毕业设计论文-期末大作业.rar

    本项目是一个基于Java SSM框架的社区疫情防控管理系统,旨在通过信息化手段提升社区疫情防控的效率和准确性。系统集成了居民信息管理、健康监测、疫情上报、隔离管理等多项功能,能够实时跟踪和记录社区居民的健康状况,及时发现潜在的风险人员,并对其进行有效的隔离和管理。系统采用了Spring、Spring MVC和MyBatis三大框架技术,确保了系统的稳定性和扩展性。通过前端页面与后端逻辑的紧密配合,系统实现了数据的动态展示和交互操作,极大地方便了社区工作人员的日常工作。此外,系统还具备强大的数据统计和分析功能,能够帮助管理人员全面掌握社区的疫情动态,制定科学合理的防控措施。项目为完整毕设源码,先看项目演示,希望对需要的同学有帮助。

    Motorcad 外转子式42极36槽 永磁同步电机,直流无刷电机设计案例, 该电机55kw,220rpm,功率密度较高

    Motorcad 外转子式42极36槽 永磁同步电机,直流无刷电机设计案例,。 该电机55kw,220rpm,功率密度较高

    labview控制 西门子S7-1200 1214 dcdcdcplc 程序 plc只需要设置连接机制与IP即可 通讯为TCP IP协议

    labview控制 西门子S7-1200 1214 dcdcdcplc 程序 plc只需要设置连接机制与IP即可 通讯为TCP IP协议

    城市驾驶舱解决方案.pdf

    城市驾驶舱解决方案.pdf

    Shell教程v1.0中文PDF完整版最新版本

    Shell是一种用C语言编写的程序,它作为用户与Unix/Linux系统之间的桥梁,使得用户可以通过Shell完成大部分工作。Shell既是一个命令语言,也是一个程序设计语言。 本书《Shell教程》以简洁明了的语言向读者介绍Shell编程,旨在帮助读者迅速掌握Shell编程技能,并能够编写出实用的程序和代码,特别适合初学者学习。 **目录** - **前言** - **第1章 Shell简介** - 介绍Shell及其命令的两种执行方式。 - **第2章 常见的Shell类型** - **第3章 Shell与编译型语言的对比** - **第4章 Shell的使用场景** - **第5章 编写第一个Shell脚本** - **第6章 Shell变量** - 包括Shell变量的定义、删除、只读变量以及变量类型。 - **第7章 Shell特殊变量** - 讨论Shell中的$0, $#, $*, $@, $?, $$等特殊变量及其与命令行参数的关系。 - **第8章 Shell替换** - **第9章 Shell运算符** - 包括算数运算符、关系运算符、布尔运算

    CNC编程员个人简历模板

    CNC编程员个人简历模板

    机械设计摇摆喂料机 sw21全套设计资料100%好用.zip

    机械设计摇摆喂料机 sw21全套设计资料100%好用.zip

    拍打经络操mmexport1735392775826.mp4

    中医养生,拍打经络操全身轻松百病除

    2-趣味数学2.3.7 完全免费的数学学习软件

    【软件介绍】:趣味数学是一款完全免费的数学学习软件,无需注册登录,界面简约纯净。它支持多种分类学习,如趣味数学、数学初练、应用计算、数字推理、图形推理、数字2048、题目练习和数学知识等。其中,趣味数学含154个题目和关卡,数学初练含75个题目,应用计算含310个题目,数字推理含260个题目,图形推理含116个题目,数字2048含79个关卡。题目练习功能可按初中、高中选择,并细分学期与题目类型。数学知识合集功能涵盖初中和高中的各种知识点,共100个高中学习章节,每个章节包含多个知识点。软件不仅提供丰富的数学知识学习,还支持多类型的数学刷题,所有题目均配有答案和解析。

    基于Java 实现的Android手机平台的背单词软件,利用手机解锁记忆单词 锁屏背单词力争帮大家合理地利用好碎片时间,把原本无用的时间变得有用,把没有意义的事情(解锁)变得有意义

    【作品名称】:基于Java 实现的Android手机平台的背单词软件,利用手机解锁记忆单词。锁屏背单词力争帮大家合理地利用好碎片时间,把原本无用的时间变得有用,把没有意义的事情(解锁)变得有意义 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】: 主要功能 (1)背单词解锁:锁屏界面滑动单词解锁。 (2)语音朗读:单词真人发音。 (3)复习单词:内置专业的复习功能,有效巩固单词。 (4)学习记录:记录每日解锁次数和学习量,鼓励督促学习。 (5)生词本:卡片堆叠式记录学习不熟练和答错的单词并复习。 (6)词句翻译:查询陌生词句,了解释义与读音。 (7)锁屏壁纸选择:可选默认壁纸,也可选取手机相册照片作为锁屏壁纸。 (8)名人名句:每日一句名人名句。 【资源声明】:本资源作为“参考资料”而不是“定制需求”,代码只能作为参考,不能完全复制照搬。需要有一定的基础看懂代码,自行调试代码并解决报错,能自行添加功能修改代码。

    微信小程序源码-智慧旅游平台开发微信小程序-微信端-毕业设计源码-期末大作业.zip

    本项目是围绕智慧旅游平台开发的微信小程序,旨在为游客提供更为便捷、全面的旅游信息服务。通过该小程序,用户可以轻松查询旅游景点信息、预订门票和酒店,同时还能获取实时天气、交通指南等实用数据,极大提升了旅游的便利性和趣味性。 在功能方面,小程序集成了地图导航、旅游攻略、在线支付等多项实用功能,用户可以根据自身需求灵活选择使用。框架方面,项目采用了微信小程序原生开发框架,确保了良好的用户体验和流畅的操作性能。 此外,该项目还融入了数据分析功能,帮助旅游管理部门更好地了解游客需求,优化旅游资源配置。项目的开发不仅锻炼了学生的实践能力,也为智慧旅游平台的建设提供了有力支持。项目为完整毕设源码,先看项目演示,希望对需要的同学有帮助。

    matlab-B样条轨迹规划-1 七次非均匀B样条轨迹规划, 基于NSGAII的时间-能量-冲击最优 上自己的关节值和时间就能用,简单好用,

    matlab-B样条轨迹规划-1 七次非均匀B样条轨迹规划, 基于NSGAII的时间-能量-冲击最优。 上自己的关节值和时间就能用,简单好用,

    基于springboot的餐品美食论坛源码(java毕业设计完整源码).zip

    项目均经过测试,可正常运行! 环境说明: 开发语言:java JDK版本:jdk1.8 框架:springboot 数据库:mysql 5.7/8 数据库工具:navicat 开发软件:eclipse/idea

Global site tag (gtag.js) - Google Analytics