posts - 122,  comments - 25,  trackbacks - 0
一般业务系统中总会存在一些基础数据,在其他的业务单据中会被套引用。因此,系统必须保证这些被业务单据引用的基础数据不能任意的删除。最常见的做法就是,在删除基础数据时,预先校验该类数据是否在相关业务表中存在,若不存在才允许用户删除,否则给用户以提示。

但这样的处理方法,有些缺点,就是需要编码对每个业务类提供查询方法,或在删除逻辑中增加判断逻辑。因此,每次引用关系变化,增加或减少时免不了要修改原来的逻辑,时间越长,系统的维护成本就越来越高了。因此,有必要对系统进行重构,将这类的处理逻辑进行抽象,单独封装成一个服务,当引用关系有变更时,不用再修改原有逻辑,通过配置就可以完成变更。

通用引用关系查询服务,主要就是通过db表或xml配置文件,对系统中每个基础数据有引用的所有关系进行定义,定义属性主要是引用的表及字段名称。查询时,从配置文件中读取指定类别的引用关系,并逐一查询这些表中的记录,以确定数据是否被引用。这种处理方法的优点为,易扩展、可维护性强,引用关系变更时,仅通过维护配置文件,不必进行编码,就能实现,这样能大大的提高系统的稳定性。

xml配置文件如下:
<rule bizName='product' desc="产品关联项定义">
    
<item>
        
<refTable>sale_item</refTable>
        
<refField>product_id</refField>
        <!-- 用于查询条件的扩展,允许为空 -->
        <extCondition>CORP_ID = #corpId#</extCondition>
    
</item>
    
<item>
        
<refTable>sale_order_item</refTable>
        
<refField>product_id</refField>
        
<extCondition>CORP_ID = #corpId#</extCondition>
    
</item>
</rule>
<rule bizName='customer' desc="客户关联项定义">
    
<item>
        
<refTable>sale_order</refTable>
        
<refField>cust_id</refField>
        
<extCondition>CORP_ID = #corpId#</extCondition>
    
</item>
    
<item>
        
<refTable>sale_bill</refTable>
        
<refField>cust_id</refField>
        
<extCondition></extCondition>
    
</item>
    ... ...

</rule>

通用业务引用查询类代码片段如下:
public class BizReferenceService implements IBizReferenceService {

    
private static Map<String,List<BizReferenceRule>> ruleMaps;
    
    
private static final String PATTERN = "#[\\w]+#";
    
private static final String CFG_FILE = "bizReferenceRule.xml";
    ... ...

  
    
/**
     * 查询指定业务数据是否被其他业务表关联依赖.
     * 
@param bizName 关联业务名称
     * 
@param bizId 关联业务ID.
     * 
@param extParam 扩展条件
     * 
@return true 被关联/false 未被关联.
     
*/
    
public boolean isBizReference(String bizName,String bizId,Map<String,Object>extParam) throws ServiceException {
        Assert.notNull(bizName, 
"业务名称不能为空,bizName is NULL。");
        Assert.notNull(bizId, 
"记录ID不能为空,bizId is NULL。");

        
try {
            
//逐个检查依赖项是否有数据关联.
            List<BizReferenceRule> rules = getBizRelationRule(bizName);
            
for(BizReferenceRule rule : rules){
                StringBuilder sqlBuilder 
= new StringBuilder();
                sqlBuilder.append(
"select count(*) from ").append(rule.getRelTable()).append(" where ")
                    .append(rule.getRelField()).append(
"='").append(bizId).append("");
                String extConditon 
= rule.getExtCondition();
                
if(StringUtil.isNotBlank(extConditon)){
                    initTenantParam(extParam);
                    sqlBuilder.append(
" and ").append(getExtParamSql(extConditon,extParam));
                }
                logger.debug(sqlBuilder);
                
int nCount = bizReferenceDao.getBizRelationCount(sqlBuilder.toString());
                
if (nCount != 0return true;
            }
            
return false;
        }
        
catch(Exception ex){
            logger.error(
"调用业务关联服务错误。"+bizName+",bizId:"+bizId+",extParam"+LogUtil.parserBean(extParam),ex);
            
throw new ServiceException("调用业务关联服务错误。");
        }
    }
    
    
/**
     * 组装扩展查询条件的sql
     * 
@param condition
     * 
@param extParam
     * 
@return
     * 
@throws Exception
     
*/
    
private String getExtParamSql(String condition,Map<String,Object>extParam) throws Exception {
        List
<String> paramList = parseDyncParam(condition);
        
for(String param : paramList){
            String simpleParam 
= simpleName(param);
            
if(!extParam.containsKey(simpleParam)){
                
throw new ServiceException("动态参数值未设置! param:"+param+",extParam:"+LogUtil.parserBean(extParam));
            }
            condition 
= condition.replaceAll(param, "'"+String.valueOf(extParam.get(simpleParam))+"'");
        }
        
return condition;
    }
    
    
/**
     * 解析扩展查询条件中的动态参数名.
     * 
@param condition
     * 
@return
     * 
@throws Exception
     
*/
    
private List<String> parseDyncParam(String condition) throws Exception {
        PatternCompiler compiler 
= new Perl5Compiler();
        PatternMatcher matcher 
= new Perl5Matcher();
        MatchResult result 
= null;
        PatternMatcherInput input 
= null;
        List
<String> paramList = new ArrayList<String>();
        input 
= new PatternMatcherInput(condition);
        Pattern pattern 
= compiler.compile(PATTERN,Perl5Compiler.CASE_INSENSITIVE_MASK);
        
while (matcher.contains(input, pattern)){
            result 
= matcher.getMatch();
            input.setBeginOffset(result.length());
            paramList.add(result.group(
0));
        }
        
return paramList;
    }
    
    
/**
     * 获取业务关联查询规则.
     
*/
    
private List<BizReferenceRule> getBizRelationRule(String bizName){
        Assert.notNull(bizName, 
"业务名称不能为空,bizName is NULL。");
        
        
//配置定义未加载到内存时,读取配置文件
        if(ruleMaps == null){
            parseRuleConfig();
            
if(ruleMaps == nullreturn null;
        }
        
        
return ruleMaps.get(bizName);
    }
    
    
/**
     * 读取业务关联规则配置文件
     
*/
    @SuppressWarnings(
"unchecked")
    
private synchronized void parseRuleConfig(){
        
if(ruleMaps != null){
            
return;
        }
        
        
//解析业务引用定义文件.
         
    }
    
    
/**
     * 读取Xml文档
     * 
@return
     
*/
    
private Document getXmlDocument(){
        InputStream is 
= null;
        
try {
            ClassLoader loader 
= Thread.currentThread().getContextClassLoader();
            is 
= loader.getResourceAsStream(CFG_FILE);
            SAXBuilder sb 
= new SAXBuilder();
            
return sb.build(new BufferedInputStream(is));
        }
        
catch(Exception ex) {
            logger.error(
"读取配置文件错误. file:"+CFG_FILE, ex);
            
return null;
        }
        
finally {
            
try {
                
if(is != null){
                    is.close();
                    is 
= null;
                }
            }
            
catch(Exception ex) {
                logger.error(ex);
            }
        }
    }

     
}

其他的一些可选处理方法:
b. 在客户表增加引用计数字段;
需额外维护引用计数字段,在引用的业务逻辑增加或删除记录时,需对该字段的数值进行更新。适用于需要直接查询记录被引用次数的场景,但在集群环境下,需注意并发问题。
posted on 2009-07-14 14:42 josson 阅读(378) 评论(0)  编辑  收藏 所属分类: java 开发

只有注册用户登录后才能发表评论。


网站导航:
 
<2009年7月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用链接

留言簿(3)

随笔分类

随笔档案

收藏夹

搜索

  •  

最新评论

阅读排行榜

评论排行榜