Extjs4.2mvc+struts2+mybatis实现数据的增删改(CUD)

最近一直在用extjs来开发网站的后台管理程序,经常要对数据进行保存,有时要同时对多张表保存,如果每次都一点点开发将会非常麻烦,于是自己对前台和后台进行了封装,可以实现前台数据的自动搜集,后台数据的自动接收,数据的自动保存。


第一步:前台和后台数据传递要有一个规范:

1.model类要有一个origin字段来保存数据的原始记录,后台操作有时需要对比原始记录,增强程序的可拓展性, model类的示例代码如下:

package com.sesan.pub.model;

import com.sesan.core.annotations.ID;

public class App {
    @ID
    private String appCode;

    private String appName;

    private String shortname;

    private String imageUrl;

    private String url;

    private String des;

    private Short status;

    private App origin;
    public String getAppCode() {
        return appCode;
    }

    public void setAppCode(String appCode) {
        this.appCode = appCode;
    }

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public String getShortname() {
        return shortname;
    }

    public void setShortname(String shortname) {
        this.shortname = shortname;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDes() {
        return des;
    }

    public void setDes(String des) {
        this.des = des;
    }

    public Short getStatus() {
        return status;
    }

    public void setStatus(Short status) {
        this.status = status;
    }

    public App getOrigin() {
        return origin;
    }

    public void setOrigin(App origin) {
        this.origin = origin;
    }
    
    
}
2. ModelOperator<T>对象,T为model对象,该对象包含新增的数据集,修改的数据集和删除的数据集,代码如下:

package com.sesan.core.model;

import java.util.List;
/**
 * 单表保存数据收集对象,Grid和Form都可以使用, grid和form都可以理解为datawindow的一种
 * 
 * @author Shilian Peng
 *
 * @param <T> 模型对象,如User
 */
public class ModelOperator<T> {
	List<T> addRecords;
	List<T> updateRecords;
	List<T> deleteRecords;
	
	public List<T> getAddRecords() {
		return addRecords;
	}
	
	public void setAddRecords(List<T> addRecords) {
		this.addRecords = addRecords;
	}
	
	public List<T> getUpdateRecords() {
		return updateRecords;
	}
	
	public void setUpdateRecords(List<T> updateRecords) {
		this.updateRecords = updateRecords;
	}
	
	public List<T> getDeleteRecords() {
		return deleteRecords;
	}
	
	public void setDeleteRecords(List<T> deleteRecords) {
		this.deleteRecords = deleteRecords;
	}

}

第二步:前台在做CRUD的时候需要做相关的操作:

1.在数据的读取,新增,修改和删除操作都要使用拓展的方法, 这样在数据保存的时候就可以统一的收集数据,封装的代码如下:

    /**
     * Grid 增删改查操作类
     */  
    Ext.define('Ext.grid.ModelOperator', {
        /**
         * 收集grid保存是需要的数据
         */
         getUpdateInfos: function( ){
            var grid = this;
            var store = grid.getStore();    
            var j = store.getCount();
            var i;
            var record;
            var data;
            var m=[],n=[],d=[];
            var deleteRerords  ;
            //获取新增或修改的记录
            for(i=j-1;i>=0;i--){
                record=store.getAt(i);
                if(record.dirty){
                    data = Ext.apply({},record.data );
                    
                    //将boolean类型值转化成0 1
                    Ext.Object.each(data, function(key, value, myself) { 
                        if (value === false) {
                            data[key]=0;
                        }
                        if (value === true) {
                            data[key]=1;
                        }
                    });
                    
                    if(record.__isNew){ 
                        n.push(data);
                        
                    }else{
                        
                        data.origin=record.__origin;
                        //将boolean类型值转化成0 1
                        Ext.Object.each(data.origin, function(key, value, myself) { 
                            if (value === false) {
                                data.origin[key]=0;
                            }
                            if (value === true) {
                                data.origin[key]=1;
                            }
                        });
                        m.push(data);
                    }
                }else if( record.__isNew ){
                    store.remove(record);
                }
            }
            //获取删除的记录
            deleteRerords = store.getRemovedRecords( );
            for(i=0 ;i<deleteRerords.length;i++){
                
                if( !deleteRerords[i].__isNew ){
                    d.push(deleteRerords[i].__origin);
                    
                }
                
            }
            
             
            
            var Operator = {
                    addRecords:n,
                    deleteRecords:d,
                    updateRecords:m
            };
            return Operator;
         
     }, 
         retrieve:function( parms){
             var grid = this;
             var store = grid.getStore();
             if (parms){
                 store.proxy.extraParams=parms;
             }
             
             
             store.load({
                    scope: store,
                    callback: function(records, operation, success) {
                        if (success==true){
                            this.each(function(b) {
                                b.__origin = Ext.apply({}, b.data);
        
                            });
                        }
                    }
                }); 
         } ,
         insertDatas:function( record){
            var grid=this;
             var store = grid.getStore();
            var row ;
            
            var gridsel = grid.getSelectionModel().getSelection( ) ;
            if (gridsel.length>0){
                row =store.indexOf( gridsel[0] );
            }else{
                row = store.count();
            } 
            var records= store.insert(row,record ? record : {}); 
             records.forEach(function(record){ 
                record.__isNew=true; 
                record.dirty = true; 
             }) ;
            
            if( grid.plugins){

                var edit = grid.plugins[0] ;
                edit.startEditByPosition({row:row,column:1});
            }
             
         },
         deleteSelectRecords:function( ){
            
            var grid = this;
             var store = grid.getStore( ); 
            var sels=    grid.getSelectionModel().getSelection( ) ;
            var row;
            var i;
            if  (sels.length<=0){
                return ;
            }
            store.remove(sels); 
         },
         /**
          * 获取选择的记录集中的data值集合
          */
         getSelectDatas: function( ){
             var me= this;
             var datas=[];
             var records =  me.getSelectionModel( ).getSelection( ) ;
             len = records.length;
             if (len) {
                  for (i = 0; i < len; i++) {
                      record = records[i];
                      datas.push(record.data );
                  }
             }
             return datas;
         }  ,  
         selectAll :function(){
             var me= this; 
             me.getSelectionModel().selectAll(); 
        }
    });    
    Ext.grid.Panel.mixin('ModelOperator', Ext.grid.ModelOperator);  

2.示例代码如下:

Ext.define("app.controller.AppController", {
    extend: "Ext.app.Controller",
    
    config: { 
        refs: [{
            ref: 'window',
            selector: 'appView'
        }]
    },
     
    init: function() {
        this.control({
            'appView': {
                afterrender: this.initWindow
            } ,
            'appView button[action=addAction]' : {
                click : this.add
            },
            'appView button[action=delAction]' : {
                click : this.del
            },  
            'appView button[action=saveAction]' : {
                click : this.save
            },
            'appView button[action=refreshAction]' : {
                click : this.refresh
            } 
    
        });
     },
     
    constructor: function(){
         Ext.applyIf(this, this.config);
         this.callSuper(arguments);
     },
     initWindow : function(){
         this.refresh();
     },
     add:function(){
         var window = this.getWindow();   
        var grid =  window.down('gridpanel[itemId=grd1]');
        grid.insertDatas();


     },
     del:function(){ 
        var grid =  this.getWindow().down('gridpanel[itemId=grd1]');
        grid.deleteSelectRecords();
        

     },
     save:function(){
        
        var window = this.getWindow();
        var grid1 =  window.down('gridpanel[itemId=grd1]') 
        var appOperator =grid1.getUpdateInfos(); 
        //ajax提交数据
        Ext.Ajax.request({
            scope: this,
            url:   'app/saveModels.do',
            params: {
                appOperator: Ext.JSON.encode( appOperator)  
            },
            success : function(response, options){
                this.refresh( );
            }
        });
            

         
     },
     refresh : function(){
         
         var window = this.getWindow();
         var grid= window.down('grid');
         grid.retrieve();
         
          this.getWindow().down('gridpanel[itemId=grd1]').retrieve();
   
     }
     
  

})  

第三步:struts2接收数据的时候可以使用struts2的全局转换器:

1.转换器代码如下:

package com.sesan.core.struts2.convert;

import java.lang.reflect.Field;
import java.lang.reflect.Member; 
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map; 

import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter; 
import com.sesan.core.model.ModelOperator;
import com.sesan.core.util.StringUtils;

import net.sf.json.JSONObject;

/**
 * ModelOperator<Model> 对象转换器
 * 
 * 
 * @author Shilian Peng
 *
 */
public class ModelConvert extends DefaultTypeConverter {
 
    @Override
      public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, @SuppressWarnings("rawtypes") Class toType) {
        Class<?> actionClass = member.getDeclaringClass() ; 
        String fileName = member.getName();
        
        fileName = StringUtils.toLowerCaseFirstOne(fileName.substring(3 ) )    ;
         Field field = null;
        try {
            field = actionClass.getDeclaredField(fileName);
        } catch (NoSuchFieldException e) { 
            e.printStackTrace();
        } catch (SecurityException e) { 
            e.printStackTrace();
        }
        field.setAccessible(true);
        ParameterizedType pType = (ParameterizedType)field.getGenericType(); 
        Type[] types = pType.getActualTypeArguments(); 
        Class<?> modelClass = (Class<?>)types[0];
        JSONObject jsonObject = JSONObject.fromObject(((String[])value)[0]);
        Map<String, Object> config = new HashMap<String, Object>();
        config.put("addRecords", modelClass );
        config.put("updateRecords", modelClass );
        config.put("deleteRecords", modelClass ); 
        return JSONObject.toBean(jsonObject, ModelOperator.class, config);
    }
}

2.action 代码如下

package com.sesan.pub.action;

import java.util.HashMap;
import java.util.List;
import java.util.Map;  
import org.springframework.beans.factory.annotation.Autowired; 
import com.sesan.core.model.ModelOperator;
import com.sesan.pub.model.App;
import com.sesan.pub.service.IAppService;

public class AppAction {

    @Autowired
    private IAppService appService;

    Map<String, Object> result;

    private ModelOperator<App> appOperator;

 
    public String getAllApp() {
        result = new HashMap<String, Object>();
        try {
            List<App> rows = appService.selectAll();
            result.put("rows", rows);
            result.put("success", true);
        } catch (Exception e) {
            result.put("success", false);
            result.put("msg", e.getMessage());
            e.printStackTrace();

        }
        return "json";

    }

    public String saveModels() {

        result = new HashMap<String, Object>();
        try {
 
            appService.saveModels(appOperator);
            result.put("success", true);
        } catch (Exception e) {
            result.put("success", false);
            result.put("msg", e.getMessage());
            e.printStackTrace();
        }

        return "json";

    }

    public Map<String, Object> getResult() {
        return result;
    }

    public void setResult(Map<String, Object> result) {
        this.result = result;
    }

    public void setAppOperator(ModelOperator<App> appOperator) {
        this.appOperator = appOperator;
    }

}

第四步:service层实现数据的自动保存

1.通过传递Mapper对象 ModelOperator<T>就可以实现数据的自动保存,前提是所有的Mapper对象有相同的方法,我的程序中是使用mybatis的generator插件自动生成的代码,可以保证方法是相同的,详细代码如下:

package com.sesan.core.model;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.sesan.core.annotations.ID;
import com.sesan.core.util.StringUtils; 
/**
 * 单表保存逻辑封装类,模型对象必须有ID注解,否则认为是组合主键
 * 
 * @author Shilian Peng
 * 
 * @param <T> Mapper对象
 * @param <M> Model对象
 */
public class ModelSave<T, M> {
    /**
     * 单表保存,当发生数据库异常时返回运行时异常,事务会自动回滚
     * @param mapper
     * @param modelOperator
     */
    public void saveModels(T mapper, ModelOperator<M> modelOperator) {

        for (M record : modelOperator.getDeleteRecords()) {
            Field pkField = null;
            Method getKeyMethod = null;
            boolean KeyFlag = false;// 如果有ID注解则为true
            // 获取主键,如果有ID注解,则主键为单个字段,如果没有,则视为符合主键,可把record当做主键,因为model对象是集成主键的
            for (Field field : record.getClass().getDeclaredFields()) {
                if (field.getAnnotation(ID.class) != null) {
                    pkField = field;
                }
            }
            // 获取主键值
            if (pkField != null) {
                for (Method method : record.getClass().getMethods()) {
                    if (method.getName().equals(
                            "get"
                                    + StringUtils.toUpperCaseFirstOne(pkField
                                            .getName()))) {
                        getKeyMethod = method;
                        KeyFlag = true;
                        break;
                    }
                }
            }
            for (Method method : mapper.getClass().getMethods()) {
                if ("deleteByPrimaryKey".equals(method.getName())) {
                    try {
                        if (KeyFlag) {
                            method.invoke(mapper, getKeyMethod.invoke(record));
                        } else {
                            method.invoke(mapper, record);
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    }
                }
            }
        }

        for (M record : modelOperator.getUpdateRecords()) {
            for (Method method : mapper.getClass().getMethods()) {
                if ("updateByPrimaryKey".equals(method.getName())) {
                    try {
                        method.invoke(mapper, record);
                    } catch (IllegalAccessException e) {
                        
                        e.printStackTrace();
                        throw new RuntimeException();
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    }
                }
            }
        }
        for (M record : modelOperator.getAddRecords()) {
            for (Method method : mapper.getClass().getMethods()) {
                if (("insertSelective").equals(method.getName())) {
                    try {
                        System.out.println(record);
                        method.invoke(mapper, record);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    }
                }
            }
        }
    }
}

2.主键注解类,如果有主键注解认为主键是单个字段,如果没有的话认为是复合主键,由于model如果是复合主键那么model类是继承于复合主键的如:public class Module extends ModuleKey ,所以主键可以认为是整个对象,主键注解类代码如下:

package com.sesan.core.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 模型对象的ID注解
 * 
 * @author Shilian Peng
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ID { 
}

3.services类的示例代码:

package com.sesan.pub.service.imp;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.sesan.core.model.ModelOperator;
import com.sesan.core.model.ModelSave;
import com.sesan.pub.dao.AppMapper; 
import com.sesan.pub.model.App; 
import com.sesan.pub.service.IAppService;  

public class AppServiceImp implements IAppService {

    @Autowired
    private AppMapper appMapper ;
    
 
    @Override
    public List<App> selectAll() { 
        return appMapper.selectAll();
    }

    @Transactional(value = "pubTransactionManager", propagation = Propagation.REQUIRED, rollbackForClassName = {
            "Exception", "RuntimeException" })
    @Override
    public void saveModels(ModelOperator<App> AppsOperator) { 

        new ModelSave<AppMapper, App>().saveModels(appMapper,
                AppsOperator);
        
    }

}

这些是我这些天的劳动成果,个人感觉能够加快程序的开发速度,不过肯定也有很多不足,贴出来和大家一起探讨。

























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值