当前位置: 首页 > news >正文

网站建设中间件收费工作简历模板电子版免费

网站建设中间件收费,工作简历模板电子版免费,郑州企业服务公司,做网站租服务器作者简介#xff1a;大家好#xff0c;我是smart哥#xff0c;前中兴通讯、美团架构师#xff0c;现某互联网公司CTO 联系qq#xff1a;184480602#xff0c;加我进群#xff0c;大家一起学习#xff0c;一起进步#xff0c;一起对抗互联网寒冬 前置知识#xff1a;前…作者简介大家好我是smart哥前中兴通讯、美团架构师现某互联网公司CTO 联系qq184480602加我进群大家一起学习一起进步一起对抗互联网寒冬 前置知识前后端数据传输格式(下) 新手程序员尤其是非科班的朋友往往都非常重视具体技术点的学习比如多线程、JWT、Redis、甚至Spring源码但却经常性地忽略数据库表的设计。 为什么会造成这种情况呢 第一大部分新手程序员参与到一个项目时表都已经建好了他们的工作就是在现有工程下做做CRUD。 第二不管培训班还是网络上的学习资料很少会专门去讲怎么设计数据库、为什么要这么设计。 所以绝大部分非科班程序员都不具备数据库设计能力。这会带来哪些坏处呢我个人最大的体会是去甲方开会时对于甲方提出的各种需求你基本是懵逼的完全无法和实际编程联系在一起 啊这个需求怎么设计表结构啊听起来好难啊... 我自己的看法是数据库设计是整个项目最关键、最难的地方数据库设计的好坏直接影响编码的质量和效率。 怎么提升自己的数据库设计能力呢我有两个建议 多观察公司现有项目的某些需求设计研究它的表结构和编码然后抽取出几种常见的设计思路自己平时多想、多做、多借鉴自己给自己出需求然后考虑如何设计、SQL和代码怎么写卡住了就百度借鉴别人的设计思路 我始终觉得数据库设计其实是存在若干种模式的而每一种模式只要稍微变通一下又可以用来完成很多看似完全不同的需求。限于篇幅这里优先介绍树形结构。 树形结构是一种非常经典的表设计模型看似平凡却又包罗万象在实际开发中有着非常广泛的应用。 最典型的树形结构就是商品分类 |-家用电器         |-电视         |-空调         |-洗衣机                 |-滚筒洗衣机                 |-洗烘一体机                 |-...         |-手机/运营商/数码         |-手机通讯         |-运营商         |-手机配件                 |-手机壳                 |-贴膜                 |-... 还有省-市-区级联 |-浙江省         |-杭州市         |-宁波市         |-温州市         |-... |-安徽省         |-合肥市         |-黄山市         |-芜湖市         |-... 其他的还包括目录、部门、甚至RBAC权限控制等随处可见树形结构的设计。 严格来说真正的树应该只有一个root节点而上面提到的都有多个root节点但我还是习惯称为“树形结构”。 设计阶段 如何定义JavaBean 我以前接触过一个前端插件叫zTree。这个插件的作用是把接口返回的数据以树形结构的形式展示出来 zTree插件需要后端返回特定的数据格式才能做出上面的效果官方文档给的demo如下 !--1.准备一个Div设置id-- div classpanel-bodyul idpermissionTree classztree/ul /div// 2.需要的数据 var zNodes [{ name:父节点1 - 展开, open:true,children: [{ name:父节点11 - 折叠,children: [{ name:叶子节点111},{ name:叶子节点112},{ name:叶子节点113},{ name:叶子节点114}]},{ name:父节点12 - 折叠,children: [{ name:叶子节点121},{ name:叶子节点122},{ name:叶子节点123},{ name:叶子节点124}]},{ name:父节点13 - 没有子节点, isParent:true}]},{name:父节点2 - 折叠, children: [{...},{...}]},{ name:父节点3 - 没有子节点, isParent:true} ];// 3.把数据在指定div渲染成一棵树 $.fn.zTree.init($(#permissionTree), setting, zNodes); 有些字段省略不用在意关注外部结构即可 现在的问题是后端如何返回上面这样的数据结构 我们由外到内分两步分析zNodes需要的数据格式 先观察zNodes最外层 var zNodes [{ name:父节点1, open:true,children: [...]},{ name:父节点2,children: [...]},{ name:父节点3 - 没有子节点, isParent:true} ]; 我们发现zNode其实是一个JS对象数组现有三个对象父节点1父节点2父节点3。 等等这里有个重点 一起放慢节奏思考一下 现在我们已经明确zNodes是一个JS对象数组那么Java中什么类型的数据返回到前端会变成一个数组呢 答案是常用List 来做个试验 RestController public class TestController {RequestMapping(/testList)public List getList() {ArrayListUser userList new ArrayList();userList.add(new User(李健, 18));userList.add(new User(周杰伦, 20));userList.add(new User(李雪健, 30));return userList;}RequestMapping(/testMap)public Map getMap() {HashMapString, User hashMap new HashMap();hashMap.put(1号男嘉宾, new User(卡卡罗特, 21));hashMap.put(2号男嘉宾, new User(贝吉塔, 22));hashMap.put(3号男嘉宾, new User(雅木茶, 23));return hashMap;}RequestMapping(/testSet)public Set getSet() {HashSetUser hashSet new HashSet();hashSet.add(new User(刘备, 33));hashSet.add(new User(关羽, 32));hashSet.add(new User(张飞, 31));return hashSet;} } Postman测试以List为例 全部测完对比结果 后端返回的其实都是JSON但是本文的视角是前端所以把JSON和JS对象一同看待 上面只例举了Map、List、Set其实数组的返回结果和List、Set一样。但通常不用数组因为数组可用的方法太少不利于后端通过数组对数据进行操作。 我们发现Map到了前端对应一个JS对象而Set和List都是对应一个JS数组。实际开发中前端如果需要一个数组我们通常返回List即可Set的情况较少除非要去重。 至此关于如何设计JavaBean我们确定了一个大方向最外层用List返回。 接下来我们往zNodes的内层走走分析一下后端的List应该存什么对象也就是如何设计JavaBean。 var zNodes [{ name:父节点1 - 展开11111, open:true,children: [{ name:父节点11 - 折叠,children: [...]},{ name:父节点12 - 折叠,children: [...]},{ name:父节点13 - 没有子节点, isParent:true}]},{ name:父节点2 - 折叠,children: [...]},{ name:父节点3 - 没有子节点, isParent:true} ]; 我们发现父节点1的children其实也是一个对象数组而且内部JS对象的结构和最外面三个父节点JS对象一致。很多新手看到这样的数据老觉得这里是递归无穷无尽后端干脆没法设计... 那么前端是否是无限递归呢 我们退回来慢慢推导。 假设后端List中存的对象叫JsBean那么JsBean最终传到前端就变成了zNodes对象数组中的JS对象。 List [JsBean, JsBean, JsBean...] zNodes[{...}, {...}, {...}] 这个应该能理解吧后端List对应前端zNodes的[]JsBean对应zNodes数组内部的JS对象。 后端JsBean public class JsBean{private String name;private Boolean open;private Boolean isParent;// 前端JS对象的children数组(对应List)里存的还是JS对象对应JsBean所以是ListJsBeanprivate ListJsBean children; } 前端JS对象请和上面JsBean对照看看这样设计是否合理 { name:父节点1 - 展开11111, open:true,children: [{ name:父节点11 - 折叠,children: [...]},{ name:父节点12 - 折叠,children: [...]},{ name:父节点13 - 没有子节点, isParent:true}] } 先不考虑递归啥的就暂时当数据只有三级好了~ 那么后端这样设计Bean表示到底对不对呢做个试验吧 RestController public class TestController {RequestMapping(testList)public List getList() {// 定义一个List用来存储最终结果ArrayListJsBean superMen new ArrayList();//------------------------七龙珠从下往上看会好理解些开枝散叶------------------------// 孙悟饭的儿子们悟饭儿子1、悟饭儿子2ListJsBean grandChildren1 new ArrayList();grandChildren1.add(new JsBean(悟饭儿子1, false, false, null));grandChildren1.add(new JsBean(悟饭儿子2, false, false, null));// 孙悟天的儿子们悟天儿子1、悟天儿子2ListJsBean grandChildren2 new ArrayList();grandChildren2.add(new JsBean(悟天儿子1, false, false, null));grandChildren2.add(new JsBean(悟天儿子2, false, false, null));// 孙悟空的儿子悟饭、悟天ListJsBean children new ArrayList();children.add(new JsBean(悟饭, false, true, grandChildren1));children.add(new JsBean(悟天, false, true, grandChildren2));// 孙悟空本人JsBean wukong new JsBean(悟空, true, true, children);//------------------------火影忍者从下往上看会好理解些开枝散叶------------------------// 鸣人的徒弟们博人1、博人2ListJsBean grandChildren3 new ArrayList();grandChildren3.add(new JsBean(博人1, false, false, null));grandChildren3.add(new JsBean(博人2, false, false, null));// 佐助的徒弟们佐良娜1、佐良娜2ListJsBean grandChildren4 new ArrayList();grandChildren4.add(new JsBean(佐良娜1, false, false, null));grandChildren4.add(new JsBean(佐良娜2, false, false, null));// 卡卡西的徒弟们鸣人、佐助ListJsBean children2 new ArrayList();children2.add(new JsBean(鸣人, false, true, grandChildren3));children2.add(new JsBean(佐助, false, true, grandChildren4));// 卡卡西本人JsBean kakaxi new JsBean(卡卡西, true, true, children2);//------------------------处理结果------------------------// 只把孙悟空和卡卡西加入ListsuperMen.add(wukong);superMen.add(kakaxi);return superMen;} }Postman得到结果 [{name: 悟空,open: true,isParent: true,children: [{name: 悟饭,open: false,isParent: true,children: [{name: 悟饭儿子1,open: false,isParent: false,children: null},{name: 悟饭儿子2,open: false,isParent: false,children: null}]},{name: 悟天,open: false,isParent: true,children: [{name: 悟天儿子1,open: false,isParent: false,children: null},{name: 悟天儿子2,open: false,isParent: false,children: null}]}]},{name: 卡卡西,open: true,isParent: true,children: [{name: 鸣人,open: false,isParent: true,children: [{name: 博人1,open: false,isParent: false,children: null},{name: 博人2,open: false,isParent: false,children: null}]},{name: 佐助,open: false,isParent: true,children: [{name: 佐良娜1,open: false,isParent: false,children: null},{name: 佐良娜2,open: false,isParent: false,children: null}]}]} ] 后端返回一个List所以前端整体看是一个JS数组。数组最外层是两个JS对象 展开 是不是和 zNodes一模一样 也就是说后端这样设计JavaBean完全没问题。前端JS对象数组并不是所谓的无限递归它的层级取决于数据库表中数据设计了多少层。所以在树形结构中不要去担心前端会不会出现无限递归而要问后端要设置多少级数据从后往前设计由后端来控制整个树形结构的深度。 就好比刚才的程序中我让悟天孩子不再有孩子于是返回前端后悟天儿子1就没有children了 也就是说前端是跟着后端走的不要尝试从前端逆推那样你会觉得似乎是无限递归无从下手。 至此JavaBean设计完毕后端只要返回Superman的List集合就能刚好满足zNodes的数据格式 public class Superman{private String name;private Boolean open;private Boolean isParent;// 前端JS对象的children数组(对应List)里存的还是JS对象对应Superman所以是ListSupermanprivate ListSuperman children; } 如何设计表 接下来我们考虑下数据库表如何设计刚才只是验证了Bean的设计数据是我们在代码里造的。 对于树形结构我们最直观的想法自然是为每一级都创建一个对应的表。又由于父可以有多个子具备一对多的关系。遇到一对多我首先最直观的想法是使用外键(pid)关联。 分析龙珠里的人物关系我发现数据最深层级是3级。 如果每一级都创建一张表共需设计三张表。比如一级的我都存入t_first二级的我都存入t_second...然后用外键关联每张表。 t_first id name open isParent 1 孙悟空 0 1 2 贝吉塔 0 1 t_secondpid指向t_first表明他们的父亲 id name open isParent pid 1 孙悟饭 0 1 1 2 孙悟天 0 0 1 3 特兰克斯 0 0 2 t_thirdpid指向t_second表明她的父亲 id name open isParent pid 1 小芳 0 0 1 先不说表字段是否合理这个表本身就不合理...万一小芳再生小孩呢层级加深我需要再建一张表。另外遇到某些业务场景下数据层级很深的情况那我要建多少张表 我们能不能把这些表合并用一张表表示复杂的父子层级关系 很容易发现这三张表字段都是基本相同合并起来还是比较容易的。我们采用并集。 合并的第一步是让三张表具有相同的字段其实就是给t_first补充pid字段 t_first id name open isParent pid 1 孙悟空 0 1 ? 2 贝吉塔 0 1 ? t_secondpid指向t_first表明他们的父亲 id name open isParent pid 1 孙悟饭 0 1 1 2 孙悟天 0 0 1 3 特兰克斯 0 0 2 t_thirdpid指向t_second表明她的父亲 id name open isParent pid 1 小芳 0 0 1 万事开头难我们发现t_first加了pid字段后竟不知该填什么数据用?占位...问题出在哪因为在我们目前这个设定中孙悟空和贝吉塔是第1级他们没有父亲。那就干脆写个0好了。以后看到pid0的就知道这是第1级。 嗯等等。如果用pid表示父亲的idpid0代表第1级那么第2级的pid指向第1级的id不就形成了一对多的关系了吗 所以合并第二步就是把三张表压缩成一张 id name isParent open pid 1 孙悟空 1 0 0 2 贝吉塔 1 0 0 3 孙悟饭 1 0 1 4 孙悟天 0 0 1 5 特兰克斯 0 0 2 6 小芳 0 0 3 这样一来原先三张表通过外键关联现在变成了一张表自关联pid关联id。 接着我们尝试一下能否根据这张表查出具有层级关系的数据 先找第1级pid0得到孙悟空(id1)、贝吉塔(id2)再找第2级 pid1的都是孙悟空的孩子 孙悟饭(id3)孙悟天(id4) pid2的都是贝吉塔的孩子 特兰克斯(id5) 最后找第3级 pid3的都是孙悟饭的孩子 小芳(id6) pid4的都是孙悟天的孩子无pid5的都是特兰克斯的孩子无 尝试查找第4级 pid6的都是小芳的孩子无 嗯完美都查出来了但我们发现查找过程中根本没用到nameisParentopen。 对于一颗树其实只需要id和pid相当于树干脉络其他的都是点缀。 name前端展示时显示当前节点的名称 isParent当前节点下面有没有孩子 open前端根据它确定是否要展开显示 为了给上面的字段分类我新创了两个名词 结构字段pid、id业务字段name、isParent、open pid和id属于结构字段它俩是维持树形结构的必须字段是根基而其他3个字段属于业务字段用于展示数据或作为前端的判断标识可以根据业务需求进行增添。 其中isParent蛮有意思的这里单独说一下。比如我在数据库中随便指着一个节点你能否马上告诉我他下面有没有孩子(之所以有这个需求是因为前端有可能需要把有孩子的节点渲染成文件夹样式 答案是不能。不要想pid它只是告诉你当前节点的parent是谁而不是当前节点有没有子节点。如果没有isParent字段你必须去数据库查有没有以这个节点的id为pid的数据这显然很麻烦。所以我们可以在插入或更新时去维护这个字段一般这种分类很少会改动查询居多。 那么最终我们得到一个可以表示树形层级结构的表 t_superman id name isParent open pid 1 孙悟空 1 0 0 2 贝吉塔 1 0 0 3 孙悟饭 1 0 1 4 孙悟天 0 0 1 5 特兰克斯 0 0 2 6 小芳 0 0 3 一句话总结 对于多层级的表来说最重要的id和pid它们决定了数据的层级脉络其他的属于附属内容。比如该节点是不是父节点isParent如果是前端渲染时是否展开open。这些对于数据结构本身是无关紧要的。 但是反过来从业务内容来讲pid是其次的name等附属内容才是最重要的它们才是数据主体。 读取表中数据 至此我们完成了JavaBean和表的设计接下来我们学习一下如何查询数据并返回树形结构的数据。一般我们参与开发时主要做的就是这一步。从这个角度来看上面的设计固然重要但学会如何编码才是当务之急。 这里介绍三种方法 递归读取for循环读取map集合读取 本节介绍的三种方法都是全量嵌套查询而一般情况下根据pid分步查询更为妥当既简单又不容易出错。希望大家学了这篇文章后不要无脑使用全量嵌套。为了炫技而炫技可能会被打。 环境准备 SQL文件 /*Navicat Premium Data TransferSource Server : learnforfunSource Server Type : MySQLSource Server Version : 50719Source Host : localhost:3306Source Schema : testTarget Server Type : MySQLTarget Server Version : 50719File Encoding : 65001Date: 31/12/2019 10:56:18 */SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for t_superman -- ---------------------------- DROP TABLE IF EXISTS t_superman; CREATE TABLE t_superman (id int(11) NOT NULL AUTO_INCREMENT,name varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,open tinyint(2) DEFAULT NULL,pid int(11) NOT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT7 DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin;-- ---------------------------- -- Records of t_superman -- ---------------------------- BEGIN; INSERT INTO t_superman VALUES (1, 孙悟空, 0, 0); INSERT INTO t_superman VALUES (2, 贝吉塔, 0, 0); INSERT INTO t_superman VALUES (3, 孙悟饭, 0, 1); INSERT INTO t_superman VALUES (4, 孙悟天, 0, 1); INSERT INTO t_superman VALUES (5, 特兰克斯, 0, 2); INSERT INTO t_superman VALUES (6, 小芳, 0, 3); COMMIT;SET FOREIGN_KEY_CHECKS 1; Pom依赖 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdtk.mybatis/groupIdartifactIdmapper-spring-boot-starter/artifactIdversion2.1.5/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency /dependencies 启动类 SpringBootApplication MapperScan(com.bravo.tree.mapper) public class SpringbootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootDemoApplication.class, args);} }Pojo Data AllArgsConstructor NoArgsConstructor Table(name t_superman) public class Superman {IdGeneratedValue(strategy GenerationType.IDENTITY)private Integer id;private String name;private String isParent;private Boolean open;private Integer pid;// 前端JS对象的children数组(对应List)里存的还是JS对象对应Superman所以是ListSupermanTransientprivate ListSuperman children new ArrayList(); } Controller RestController public class SupermanController {Autowiredprivate SupermanService supermanService;RequestMapping(/getAllSuperman)public List getAllSuperman() {// supermanService才是重点接下来演示三种全量嵌套查询的方法ListSuperman list supermanService.getAllSuperman();return list;} } 递归读取 我个人不是很擅长递归不知道其他人是否一样。下面3个方法其实可以合并但是为了降低理解难度我还是拆开 Service public class SupermanService {Autowiredprivate SupermanMapper supermanMapper;/*** 查询所有赛亚人* return*/public ListSuperman getAllSuperman() {// 查出所有的根节点孙悟空、贝吉塔ListSuperman rootSupermanList getRootSuperman();// 分别查出孙悟空和贝吉塔的孩子并设置for (Superman superman : rootSupermanList) { ListSuperman children queryChildrenByParent(superman);superman.setChildren(children);}// 返回return rootSupermanList;}/*** 查出根节点pid0* return*/private ListSuperman getRootSuperman() {Superman superman new Superman();superman.setPid(0);return supermanMapper.select(superman);}/*** 递归查询孩子* param parent* return*/private ListSuperman queryChildrenByParent(Superman parent) {// 准备查询条件querySuperman query new Superman();query.setPid(parent.getId());// 查出孩子ListSuperman children supermanMapper.select(query);// 查出每个孩子的孩子并设置for (Superman child : children) {ListSuperman grandChildren queryChildrenByParent(child);// 递归child.setChildren(grandChildren);}return children;} }特别注意递归的结束条件否则容易出现死递归造成内存溢出。在当前案例中叶子节点是没有child的所以不会再调用查询方法也就走出递归了。 优点直观并没有觉得...递归对我而言很难 缺点在我们程序中每次递归调用都会查询一次数据库效率非常低 之前提到过假设查询数据库总耗时10s那么光是连接数据库就要花3s也就是说频繁调用数据库必然会降低性能。 上面递归方式不是重点所以这里就不继续深入只给出优化的方向 造一个根节点简化递归根节点不止一个所以上面代码只能配合for每个根节点来一遍递归很冗余先查出全部数据再递归而不是每递归一层就查询一次数据库效率很低 for循环读取 吸取上面递归查询的教训我们做两点改良 先全量查询数据再考虑如何组装树形结构用for循环代替递归 /*** 查询所有赛亚人* return*/ public ListSuperman getAllSuperman() {// 用来存储最终的结果ListSuperman list new ArrayList();// 先查出全部数据ListSuperman data supermanMapper.selectAll();// 双层for循环完成数据组装for (Superman left : data) {for (Superman right : data) {// 如果右边是左边的孩子就设置进去if(left.getId() right.getPid()){left.getChildren().add(right);}}// 只把第1级加到listif(left.getPid() 0){list.add(left);}}return list; } 示意图 你可能会觉得不对啊左边孙悟空全部喊一遍后设置了孙悟饭、孙悟天。但是此时悟饭没有设置小芳啊 别忘了孙悟空设置的其实是孙悟饭的引用。等左边悟饭全部喊一遍把小芳加进来时悟空里的悟饭不就有小芳了吗 关于这点需要的话可以停下来想想是不是这么回事。 优点只查询一次数据库而且很直观个人觉得比递归直观) 缺点双层for效率仍然很低如果list长度为n那么要循环n2次。也就是说如果现在这个List表示的是全国的高校学校-院系-专业截止至2019年6月15日教育部公布的全国高等学校共计有2956所那么需要循环29562次差不多是30002900w次... map集合读取 其实看到上面使用双层for相信一部分同学已经能想到《实用小算法》了~没错我们当时说《实用小算法》就是为了解决“数据匹配问题”而生的 /*** 查询所有赛亚人* return*/ public ListSuperman getAllSuperman() {// 用来存储最终的结果ListSuperman list new ArrayList();// 源数据需要处理ListSuperman data supermanMapper.selectAll();// Map用来转存ListHashMapInteger, Superman hashMap new HashMap();// 先把List转为Map把Map作为左侧parentfor (Superman superman : data) {hashMap.put(superman.getId(), superman);}// 右边child进行for循环找parentfor (Superman child : data) {if(child.getPid() 0){list.add(child);// 找到第一级悟空、贝吉塔} else {// 不是第一级那么肯定有parent帮它找到parent并把它自己设置到parent里Superman parent hashMap.get(child.getPid());// hash索引找爸爸很快parent.getChildren().add(child);}}return list; } 示意图 优点查询效率提高了 缺点空间利用率降低了List/Array可以只存元素而Map需要额外存储key 本次循环次数是2n双层for循环是n2 非要说的话就是典型的“空间换时间”。 最终代码 Controller public class SupermanController {Autowiredprivate SupermanService supermanService;/*** 全量嵌套查询数据具备树形结构* return*/RequestMapping(/getAllSuperman)public List getAllSuperman() {ListSuperman list supermanService.getAllSuperman();return list;}/*** 根据pid分步查询实际开发最常用* param pid* return*/RequestMapping(getSupermanByPid)public List getSupermanByPid(Integer pid) {ListSuperman list supermanService.getSupermanByPid(pid);return list;} } Service Service public class SupermanService {Autowiredprivate SupermanMapper supermanMapper;/*** 查询所有赛亚人* return*/public ListSuperman getAllSuperman() {// 用来存储最终的结果ListSuperman list new ArrayList();// 源数据需要处理ListSuperman data supermanMapper.selectAll();// Map转ListHashMapInteger, Superman hashMap new HashMap();for (Superman superman : data) {hashMap.put(superman.getId(), superman);}// 遍历源数据for (Superman child : data) {if(child.getPid() 0){list.add(child);// 找到根节点并存储} else {// 如果不是根节点则找到上一级并把自己设置为上一级的子节点Superman parent hashMap.get(child.getPid());parent.getChildren().add(child);}}return list;}/*** 查询pid对应的下级内容* param pid* return*/public ListSuperman getSupermanByPid(Integer pid) {Superman query new Superman();query.setPid(pid);return supermanMapper.select(query);} }延伸思考 写到最后你会突然发现其实不管业务场景是下面哪种 省-市省-市-区高校-院系-专业-班级商品分类... 其实都是一样的上面的模式其实就分为两部分 表设计层面代码层面 首先表结构设计都是一样的都是树形结构变化的只是业务字段。 结构字段pid、id业务字段city_name/school_name/category_name 其次代码层面也不会因为前端是二级联查、三级联查还是N级联查而改变双层for可以应付N级联查代码都不需要改动。 到底前端是几级和表结构无关而和表数据有关。 二级省-市 id name pid 1 浙江省 0 2 安徽省 0 3 温州市 1 4 杭州市 1 5 合肥市 2 6 芜湖市 2 三级省-市-县 id name pid 1 浙江省 0 2 安徽省 0 3 温州市 1 4 杭州市 1 5 合肥市 2 6 芜湖市 2 7 苍南县 3 8 平阳县 3 四级省-市-县-镇 id name pid 1 浙江省 0 2 安徽省 0 3 温州市 1 4 杭州市 1 5 合肥市 2 6 芜湖市 2 7 苍南县 3 8 平阳县 3 9 钱库镇 7 10 金乡镇 7 四级学校-院系-专业-班级 id name pid 1 浙江大学 0 2 浙江工业大学 0 3 外国语学院 1 4 计算机学院 1 5 人文学院 2 6 土木工程 2 7 日语 3 8 英语 3 9 日语111班 7 10 日语112班 7 一模一样的表结构但是前端展示出来的分别是二级、三级、四级也就是说结构字段决定树形结构而业务数据决定树的深度。 作者简介大家好我是smart哥前中兴通讯、美团架构师现某互联网公司CTO 进群大家一起学习一起进步一起对抗互联网寒冬
http://www.w-s-a.com/news/974106/

相关文章:

  • 蒙古网站后缀mysql8.0 wordpress
  • 免费建立一个网站互联网推广培训
  • WordPress多站点绑定域名深圳住房建设部官方网站
  • 网站建设公司zgkr上海网页网络技术有限公司
  • wordpress附件扩展格式徐州seo关键词
  • wordpress博客站模板织梦网站 联系方式修改
  • 北京城乡建设厅网站重庆网站建设解决方案
  • 网站建设和维护工作内容网站的空间与域名
  • 济南做门户网站开发公司网页发布的步骤
  • 江苏省交通厅门户网站建设管理办法做的网站怎么让百度收录
  • 关于怎么做网站网站site的收录数量要多远索引量
  • 传世网站建设阳光创信-网站建设首选品牌
  • 周口建设网站中国装修公司十大排名
  • wordpress自助发卡青浦网站优化
  • 南京建设银行公积金查询网站wordpress加载插件下载
  • 做网站怎么那么难网站的建设与管理的心得体会
  • 黄冈网站建设哪家快些网站规划与建设评分标准
  • 建站平台 绑定域名怎么在手机上做网站
  • 做电影网站违法吗莱芜 网站
  • 品牌咨询公司泉州seo不到首页不扣费
  • 做网站做一个什么主题的怎样搭建一个企业网站
  • 做设计的有什么网站桂林论坛网站有哪些
  • 做的网站不能放视频开发公司春联
  • 重庆装修房子可以提取公积金吗长沙优化官网公司
  • 做外贸的网站都有哪些带后台的html网站源码
  • 厦门百度快速优化排名手机系统优化工具
  • 宁波网站制作公司推荐公司建站多少钱
  • 网络营销薪酬公司温州网站优化定制
  • 橙色在网站中的应用淘宝客绑定网站备案号
  • 杭州视频网站建设成都设计院排行