




已阅读5页,还剩13页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
用 Hibernate 和 Spring 开发持久层摘 要:在本文中,用一个真实世界的例子向您介绍两个最激动人心的企业新技术。Hibernate 是一个对象关系映射工具,而 Spring 是一个 AOP 框架和 IOC 容器。介绍了如何结合这两者,为企业应用程序构建一个事务持久层。在本文中,通过具体介绍使用 Hibernate 和 Spring 在企业应用程序中构建一个事务持久层,您会认识到这些技术。关键词: AOP 框架;IOC 容器;事务持久层;对象关系映射Develop a persistence layer using Hibernate and SpringHU Chang-longInformation Engineering Institute, University of China Geosciences, Whuhan Hubei 430074 Abstract: In this article,we uses a real-world example to introduce you to two of the most exciting new technologies for the enterprise. Hibernate is an object-relation mapping tool and Spring is an AOP framework and IOC container. Follow along as we shows you how to combine the two to build a transactional persistence tier for your enterprise applications. Together, these two technologies will provide the foundation of your development efforts in this article. Youll use Hibernate to map some persistent objects to a relational database and Spring to make Hibernate easier to use and provide declarative transaction support. Key words: AOP framework; IOC container; transactional persistence; ORM;1、引言Hibernate 是 Java 平台上的一种流行的、容易使用的开放源代码对象关系(OR)映射框架。Spring 是一个 AOP 框架和 IOC 容器。这两种技术一起提供了本文中介绍的开发工作的基础。将使用 Hibernate 把一些持久性对象映射到关系数据库中,用 Spring 使 Hibernate 更容易使用并提供声明性事务支持。由于为示例类编写测试代码时使用了 DbUnit,我们还附带介绍了一点 TDD (测试驱动的开发)的内容。我们将首先介绍两种开发技术,然后分析例子。1.1 Hibernate 简介 Hibernate 是 Java 平台上的一种全功能的、开放源代码 OR 映射框架。Hibernate 在许多方面类似于 EJB CMP CMR (容器管理的持久性/容器管理的关系)和 JDO(Java Data Objects)。与 JDO 不同,Hibernate 完全着眼于关系数据库的 OR 映射,并且包括比大多数商业产品更多的功能。大多数 EJB CMP CMR 解决方案使用代码生成实现持久性代码,而 JDO 使用字节码修饰。与之相反,Hibernate 使用反射和运行时字节码生成,使它对于最终用户几乎是透明的(以前 Hibernate 的实现只使用反射,它有助于调试,当前版本Hibernate2.1.2保留了这种选项)。Hibernate 可以模拟继承(有几种方式)、关联(一对一或者一对多、containment 和 aggregation)和 composition。我将在本文中讨论每种关系类型的几个例子。Hibernate 提供了一种称为 Hibernate Query Language (HQL) 的 查询语言,它类似于 JDO 的 JDOQL 和 EJB 的 EJB QL,尽管它更接近于前者。但是 Hibernate 没有就此止步:它还可以进行直接的 SQL 查询和/或使用 object criteria 很容易地在运行时构成查询条件。在本文的例子中我将只使用 HQL。与 EJB CMP CMR 不同,Hibernate 像 JDO 一样可以在 J2EE 容器内部或者外部工作,这可以让那些进行 TDD 和敏捷开发的人受益。1.2 Hibernate总览对Hibernate非常高层的概览: 这幅图展示了Hibernate使用数据库和配置文件数据来为应用程序提供持久化服务(和持久化的对象)。 让我们更细致地观察一下运行时的体系结构。 挺不幸的,Hibernate是比较复杂的,提供了好几种不同的运行方式。我们展示一下两种极端情况。轻型体系中,应用程序自己提供JDBC连接,并且自行管理事务。这种方式使用了Hibernate API的一个最小子集。 全面解决体系中,对于应用程序来说,所有的底层JDBC/JTA API都被抽象了,Hibernate会替你照管所有的细节。 下面是图中一些对象的定义: SessionFactory (net.sf.hibernate.SessionFactory) 对编译过的映射文件的一个线程安全的,不可变的缓存快照。它是Session的工厂。是ConnectionProvider的客户。 可能持有事务之间重用的数据的缓存。 会话,Session (net.sf.hibernate.Session) 单线程,生命期短促的对象,代表应用程序和持久化层之间的一次对话。封装了一个JDBC连接。也是Transaction的工厂。 持有持久化对象的缓存。 持久化对象(Persistent Object)及其集合(Collection) 生命期短促的单线程的对象,包含了持久化状态和商业功能。它们可能是普通的JavaBeans,唯一特别的是他们现在从属于且仅从属于一个Session。 临时对象(Transient Object)及其集合(Collection) 目前没有从属于一个Session的持久化类的实例。他们可能是刚刚被程序实例化,还没有来得及被持久化,或者是被一个已经关闭的Session所实例化的。 事务,Transaction (net.sf.hibernate.Transaction) (可选) 单线程,生命期短促的对象,应用程序用它来表示一批工作的原子操作。是底层的JDBC,JTA或者CORBA事务的抽象。一个Session可能跨越多个Transaction 事务。 ConnectionProvider (net.sf.hibernate.connection.ConnectionProvider) (可选)JDBC连接的工厂和池。从底层的Datasource或者 DriverManager抽象而来。对应用程序不可见。 TransactionFactory (net.sf.hibernate.TransactionFactory) (可选)事务实例的工厂。对应用程序不可见。 在上面的轻型结构中,程序没有使用Transaction / TransactionFactory 或者ConnectionProvider API,直接和JTA/JDBC对话了。 1.3 持久化对象标识(Persistent Object Identity )应用程序可能同时在两个不同的session中存取同一个持久化对象。然而,两个Session实例是不可能共享一个持久化类的实例的。有两种不同的用来辨别对象是否相同的方法。 Persistent Identity,持久化辨别 foo.getId().equals( bar.getId() ) JVM Identity, JVM辨别 foo=bar 对于同一个特定的Session返回的对象来说,这二者是等价的。然而,当程序并行在两个不同的session中访问含义上“相同”(持久化辨别)的商业对象时,两个对象实例从JVM的角度上来看却是“不同”的(JVM辨别) 这种方式把并行访问(应用程序不需要对任何商业对象进行同步,只要求遵循每个Session一个线程的原则)和对象辨别(在应用程序的一个session之中,可以安全的用=来比较对象)的难题留给了Hibernate和数据库。 1.4 JMX集成JMX是用来管理Java组件的J2EE标准。Hibernate可以被标准的JMX Mbean管理,但是因为大多数程序还没有支持JMX,Hibernate也支持一些非标准的配置方式。 请查阅Hibernate网站,可以得到关于如何在JBOSS中把Hibernate配置成为一个JMX组件的更多信息。 1.5 JCA支持Hibernate也可以被配置成为一个JCA连接器。更多细节,请参阅网站。 1.6 Spring 简介Spring 既是一个 AOP 框架、也是一个 IOC 容器。对象最好的地方是可以替换它们,而 Spring 最好的地方是它有助于您替换它们。有了 Spring,只要用 JavaBean 属性和配置文件加入依赖性(协作对象)。然后可以很容易地在需要时替换具有类似接口的协作对象。Spring 为 IOC 容器和 AOP 提供了很好的入口(on-ramp)。因此,不需要熟悉 AOP 就可以理解本文中的例子。所需要知道的就是将要用 AOP 为示例应用程序声明式地添加事务支持,与使用 EJB 技术时的方式基本相同。要了解 IOC 容器、AOP 和 Spring 的更多内容,请参阅 参考资料。简单地说,AOP 让开发人员可以创建非行为性的关注点,称为横切关注点,并将它们插入到应用程序代码中。使用 AOP 后,公共服务(比如日志、持久性、事务等)就可以分解成方面并应用到域对象上,同时不会增加域对象的对象模型的复杂性。IOC 允许创建一个可以构造对象的应用环境,然后向这些对象传递它们的协作对象。正如单词 倒置 所表明的,IOC 就像反过来的 JNDI。没有使用一堆抽象工厂、服务定位器、单元素(singleton)和直接构造(straight construction),每一个对象都是用其协作对象构造的。因此是由容器管理协作对象(collaborator)。1.7 Spring概览Spring包含许多功能和特性,并被很好地组织在下图所示的七个模块中。本节将依次介绍每个模块. Spring框架概览 Core包是框架的最基础部分, 并提供依赖注入(Dependency Injection)特性来使你可管理Bean容器功能。 这里的基础概念是BeanFactory,它提供Factory模式来消除对程序性单例的需要, 并允许你从程序逻辑中分离出依赖关系的配置和描述。 构建于Beans包上Context包,提供了一种框架式的Bean访问方式, 有些象JNDI注册。Context包的特性得自Beans包,并添加了文本消息的发送,通过比如资源串, 事件传播,资源装载的方式和Context的透明创建,如通过Servlet容器。 DAO包提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码。 该包也提供了一种方法实现编程性和声明性事务管理,不仅仅是针对实现特定接口的类, 而且对所有的POJO。 ORM包为流行的关系对象映射APIs提供了集成层,包括JDO,Hibernate和iBatis。 通过ORM包,你可与所有Spring提供的其他特性相结合来使用这些对象/关系映射, 如前边提到的简单声明性事务管理。 Spring的AOP包提供与AOP联盟兼容的面向方面编程实现,允许你定义, 如方法拦截器和切点,来干净地给从逻辑上说应该被分离的功能实现代码解耦。 使用源码级的元数据功能,你可将各种行为信息合并到你的代码中,有点象.Net的attribute。 Spring的Web包提供了基本的面向Web的综合特性,如Multipart功能, 使用Servlet监听器的Context的初始化和面向Web的Applicatin Context。 当与WebWork或Struts一起使用Spring时,这个包使Spring可与其他框架结合。 Spring的Web MVC包提供了面向Web应用的Model-View-Controller实现。 Spring的MVC实现不仅仅是一种实现,它提供了一种domain model代码和web form的清晰分离, 这使你可使用Spring框架的所有其他特性,如校验. 1.8 使用场景利用积木方式来描述你在各种场合使用Spring的情况, 从Applet一直到完整的使用Spring的事务管理功能和Web框架的企业应用。 典型的完整Spring Web应用 一个典型的使用大部分Spring特性的Web应用。使用TransactionProxyFactoryBeans, Web应用是完全事务性的,就像使用EJB提供的那种容器管理的事务一样 所有的你的自定义业务逻辑可以通过简单的POJO来实现,并通过Spring的Dependency Injection容器进行管理。其他的服务,如发送email和校验,独立于Web层, 使你能够决定在哪里执行校验规则。 Spring的ORM支持包含了Hibernate,JDO和iBatis。如使用HibernateDaoSupport, 你可复用已经存在的Hibernate映射。从Controller无缝整合web层和领域模型, 消除对ActionForms的需要和其他转换HTTP参数为领域模型的类。 使用了第三方框架的Spring中间层 有时,现有情况不允许你彻底地转换到一种不同的框架。Spring没有 强迫你使用它的全部,它不是一种全有全无 的解决方案。现有的使用WebWork,Struts,Tapestry或其他的UI框架的前端程序可极佳的 与基于Spring的中间层进行集成,使你可使用Spring提供的事务处理特性。 你唯一要做的事是使用ApplicationContext来挂接你的业务逻辑和 通过WebApplicationContext来集成你的Struts前端程序。 远程使用场景 当你需要通过WebService来访问你的现有代码时, 你可使用Spring的Hessian-,Burlap-, Rmi- 或者 JaxRpcProxyFactory类。 使得突然给现有应用增加远程访问时不再那么困难。 EJBs - 封装现有的POJO Spring也为EJB提供了访问层和抽象层, 使你可复用已存在的POJO并将他们包装在Stateless SessionBean中, 以便在可能需要声明式安全(EJB中的安全管理,译者注)的可升级的可容错的Web应用中使用。 2 具体到业务在本文的其余部分,所有的讨论都将基于一个实际的例子。起点是一个企业应用程序,要为它实现一个事务持久层。持久层是一个对象关系数据库,它包括像 User、User Group、Roles 和 ContactInfo 这些熟悉的抽象。在深入到数据库的要素查询和事务管理之前,需要建立它的基础:对象关系映射。我们将采用 Hibernate 设置它,并只使用一点 Spring。2.1 用 Hibernate 进行 OR 映射 Hibernate 使用 XML (*.hbm.xml) 文件将 Java 类映射到表,将 JavaBean 属性映射到数据库表。幸运的是,有一组 XDoclet 标签支持 Hibernate 开发,这使得创建所需要的 *.hbm.xml 文件更容易了。清单 1 中的代码将一个 Java 类映射到数据库表。清单 1. 将 Java 类映射到 DB 表Listing 1. Mapping a Java class to a DB tableUser.java/* * hibernate.class table=T_USER */public class User private Long id = new Long(-1);private String email;private String password;./* * return * hibernate.id column=PK_USER_ID * unsaved-value=-1 * generator-class=native */public Long getId() return id;/* * perty column=VC_EMAIL * type=string * update=false * insert=true * unique=true * not-null=true * length=82 * return */public String getEmail() return email;/* * perty column=VC_PASSWORD * type=string * update=false * insert=true * unique=true * not-null=true * length=20 * return */public String getPassword() return password;.可以看到,hibernate.class table=T_USER 标签将 User 映射到 T_USER 表。perty column=VC_PASSWORD 将 JavaBean 属性 password 映射到 VC_PASSWORD 列。hibernate.id column=PK_USER_ID 标签声明id 属性是主键,它将使用本机(generator-class=native)数据库机制生成键(例如,Oracle sequences 和 SQL Server Identity 键)。Hibernate 可以指定 generator-class=native 以外的、其他可以想象的得到主键获得策略。type和length 属性用于从 Hibernate *.hbm.xml OR 映射文件生成表。这些 final 属性是可选的,因为使用的可能不是 green-field 数据库。在这个例子中,已经有数据库了,所以不需要额外的属性。看过了表如何映射到类以及列如何映射到 JavaBean 属性,该使用 Hibernate 在 OR 数据库中设置一些关系了。设置对象关系在本节中,我将只触及 Hibernate 提供的设置对象间关系的选项的一小部分。首先设置像 User、User Group、Roles 和 ContactInfo 这些类之间的关系。其中一些关系如图 1 所示,这是数据库的验证对象模型。图 1. 关系的图示Figure 1. Graphical representation of relationships 如您所见,在上述抽象中存在各种各样的关系。User 与 ContactInfo 有一对一关系。ContactInfo 的生命周期与 User 相同(用数据库的术语,UML 中的组成 aka 级联删除)。如果删除 User,则相应的 ContactInfo 也会删除。在 Users 与 Roles 之间存在多对多关系(即与独立生命周期相关联)。在 Groups 与 Users 之间存在一对多关系,因为组有许多用户。用户可以存在于组外,即是 aggregation 而不是 composition (用数据库的说法,在 Groups 和 Users 之间没有级联删除关系)。此外,User 和 Employee 有子类关系,就是说,Employee 的类型为 User。表 1 显示了如何用 XDoclet 标签创建一些不同类型的对象关系。(使用XDoclet 标签只是生成Hibernate的多种方式之一)表 1. 用 XDoclet 创建对象关系Table 1. Creating object relationships using XDoclets关系Java/XDocletSQL DDL(由 Hibernate Schema Export 生成的 MySQL)组包含用户一对多Aggregation 双向(GroupUsers)Group.java/* return* hibernate.bag name=users* cascade=save-update* lazy=true* inverse=true* * hibernate.collection-key * column=FK_GROUP_ID* * hibernate.collection-one-to-many * class=net.sf.hibernateExamples.User*/public List getUsers() return users;User.java/* hibernate.many-to-one * column=FK_GROUP_ID * class=net.sf.hibernateExamples.Group*/public Group getGroup() return group;create table T_USER (PK_USER_ID BIGINT NOT NULL AUTO_INCREMENT,USER_TYPE VARCHAR(255) not null,FK_GROUP_ID BIGINT,VC_EMAIL VARCHAR(82) not null unique,primary key (PK_USER_ID)create table TBL_GROUP (PK_GROUP_ID BIGINT NOT NULL AUTO_INCREMENT,VC_DESCRIPTION VARCHAR(255),VC_NAME VARCHAR(40) unique,primary key (PK_GROUP_ID)alter table T_USER add index (FK_GROUP_ID), add constraint FK_111 foreign key (FK_GROUP_ID) references TBL_GROUP (PK_GROUP_ID)用户有联系信息一对一Composition 单向(UserContactInfo)User.java/* return* * hibernate.one-to-one cascade=all * */public ContactInfo getContactInfo() return contactInfo;ContactInfo.java(Nothing to see here. Unidirectional!)create table T_USER (PK_USER_ID BIGINT NOT NULL AUTO_INCREMENT,USER_TYPE VARCHAR(255) not null,FK_GROUP_ID BIGINT,VC_EMAIL VARCHAR(82) not null unique,primary key (PK_USER_ID)create table TBL_CONTACT_INFO (PK_CONTACT_INFO_ID BIGINT not null,.primary key (PK_CONTACT_INFO_ID)用户与角色关联多对多Association 单向(Users-Roles)User.java/* return* hibernate.bag * table=TBL_JOIN_USER_ROLE* cascade=all* inverse=true* * hibernate.collection-key * column=FK_USER_ID* * hibernate.collection-many-to-many * class=net.sf.hibernateExamples.Role * column=FK_ROLE_ID* */public List getRoles() return roles;Role.javaNothing to see here. Unidirectional!create table TBL_ROLE (PK_ROLE_ID BIGINT NOT NULL AUTO_INCREMENT,VC_DESCRIPTION VARCHAR(200),VC_NAME VARCHAR(20),primary key (PK_ROLE_ID)create table T_USER (PK_USER_ID BIGINT NOT NULL AUTO_INCREMENT,USER_TYPE VARCHAR(255) not null,FK_GROUP_ID BIGINT,VC_EMAIL VARCHAR(82) not null unique,primary key (PK_USER_ID)create table TBL_JOIN_USER_ROLE (FK_USER_ID BIGINT not null,FK_ROLE_ID BIGINT not null)雇员是用户Inheritance 用户 雇员User.java/* hibernate.class table=T_USER * discriminator-value=2 *hibernate.discriminator column=USER_TYPE.*/public class User Employee.java/* hibernate.subclass discriminator-value = 1*/public class Employee extends Usercreate table T_USER (PK_USER_ID BIGINT NOT NULL AUTO_INCREMENT,USER_TYPE VARCHAR(255) not null,FK_GROUP_ID BIGINT,VC_EMAIL VARCHAR(82) not null unique,primary key (PK_USER_ID)要了解在 Hibernate 中设置对象关系的更多内容,请参阅 参考资料。Hibernate 中的查询Hibernate 有三种类型的查询:Criteria, object compositionSQLHQL在下面的例子中将只使用 HQL。本节还要使用 Spring,用它的 AOP-driven HibernateTemplate 简化 Hibernate 会话的处理。清单 2 展示了两个方法:一个使用 HQL 查询的组查询,另一个是后面接一个操作的组查询。注意在第二个方法中,Spring HibernateTemplate 是如何简化会话管理的。清单 2. 使用查询Listing 2. Using queriesimport net.sf.hibernate.HibernateException;import net.sf.hibernate.Session;import net.sf.hibernate.Query;import org.springframework.orm.hibernate.HibernateCallback;import org.springframework.orm.hibernate.support.HibernateDaoSupport;public class UserDAO extends HibernateDaoSupport /* Demonstrates looking up a group with a HQL query* param email* return*/public Group findGroupByName(String name) return (Group) getHibernateTemplate().find(from Group g where =?,name).get(0);/* * Demonstrates looking up a group and forcing it to populate users (relationship was lazy) * param email * return */public Group findPopulatedGroupByName(final String name) HibernateCallback callback = new HibernateCallback() public Object doInHibernate(Session session) throws HibernateException, SQLException Group group =null;String query = from Group g where =?;Query queryObject = getHibernateTemplate().createQuery(session, query);queryObject.setParameter(0, name);group = (Group) queryObject.list().get(0);group.getUsers().size(); / 强制装载force loadreturn group;return (Group) getHibernateTemplate().execute(callback); .您可能会注意到第二个方法比第一个方法复杂得多,因为它强制装载 users 集合。因为 Group-Users 之间的关系设置为 lazy initialize(即表 2 中 lazy=true),组对象需要一个活跃的会话以查询用户。在定义 Group 和 Users 之间关系时设置这个属性为 lazy=false,则不需要第二个方法。在这种情况下,可能使用第一种方法 (findGroupByName) 列出组,用第二种方法(findPopulatedGroupByName)查看组细节。Spring IOC 和 Hibernate使用 Spring 时,在 J2EE 容器内和容器外工作一样容易。比如在最近的项目中,我在 Eclipse 中,使用 HSQL 和本地数据库对使用 Hibernate 事务管理器的 Hypersonic SQL 数据库进行持久性单元测试。然后,在部署到 J2EE 服务器时,将持久层转换为使用 J2EE 数据源(通过 JNDI)、JTA 事务和使用 FireBird (一个开放源代码版本的 Interbase)。这是用 Spring 作为 IOC 容器完成的。从清单 3 中可以看出,Spring 允许加入依赖性。清单中应用程序上下文文件配置 dataSource 的。dataSource 传递给 sessionFactory,sessionFactory 传递给 UserDAO。清单 3. Spring IOC 和 HibernateListing 3. Spring IOC and Hibernatecom.mysql.jdbc.Driverjdbc:mysql:/localhost:3306/mysqlroot net/sf/hibernateExamples/User.hbm.xml net/sf/hibernateExamples/Group.hbm.xml net/sf/hibernateExamples/Role.hbm.xml net/sf/hibernateExamples/ContactInfo.hbm.xml net.sf.hibernate.dialect.MySQLDialect设置了 UserDAO 后,下一步就是定义并使用更多的查询以展示可以完成的操作。Hibernate 可以用预定义查询将查询存储到源代码之外,如清单 4 所示。清单 4. 预定义查询Listing 4. Named queriesUser.java/* * hibernate.class table=T_USER discriminator-value=2 * hibernate.discriminator column=USER_TYPE * hibernate.query name=AllUsers query=from User user order by user.email asc * hibernate.query name=OverheadStaff * query=from Employee employee join employee.group g where not in (ENGINEERING,IT) * * hibernate.query name=CriticalStaff * query=from Employee employee join employee.group g where in (ENGINEERING,IT) * * hibernate.query name=GetUsersInAGroup * query=select user from Group g join g.users user * * hibernate.query name=GetUsersNotInAGroup * query=select user from User user where user.group is null * * hibernate.query name=UsersBySalaryGreaterThan * query=from User user inner join user.contactInfo info where info.salary ?1 * * hibernate.query name=UsersBySalaryBetween * query=from User user join user.contactInfo info where info.salary between ?1 AND ?2 * * hibernate.query name=UsersByLastNameLike * query=from User user join user.contactInfo info where info.lastName like ?1 * * hibernate.query name=GetEmailsOfUsers * query=select user.email from Group g join g.users as user where = ?1 * */public class User .上述代码定义了几个预定义查询。预定义查询 是存储在 *.hbm.xml 文件中的查询。在清单 5 中,可以看到如何执行预定义查询。清单 5. 使用预定义查询Listing 5. Using named queriesUserDAO.java/* 查询返回String的示例。 */ public String getUserEmailsInGroup(String groupName)List emailList =getHibernateTemplate().findByNamedQuery(GetEmailsOfUsers); return (String )emailList.toArray
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年木材加工、处理机械项目申请报告
- 水龙吟-苏轼课件
- 机电设备安装调试与验收方案
- 水粉插画基础知识培训课件
- 混凝土施工中预应力钢筋张拉技术方案
- 基础设施施工工艺优化
- 混凝土施工的临时设施搭建与管理方案
- 城镇集中供热的用户需求与服务管理方案
- 水痘和腮腺炎培训课件
- 用户体验设计42课件
- 2023年辅警招聘-公共基础知识考试题库(含答案)
- 巷道围岩注浆加固施工安全技术措施
- 实验中学初一新生分班考试数学试卷附答案
- 区治安巡防队员面试题
- 施工组织设计施工总体部署完整版
- TUPSW微机控制电力专用不间断电源(UPS)系统使用说明书
- 骨质疏松诊治与中医药
- LY/T 2383-2014结构用木材强度等级
- GB/T 528-2009硫化橡胶或热塑性橡胶拉伸应力应变性能的测定
- 中日关系历史
- GB/T 15171-1994软包装件密封性能试验方法
评论
0/150
提交评论