42.大数据之旅——java分布式项目03

该博客详细介绍了在大数据背景下,使用Java进行分布式项目开发的商品管理模块。内容包括通用Mapper的实现,如查询总记录数和所有模块信息,商品分类树的展现,EasyUI树的JSON格式说明,商品的新增、编辑、删除功能的实现,以及懒加载思想在商品分类查询中的应用。并探讨了懒加载的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通用Mapper实现查询总记录数


实现步骤:
0.引入通用Mapper的jar包
Pom.xml代码:

<!-- 通用Mapper,所有的单表的代码都不用编写 -->
<dependency>
<groupId>com.github.abel533</groupId>
<artifactId>mapper</artifactId>
<version>2.3.2</version>
</dependency>
  1. 在Mybatis核心配置文件里,配置mapperInterceptor插件
  2. 写一个泛型接口,并让其他所有mapper接口继承这个泛型接口
  3. 写一个类,继承MapperTemplate模板
  4. 开发具体方法
  5. 在泛型接口里,利用@XXXProvider注解,调用类里的方法
  6. 测试

一、查询数据库总记录数
SysMapperProvider.class代码:

public class SysMapperProvider extends MapperTemplate{
 
public SysMapperProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
 
super(mapperClass, mapperHelper);
System.out.println(mapperClass.getName());
 
}
public SqlNode selectCount(MappedStatement ms){
/*
 * 1.ms.getId()得到的是namespace.id;
 * 比如:当ModuleMapper接口执行 父接口里的 selectCount()方法时,mybatis会通过@SelectProvider
 * 形成如下的结构:
 * namepace=cn.tarena.ht.mapper.ModuleMapper
 * <select id="selectCount" resultType="待设置">
 *  sql语句
 * </select>
 * 2.所以此时,ms.getId()=cn.tarena.ht.mapper.ModuleMapper.selectCount
 * 3.我们的目的是动态的获取表名,而这个表名我们是在实体类上,加@Talbe(name="表名")来做的,所以最终的目的就是获取Module这个实体类
 * 4.获取实体类的步骤如下:
 * ①把cn.tarena.ht.mapper.ModuleMapper.selectCount截串,截出cn.tarena.ht.mapper.ModuleMapper,得到ModuleMapper接口
 * ②得到ModuleMapper接口之后,可以获得它的父接口,SysMapper<Module>
 * ③得到父接口后,因为这个接口含有泛型,所以可以把这个父接口转换为泛型类型,即ParameterizedType
 * ④得到ParameterizedType后,就可以获得泛型里的实体type,强转成通配类型的class<?>
 * ⑤有了class<?>之后,就能获得class上的指定类型的注解
 * ⑥通过@Table注解的name属性,就可以获得表名
 * 这样,表名就可以动态获得了
 * 
 */
String namespaceId=ms.getId();//cn.tarena.ht.mapper.ModuleMapper.selectCount
String interfaceClassName=namespaceId.substring(0, namespaceId.lastIndexOf("."));
String tableName=null;
try {
Class<?> interfaceClass=Class.forName(interfaceClassName);
Type[] types=interfaceClass.getGenericInterfaces();
Type t=types[0];
if(t instanceof ParameterizedType){
ParameterizedType superInterfaceType=(ParameterizedType) t;
Type[] entityClasses=superInterfaceType.getActualTypeArguments();
Class<?> entityClass=(Class<?>) entityClasses[0];
tableName=entityClass.getAnnotation(Table.class).name();
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String text="select count(*) from "+tableName;
SqlNode sqlNode=new StaticTextSqlNode(text);
return sqlNode;
 
}
}
 

SysMapper接口代码:

public interface SysMapper<T> {
 
@SelectProvider(type=SysMapperProvider.class,method="dynamicSQL")
public int selectCount();
 
}
ModuleMapper接口代码:
public interface ModuleMapper extends SysMapper<Module>{
}
 
Module(pojo)代码:
@Table(name="dept_p")
public class Module extends BasePojo{
 
}
 

通用Mapper实现查询所有模块信息


SysMapperProvider代码:

public SqlNode select(MappedStatement ms){
//得到父接口里泛型的实体类
Class<?> entityClass=this.getSelectReturnType(ms);
//设置返回值类型
this.setResultType(ms, entityClass);
//得到表名
String tableName=entityClass.getAnnotation(Table.class).name();
 
String text="select * from "+tableName;
SqlNode sqlNode=new StaticTextSqlNode(text);
return sqlNode;
}
SysMapper代码:
@SelectProvider(type=SysMapperProvider.class,method="dynamicSQL")
public List<T> select();

商品新增—商品分类树展现


ItemCatController代码:

@Controller
public class ItemCatController {
 
@Autowired
private ItemCatService itemCatService;
 
/*商品分类树展示要求的json格式
 * easyui树要求的json格式:
 * {"id":2,"text":"商品名",state:"closed"}state的属性如果是closed,表示这个是父节点,它还有子节点。open代表子节点
 * 商品分类树实现思路:
 * 商品分类树,最开始只展示第一级商品分类,然后,当点击某个一级商品分类时,前台会把这个一级商品分类的id传过来,根据这个id,去查询它的二级子分类
 * 然后把二级子分类展现出来,也就是说,当你点击某一个父节点时,它才会去加载对应的数据,不会一起把所有数据都查出来,这样能够避免出现过大的数据量在网络中进行传输
 * 当点击某个二级子分类时,会根据这个二级分类的id,去查询对应的第三级分类
 * 
 * 对应的前台代码在web-app/js/common.js的99行到140行
 */
@RequestMapping("/item/cat/list")
@ResponseBody
public List<ItemCat> list(@RequestParam(value="id",defaultValue="0")Long parentId){
return itemCatService.findItemCatByParentId(parentId);
 
 
}
}

EasyUI树的JSON格式说明


在这里插入图片描述

 initItemCat : function(data){
            $(".selectItemCat").each(function(i,e){//i= index 下标,e:element:元素
                    var _ele = $(e);
                    if(data && data.cid){
                            _ele.after("<span style='margin-left:10px;'>"+data.cid+"</span>");
                    }else{
                            _ele.after("<span style='margin-left:10px;'></span>");
                    }
                    _ele.unbind('click').click(function(){
                            $("<div>").css({padding:"5px"}).html("<ul>")
                            .window({
                                    width:'500',
                                height:"450",
                                modal:true,
                                closed:true,
                                iconCls:'icon-save',
                                title:'选择类目',
                                onOpen : function(){ //当窗口打开后执行
                                        var _win = this;
                                        $("ul",_win).tree({
                                                url:'/item/cat/list',
                                                animate:true,
                                                onClick : function(node){
                                                        if($(this).tree("isLeaf",node.target)){
                                                                // 填写到cid中
                                                                _ele.parent().find("[name=cid]").val(node.id);
                                                                _ele.next().text(node.text).attr("cid",node.id);
                                                                $(_win).window('close');
                                                                if(data && data.fun){
                                                                        data.fun.call(this,node);
                                                                }
                                                        }
                                                }
                                        });
                                },
                                onClose : function(){
                                        $(this).window("destroy");
                                }
                            }).window('open');
                    });
            });
    },

后台组织Json数据的Controller代码:

@RequestMapping("/item/cat/list")
public void list(HttpServletResponse res){
System.out.println("收到前台Ajax请求");
//                [{ "id":1,"text":"parent","state":"closed"}]
String json="[{\"id\":1,\"text\":\"parent\",\"state\":\"closed\"}]";
try {
res.getWriter().write(json);
} catch (IOException e) {
 
e.printStackTrace();
}
 
}

第二种方法:通过java对象+SpringMVC的特定注解,完成json数据的转换
pojo代码:

public class ItemCat {
 
private long id;
private String text;
private String state;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
 
}

controller代码:

@RequestMapping("/item/cat/list")
@ResponseBody
public List<ItemCat> list(){
System.out.println("收到前台Ajax请求");
ItemCat item=new ItemCat();
item.setId(1);
item.setText("javaSpringMVC");
item.setState("closed");
List<ItemCat> list=new ArrayList<>();
list.add(item);
return list;
}
 

知识点:
1.@ResponseBody注解:这个注解可以将对象或者对象集合转成json串格式,然后底层调用了HttpServletResponse.Write(Json)方法,将json串写到响应正文中。
在这里插入图片描述
2.使用@ResponseBody注解,返回值必须是对象类型或者是对象集合。
这里有个坑:
本例中,只封装了一个ItemCat对象的信息,有的同学问,返回值类型为什么不是ItemCat,而是List。这是因为如果返回的是单个对象,@ResponseBody解析的Json串是这样的:

{"id":1,"text":"javaSpringMVC","state":"closed"}

注意,没有了两边的 [ ] ,这样就不符合EasyUi树数据的结构,所以就显示不出来了。
根据前面的基础,完成京淘商品的分类查询树
持久类ItemCat代码:

@Table(name="tb_item_cat")
public class ItemCat extends BasePojo {
 
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)        //自增主键
private long id;
private String name;
private long parentId;
private int status;
private int sortOrder;
private boolean isParent;
 
public long getId() {
return id;
}
 
public void setId(long id) {
this.id = id;
}
 
public String getName() {
return name;
}
 
public void setName(String name) {
this.name = name;
}
 
public long getParentId() {
return parentId;
}
 
public void setParentId(long parentId) {
this.parentId = parentId;
}
 
public int getStatus() {
return status;
}
 
public void setStatus(int status) {
this.status = status;
}
 
public int getSortOrder() {
return sortOrder;
}
 
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
 
public boolean isParent() {
return isParent;
}
 
public void setParent(boolean isParent) {
this.isParent = isParent;
}
 
public String getText(){
return name;
}
 
public String getState(){
return isParent?"closed":"open";
}
 
 
}

注意:
因为EasyUI树数据要求的节点属性名为text,所以要加上getText()方法,以满足数据格式需要,此外就是需要额外加上getState()方法,来控制根节点或叶节点。

 <resultMap type="ItemCat" id="itemCatRM">
          <id property="id" column="id"/>
          <result property="parentId" column="parent_id"/>
          <result property="name" column="name"/>
          <result property="sortOrder" column="sort_order"/>
          <result property="isParent" column="is_parent"/>
          <result property="status" column="status"/>
          <result property="created" column="created"/>
          <result property="updated" column="updated"/>
  </resultMap>
  
<select id="findByParentId" parameterType="long" resultMap="itemCatRM">
 select * from tb_item_cat where parent_id=#{id}
</select>

ItemCatMapper.xml代码:

知识点:
传参类型可以是long型
ItemCatMapper接口类代码:

public interface ItemCatMapper extends SysMapper<ItemCat> {
 
public List<ItemCat> findByParentId(long id);
 
}
ItemCatservice代码:
@Service
public class ItemCatService {
 
 
@Autowired
private ItemCatMapper itemCatMapper;
 
public List<ItemCat> findByParentId(Long id) {
 
return itemCatMapper.findByParentId(id);
}
 
 
}

ItemCatController代码:

@Controller
public class ItemCatController {
 
@Autowired
private ItemCatService itemCatService;
 
@RequestMapping("/item/cat/list")
@ResponseBody
public List<ItemCat> list(@RequestParam(defaultValue="0")Long id){
System.out.println(id);
List<ItemCat> list=itemCatService.findByParentId(id);
System.out.println(list.size());
return list;
}
}

代码实现思路:

  1. 形参里有一个id值,当第一次点击选择类目时,这时候我们没有做任何的选中操作,所以传过来的id值是null,但是我们可以恰好利用这一点,把初始化默认值设为0(利用注解),因为我们最开始要展示的肯定是第一级商品分类,所以当第一次点击’选择类目‘时,我们可以这个id=0作为一级商品分类的parent_id来查询(因为第一级分类就是根,没有父分类)。
  2. 第一级分类树数据都查出来之后,当点击某一个一级商品分类时,此时,这个一级商品分类的id就传给后台了,我们可以拿这个一级商品的id作为二级商品的parent_id来查询,继而实现异步加载某一级商品分类树

商品新增保存+商品描述保存+@GeneratedValue


ItemController代码:

/*
 * 保存方法的前台对应的代码在 web-app/WEB-INF/views/item-add.jsp的第76-110行
 * 注意前台页面<input name的属性名和item对象的私有属性名一致即可
 * 此外,需要注意的是,商品描述属于大字段,一般单独建一张表来存储,数据库对应的表是:tb_item_desc
 */
@RequestMapping("/item/save")
@ResponseBody
public SysResult save(Item item,String desc){
return itemService.save(item,desc);
}
 

itemService代码:

public SysResult save(Item item, String desc) {
try {
item.setStatus(1);//设置默认状态
item.setCreated(new Date());
item.setUpdated(item.getCreated());
itemMapper.insert(item);
 
//因为item的id是自增的,虽然已经调用了insert方法,但是此时拿item的id值是拿不到的,
//所以,要在Item类里的id属性上,用一个特定的注解,让自增生成的id值回写回来,才能拿到
//这个特定的注解是:@GeneratedValue(strategy=GenerationType.IDENTITY)
ItemDesc itemDesc=new ItemDesc();
itemDesc.setItemId(item.getId());
itemDesc.setItemDesc(desc);
itemDesc.setCreated(item.getCreated());
itemDesc.setUpdated(item.getUpdated());
itemDescMapper.insert(itemDesc);
 
//新增操作成功或失败,要告诉前台,这里也要组织对应的json格式
//如果成功,则返回{"status":200,"msg":"OK","data":"null","ok":true}
//我们已经为Json格式写好了一个对象,SysResult 如果成功,直接调用SysResult.ok()方法即可
return SysResult.ok();
} catch (Exception e) {
return  SysResult.build(201, e.getMessage());
}

注意,这里我们返回的SysResult对象,因为前台的Ajax请求有一个状态码的校验,status==200,

{"status":200,"msg":"OK","data":null,"ok":true}

提示商品新增成功,所以这里我们也需要组织对应的json数据。我们用了一个SysResult对象做了封装,达到复用,以后只要是需要给前台返回Ajax的状态标识,都可以用这个对象来做。

通用类SysResult代码-略:

商品编辑


前台代码,当在item-list.jsp页面点击“编辑”按钮时触发的js代码:

{
        text:'编辑',
        iconCls:'icon-edit',
        handler:function(){
                var ids = getSelectionsIds();
                if(ids.length == 0){
                        $.messager.alert('提示','必须选择一个商品才能编辑!');
                        return ;
                }
                if(ids.indexOf(',') > 0){
                        $.messager.alert('提示','只能选择一个商品!');
                        return ;
                }
                
                $("#itemEditWindow").window({
                        onLoad :function(){
                                //回显数据
                                var data = $("#itemList").datagrid("getSelections")[0];
                                data.priceView = KindEditorUtil.formatPrice(data.price);
                                $("#itemeEditForm").form("load",data);
                                
                                // 加载商品描述
                                $.getJSON('/item/query/item/desc/'+data.id,function(_data){
                                        if(_data.status == 200){
                                                //UM.getEditor('itemeEditDescEditor').setContent(_data.data.itemDesc, false);
                                                itemEditEditor.html(_data.data.itemDesc);
                                        }
                                });
                                
                                //加载商品规格
                                $.getJSON('/item/param/item/query/'+data.id,function(_data){
                                        if(_data && _data.status == 200 && _data.data && _data.data.paramData){
                                                $("#itemeEditForm .params").show();
                                                $("#itemeEditForm [name=itemParams]").val(_data.data.paramData);
                                                $("#itemeEditForm [name=itemParamId]").val(_data.data.id);
                                                
                                                //回显商品规格
                                                 var paramData = JSON.parse(_data.data.paramData);
                                                
                                                 var html = "<ul>";
                                                 for(var i in paramData){
                                                         var pd = paramData[i];
                                                         html+="<li><table>";
                                                         html+="<tr><td colspan=\"2\" class=\"group\">"+pd.group+"</td></tr>";
                                                         
                                                         for(var j in pd.params){
                                                                 var ps = pd.params[j];
                                                                 html+="<tr><td class=\"param\"><span>"+ps.k+"</span>: </td><td><input autocomplete=\"off\" type=\"text\" value='"+ps.v+"'/></td></tr>";
                                                         }
                                                         
                                                         html+="</li></table>";
                                                 }
                                                 html+= "</ul>";
                                                 $("#itemeEditForm .params td").eq(1).html(html);
                                        }
                                });
                                
                                KindEditorUtil.init({
                                        "pics" : data.image,
                                        "cid" : data.cid,
                                        fun:function(node){
                                                KindEditorUtil.changeItemParam(node, "itemeEditForm");
                                        } 
                                });
                        }
                }).window("open");
        }
    }

前台代码:

$.post("/item/update",$("#itemeEditForm").serialize(), function(data){
if(data.status == 200){
$.messager.alert('提示','修改商品成功!','info',function(){
$("#itemEditWindow").window('close');
$("#itemList").datagrid("reload");
});
}
});

ItemController代码:

/*
 * 更新的前台代码在web-app/WEB-INF/views/item-edit.jsp 第67行到第111行
 */
@RequestMapping("/item/update")
@ResponseBody
public SysResult update(Item item,String desc){
return itemService.update(item,desc);
}
ItemService代码:
public void update(Item item)throws Exception {
item.setStatus(1);
item.setUpdated(new Date());
itemMapper.updateByPrimaryKeySelective(item);
 
}
 

商品删除


前台item-list.jsp中的代码:

{
        text:'删除',
        iconCls:'icon-cancel',
        handler:function(){
                var ids = getSelectionsIds();
                if(ids.length == 0){
                        $.messager.alert('提示','未选中商品!');
                        return ;
                }
                $.messager.confirm('确认','确定删除ID为 '+ids+' 的商品吗?',function(r){
                    if (r){
                            var params = {"ids":ids};
                        $.post("/item/delete",params, function(data){
                                    if(data.status == 200){
                                            $.messager.alert('提示','删除商品成功!',undefined,function(){
                                                    $("#itemList").datagrid("reload");
                                            });
                                    }
                            });
                    }
                });
        }

ItemController代码:

@RequestMapping("/item/delete")
@ResponseBody
public SysResult itemDelete(String[] ids){
itemService.delete(ids);
return SysResult.ok();
}
 
ItemService代码:
public void delete(String[] ids) {
itemMapper.deleteByIDS(ids);
 
}

懒加载思想


懒加载,在京淘项目的商品分类查询中,
并没有一次把全部数据查询出来,而是比如:当点击某一个一级商品分类时,才去查对应的二级商品分类,不点击,就不查。这就像一个比较懒的人,打他一下他才工作,不打就不工作。

懒加载的好处:
每次加载一小部分的数据,数据小,所以网络传输的时间短,响应速度快。这样能避免一次加载全部数据带来的问题,比如数据量过大,占用过多的内容,以及占用更大的带宽传输

懒加载的坏处:
懒加载会造成多次访问数据库

上一篇 41…大数据之旅——java分布式项目02

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值