Lesson 7 加载关联对象
模型定义
我们现在利用SerializeScope来简化输出结果,描述一下如何加载关联对象。
开始之前,我们在Bank.xml 中增加几个对象定义,用来演示相关对象的加载。
在bank.xml中,增加以下定义:
<city
name="成都|广元|广州|深圳"
province="$(province)"
/>
<company
name="成都双链科技有限公司|华为技术有限公司"
city="$(city)"
address="成都市武侯区天府二街新希望天祥广场A座2511室|中国广东省深圳市龙岗区坂田街道华为基地"
platform="$(platform)"
/>
<business_unit
name="双链物联网|双链系统工程|双链大数据"
company="$(company)"
/>
<product
name="智能称重|供应链系统|双链索骥"
description="无人值守的称重的物联网解决方案|完备的供应链系统解决方案|大数据方案"
business_unit="$(business_unit)"
/>
<sku
name="SS-01智能磅秤|SS-01智能摄像头"
description="无人值守,自动数据推送,远程可管理的磅秤|自动识别并关联磅秤的磅位摄像头"
purchase_url= "https://www.doublechaintech.com/product/ss01|https://www.doublechaintech.com/product/ss02"
product="$(product)"
/>
<department
name="市场部|研发部"
company="$(company)"
/>
<employee
name="张三|李四|王五"
department="$(department)"
/>
这些定义,构建了一个如下图所示的对象关系:
增加以上模型后,重新生成代码。
在DaaS目录 下执行
./gencode.sh bank.xml all ../output-project-name/.
然后导入数据:
mysql -u用户 -p密码 bank < WEB-INF/bank_core_src/META-INF/bank_mysql.sql
注:请用你实际的数据库用户和密码,执行对应的数据库导入,上例仅用于mysql。请参见“Daas-Start-Kit快速配置与开发准备” 了解数据导入的细节
使用Tokens
系统默认的,会生成每个对象的Tokens。可以使用Tokens来加载某个对象(直接)相关联的对象。 对于大部分管理功能来说,这个已经够用了。我们将以Company对象为基准点,所以我们需要重载 “CompanyManagerImpl”。
准备工作
-
Step 1. 确保在目录“bizcore/WEB-INF/bank_custom_src”下有文件“com/doublechain/bank/company/CompanyCustomManagerImpl.java”
-
Step 2. 确保文件“bank_manager.xml”或“bank_custom.xml”中有以下定义
<bean id="companyManager" class="com.doublechain.bank.company.CompanyCustomManagerImpl" parent="companyManagerBase" > </bean> -
Step 3. 简单起见,允许所有人访问,所以覆写checkAccess,直接返回accessOK, 如下图所示:
-
Step 4. 增加一个 viewDetail 方法,用于演示。
(完整代码后面会附上)这里演示了 tokens 的用法。这段代码实际上等价于 CompanyManagerImpl 中的 view() 方法。 为了便于解释,把一些细节复制到此处。 您可以查看完整的源代码。 这时直接访问,可以看到结果:
-
Step 5. 增加一个 SerializeScope 用来简化输出,方便查看
为了能够更直观的表示序列化的目标,这里全部使用了 getXxxSummaryScope()方法来构造整个序列化范围,而且所有list都注明了noListMeta。 Summary 只会包含基本类型的字段。可以在源代码中查看。 -
Step 6. 用新序列化scope来输出
运行 gradle bootRun 将服务重新编译运行,使用post man 测试:
可以看到SerializeScope已经生效,输出的json结果清爽多了,只保留了简单字段,这样我们后面就可以方便的比较差异了。
此时完整的代码如下
package com.doublechain.bank.company;
import java.util.Map;
import com.doublechain.bank.BankViewScope;
import com.doublechain.bank.BaseViewPage;
import com.doublechain.bank.CustomBankUserContextImpl;
import com.doublechain.bank.businessunit.BusinessUnit;
import com.doublechain.bank.city.City;
import com.doublechain.bank.department.Department;
import com.doublechain.bank.product.Product;
import com.terapico.caf.viewpage.SerializeScope;
import com.terapico.uccaf.BaseUserContext;
public class CompanyCustomManagerImpl extends CompanyManagerImpl{
@Override
public Object checkAccess(BaseUserContext baseUserContext, String methodName, Object[] parameters) throws IllegalAccessException {
return accessOK();
}
protected SerializeScope getSerializeScope() {
return BankViewScope.getCompanySummaryScope().clone()
.field(Company.CITY_PROPERTY, BankViewScope.getCitySummaryScope().clone()
.field(City.PROVINCE_PROPERTY, BankViewScope.getProvinceSummaryScope())
)
.field(Company.BUSINESS_UNIT_LIST, BankViewScope.getBusinessUnitSummaryScope().clone()
.field(BusinessUnit.PRODUCT_LIST, BankViewScope.getProductSummaryScope().clone()
.field(Product.SKU_LIST, BankViewScope.getSkuSummaryScope()).noListMeta()
).noListMeta()
).noListMeta()
.field(Company.PLATFORM_PROPERTY, BankViewScope.getPlatformSummaryScope())
.field(Company.DEPARTMENT_LIST, BankViewScope.getDepartmentSummaryScope().clone()
.field(Department.EMPLOYEE_LIST, BankViewScope.getEmployeeSummaryScope()).noListMeta()
).noListMeta();
}
public Object viewDetail(CustomBankUserContextImpl ctx, String companyId) throws Exception {
Map<String, Object> tokens = CompanyTokens.start().withCity()
.withPlatform()
.withBusinessUnitList()
.withDepartmentList()
.sortBusinessUnitListWith("id","desc")
.sortDepartmentListWith("id","desc").done();
Company company = loadCompany( ctx, companyId, tokens);
Company data = present(ctx,company, allTokens());
return BaseViewPage.serialize(data, getSerializeScope());
}
}
调整token
我们先把所有的列表token 都取消
运行 gradle bootRun 将服务重新编译运行,使用post man 测试:
可以看到只有City 和 platform了
我们再尝试一下增加list并修改排序:
运行 gradle bootRun 将服务重新编译运行,使用post man 测试:
可以看到 business unit list已经出现了,并且是按照ID升序排列的。
所以Tokens 使用起来很简单:需要,就加上;不需要,就去掉。
加载更多的相关对象
Tokens目前有限制,只能指定直接关联的对象。很多时候在业务中,需要加载多层关系,需要使用代码来完成。我们下面演示一下加载完整的Company的代码.
首先我们增加一个方法: viewAllDetail 用于演示:
运行 gradle bootRun 将服务重新编译运行,使用post man 测试:
Enhance 引用的对象
现在我们来enhance ‘省’的信息:
运行 gradle bootRun 将服务重新编译运行,使用post man 测试:
即:当我们需要加载“向上”的相关对象时,可以使用 BaseUtils里的 “collectReferencedObjectWithType()”方法,然后调用DAO group的 enhance() 方法。
collectReferencedObjectWithType方法自动生成在 XXXBaseUtils.java 中,它有3个声明,用来处理不同起点的收集情况:
-
public static List collectReferencedObjectWithType(BankUserContext userContext, BaseEntity rootObject, Class clazz) throws Exception
这个用于起点是一个BaseEntity对象(即模型中定义的对象。前面讲过,所有模型中定义的对象,生成的代码中,都是BaseEntity的子类对象) rootObject 是起点对象,clazz是目标对象的类型。
上例中的代码,就是从company出发,收集到 province
这个方法是通过反射递归查找的,所以能收集到最终对象的前提是,要从根对象开始,依次收集沿途经过的节点。
目前这样提供的原因是:可能需要加载的场景太多,如果全部生成,会有巨量的API,实际上反而不利于使用,所以只提供了一步一步加载的基本手段。未来DaaS会提供更简洁的加载手段。
-
public static List collectReferencedObjectWithType(BankUserContext userContext, SmartList<? extends BaseEntity> rootObjectList, Class clazz) throws Exception
这个用于起点是一个SmartList的情况。我们可以一次收集以SmartList中的所有元素为起点的相关对象。
-
public static List collectReferencedObjectWithType(BankUserContext userContext, List<? extends BaseEntity> rootObjectList, Class clazz) throws Exception
这个用于起点是普通List的情况。因为返回的结果是List,使用这个接口就可以不断的收集下去,直到所有需要的数据都加载完毕。
到目前为止,我们加载了这些对象:
观察PostMan的输出,你可以发现,business unit list 只加载本身的基本属性,并没有记载完整的记录。
这时我们需要enhance这个list.
Enhance被引用的对象
business unit list 现在还缺少的是它的product list,我们使用loadOurs接口(前面enhance类方法曾经提到过):
这个接口的功能也很直观: businessUnit 的DAO说:“来加载我们的 product 列表吧!”(EO 是 empty options 的语法糖)
运行 gradle bootRun 将服务重新编译运行,使用post man 测试:
可以看到 business unit list 中有了 Product list。
但是 product list 也只是有基本数据而已,没有更深入的SKU信息。
那么,可以合理的推测出来,我们继续 loadOurs 就可以了:
运行 gradle bootRun 将服务重新编译运行,使用post man 测试:
可以看到 product 下的sku list 也被加载进来了。
这时我们已经enhance了如下对象:
同样的道理,我们可以enhance employee 这个分支:
运行 gradle bootRun 将服务重新编译运行,使用post man 测试:
完整的函数如下:
public Object viewAllDetail(CustomBankUserContextImpl ctx, String companyId) throws Exception {
// 首先,加载对象本身
Company company = daoOf(ctx).load(companyId, CompanyTokens.all());
// 然后enhance province
List<Province> provinces = BankBaseUtils.collectReferencedObjectWithType(ctx, company, Province.class);
ctx.getDAOGroup().enhanceList(provinces, Province.class);
// 接着enhance business unit list中的product list
SmartList<Product> productList = businessUnitDaoOf(ctx).loadOurProductList(ctx,company.getBusinessUnitList(), EO);
// 再enhance product list 的 sku list
productDaoOf(ctx).loadOurSkuList(ctx, productList, EO);
// enhance '部门',将其下的 employee 也加载起来
departmentDaoOf(ctx).loadOurEmployeeList(ctx, company.getDepartmentList(), EO);
return BaseViewPage.serialize(company, getSerializeScope());
}