本讲开始,进入后端开发培训的内容。
1 DWF的后端架构
DWF后端层次结构图如下:
1.Controller层:包含各个Controller类,按照URL将请求分发给相应的函数处理。出于代码复用、层间解耦、异常处理等方面的考虑,Controller层的代码不应包含对业务逻辑的处理,所有与业务逻辑有关的代码应封装到Service层。
2.Service层:包含业务逻辑的相关类。Service层按照功能内聚的业务模块建模,一个service层的类可使用多个DAO的实体进行持久化操作。
3.DAO层:DAO 是 Data Access Object(数据访问对象)的缩写,提供对各entity进行增删改查等操作的DAO类。DAO层按照数据对象建模,例如:有Title和Body的领域模型,则对应有TitleDao和BodyDao。
此外,介绍几个辅助层:
1.Domain层:包含领域模型,或者说,是JPA维护的Entity,与数据库表一一对应。
2.Utils类:utils包包含一些常用的工具类,如文件操作、正则表达式处理等。
下图描述DWF后端标准文件夹的组织形式:
2 与数据库进行交互的RESTful API
RESTful API主要用来与数据库交互。在DWF,RESTful API与数据库交互有三种方式:
1.使用DWF内置的OMF(对象管理框架)函数访问实体类、关联类的数据。
2.使用DWF内置的Hibernate EntityManager直接访问数据库。
3.使用JDBC或其它方式自行创建连接并维护连接池。
除非有特殊需求,尽量使用前两种数据库访问方式。
2.1 使用DWF内置的OMF(对象管理框架)函数访问实体类、关联类的数据
可以注入ObjectAccessService,实现对实体类的查询操作。
@RestController @RequestMapping("dwf/v1/app-ext") public class AppExtController { @Autowired ObjectAccessService objectAccessService; @GetMapping(path = "helloworld") public ResponseMsg<?> listAllClasses(@RequestParam String name){ List objs = objectAccessService.getByCondition("Component", ""); return new ResponseMsg<>(objs); } }
ObjectAccessService 是DWF的一个由Spring管理的Bean,包含了对DWF管理对象的查询接口:
getByCondition public List<Map<String,Object>> getByCondition(String className, String condition) 查询对象(不分页) 参数: className - 实体类名 condition - 查询条件,以and开头。eg: "and plt_oid='43ab314ba35c1345b32a2'" |
getByCondition public List<Map<String,Object>> getByCondition(String className, String condition, Integer pageSize, Integer startIndex, String sampleMethod) 查询对象(分页) 参数: className - 实体类名 condition - 查询条件,以and开头。eg:"and plt_oid = '123'" pageSize - 分页大小,如果不分页可设置为null startIndex - 分页的起始偏移,如果不分页可设置为null sampleMethod - 采样方式,如果使用默认顺序采样方式可设置为null |
另外,对对象的增删改操作,可由另外两个Bean完成:ItemClassAccessService 和 RelationAccessService。
对于实体对象的增删改:ItemClassAccessService:
addEntityObjects public List<Map<String,Object>> addEntityObjects(String className, List<Map<String,Object>> objs) 批量添加实体类对象 参数: className - 实体类名 objs - 要添加的对象列表。每个对象是一个Map:key是属性名,value是属性值返回:已添加的对象列表 |
deleteByOids public void deleteByOids(String className, List<String> oids) 根据oid删除实体类 参数: className - 实体类名 oids - oid列表 |
updateEntityObjs public List<Map<String,Object>> updateEntityObjs(String className, List<Map<String,Object>> objs) 批量更新实体类对象。根据对象中的oid确定更新的目标对象 参数: className - 实体类名 objs - 实体类对象列表 返回: 修改后的对象 |
cudBatch public List<Object> cudBatch(List<CudEvent> events) 在一个事务中批量执行多个增、删、改操作 参数: events - 指定的操作返回:相应的操作结果 public class CudEvent { } |
addEntityObjectsFast public List<String> addEntityObjectsFast(String className, List<Map<String,Object>> objs) 批量快速插入实体类对象。不执行插件,不执行脚本和存储过程,可用于大数据量的导入 参数: className - 实体类名 objs - 对象列表 返回: 插入对象的oid列表 |
对关联类的增删改操作,可由RelationAccessService完成:
addRelations public List<Map<String,Object>> addRelations(String relName, List<Map<String,Object>> relObjs) 添加关联对象 参数: relName - 关联类名 relObjs - 关联对象列表 返回: 添加的关联对象列表 |
deleteByOids public void deleteByOids(String relClassName, List<String> oids) 根据Oid删除关联对象 参数: relClassName - 关联类名 oids - 关联对象OID列表 |
updateRelations public List<Map<String,Object>> updateRelations(String relName, List<Map<String,Object>> relObjs) 更新关联对象。 不允许更新关联的左右连接类(leftClass, rightClass), 左右oid(leftOid, rightOid), rightRev)。如果传入的对象中包含这些属性,这些属性会被自动忽略。 参数: relName - 关联类名 relObjs - 要更新的关联对象。根据其中的oid决定要更新的对象返回:更新后的对象 |
getLeftEntityObj public Map<String,Object> getLeftEntityObj(String relName, String oid) 获取关联对象的左实体对象 参数: relName - 关联类名 oid - 关联对象名 返回: 关联对象的左实体对象 |
getRightEntityObj public Map<String,Object> getRightEntityObj(String relName, String oid) 获取关联对象的右实体对象 参数: relName - 关联类名 oid - 关联对象名 返回: 获取关联对象的右实体对象 |
2.2 使用DWF内置的Hibernate EntityManager直接访问数据库
可以在DAO中注入Hibernate框架的EntityManager对象实现增删改查。以下是DAO的程序实例:
@Service public class AppInfoDao { @PersistenceContext EntityManager em; public List<AppInfoDO> getAll(){ return em.createQuery("select obj from AppInfoDO obj order by modifyTime desc, appName").getResultList(); } public AppInfoDO getById(String id){ return em.find(AppInfoDO.class, id); } public AppInfoDO getByAppName(String appName){ List<AppInfoDO> list = em.createQuery("select obj from AppInfoDO obj where obj.appName='" + appName + "'", AppInfoDO.class) .getResultList(); if(list.isEmpty()) return null; else return list.get(0); } public void update(AppInfoDO appInfoDO){ em.merge(appInfoDO); } @Transactional public String add(AppInfoDO appInfoDO){ appInfoDO.setId(UUIDGenerator.getUUID()); em.persist(appInfoDO); return appInfoDO.getId(); } public void delete(String id){ AppInfoDO oldAppInfoDO = getById(id); if(oldAppInfoDO != null) em.remove(oldAppInfoDO); } public void deleteByName(String appName){ em.remove(getByAppName(appName)); } }
对Hibernate的介绍,可参考:https://www.baeldung.com/hibernate-entitymanager
注:Hibernate框架默认的查询语句是HQL。如果需要使用原生SQL,可以使用entityManager.createNativeQuery(..)。