红皮书-1.0持久层技术新一代云ERP解决方案2目录一.前言..41.1.U8cloud数据访问层特点.4二.JDBCFrameWork..62.1.JdbcSession的创建:.62.2.数据访问异常的处理.62.3.结果集合的处理.72.4.更新的实现.92.4.1.无参数更新数据..92.4.2.带参数更新数据.102.4.3.批量带参数更新数据..102.4.4.批量无参数更新.112.5.JdbcSession中特殊参数类型..11三.对象的持久化..143.1.普通JavaBean的持久化.143.1.1.对象定义.143.1.2.构造数据映射对象..153.1.3.对象读取.163.1.4.对象写入.173.1.5.对象更新.173.1.6.对象删除.173.2.SuperVO的持久化.18四.常用的API的使用.204.1.PersistenceManager..204.2.IVOPersistence持久化机制接口..21新一代云ERP解决方案34.3.IUAPQueryBS接口..214.4.结果集的控制..22五.附录.235.1.常见问题解析..23新一代云ERP解决方案4一.前言在U8cloud的开发中可以直接通过JDBC编程来访问数据库。JDBC可以说是JAVA访问关系数据库的最原始、最直接的方法。这种方式的优点是运行效率高,缺点是在Java程序代码中嵌入大量SQL语句,冗余是不可避免的,开发人员常常发现自己在一次又一次地编写相同的普通代码,如获得连接、准备语句、循环结果集以及其他一些JDBC特定元素,使得项目难以维护。特别是当涉及到非常多的关系数据表、需要在多个不同类型的关系数据库系统中使用时,通过在程序中使用JDBC开发实施起来更加困难。所以引入对象-关系映射是提高开发效率、提升软件产品的可维护、扩展性的现实需要。实践表明,在基于数据处理为主的企业级应程序开发中,通过引入对象-关系映射中间件,可以节省与对象持久化有关的差不多40%的编程工作量,同时提升软件产品可维护及易扩展性,提升软件产品质量。因此,在U8cloud的业务开发中必要通过引入对象-关系映射系统中间件,实现数据库的快速开发。目前U8cloud可以通过JDBC开发了单独的持久化层,把数据库访问操作封装起来,提供简洁的API,供业务层统一调用,实现了自己的ORM系统。通过U8cloud数据库访问框架可以把内存中的对象持久化到数据库、把数据库中的关系数据加载到内存中,同时保证了系统频繁地访问数据库的性能,降低访问数据库的频率。1.1.U8cloud数据访问层特点具有透明性业务对象在不知道数据访问实现的具体细节情况下,可以使用数据源。由于实现细节隐藏在数据访问层的内部,所以访问是透明的。易于迁移数据访问层使应用程序很容易迁移到其他数据库实现。业务对象不了解底层的数据实现,所以迁移仅仅涉及到修改数据访问层。减少业务对象中代码复杂性因为数据访问层管理着所有的数据访问复杂性,所以它可以简化业务对象和使用数据访问层的其他数据客户端的代码。数据访问层,含有许多与实现相关的代码(例如SQL语句)。这样给开发人员带来了更高的效率、更好的可维护性、提高了代码的可读性等一系列好处。新一代云ERP解决方案5易于维护和管理把所有的数据访问集中在单独的层上,由于所有的数据访问操作现在都委托给数据访问层,集中化管理数据访问。这种集中化可以使应用程序易于维护和管理。新一代云ERP解决方案6二.JDBCFrameWorkU8cloud系统与数据库进行交互需要反复地调用SQL语句、连接管理、事务生命周期、结果处理和异常处理。这些操作都是很常见的;不过这个重复的使用并不是必要的。通过新数据访问框架可以解决与一个兼容JDBC的数据库的重复交互问题。JdbcFramework特点:1.灵活2.简洁3.高的容错性4.异常的统一处理利用JdbcSession执行一个SQL,几乎不需要代码。你所需要的全部仅仅是一个数据源和一个JdbcSession。一旦你得到了它们,你将可以使用JdbcSession提供的大量方便的方法2.1.JdbcSession的创建:PersistenceManagersessionManager=PersistenceManager.getInstance();try{JdbcSessionsession=sessionManager.getJdbcSession();//开始jdbc会话相关业务操作}catch(DbExceptione){}finally{sessionManager.release();//需要关闭会话}2.2.数据访问异常的处理DbException是一个抽象异常类,它被用来处理SQLException和我们的数据访问异常之间的转换。在数据访问框架中提供了不同数据库对不同DbException的实现并对每种数据库的新一代云ERP解决方案7SQLErrorCode进行了处理,统一转化成不同的方法,通过DbException能够精确的指定出数据访问过程中所出现的问题。try{数据访问过程}catch(DbExceptione){//根据数据库的不同统一异常处理if(e.isBadSQLGrammar())//如果是语法错误//todo相关处理if(e.isDataIntegrityViolation())//如果是数据一致性错误//todo相关处理}finally{//关闭会话}2.3.结果集合的处理对查询结果集合的处理,主要由ResultProcessor类来实现,这是一个接口ResultProcessor包含有一个简单的方法,不同的ResultProcessor实现返回不同的结果对象。系统提供了一系列常用的默认实现。新一代云ERP解决方案8ResultSetProcessorArrayListProcessorArrayProcessorMapProcessorVectorProcessorBeanProcessorColumnProcessorMapListProcessorBeanListProcessor ArrayProcessor数组处理器,返回一个对象数组,结果集中只有一行数据,其中结果集中每一列对应数组的一个元素。 ArrayListProcessor数组集合处理器,返回一个ArrayList集合,集合中的每一个元素是一个数组,每个数组对应结果集中的一行数据,其中结果集中每一列对应数组的一个元素。 MapProcessorHashMap处理器,返回一个HashMap,结果集中只有一行数据,其中结果集合中每一列的列名和列值对应HashMap的一个关键字和相应的值。 MapListProcessorHashMap集合处理器,返回一个ArrayList集合,集合中的每一个元素是一个HashMap,每个HashMap对应结果集中的一行数据,其中结果集合中每一列的列名和列值对应HashMap的一个关键字和相应的值。 BeanProcessor值对象处理器,返回一个JavaBean,结果集中只有一行数据,该处理器能自动把结果集中的值按列的名称映射到javaBean中,如结果集中有名称为”name”的字段,那么只要该java对象中有getName()方法就能把结果集合中”name”对应的值映射到对象中。 BeanProcessor值对象集合处理器,返回一个ArrayList集合,集合中的每一个元素是一个javaBean,每个javaBean对应结果集合中一行数据,其中每个JavaBean中的数据映射关系和BeanProcess同理。 ColumnProcessor列值处理器,返回一个Java对象,结果集中只有一行数据,该对象对应与结果集中某一列的值,该处理器通过结果集列的序号或名称来确定列。 BeanMappingListProcessor:值对象集合处理器,根据映射信息返回一个ArrayList集新一代云ERP解决方案9合,集合中的每一个元素是一个javaBean,每个javaBean对应结果集合中一行数据,其中每个JavaBean中的数据映射关系和BeanMappingProcess同理 BeanMappingProcessor:值对象处理器,根据映射信息返回一个JavaBean,结果集中只有一行数据,该处理器能自动把结果集中的值按列的名称映射到javaBean中,如结果集中有名称为”name”的字段,那么只要该java对象中有getName()方法就能把结果集合中”name”对应的值映射到对象中上述的默认实现并不能满足所有的业务需求,在开发过程中还需要实现自定义的结果集处理器。如:2.4.更新的实现2.4.1.无参数更新数据PersistenceManagersessionManager=null;try{sessionManager=PersistenceManager.getInstance();JdbcSessionsession=sessionManager.getJdbcSession();//开始jdbc会话Stringsql="updatebd_invmandocsetpk_invmandoc='0001AA10000000000DDD'wherepk_invmandoc='0001AA10000000000DDD'";session.executeUpdate(sql);}catch(DbExceptione){}finally{if(sessionManager!=null)sessionManager.release();//需要关闭会话}publicclassCustomProcessorextendsBaseProcessor{publicObjectprocessResultSet(ResultSetrs)throwsSQLException{…自定义实现…}}新一代云ERP解决方案102.4.2.带参数更新数据PersistenceManagersessionManager=null;try{sessionManager=PersistenceManager.getInstance();JdbcSessionsession=sessionManager.getJdbcSession();//开始jdbc会话Stringsql="updatebd_invmandocsetpk_invmandoc='0001AA10000000000DDD'wherepk_invmandoc=?”;SQLParameterparameter=newSQLParameter();构造参数对象parameter.addParam("0001AA10000000000DDD");添加参数,JdbcSession会将参数对象中的每个对象放入到预处理对象的相应位置session.executeUpdate(sql,parameter);}catch(DbExceptione){}finally{if(sessionManager!=null)sessionManager.release();//需要关闭会话}2.4.3.批量带参数更新数据PersistenceManagersessionManager=null;try{sessionManager=PersistenceManager.getInstance();JdbcSessionsession=sessionManager.getJdbcSession();//开始jdbc会话Stringsql="updatebd_invmandocsetpk_invmandoc='0001AA10000000000DDD'wherepk_invmandoc=?";SQLParameterparameter=newSQLParameter();parameter.addParam("0001AA10000000000DDD");session.addBatch(sql,parameter);introws=session.executeBatch();新一代云ERP解决方案11}catch(DbExceptione){}finally{if(sessionManager!=null)sessionManager.release();//需要关闭会话}2.4.4.批量无参数更新PersistenceManagersessionManager=null;try{sessionManager=PersistenceManager.getInstance();JdbcSessionsession=sessionManager.getJdbcSession();//开始jdbc会话Stringsql="updatebd_invmandocsetpk_invmandoc='0001AA10000000000DDD'wherepk_invmandoc=’'0001AA10000000000DDD'’";session.addBatch(sql);introws=session.executeBatch();}catch(DbExceptione){}finally{if(sessionManager!=null)sessionManager.release();//需要关闭会话}2.5.JdbcSession中特殊参数类型在JdbcSession中有3中特殊的参数特性Null,Blob,Clob;使用Null参数类型PersistenceManagersessionManager=null;try{新一代云ERP解决方案12sessionManager=PersistenceManager.getInstance();JdbcSessionsession=sessionManager.getJdbcSession();//开始jdbc会话Stringsql=更新或插入Null字段的SQL语句……SQLParameterparameter=newSQLParameter();parameter.addNullParam(Types.INTEGER);session.addBatch(sql,parameter);introws=session.executeBatch();}catch(DbExceptione){}finally{if(sessionManager!=null)sessionManager.release();//需要关闭会话}使用Blob参数类型PersistenceManagersessionManager=null;try{sessionManager=PersistenceManager.getInstance();JdbcSessionsession=sessionManager.getJdbcSession();//开始jdbc会话Stringsql=更新或插入Blob字段的SQL语句……SQLParameterparameter=newSQLParameter();parameter.addBlobParam(newObject());session.addBatch(sql,parameter);introws=session.executeBatch();}catch(DbExceptione){}finally{if(sessionManager!=null)session.closeAll();//关闭会话}使用Clob参数类型新一代云ERP解决方案13SessionManagersessionManager=null;try{sessionManager=PersistenceManager.getInstance();JdbcSessionsession=sessionManager.getJdbcSession();//开始jdbc会话Stringsql=更新或插入Clob字段的SQL语句……SQLParameterparameter=newSQLParameter();parameter.addClobParam(newString());session.addBatch(sql,parameter);introws=session.executeBatch();}catch(DbExceptione){}finally{if(sessionManager!=null)session.closeAll();//关闭会话}新一代云ERP解决方案14三.对象的持久化通过持久层框架可以很方便的实现把内存中的对象持久化到数据库、把数据库中的关系数据加载到内存中。作为入门的一个例子,我们使用一个简单的JavaBeanPerson类(注意需要进行持久化的VO类必须符合JavaBean规范)3.1.普通JavaBean的持久化3.1.1.对象定义Person.java//importsimplied….publicclassPerson{privateintid;privateStringname;intage;publicintgetId(){returnid;}publicvoidsetId(intid){this.id=id;}…}在定义了Person类以后,需要将Person类映射成数据表,需要注意的是目前的持久层对JavaBean和数据表之间的关系有一定限制,如一个JavaBean只能映射成一个数据表。下面这个例子,我们使用一个简单的表,将一个表映射成一个JavaBean,JavaBean和表是一对一的关系。Person.sqlCREATETABLEPERSON(新一代云ERP解决方案15IDNUMBER(5,0)NOTNULL,NAMEVARCHAR(40)NOTNULL,AGENUMBER(3,0)NOTNULL,PRIMARYKEY(ID))3.1.2.构造数据映射对象负责定义数据库与javaBean的映射信息的接口定义如下:publicinterfaceIMappingMeta{/***得到该数据库表的主键名称*@return主键名称*/publicabstractStringgetPrimaryKey();/***得到值对象对应的表名*@return表名*/publicabstractStringgetTableName();/***得到值对象的属性数组*@return*/publicabstractString[]getAttributes();/***得到值对象的属性数组一一对应的表的列名数组*@return*/publicabstractString[]getColumns();}根据ImappingMeta接口的定义,可以知道任何实现了ImappingMeta接口的对象映射元数新一代云ERP解决方案16据类都必须提供Javabean对应的数据库表名称、主键名称、字段名称,以及和数据库字段一一对应的Javabean对象的属性名称。根据前面的例子我们可以定义Person.java的对象映射元数据类PersonVOMeta.java注意对象映射元数据类命名规范为nc.vo.xxx.XXXVOMetaimportnc.jdbc.framework.mapping.IMappingMeta;publicclassPersonVOMetaimplementsIMappingMeta{privateString[]attributes=newString[]{"id","name","age"};privateString[]columns=newString[]{"ID","NAME","AGE"};publicStringgetPrimaryKey(){return"ID";}publicStringgetTableName(){return"Person";}publicString[]getAttributes(){returnattributes;}publicString[]getColumns(){returncolumns;}}在完成了对象映射元数据类后,就剩下的应用的编码工作了。为简化这个工作,在U8cloud系统中提供了nc.bs.dao.BaseDAO,该类是数据库访问帮助类封装了常用的持久层访问操作,提供了IUAPQueryBS和IVOPersistence以及superDMO中的所有方法。建议新的代码都使用BaseDAO。BaseDAOdao=newBaseDAO()//使用默认的数据源BaseDAOdao=newBaseDAO(dataSource)//使用指定的数据源3.1.3.对象读取BaseDAO对象已完成初始化,就可以方便地使用它了。首先,我们用它从数据库中读取一个Person对象。(在本例中,假设PERSON表中已存在10条记录,ID从1到10)。要从新一代云ERP解决方案17数据库中得到一个Person对象,只需要BaseDAO实例。让我们读入ID是5的Person对象。BaseDAOdao=newBaseDAO();PersonVOMetameta=newPersonVOMeta();dao.retrieveByClause(Person.class,meta,"id=5");3.1.4.对象写入现在创建一个Person对象,并将它写入数据库。BaseDAOdao=newBaseDAO();Personperson=newPerson();person.setName(“tom”);person.setAge(“20”);PersonVOMetameta=newPersonVOMeta();dao.insertObject(person,meta);//默认会自动为Person对象生成主键如果想要保留Person类中的主键并插入到数据表中应该使用dao.insertObjectWithPK(person,meta);3.1.5.对象更新现在利用Person对象,更新数据库中的ID=4的对应数据BaseDAOdao=newBaseDAO();Personperson=newPerson();Person.setId(4);person.setName(“tom”);person.setAge(“20”);PersonVOMetameta=newPersonVOMeta();dao.updateObject(person,meta);3.1.6.对象删除现在利用Person对象,删除数据库中的ID=4的对应数据新一代云ERP解决方案18BaseDAOdao=newBaseDAO();Personperson=newPerson();Person.setId(“4”);PersonVOMetameta=newPersonVOMeta();dao.deleteObject(person,meta);BaseDAO针对JavaBean的持久化(CRUD操作)提供了各种应用不同的方法,具体使用方法和功能请参考BaseDAO的JavaDoc。3.2.SuperVO的持久化SuperVO是U8cloud系统中比较特殊的值对象,该值对象自身已经包涵了值对象到数据库中的映射信息,所以SuperVO的持久化更为简单方便,无需构造对象映射元数据类,可以直接对VO进行持久化操作。查询操作:BaseDAOdao=newBaseDAO();Collectionvos=dao.retrieveAll(XXXSuperVO.class);//查询数据表中所有数据到对象集合中更新操作BaseDAOdao=newBaseDAO();XXXSuperVOvo=newXXXSuperVO();……dao.updateVO(vo);删除操作BaseDAOdao=newBaseDAO();XXXSuperVOvo=newXXXSuperVO();……dao.deleteVO(vo);插入操作BaseDAOdao=newBaseDAO();XXXSuperVOvo=newXXXSuperVO();新一代云ERP解决方案19……dao.insertVO(vo);BaseDAO针对SuperVO的持久化(CRUD操作)提供了各种应用不同的方法,具体使用方法和功能请参考BaseDAO的JavaDoc新一代云ERP解决方案20四.常用的API的使用4.1.PersistenceManager该类主要解决复杂数据的查询(联查)和复用数据库连接的方式,该类能在一个复杂业务中复用一个连接,需要最后在业务结束的时候手动的关闭连接,使用该类的优点是在做复杂业务的时候复用连接能够提高性能,缺点是需要人为关闭连接,如果在开发过程中忘记关闭了连接会导致连接泄露的系统问题。该类建议的使用方法如下:PersistenceManagerpersistence=null;try{persistence=PersistenceManager.getInstance(数据源)JdbcSessionsession=persistence.getJdbcSession();//数据调用1Session.delete()//数据库调用2Session.insert()//数据库调用3Session.excuteQuery()//或其它方法调用传入SessionObjecto=Callmethod(Session);}catch(DAOExceptione){ThrowsnewxxBusinessRunTimeException(“”);}finaly{Persistence.Release();}PersistenceManager包括supervo及objmaping的查询和持久化,PersistenceManager具体提供的方法和功能请参考PersistenceManager的JavaDoc.新一代云ERP解决方案214.2.IVOPersistence持久化机制接口该接口提供基本的单表操作,分为支持SuperVO和普通JavaBean,对于通用持久化或泛化持久化,无任何业务逻辑的insert、update、delete操作强烈建议前台使用该接口,需要业务逻辑的持久化操作必须使用具体的名称来完成业务方法的Service声明。IVOPersistence具体提供的方法和功能请参考IVOPersistence的JavaDoc.调用机制privateIVOPersistencegetVOPersistence()throwsBusinessException{if(iVOPersistence==null)try{iVOPersistence=(IVOPersistence)NCLocator.getInstance().lookup(IVOPersistence.class.getName());}catch(ComponentExceptione){thrownewSysInitException("IUAPQueryBSnotfound!");}returniVOPersistence;}4.3.IUAPQueryBS接口数据库查询接口,该接口提供基本的单表查询操作,支持SuperVO和其它任何含有映射原数据的javaBean的实现机制,IUAPQueryBS具体提供的方法和功能请参考IUAPQueryBS的JavaDoc。调用机制privateIUAPQueryBSgetUAPQuery()throwsBusinessException{if(iIUAPQueryBS==null)try{iIUAPQueryBS=(IUAPQueryBS)新一代云ERP解决方案22NCLocator.getInstance().lookup(IUAPQueryBS.class.getName());}catch(ComponentExceptione){thrownewSysInitException("IUAPQueryBSnotfound!");}returniIUAPQueryBS;}注意:IVOPersistence和IUAPQueryBS提供了需要远程调用的简单的数据库持久化操作,他们都通过Nclocator方式来查找调用,如果是后台的数据库操作推荐使用BaseDao或者PersistenceManager。4.4.结果集的控制在U8cloud持久层中为了防止执行大查询后返回结果集合过多导致系统内存溢出系统默认设置了最大返回结果集行数是10万行,如果如要返回更多行数或者不限制返回行数,需要做如下设置:在SuperDMO中设置返回行数SuperDMOsuperDmo=newSuperDMO();superDmo.setMaxRows(行数);如果rows=-1表示不限制返回行数在BaseDAO中设置返回行数BaseDAObaseDao=newBaseDAO();BaseDAO.setMaxRows(行数);如果rows=-1表示不限制返回行数在ResultsetProcessor中设置返回行数BaseProcessorprocessor=newArrayListProcessor();Processor.setMaxRows(行数);在结果集中设置返回行数CrossDBResultsetresultset=(CrossDBResultset)rs;Resultset.setMaxRows(行数);新一代云ERP解决方案23五.附录5.1.常见问题解析以下列出了在使用持久层框架中容易误解和出错的地方,供参考: 在SQLParameter中,如果要添加空的参数类型,字段数据类型是java.sql.Types类定义的常量名称。常用的数据类型包括BLOB,CHAR,CLOB,INTEGER,NUMERIC,TIME,TIMESTAMP和VARCHAR等。 对于数据表中Blob的字段,如果要使用对象映射必须在需要持久化的值对象中把与Blob字段对应的的对象属性类型定义成Object类型。 对于文本类型较大的数据,可以将CLOB映射成String,如果要使用对象映射必须在需要持久化的值对象中把与Clob字段对应的的对象属性类型定义成String类型。 使用对象映射的JavaBean必须拥有缺省的构造器,最好实现Serializable接口。 JdbcSession中的批量执行已经自动实现了分页批量提交的功能,在使用大批量提交的过程不再需再进行批量提交,只要在最后执行executeBatch()即可。 对于复杂的业务,如果数据库的操作有很多步骤,应该使用PersistenceManager尽量保持连接的复用,以提高性能。 如果在数据库操作中使用了临时表,必须保证调用该操作的Service是一个EJB,保证该操作在一个事务中。 在保证业务正确的情况下,事务应该尽量短。新一代云ERP解决方案24新一代云ERP解决方案NewGenerationofCloudERPSolution用友网络科技股份有限公司YonyouNetworkTechCo.Ltd.