DaaS 服务端 Java二次开发指南-Lesson 7 加载关联对象

480 阅读6分钟

回到目录

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());
}

回到目录