项目中往往需要动态的创建一个表单,或者添加一个新的数据模板,这时候因为需要在运行时动态的创建表以及动态的维护表字段甚至表关系,本篇博客,做一个简单的示例
数据库表
DDL
/*
资源模板及实例表
*/
DROP TABLE IF EXISTS `resource_template`;
CREATE TABLE `resource_template` (
`template_id` bigint(12) NOT NULL AUTO_INCREMENT COMMENT '模板id,12位数字,序列生成',
`template_name` varchar(256) DEFAULT NULL COMMENT '模板名称',
`owner_id` bigint(10) NOT NULL COMMENT '归属用户id',
`inure_time` datetime DEFAULT NULL COMMENT '资源有效开始时间',
`expire_time` datetime DEFAULT NULL COMMENT '资源失效时间',
`status` decimal(1,0) DEFAULT NULL COMMENT '状态:0:停用;1:启用',
`description` varchar(255) DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`template_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100000000000 DEFAULT CHARSET=utf8 COMMENT='资源模板';
DROP TABLE IF EXISTS `resource_info`;
CREATE TABLE `resource_info` (
`instance_id` char(16) NOT NULL COMMENT '资源实例id,16位字符',
`owner_id` bigint(10) DEFAULT NULL COMMENT '归属用户id',
`template_id` bigint(12) NOT NULL COMMENT '模板编码,从resource_template关联过来,根据模板生成一个实例',
`resource_name` varchar(256) DEFAULT NULL,
`inure_time` datetime DEFAULT NULL COMMENT '资源生效时间',
`expire_time` datetime DEFAULT NULL COMMENT '资源失效时间',
`status` decimal(1,0) NOT NULL COMMENT '状态:0:失效;1:有效 2:已删除',
`create_id` bigint(20) DEFAULT NULL COMMENT '创建人员',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modify_id` bigint(20) DEFAULT NULL COMMENT '修改人员',
`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
`history_id` bigint(20) DEFAULT NULL,
`operation_srl` varchar(24) DEFAULT NULL COMMENT '操作流水',
`request_seq` varchar(255) DEFAULT NULL,
PRIMARY KEY (`instance_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='资源实例';
/*
实体管理表
*/
DROP TABLE IF EXISTS `entity_type`;
CREATE TABLE `entity_type` (
`entity_type_id` bigint(12) NOT NULL AUTO_INCREMENT COMMENT '序列号',
`entity_type` smallint(6) NOT NULL COMMENT '实体类型 4位数字',
`entity_type_name` varchar(128) NOT NULL COMMENT '实体类型名称',
`entity_table` varchar(64) NOT NULL COMMENT '模板表名',
`entity_id_col` varchar(64) NOT NULL COMMENT '模板编码列名',
`entity_parentid_col` varchar(64) DEFAULT NULL COMMENT '关联的上级模板id',
`entity_name_col` varchar(64) NOT NULL COMMENT '模板名列名',
`instance_table` varchar(64) NOT NULL COMMENT '实体表名',
`instance_id_col` varchar(64) NOT NULL COMMENT '实体主键列名',
`instance_name_col` varchar(64) NOT NULL COMMENT '实体名称列名',
`instance_entityid_col` varchar(64) NOT NULL COMMENT '实体关联模板列名',
PRIMARY KEY (`entity_type_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100000000000 DEFAULT CHARSET=utf8 COMMENT='实体类型定义,用于定义实体类型,也用于生成全局实体视图';
DROP TABLE IF EXISTS `element_group`;
CREATE TABLE `element_group` (
`entity_type` smallint(6) NOT NULL COMMENT '实体类别',
`group_id` bigint(12) NOT NULL COMMENT '元素组编码,12位数组,其中前4位为实体类型编码,后8位由实体定义',
`group_name` varchar(128) NOT NULL COMMENT '元素组名称',
`description` varchar(128) DEFAULT NULL COMMENT '描述信息'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='元素组,实体属性在页面上的分组';
DROP TABLE IF EXISTS `element_group_relation_cfg`;
CREATE TABLE `element_group_relation_cfg` (
`element_group_id` bigint(12) NOT NULL COMMENT '元素组编码',
`element_id` bigint(12) NOT NULL COMMENT '元素编码',
`status` decimal(1,0) NOT NULL COMMENT '状态,启用标识 0:停用 1:启用'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='元素与元素组的关系';
DROP TABLE IF EXISTS `element_info`;
CREATE TABLE `element_info` (
`eneity_type` smallint(6) NOT NULL COMMENT '归属实体类型',
`element_id` bigint(12) NOT NULL COMMENT '元素ID 12位数字 前4位为实体类型 后8位业务定义',
`element_name` varchar(64) NOT NULL COMMENT '元素名称',
`element_prefix` varchar(16) DEFAULT NULL COMMENT '元素前缀',
`element_postfix` varchar(16) DEFAULT NULL COMMENT '元素后缀,单位等',
`element_code` varchar(64) DEFAULT NULL COMMENT '英文名',
`element_desc` varchar(1024) DEFAULT NULL COMMENT '说明',
`tips` varchar(128) DEFAULT NULL COMMENT '界面提示说明',
`verify_id` smallint(6) NOT NULL COMMENT 'ELEMENT_DESC',
`verify_js` varchar(1024) DEFAULT NULL COMMENT '自定义校验JS函数(当verify_id=999有效)',
`element_type` smallint(6) NOT NULL COMMENT '元素类型',
`datasource_type` smallint(6) DEFAULT NULL COMMENT '数据源类型',
`datadict_type` varchar(4) DEFAULT NULL COMMENT '字典类型(当datasource_type=102时有效)',
`datadict_classid` varchar(4) DEFAULT NULL COMMENT '数据类别(当datasource_type=102时有效)',
`datadict_entity_id` varchar(64) DEFAULT NULL COMMENT '字典编码(当datasource_type=102时有效)',
`maxlength` int(11) DEFAULT NULL COMMENT '最大长度'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='参数界面元素定义';
DROP TABLE IF EXISTS `entity_attr_cfg`;
CREATE TABLE `entity_attr_cfg` (
`entity_type` smallint(6) NOT NULL COMMENT '归属实体类型',
`entity_id` bigint(12) NOT NULL COMMENT '关联的模板id',
`attr_id` bigint(12) DEFAULT NULL COMMENT '关联的属性id',
`description` varchar(64) DEFAULT NULL COMMENT '属性描述',
`can_modify` tinyint(4) DEFAULT NULL COMMENT '新增完之后,是否可修改 0不可以修改,1可以修改',
`can_empty` bigint(20) DEFAULT NULL COMMENT '是否允许为空',
`default_value` varchar(255) DEFAULT NULL COMMENT '默认值',
`status` tinyint(4) DEFAULT NULL COMMENT '状态',
`inure_time` datetime DEFAULT NULL COMMENT '生效时间',
`expire_time` datetime DEFAULT NULL COMMENT '失效时间',
`regex_value` varchar(255) DEFAULT NULL COMMENT '正则校验表达式',
`check_db_func` varchar(64) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='属性配置';
DROP TABLE IF EXISTS `entity_attr_element_cfg`;
CREATE TABLE `entity_attr_element_cfg` (
`entity_type` smallint(6) NOT NULL COMMENT '实体类型:由业务系统定义',
`entity_id` bigint(12) NOT NULL COMMENT '实体编码,填对应实体的编码',
`attr_id` bigint(12) NOT NULL COMMENT '属性编码12位数字,关联entity_attr_lib.attr_id',
`element_id` bigint(12) NOT NULL COMMENT '元素编码 12位数字,关联element_info.element_id',
`status` decimal(1,0) NOT NULL COMMENT '状态 启用标识 0:停用 1:启用',
`hide_on_null` tinyint(4) DEFAULT NULL COMMENT '为空的时候是否隐藏 0:全部展示 1:属性值为空不展示',
`sort_by` smallint(6) DEFAULT NULL COMMENT '排序顺序 4位数字'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实体,属性与元素的关系,用于控制实体的属性是否在前端页面上展示';
DROP TABLE IF EXISTS `entity_attr_lib`;
CREATE TABLE `entity_attr_lib` (
`entity_type` smallint(6) NOT NULL COMMENT '所属的实体类型',
`attr_id` bigint(12) NOT NULL COMMENT '属性编码 12位数字,其中前4位为实体类型编码,后8位由实体业务自定义',
`attr_name` varchar(128) NOT NULL COMMENT '实体名称',
`tag` varchar(128) DEFAULT NULL COMMENT '实体标签',
`description` varchar(256) DEFAULT NULL COMMENT '描述信息'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实体属性库,由业务实体引用,如产品,服务,企业,机构等';
DROP TABLE IF EXISTS `entity_attr_value`;
CREATE TABLE `entity_attr_value` (
`id` char(16) NOT NULL COMMENT '实例id',
`owner_id` bigint(20) DEFAULT NULL COMMENT '归属用户id',
`entity_id` bigint(12) NOT NULL COMMENT '实例id',
`entity_type` smallint(6) NOT NULL COMMENT '实例类型',
`attr_id` bigint(20) DEFAULT NULL COMMENT '属性id',
`instance_id` char(16) DEFAULT NULL COMMENT '归属的资源实例id',
`attr_value` varchar(255) DEFAULT NULL COMMENT '属性值',
`inure_time` datetime DEFAULT NULL COMMENT '生效时间',
`expire_time` datetime DEFAULT NULL COMMENT '失效时间',
`create_id` bigint(20) DEFAULT NULL COMMENT '创建人员',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modify_id` bigint(20) DEFAULT NULL COMMENT '修改人员',
`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
`operation_srl` varchar(24) DEFAULT NULL COMMENT '操作流水',
`history_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='土地资源属性实例';
DROP TABLE IF EXISTS `entity_elegrp_cfg`;
CREATE TABLE `entity_elegrp_cfg` (
`entity_type` smallint(6) NOT NULL COMMENT '实体类别,关联entity_type.entity_id',
`entity_id` bigint(12) NOT NULL COMMENT '实体编码',
`element_type` smallint(6) NOT NULL COMMENT '元素类型 0:元素组,1:元素',
`element_id` bigint(12) NOT NULL COMMENT '元素编码',
`sequence` smallint(6) DEFAULT NULL COMMENT '排序顺序',
`status` decimal(1,0) NOT NULL COMMENT '状态,启用标志 0:停用 1:启用',
`group_logo` varchar(128) DEFAULT NULL COMMENT '元素组标题图片'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实体与元素或者元素组的关系';
DROP VIEW IF EXISTS `view_entity_def`;
CREATE VIEW `view_entity_def` AS SELECT
`a`.`entity_type` AS `entity_type`,
`a`.`entity_type_name` AS `entity_type_name`,
`b`.`template_id` AS `entity_id`,
`b`.`template_name` AS `entity_name`,
`b`.`status` AS `status`,
`b`.`inure_time` AS `inure_time`,
`b`.`expire_time` AS `expire_time`,
`b`.`owner_id` AS `ownerId`
FROM
(
`entity_type` `a`
JOIN `resource_template` `b`
)
WHERE
(
`a`.`entity_table` = 'resource_template'
);
DROP VIEW IF EXISTS `view_resource_info`;
CREATE VIEW `view_resource_info` AS SELECT
`a`.`instance_id` AS `instance_id`,
`a`.`owner_id` AS `owner_id`,
`a`.`template_id` AS `template_id`,
`a`.`resource_name` AS `resource_name`,
`a`.`status` AS `status`,
`a`.`inure_time` AS `inure_time`,
`a`.`expire_time` AS `expire_time`,
`a`.`create_time` AS `create_time`,
max(
IF (
(`d`.`tag` = 'attr1'),
`b`.`attr_value`,
NULL
)
) AS `attr1`,
max(
IF (
(`d`.`tag` = 'attr2'),
`b`.`attr_value`,
NULL
)
) AS `attr2`,
max(
IF (
(`d`.`tag` = 'attr3'),
`b`.`attr_value`,
NULL
)
) AS `attr3`
FROM
(
(
(
`resource_info` `a`
JOIN `entity_attr_value` `b`
)
JOIN `entity_attr_cfg` `c`
)
JOIN `entity_attr_lib` `d`
)
WHERE
(
(
`a`.`instance_id` = `b`.`instance_id`
)
AND (
`b`.`attr_id` = `c`.`attr_id`
)
AND (
`b`.`attr_id` = `d`.`attr_id`
)
)
GROUP BY
`a`.`instance_id`;
DML
insert into `entity_type` (`entity_type_id`, `entity_type`, `entity_type_name`, `entity_table`, `entity_id_col`, `entity_name_col`, `instance_table`, `instance_id_col`, `instance_name_col`, `instance_entityid_col`) values (NULL, '1001', '资源管理', 'resource_template', 'template_id', 'template_name', 'resource_info', 'instance_id', 'resource_name', 'template_id');
insert into `entity_attr_lib` (`entity_type`, `attr_id`, `attr_name`, `tag`, `description`) values ('1001', '100100000001', '属性1', 'attr1', '属性1');
insert into `entity_attr_lib` (`entity_type`, `attr_id`, `attr_name`, `tag`, `description`) values ('1001', '100100000002', '属性2', 'attr2', '属性2');
insert into `entity_attr_lib` (`entity_type`, `attr_id`, `attr_name`, `tag`, `description`) values ('1001', '100100000003', '属性3', 'attr3', '属性3');
insert into `element_info` (`eneity_type`, `element_id`, `element_name`, `element_prefix`, `element_postfix`, `element_code`, `element_desc`, `tips`, `verify_id`, `verify_js`, `element_type`, `datasource_type`, `datadict_type`, `datadict_classid`, `datadict_entity_id`, `maxlength`) values ('1001', '100100000001', '属性1', null, null, null, '属性1', '', '101', null, '113', null, null, null, null, null);
insert into `element_info` (`eneity_type`, `element_id`, `element_name`, `element_prefix`, `element_postfix`, `element_code`, `element_desc`, `tips`, `verify_id`, `verify_js`, `element_type`, `datasource_type`, `datadict_type`, `datadict_classid`, `datadict_entity_id`, `maxlength`) values ('1001', '100100000002', '属性2', null, null, null, '属性2', null, '108', null, '112', null, null, null, null, null);
insert into `element_info` (`eneity_type`, `element_id`, `element_name`, `element_prefix`, `element_postfix`, `element_code`, `element_desc`, `tips`, `verify_id`, `verify_js`, `element_type`, `datasource_type`, `datadict_type`, `datadict_classid`, `datadict_entity_id`, `maxlength`) values ('1001', '100100000003', '属性3', null, '个', null, '属性3', null, '111', null, '101', null, null, null, null, null);
DynamicUtil项目
新建一个实体管理的工具类项目,后期将其打成jar包的方式,发布到Maven私服,供其他项目引入使用
首先,我们看下EntityUtil这个类
package com.yj.utils;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import com.yj.dao.BaseDaoForEntity;
import com.yj.entity.EntityType;
import com.yj.exception.EntityException;
import com.yj.exception.EntityExceptionEnums;
@Component
public class EntityUtil implements ApplicationContextAware {
private static BaseDaoForEntity baseDaoForEntity;
private static ApplicationContext applicationContext;
private static HandlerService handlerService;
@Autowired
public void setHandlerService(HandlerService handlerService) {
EntityUtil.handlerService = handlerService;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (EntityUtil.applicationContext == null) {
EntityUtil.applicationContext = applicationContext;
}
}
@Autowired
public void setBaseDao(BaseDaoForEntity baseDao) {
EntityUtil.baseDaoForEntity = baseDao;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@PostConstruct
public static void init() {
List<EntityType> list = baseDaoForEntity.getEntityType(null);
if (null == list || list.isEmpty()) {
throw new EntityException(EntityExceptionEnums.INIT_ERR);
}
for(EntityType entityType:list){
handlerService.set(entityType.getEntityType(), entityType);
}
}
public static void refresh() throws EntityException {
handlerService.clear();
init();
}
}
这个类在项目启动时,从entity_type表加载配置,存在map中,key为entity_type字段,value为整个entity_type对象,我们到时候可以根据entity_type比如1001,获取该entity的相关配置。
对实体管理表的操作,我们都放在BaseEntityService这个基类中,而对于具体的资源模板及资源实例表,则由继承它的各个子类各自去实现,毕竟对于每个模板及实例,他们的操作是都不同的。
DynamicForm项目
ResourceService
package com.yj.service;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.yj.dao.resource.ResourceDao;
import com.yj.entity.InstanceSaveReq;
import com.yj.exception.BusinessException;
import com.yj.exception.BusinessExceptionEnums;
import com.yj.exception.EntityException;
import com.yj.model.comm.ResultUtil;
import com.yj.model.resource.ResourceInfo;
import com.yj.model.resource.ResourceInfoQueryBO;
@Service
@Transactional(rollbackFor = Exception.class)
public class ResourceService extends BaseEntityService<ResourceInfo> {
@Value("${entityType.resource}")
private int entityType;
@Autowired
private ResourceDao resourceDao;
@Override
protected int getEntityTypeCode() {
return entityType;
}
@Override
protected String saveInstanceInfo(ResourceInfo resourceInfo) {
String id = getSequence();
resourceInfo.setInstanceId(id);
resourceDao.saveInstanceInfo(resourceInfo);
return id;
}
@Override
protected int updateInstanceInfo(ResourceInfo resourceInfo) {
return resourceDao.updateInstanceInfo(resourceInfo);
}
@Override
protected ResourceInfo getInstanceInfo(String instanceId) {
return resourceDao.getInstanceInfo(instanceId);
}
public ResultUtil listInstances(ResourceInfoQueryBO resourceInfoQueryBO) throws BusinessException {
ResultUtil resultUtil = new ResultUtil();
if (0 == resourceInfoQueryBO.getOwnerId()) {
throw new BusinessException(BusinessExceptionEnums.NULL_OWNERID);
}
int total = resourceDao.pageCount(resourceInfoQueryBO);
List<ResourceInfoQueryBO> list = null;
if (total > 0) {
list = resourceDao.pageQuery(resourceInfoQueryBO);
}
resultUtil.setTotal(total);
resultUtil.setRows(list);
return resultUtil;
}
/**
* 修改的情况下,要判断该资源能否被修改 flag=0 可被修改 flag>0 不可被修改
*/
private int canModify(ResourceInfo instanceInfo) throws BusinessException {
int flag = 0;
String instanceId = instanceInfo.getInstanceId();
if (StringUtils.isNotBlank(instanceId)) {
flag = resourceDao.canModify(instanceInfo);
}
if (flag > 0) {
throw new BusinessException(BusinessExceptionEnums.RESOURCE_CANMODIFY_ERR);
}
return flag;
}
public void saveResource(InstanceSaveReq<ResourceInfo> instanceSaveReq) throws BusinessException, EntityException {
try {
ResourceInfo instanceInfo = instanceSaveReq.getInstanceInfo();
canModify(instanceInfo);
saveInstance(instanceSaveReq);
} catch (EntityException e) {
throw e;
} catch (BusinessException e) {
throw e;
}
}
public void changeResourceStatus(ResourceInfo resourceInfo) throws BusinessException, EntityException {
canModify(resourceInfo);
changeInstanceStatus(resourceInfo);
}
@Override
protected String getSequence() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16);
}
}
准备完毕,我们开始对resourceService进行单元测试
①getTemplates获取模板列表
@Test
public void getTemplates() {
ResourceTemplate resourceTemplate = new ResourceTemplate();
resourceTemplate.setOwnerId(1);
List<Object> list = resourceService.getTemplates(resourceTemplate);
}
根据ownerId获取模板列表
②getTemplateAttrCfg根据模板Id获取模板的配置信息(用于新增页面)
@Test
public void getEntityAttrCfg() {
InstanceSaveResp instanceSaveResp = resourceService.getTemplateAttrCfg(100000000000L);
}
我们看下BaseEntityService中的getTemplateAttrCfg方法
public InstanceSaveResp getTemplateAttrCfg(long templateId) throws EntityException {
log.debug("新增");
InstanceSaveResp instanceSaveResp = new InstanceSaveResp();
List<ElementGroupResp> elementGroupList;
try {
elementGroupList = baseDaoForEntity.queryGroupByTemplateId(templateId, getEntityType());
log.debug("elementGroupList:" + elementGroupList);
if (elementGroupList != null) {
for (int i = 0; i < elementGroupList.size(); i++) {
ElementGroupResp elementGroup = elementGroupList.get(i);
log.debug("elementGroupId:" + elementGroup.getGroupId());
List<ParaElmentResp> elementList = baseDaoForEntity.queryElementByGroupId(elementGroup.getGroupId(),
templateId, "0", getEntityType());
log.debug("elementList:" + elementList);
elementGroup.setElementList(elementList);
}
}
instanceSaveResp.setElementGroupResp(elementGroupList);
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
throw new EntityException(EntityExceptionEnums.ENTITY_ATTR_CFG);
}
return instanceSaveResp;
}
先根据templateId获取分组
- entity_elegrp_cfg中的element_type为0的情况,关联的是view_entity_def表,entity_elegrp_cfg表,element_group表,此时entity_elegrp_cfg表的element_id存的是element_group表的group_id
- entity_elegrp_cfg中的element_type为1的情况,关联的是view_entity_def表,entity_elegrp_cfg表,element_info表,此时entity_elegrp_cfg表的element_id存的是element_info表的element_id
再根据分组ID获取分组下的element
- entity_elegrp_cfg中的element_type为0的情况下关联的是view_entity_def表,entity_elegrp_cfg表,entity_attr_cfg表,element_info表,entity_attr_element_cfg表,entity_attr_lib表,element_group表,element_group_relation_cfg表
- entity_elegrp_cfg中的element_type为1的情况下关联的是view_entity_def表,entity_elegrp_cfg表,entity_attr_cfg表,element_info表,entity_attr_element_cfg表,entity_attr_lib表,不用关联element_group表,element_group_relation_cfg表
其中entity_attr_element_cfg表的element_id关联的是element_info表的element_id,attr_id关联的是entity_attr_cfg表的attr_id
我们新增一个模板,给该模板配置三个属性,这三个属性的element_type都为1的情况,即没有分组的情况
insert into `resource_template` (`template_id`, `template_name`,`owner_id`, `inure_time`, `expire_time`, `status`, `description`) VALUES ('100000000000', '资源管理模板',1, NOW(), '2050-01-01 00:00:00', '1', '资源管理模板');
insert into `entity_attr_cfg` (`entity_type`, `entity_id`, `attr_id`, `description`, `can_modify`, `can_empty`, `default_value`, `status`, `inure_time`, `expire_time`, `regex_value`, `check_db_func`) values ('1001', '100000000000', '100100000001', '属性1', '2', '0', null, '1', NOW(), '2050-01-01 00:00:00', null, null);
insert into `entity_attr_cfg` (`entity_type`, `entity_id`, `attr_id`, `description`, `can_modify`, `can_empty`, `default_value`, `status`, `inure_time`, `expire_time`, `regex_value`, `check_db_func`) values ('1001', '100000000000', '100100000002', '属性2', '1', '0', null, '1', NOW(), '2050-01-01 00:00:00', null, null);
insert into `entity_attr_cfg` (`entity_type`, `entity_id`, `attr_id`, `description`, `can_modify`, `can_empty`, `default_value`, `status`, `inure_time`, `expire_time`, `regex_value`, `check_db_func`) values ('1001', '100000000000', '100100000003', '属性3', '1', '0', null, '1', NOW(), '2050-01-01 00:00:00', null, null);
insert into `entity_attr_element_cfg` (`entity_type`, `entity_id`, `attr_id`, `element_id`, `status`, `hide_on_null`, `sort_by`) values ('1001', '100000000000', '100100000001', '100100000001', '1', '1', '1');
insert into `entity_attr_element_cfg` (`entity_type`, `entity_id`, `attr_id`, `element_id`, `status`, `hide_on_null`, `sort_by`) values ('1001', '100000000000', '100100000002', '100100000002', '1', '1', '2');
insert into `entity_attr_element_cfg` (`entity_type`, `entity_id`, `attr_id`, `element_id`, `status`, `hide_on_null`, `sort_by`) values ('1001', '100000000000', '100100000003', '100100000003', '1', '1', '3');
insert into `entity_elegrp_cfg` (`entity_type`, `entity_id`, `element_type`, `element_id`, `sequence`, `status`, `group_logo`) values ('1001', '100000000000', '1', '100100000001', '1', '1', null);
insert into `entity_elegrp_cfg` (`entity_type`, `entity_id`, `element_type`, `element_id`, `sequence`, `status`, `group_logo`) values ('1001', '100000000000', '1', '100100000002', '2', '1', null);
insert into `entity_elegrp_cfg` (`entity_type`, `entity_id`, `element_type`, `element_id`, `sequence`, `status`, `group_logo`) values ('1001', '100000000000', '1', '100100000003', '3', '1', null);
查询后,可以看到在没有分组的情况下,我们为了保持数据结构的一致性,也会将element放在group中,此时的group的entityType为1,groupId为elementId,groupName为elementName
我们再新增一个模板,给该模板配置三个属性,这三个属性的element_type都为0的情况,即有归属分组的情况
insert into `resource_template` (`template_id`, `template_name`,`owner_id`, `inure_time`, `expire_time`, `status`, `description`) VALUES ('100000000001', '资源管理模板2',1, NOW(), '2050-01-01 00:00:00', '1', '资源管理模板2');
INSERT INTO `element_group` (`entity_type`, `group_id`, `group_name`, `description`) VALUES ('1001', '100100000000', '分组1', '分组1');
insert into `element_group_relation_cfg` (`element_group_id`, `element_id`, `status`) values ('100100000000', '100100000001', '1');
insert into `element_group_relation_cfg` (`element_group_id`, `element_id`, `status`) values ('100100000000', '100100000002', '1');
insert into `element_group_relation_cfg` (`element_group_id`, `element_id`, `status`) values ('100100000000', '100100000003', '1');
insert into `entity_attr_cfg` (`entity_type`, `entity_id`, `attr_id`, `description`, `can_modify`, `can_empty`, `default_value`, `status`, `inure_time`, `expire_time`, `regex_value`, `check_db_func`) values ('1001', '100000000001', '100100000001', '属性1', '2', '0', null, '1', NOW(), '2050-01-01 00:00:00', null, null);
insert into `entity_attr_cfg` (`entity_type`, `entity_id`, `attr_id`, `description`, `can_modify`, `can_empty`, `default_value`, `status`, `inure_time`, `expire_time`, `regex_value`, `check_db_func`) values ('1001', '100000000001', '100100000002', '属性2', '1', '0', null, '1', NOW(), '2050-01-01 00:00:00', null, null);
insert into `entity_attr_cfg` (`entity_type`, `entity_id`, `attr_id`, `description`, `can_modify`, `can_empty`, `default_value`, `status`, `inure_time`, `expire_time`, `regex_value`, `check_db_func`) values ('1001', '100000000001', '100100000003', '属性3', '1', '0', null, '1', NOW(), '2050-01-01 00:00:00', null, null);
insert into `entity_attr_element_cfg` (`entity_type`, `entity_id`, `attr_id`, `element_id`, `status`, `hide_on_null`, `sort_by`) values ('1001', '100000000001', '100100000001', '100100000001', '1', '1', '1');
insert into `entity_attr_element_cfg` (`entity_type`, `entity_id`, `attr_id`, `element_id`, `status`, `hide_on_null`, `sort_by`) values ('1001', '100000000001', '100100000002', '100100000002', '1', '1', '2');
insert into `entity_attr_element_cfg` (`entity_type`, `entity_id`, `attr_id`, `element_id`, `status`, `hide_on_null`, `sort_by`) values ('1001', '100000000001', '100100000003', '100100000003', '1', '1', '3');
insert into `entity_elegrp_cfg` (`entity_type`, `entity_id`, `element_type`, `element_id`, `sequence`, `status`, `group_logo`) values ('1001', '100000000001', '0', '100100000000', '1', '1', null);
注意,此时多插了element_group表,element_group_relation_cfg表,并且entity_elegrp_cfg中的element_type=0,element_id为element_group表的group_id
查询后,可以看到三个element归属在一个group下
③saveInstance保存/修改实体
@Test
public void saveInstance() {
InstanceSaveReq instanceSaveReq = new InstanceSaveReq();
ResourceInfo resourceInfo =new ResourceInfo();
//resourceInfo.setInstanceId("ba22cd576f894b31");
resourceInfo.setResourceName("测试实例1");
resourceInfo.setTemplateId(100000000000L);
resourceInfo.setStatus(1);
resourceInfo.setOwnerId(1L);
resourceInfo.setInureTime("2019-01-01 00:00:00");
resourceInfo.setExpireTime("2050-01-01 00:00:00");
resourceInfo.setCreateId(1);
resourceInfo.setModifyId(1L);
instanceSaveReq.setInstanceInfo(resourceInfo);
List<ParaElmentResp> elmentResps =new ArrayList<ParaElmentResp>();
ParaElmentResp paraElmentResp1=new ParaElmentResp();
paraElmentResp1.setAttrId(100100000001L);
paraElmentResp1.setAttrValue("属性1");
elmentResps.add(paraElmentResp1);
ParaElmentResp paraElmentResp2=new ParaElmentResp();
paraElmentResp2.setAttrId(100100000002L);
paraElmentResp2.setAttrValue("属性2");
elmentResps.add(paraElmentResp2);
ParaElmentResp paraElmentResp3=new ParaElmentResp();
paraElmentResp3.setAttrId(100100000003L);
paraElmentResp3.setAttrValue("属性3");
elmentResps.add(paraElmentResp3);
instanceSaveReq.setElmentResps(elmentResps);
String instanceId = resourceService.saveInstance(instanceSaveReq);
log.info(instanceId);
}
我们看下BaseEntityService的saveInstance方法
public String saveInstance(InstanceSaveReq instanceSaveReq) throws EntityException {
try {
String result = null;
T instanceInfo = (T) instanceSaveReq.getInstanceInfo();
Map<String, Object> map = CommUtilForEntity.objectToMap(instanceInfo);
if (null == map.get("expire_time")) {
map.put("expire_time", "2050-01-01 00:00:00");
}
Long templateId = (Long) map.get(getEntityType().getInstanceEntityIdCol());
log.debug("templateId:" + templateId);
List<ParaCfg> paraCfgs = baseDaoForEntity.queryParaList(templateId, getEntityType().getEntityType());
List<ParaElmentResp> elmentResps = instanceSaveReq.getElmentResps();
if (elmentResps == null) {
elmentResps = new ArrayList<ParaElmentResp>();
}
Object instanceId = map.get(getEntityType().getInstanceIdCol());
log.debug("instanceId:" + instanceId);
if (instanceId == null || StringUtils.isBlank(instanceId + "")) {
log.info("新增");
for (ParaCfg paraCfg : paraCfgs) {
if (paraCfg.getCanEmpty() == 0 && StringUtils.isNotBlank(paraCfg.getDefaultValue())
&& !isParaExists(elmentResps, paraCfg.getAttrId())) {
ParaElmentResp appPara = new ParaElmentResp();
appPara.setAttrId(paraCfg.getAttrId());
appPara.setEntityId(String.valueOf(paraCfg.getEntityId()));
appPara.setAttrValue(paraCfg.getDefaultValue());
appPara.setTag(paraCfg.getTag());
elmentResps.add(appPara);
}
}
int num = instanceNameIsUnique((String) map.get(getEntityType().getInstanceNameCol()), null,
getEntityType());
if (num > 0) {
throw new EntityException(EntityExceptionEnums.INSTANCE_NAME_UNQUE);
} else {
String newInstanceId = saveInstanceInfo(instanceInfo);
log.debug("实例化成功,主键id:" + newInstanceId);
result = newInstanceId;
if (elmentResps != null) {
String now = CommUtilForEntity.getCurTimeStr();
for (ParaElmentResp paraElment : elmentResps) {
EntityAttrValue entityAttrValue = new EntityAttrValue();
entityAttrValue.setId(getSequence());
entityAttrValue.setOwnerId((Long) map.get("owner_id"));
entityAttrValue.setEntityId(String.valueOf(templateId));
entityAttrValue.setEntityType("" + getEntityType().getEntityType());
entityAttrValue.setInstanceId(newInstanceId);
entityAttrValue.setAttrId(paraElment.getAttrId());
entityAttrValue.setAttrValue(paraElment.getAttrValue());
entityAttrValue.setInureTime((String) map.get("inure_time"));
entityAttrValue.setExpireTime((String) map.get("expire_time"));
entityAttrValue.setCreateTime(now);
entityAttrValue.setModifyTime(now);
entityAttrValue.setCreateId((Long) map.get("create_id"));
entityAttrValue.setModifyId((Long) map.get("modify_id"));
Map<String, Object> attrValueMap = CommUtilForEntity.objectToMap(entityAttrValue);
commDaoForEntity.insert(attrValueMap, "entity_attr_value");
}
}
}
} else {// 修改
log.info("修改");
result = (String) instanceId;
int num = instanceNameIsUnique((String) map.get(getEntityType().getInstanceNameCol()),
((String) instanceId), getEntityType());
if (num > 0) {
throw new EntityException(EntityExceptionEnums.INSTANCE_NAME_UNQUE);
} else {
updateInstanceInfo(instanceInfo);
// 先删除,后添加
String now = CommUtilForEntity.getCurTimeStr();
for (ParaElmentResp paraElment : elmentResps) {
/*
* if (paraElment.getCanModify() != 1) { continue; }
*/
deleteAttr((String) instanceId, paraElment.getAttrId());
EntityAttrValue entityAttrValue = new EntityAttrValue();
entityAttrValue.setId(getSequence());
entityAttrValue.setOwnerId((Long) map.get("owner_id"));
entityAttrValue.setEntityId(String.valueOf(templateId));
entityAttrValue.setEntityType("" + getEntityType().getEntityType());
entityAttrValue.setInstanceId((String) map.get(getEntityType().getInstanceIdCol()));
entityAttrValue.setAttrId(paraElment.getAttrId());
entityAttrValue.setAttrValue(paraElment.getAttrValue());
entityAttrValue.setInureTime((String) map.get("inure_time"));
entityAttrValue.setExpireTime((String) map.get("expire_time"));
entityAttrValue.setCreateTime(now);
entityAttrValue.setModifyTime(now);
entityAttrValue.setCreateId((Long) map.get("create_id"));
entityAttrValue.setModifyId((Long) map.get("modify_id"));
Map<String, Object> attrValueMap = CommUtilForEntity.objectToMap(entityAttrValue);
commDaoForEntity.insert(attrValueMap, "entity_attr_value");
}
}
}
return result;
} catch (EntityException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new EntityException(EntityExceptionEnums.SAVE_INSTANCE);
}
}
新增的情况(resourceInfo对象主键值为空):
- 根据resourceInfo对象内的templateId,从entity_attr_cfg获取该模板的动态属性
- 动态属性不能为空,并且有默认值,并且我们前端没有设置该属性值时,我们为其设置上默认值,然后添加到我们前端提交过来elmentResps数组内
- 判断实例名称是否重复
- 调用saveInstanceInfo方法,该抽象方法由各个具体的子类实现
- 保存动态属性,因为我们前端只关心AttrId跟AttrValue两个字段,在这里我们为其补充上其他的字段,比如instanceId,inureTime,expireTime等等,然后入库
- 返回实例的主键ID
修改的情况(resourceInfo对象主键值非空):
- 根据resourceInfo对象内的templateId,从entity_attr_cfg获取该模板的动态属性
- 判断实例名称是否重复
- 调用updateInstanceInfo方法,该抽象方法由各个具体的子类实现
- 根据我们提交过来的elmentResps数组内的AttrId和instanceId,从entity_attr_value表删除记录
- 保存动态属性,因为我们前端只设置了AttrId跟AttrValue两个字段,在这里我们为其补充上其他的字段,比如instanceId,inureTime,expireTime等等,然后入库
- 返回实例的主键ID
④listInstances 获取实例列表
@Test
public void listInstances() {
ResourceInfoQueryBO resourceInfoQueryBO = new ResourceInfoQueryBO();
resourceInfoQueryBO.setOwnerId(1);
resourceInfoQueryBO.setPage(1);
resourceInfoQueryBO.setRows(3);
resourceInfoQueryBO.initPage();
ResultUtil resultUtil = resourceService.listInstances(resourceInfoQueryBO);
}
从view_resource_info表获取实例列表
⑤getEntityAttrValue获取实例详情
@Test
public void getEntityAttrValue() {
InstanceSaveResp instanceSaveResp = resourceService.getEntityAttrValue("ba22cd576f894b31");
}
我们看下BaseEntityService的getEntityAttrValue方法
public InstanceSaveResp getEntityAttrValue(String instanceId) throws EntityException {
log.debug("修改");
InstanceSaveResp instanceSaveResp = new InstanceSaveResp();
List<ElementGroupResp> elementGroupList = null;
try {
Map<String, Object> queryProps = CommUtilForEntity.objectToMap(getEntityType());
queryProps.put("instanceId", instanceId);
Long templateId = baseDaoForEntity.getTemplateIdByInstance(queryProps);
log.debug("templateId:" + templateId);
if (null == templateId) {
throw new EntityException(EntityExceptionEnums.INSTANCE_TEMPLATE_ERR);
}
elementGroupList = baseDaoForEntity.queryGroupByTemplateId(templateId, getEntityType());
log.debug("elementGroupList:" + elementGroupList);
if (elementGroupList != null) {
for (int i = 0; i < elementGroupList.size(); i++) {
ElementGroupResp elementGroup = elementGroupList.get(i);
log.debug("elementGroupId:" + elementGroup.getGroupId());
List<ParaElmentResp> elementList = baseDaoForEntity.queryElementByGroupId(elementGroup.getGroupId(),
templateId, instanceId, getEntityType());
log.debug("elementList:" + elementList);
elementGroup.setElementList(elementList);
}
}
instanceSaveResp.setElementGroupResp(elementGroupList);
instanceSaveResp.setInstanceInfo(getInstanceInfo(instanceId));
} catch (EntityException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
throw new EntityException(EntityExceptionEnums.ENTITY_ATTR_VALUE);
}
return instanceSaveResp;
}
- 根据resourceInfo对象内的templateId,获取分组
- 再根据groupId获取分组下的element
- 调用getInstanceInfo方法获取实例的基本信息,该抽象方法由具体的子类实现
- 返回给前端后,前端根据element对象中的tag来区别属性,进行展示
⑥changeInstanceStatus修改实体状态(启用/停用)
@Test
public void changeInstanceStatus() {
ResourceInfo resourceInfo = new ResourceInfo();
resourceInfo.setInstanceId("ba22cd576f894b31");
resourceInfo.setStatus(1);
resourceService.changeInstanceStatus(resourceInfo);
}