已阅读5页,还剩31页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Hibernate 所有缓存机制详解 缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。对大多数应用来说,应该慎重地考虑是否需要使用集群范围的缓存,因为访问的速度不一定会比直接访问数据库数据的速度快多少。持久化层可以提供多种范围的缓存。如果在事务范围的缓存中没有查到相应的数据,还可以到进程范围或集群范围的缓存内查询,如果还是没有查到,那么只有到数据库中查询。事务范围的缓存是持久化层的第一级缓存,通常它是必需的;进程范围或集群范围的缓存是持久化层的第二级缓存,通常是可选的。1简介Hibernate提供了两级缓存,第一级是Session的缓存。由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。第一级缓存是必需的,不允许而且事实上也无法卸除。在第一级缓存中,持久化类的每个实例都具有唯一的OID。第二级缓存是一个可插拔的的缓存插件,它是由SessionFactory负责管理。由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此第二级缓存是进程范围或者集群范围的缓存。这个缓存中存放的对象的松散数据。第二级对象有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。缓存适配器用于把具体的缓存实现软件与Hibernate集成。第二级缓存是可选的,可以在每个类或每个集合的粒度上配置第二级缓存。1条件适合存放到第二级缓存中的数据1 很少被修改的数据2 不是很重要的数据,允许出现偶尔并发的数据3 不会被并发访问的数据4 参考数据不适合存放到第二级缓存的数据1 经常被修改的数据2 财务数据,绝对不允许出现并发3 与其他应用共享的数据。1特点1) 条件查询的时候,总是发出一条select * from table_name where . (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。2) 把获得的所有数据对象根据ID放入到第二级缓存中。3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。4) 删除、更新、增加数据的时候,同时更新缓存。Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query缓存。Hibernate的Query缓存策略的过程如下:1) Hibernate首先根据这些信息组成一个Query Key,Query Key包括条件查询的请求一般信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。2) Hibernate根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果列表根据Query Key放入到Query缓存中。3) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。1hibernate提供的一级缓存 hibernate是一个线程对应一个session,一个线程可以看成一个用户。也就是说session级缓存(一级缓存)只能给一个线程用,别的线程用不了,一级缓存就是和线程绑定了。hibernate一级缓存生命周期很短,和session生命周期一样,一级缓存也称session级的缓存或事务级缓存。如果tb事务提交或回滚了,我们称session就关闭了,生命周期结束了。缓存和连接池的区别:缓存和池都是放在内存里,实现是一样的,都是为了提高性能的。但有细微的差别,池是重量级的,里面的数据是一样的,比如一个池里放100个Connection连接对象,这个100个都是一样的。缓存里的数据,每个都不一样。比如读取100条数据库记录放到缓存里,这100条记录都不一样。缓存主要是用于查询/同一个session中,发出两次load方法查询Studentstudent=(Student)session.load(Student.class,1);System.out.println(=+student.getName();/不会发出查询语句,load使用缓存student=(Student)session.load(Student.class,1);System.out.println(=+student.getName();第二次查询第一次相同的数据,第二次load方法就是从缓存里取数据,不会发出sql语句到数据库里查询。/同一个session,发出两次get方法查询Studentstudent=(Student)session.get(Student.class,1);System.out.println(=+student.getName();/不会发出查询语句,get使用缓存student=(Student)session.get(Student.class,1);System.out.println(=+student.getName();第二次查询第一次相同的数据,第二次不会发出sql语句查询数据库,而是到缓存里取数据。/同一个session,发出两次iterate查询实体对象Iteratoriter=session.createQuery(fromStudentswheres.id5).iterate();while(iter.hasNext()Studentstudent=(Student)iter.next();System.out.println(student.getName();System.out.println(-);/它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存iter=session.createQuery(fromStudentswheres.id5).iterate();while(iter.hasNext()Studentstudent=(Student)iter.next();System.out.println(student.getName();一说到iterater查询就要立刻想起:iterater查询在没有缓存的情况下会有N+1的问题。执行上面代码查看控制台的sql语句,第一次iterate查询会发出N+1条sql语句,第一条sql语句查询所有的id,然后根据id查询实体对象,有N个id就发出N条语句查询实体。第二次iterate查询,却只发一条sql语句,查询所有的id,然后根据id到缓存里取实体对象,不再发sql语句到数据库里查询了。/同一个session,发出两次iterate查询,查询普通属性Iteratoriter=session.createQuery(fromStudentswheres.id5).iterate();while(iter.hasNext()Stringname=(String)iter.next();System.out.println(name);System.out.println(-);/iterate查询普通属性,一级缓存不会缓存,所以发出查询语句/一级缓存是缓存实体对象的iter=session.createQuery(fromStudentswheres.id5).iterate();while(iter.hasNext()Stringname=(String)iter.next();System.out.println(name);执行代码看控制台sql语句,第一次发出N+1条sql语句,第二次还是发出了N+1条sql语句。因为一级缓存只缓存实体对象,tb不会缓存普通属性,所以第二次还是发出sql查询语句。/两个session,每个session发出一个load方法查询实体对象trysession=HibernateUtils.getSession();session.beginTransaction();Studentstudent=(Student)session.load(Student.class,1);System.out.println(=+student.getName();session.getTransaction().commit();catch(Exceptione)e.printStackTrace();session.getTransaction().rollback();finallyHibernateUtils.closeSession(session);第二个session调用load方法trysession=HibernateUtils.getSession();session.beginTransaction();Studentstudent=(Student)session.load(Student.class,1);/会发出查询语句,session间不能共享一级缓存数据/因为他会伴随着session的消亡而消亡System.out.println(=+student.getName();session.getTransaction().commit();catch(Exceptione)e.printStackTrace();session.getTransaction().rollback();finallyHibernateUtils.closeSession(session);第一个session的load方法会发出sql语句查询实体对象,第二个session的load方法也会发出sql语句查询实体对象。因为session间不能共享一级缓存的数据,所以第二个session的load方法查询相同的数据还是要到数据库中查询,因为它找不到第一个session里缓存的数据。/同一个session,先调用save方法再调用load方法查询刚刚save的数据Studentstudent=newStudent();student.setName(张三);/save方法返回实体对象的idSerializableid=session.save(student);student=(Student)session.load(Student.class,id);/不会发出查询语句,因为save支持缓存System.out.println(=+student.getName();先save保存实体对象,再用load方法查询刚刚save的实体对象,则load方法不会发出sql语句到数据库查询的,而是到缓存里取数据,因为save方法也支持缓存。当然前提是同一个session。/大批量的数据添加for(inti=0;i100;i+)Studentstudent=newStudent();student.setName(张三+i);session.save(student);/每20条更新一次if(i%20=0)session.flush();/清除缓存的内容session.clear();大批量数据添加时,会造成内存溢出的,因为save方法支持缓存,每save一个对象就往缓存里放,如果对象足够多内存肯定要溢出。一般的做法是先判断一下save了多少个对象,如果save了20个对象就对缓存手动的清理缓存,这样就不会造成内存溢出。注意:清理缓存前,要手动调用flush方法同步到数据库,否则save的对象就没有保存到数据库里。注意:大批量数据的添加还是不要使用hibernate,这是hibernate弱项。可以使用jdbc(速度也不会太快,只是比hibernate好一点),或者使用工具产品来实现,比如oracle的OracleSQLLoader,导入数据特别快。Hibernate二级缓存 二级缓存需要sessionFactory来管理,它是进初级的缓存,所有人都可以使用,它是共享的。二级缓存比较复杂,一般用第三方产品。hibernate提供了一个简单实现,用Hashtable做的,只能作为我们的测试使用,商用还是需要第三方产品。使用缓存,肯定是长时间不改变的数据,如果经常变化的数据放到缓存里就没有太大意义了。因为经常变化,还是需要经常到数据库里查询,那就没有必要用缓存了。hibernate做了一些优化,和一些第三方的缓存产品做了集成。老师采用EHCache缓存产品。和EHCache二级缓存产品集成:EHCache的jar文件在hibernate的lib里,我们还需要设置一系列的缓存使用策略,需要一个配置文件ehcache.xml来配置。这个文件放在类路径下。/默认配置,所有的类都遵循这个配置我们也可以对某个对象单独配置:还需要在hibernate.cfg.xml配置文件配置缓存,让hibernate知道我们使用的是那个二级缓存。org.hibernate.cache.EhCacheProvidertrue启用二级缓存的配置可以不写的,因为默认就是true开启二级缓存。必须还手动指定那些实体类的对象放到缓存里在hibernate.cfg.xml里:/在标签里,在标签后配置或者在实体类映射文件里:/在标签里,标签前配置usage属性表示使用缓存的策略,一般优先使用read-only,表示如果这个数据放到缓存里了,则不允许修改,如果修改就会报错。这就要注意我们放入缓存的数据不允许修改。因为放缓存里的数据经常修改,也就没有必要放到缓存里。使用read-only策略效率好,因为不能改缓存。但是可能会出现脏数据的问题,这个问题解决方法只能依赖缓存的超时,比如上面我们设置了超时为120秒,120后就可以对缓存里对象进行修改,而在120秒之内访问这个对象可能会查询脏数据的问题,因为我们修改对象后数据库里改变了,而缓存却不能改变,这样造成数据不同步,也就是脏数据的问题。第二种缓存策略read-write,当持久对象发生变化,缓存里就会跟着变化,数据库中也改变了。这种方式需要加解锁,效率要比第一种慢。还有两种策略,请看hibernate文档,最常用还是第一二种策略。二级缓存测试代码演示:注意上面我们讲的两个session分别调用load方法查询相同的数据,第二个session的load方法还是发了sql语句到数据库查询数据,这是因为一级缓存只在当前session中共享,也就是说一级缓存不能跨session访问。/开启二级缓存,二级缓存是进程级的缓存,可以共享/两个session分别调用load方法查询相同的实体对象trysession=HibernateUtils.getSession();session.beginTransaction();Studentstudent=(Student)session.load(Student.class,1);System.out.println(=+student.getName();session.getTransaction().commit();catch(Exceptione)e.printStackTrace();session.getTransaction().rollback();finallyHibernateUtils.closeSession(session);trysession=HibernateUtils.getSession();session.beginTransaction();Studentstudent=(Student)session.load(Student.class,1);/不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据/二级缓存是进程级的缓存System.out.println(=+student.getName();session.getTransaction().commit();catch(Exceptione)e.printStackTrace();session.getTransaction().rollback();finallyHibernateUtils.closeSession(session);如果开启了二级缓存,那么第二个session调用的load方法查询第一次查询的数据,是不会发出sql语句查询数据库的,而是去二级缓存中取数据。/开启二级缓存/两个session分别调用get方法查询相同的实体对象trysession=HibernateUtils.getSession();session.beginTransaction();Studentstudent=(Student)session.get(Student.class,1);System.out.println(=+student.getName();session.getTransaction().commit();catch(Exceptione)e.printStackTrace();session.getTransaction().rollback();finallyHibernateUtils.closeSession(session);trysession=HibernateUtils.getSession();session.beginTransaction();Studentstudent=(Student)session.get(Student.class,1);/不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据/二级缓存是进程级的缓存System.out.println(=+student.getName();session.getTransaction().commit();catch(Exceptione)e.printStackTrace();session.getTransaction().rollback();finallyHibernateUtils.closeSession(session);注意:二级缓存必须让sessionfactory管理,让sessionfactory来清除二级缓存。sessionFactory.evict(Student.class);/清除二级缓存中所有student对象,sessionFactory.evict(Student.class,1);/清除二级缓存中id为1的student对象。如果在第一个session调用load或get方法查询数据后,把二级缓存清除了,那么第二个session调用load或get方法查询相同的数据时,还是会发出sql语句查询数据库的,因为缓存里没有数据只能到数据库里查询。我们查询数据后会默认自动的放到二级和一级缓存里,如果我们想查询的数据不放到缓存里,也是可以的。也就是说我们可以控制一级缓存和二级缓存的交换。session.setCacheMode(CacheMode.IGNORE);禁止将一级缓存中的数据往二级缓存里放。还是用上面代码测试,在第一个session调用load方法前,执行session.setCacheMode(CacheMode.IGNORE);这样load方法查询的数据不会放到二级缓存里。那么第二个session执行load方法查询相同的数据,会发出sql语句到数据库中查询,因为二级缓存里没有数据,一级缓存因为不同的session不能共享,所以只能到数据库里查询。上面我们讲过大批量的数据添加时可能会出现溢出,解决办法是每当天就20个对象后就清理一次一级缓存。如果我们使用了二级缓存,光清理一级缓存是不够的,还要禁止一二级缓存交互,在save方法前调用session.setCacheMode(CacheMode.IGNORE)。二级缓存也不会存放普通属性的查询数据,这和一级缓存是一样的,只存放实体对象。session级的缓存对性能的提高没有太大的意义,因为生命周期太短了。Hibernate查询缓存 一级缓存和二级缓存都只是存放实体对象的,如果查询实体对象的普通属性的数据,只能放到查询缓存里,查询缓存还存放查询实体对象的id。查询缓存的生命周期不确定,当它关联的表发生修改,查询缓存的生命周期就结束。这里表的修改指的是通过hibernate修改,并不是通过数据库客户端软件登陆到数据库上修改。hibernate的查询缓存默认是关闭的,如果要使用就要到hibernate.cfg.xml文件里配置:true并且必须在程序中手动启用查询缓存,在query接口中的setCacheable(true)方法来启用。/关闭二级缓存,没有开启查询缓存,采用list方法查询普通属性/同一个sessin,查询两次Listnames=session.createQuery(fromStudents).list();for(inti=0;inames.size();i+)Stringname=(String)names.get(i);System.out.println(name);System.out.println(-);/会发出sql语句names=session.createQuery(fromStudents).setCacheable(true).list();for(inti=0;inames.size();i+)Stringname=(String)names.get(i);System.out.println(name);上面代码运行,由于没有使用查询缓存,而一、二级缓存不会缓存普通属性,所以第二次查询还是会发出sql语句到数据库中查询。现在开启查询缓存,关闭二级缓存,并且在第一次的list方法前调用setCacheable(true),并且第二次list查询前也调用这句代码,可以写出下面这样:Listnames=session.createQuery(fromStudents).setCacheable(true).list();其它代码不变,运行代码后发现第二次list查询普通属性没有发出sql语句,也就是说没有到数据库中查询,而是到查询缓存中取数据。/开启查询缓存,关闭二级缓存,采用list方法查询普通属性/在两个session中调用list方法trysession=HibernateUtils.getSession();session.beginTransaction();Listnames=session.createQuery(fromStudents).setCacheable(true).list();for(inti=0;inames.size();i+)Stringname=(String)names.get(i);System.out.println(name);session.getTransaction().commit();catch(Exceptione)e.printStackTrace();session.getTransaction().rollback();finallyHibernateUtils.closeSession(session);System.out.println(-);trysession=HibernateUtils.getSession();session.beginTransaction();/不会发出查询语句,因为查询缓存和session的生命周期没有关系Listnames=session.createQuery(fromStudents).setCacheable(true).list();for(inti=0;inames.size();i+)Stringname=(String)names.get(i);System.out.println(name);session.getTransaction().commit();catch(Exceptione)e.printStackTrace();session.getTransaction().rollback();finallyHibernateUtils.closeSession(session);运行结果是第二个session发出的list方法查询普通属性,没有发出sql语句到数据库中查询,而是到查询缓存里取数据,这说明查询缓存和session生命周期没有关系。/开启缓存,关闭二级缓存,采用iterate方法查询普通属性/在两个session中调用iterate方法查询运行结果是第二个session的iterate方法还是发出了sql语句查询数据库,这说明iterate迭代查询普通属性不支持查询缓存。/关闭查询缓存,关闭二级缓存,采用list方法查询实体对象/在两个session中调用list方法查询运行结果第一个session调用list方法查询实体对象会发出sql语句查询数据,因为关闭了二级缓存,所以第二个session调用list方法查询实体对象,还是会发出sql语句到数据库中查询。/开启查询缓存,关闭二级缓存/在两个session中调用list方法查询实体对象运行结果第一个session调用list方法查询实体对象会发出sql语句查询数据库的。第二个session调用list方法查询实体对象,却发出了很多sql语句查询数据库,这跟N+1的问题是一样的,发出了N+1条sql语句。为什么会出现这样的情况呢?这是因为我们现在查询的是实体对象,查询缓存会把第一次查询的实体对象的id放到缓存里,当第二个session再次调用list方法时,它会到查询缓存里把id一个一个的拿出来,然后到相应的缓存里找(先找一级缓存找不到再找二级缓存),如果找到了就返回,如果还是没有找到,则会根据一个一个的id到数据库中查询,所以一个id就会有一条sql语句。注意:如果配置了二级缓存,则第一次查询实体对象后,会往一级缓存和二级缓存里都存放。如果没有二级缓存,则只在一级缓存里存放。(一级缓存不能跨session共享)/开启查询缓存,开启二级缓存/在两个session中调用list方法查询实体对象运行结果是第一个session调用list方法会发出sql语句到数据库里查询实体对象,因为配置了二级缓存,则实体对象会放到二级缓存里,因为配置了查询缓存,则实体对象所有的id放到了查询缓存里。第二个session调用list方法不会发出sql语句,而是到二级缓存里取数据。查询缓存意义不大,查询缓存说白了就是存放由list方法或iterate方法查询的数据。我们在查询时很少出现完全相同条件的查询,这也就是命中率低,这样缓存里的数据总是变化的,所以说意义不大。除非是多次查询都是查询相同条件的数据,也就是说返回的结果总是一样,这样配置查询缓存才有意义。Hibernate 所有缓存机制详解 hibernate提供的一级缓存 hibernate是一个线程对应一个session,一个线程可以看成一个用户。也就是说session级缓存(一级缓存)只能给一个线程用,别的线程用不了,一级缓存就是和线程绑定了。hibernate一级缓存生命周期很短,和session生命周期一样,一级缓存也称session级的缓存或事务级缓存。如果tb事务提交或回滚了,我们称session就关闭了,生命周期结束了。缓存和连接池的区别:缓存和池都是放在内存里,实现是一样的,都是为了提高性能的。但有细微的差别,池是重量级的,里面的数据是一样的,比如一个池里放100个Connection连接对象,这个100个都是一样的。缓存里的数据,每个都不一样。比如读取100条数据库记录放到缓存里,这100条记录都不一样。缓存主要是用于查询/同一个session中,发出两次load方法查询Student student = (Student)session.load(Student.class, 1);System.out.println(= + student.getName();/不会发出查询语句,load使用缓存student = (Student)session.load(Student.class, 1);System.out.println(= + student.getName(); 第二次查询第一次相同的数据,第二次load方法就是从缓存里取数据,不会发出sql语句到数据库里查询。/同一个session,发出两次get方法查询Student student = (Student)session.get(Student.class, 1);System.out.println(= + student.getName();/不会发出查询语句,get使用缓存student = (Student)session.get(Student.class, 1);System.out.println(= + student.getName(); 第二次查询第一次相同的数据,第二次不会发出sql语句查询数据库,而是到缓存里取数据。/同一个session,发出两次iterate查询实体对象Iterator iter = session.createQuery(from Student s where s.id5).iterate();while (iter.hasNext() Student student = (Student)iter.next();System.out.println(student.getName();System.out.println(-);/它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存iter = session.createQuery(from Student s where s.id5).iterate();while (iter.hasNext() Student student = (Student)iter.next();System.out.println(student.getName(); 一说到iterater查询就要立刻想起:iterater查询在没有缓存的情况下会有N+1的问题。执行上面代码查看控制台的sql语句,第一次iterate查询会发出N+1条sql语句,第一条sql语句查询所有的id,然后根据id查询实体对象,有N个id就发出N条语句查询实体。第二次iterate查询,却只发一条sql语句,查询所有的id,然后根据id到缓存里取实体对象,不再发sql语句到数据库里查询了。/同一个session,发出两次iterate查询,查询普通属性Iterator iter = session.createQuery(select from Student s where s.id5).iterate();while (iter.hasNext() String name = (String)iter.next();System.out.println(name);System.out.println(-);/iterate查询普通属性,一级缓存不会缓存,所以发出查询语句/一级缓存是缓存实体对象的iter = session.createQuery(select from Student s where s.id5).iterate();while (iter.hasNext() String name = (String)iter.next();System.out.println(name); 执行代码看控制台sql语句,第一次发出N+1条sql语句,第二次还是发出了N+1条sql语句。因为一级缓存只缓存实体对象,tb不会缓存普通属性,所以第二次还是发出sql查询语句。/两个session,每个session发出一个load方法查询实体对象try session = HibernateUtils.getSession();session.beginTransaction();Student student = (Student)session.load(Student.class, 1);System.out.println(= + student.getName();session.getTransaction().commit();catch(Exception e) e.printStackTrace();session.getTransaction().rollback();finally HibernateUtils.closeSession(session);第二个session调用load方法try session = HibernateUtils.getSession();session.beginTransaction();Student student = (Student)session.load(Student.class, 1);/会发出查询语句,session间不能共享一级缓存数据/因为他会伴随着session的消亡而消亡System.out.println(= + student.getName();session.getTransaction().commit();catch(Exception e) e.printStackTrace();session.getTransaction().rollback();finally HibernateUtils.closeSession(session); 第一个session的load方法会发出sql语句查询实体对象,第二个session的load方法也会发出sql语句查询实体对象。因为session间不能共享一级缓存的数据,所以第二个
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 电子病历系统应用管理规范
- 医疗护理绩效考核指标体系修订
- 宠物美容接待环境安全规范
- 入户礼仪培训接待流程操作手册
- 机械设备层按节奏流水施工组织方案
- 机加车间安全生产责任制度
- 江西省南昌市2026届高三年级下学期四月检测(二模)英语试题(含答案)
- 员工健康安全检查细则制度
- 医院感染自查报告
- 冰雹灾害应急物资
- 2025年健康管理师考试题库及答案
- 4S店安全管理培训课件
- 玉米压片技术培训课件
- 聚丙烯材料安全使用说明书范本
- 依法治校制度完备档案
- GB/T 5159-2025金属粉末(不包括硬质合金用粉) 与成型和烧结有联系的尺寸变化的测定方法
- 2025年充电桩建设与运营项目可行性研究报告及总结分析
- 少突胶质瘤的护理
- (2025)预防艾梅乙母婴传播项目培训测试试题(附答案)
- 销售提成计算与管理规范
- 深圳市初中学业水平考试体育与健康科目通识考试题库
评论
0/150
提交评论