V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
Sunyanzi
V2EX  ›  分享创造

[习作] SpringBoot + MyBatis 实现的预排序遍历树算法无限级分类 ...

  •  
  •   Sunyanzi · 2019-12-16 20:55:25 +08:00 · 4595 次点击
    这是一个创建于 1554 天前的主题,其中的信息可能已经有所发展或是发生改变。
    最近一阵子我都在自己写 Java 玩 ... 也没人带 ... 就面向 Google 编程一点一点摸索 ... 还挺乐在其中的 ...

    昨天写到一个无限级分类想用左右值法 ... 结果居然搜了一整天都没找到现成的轮子 ... 这就很奇怪 ...

    在 php 领域 Modified Preorder Tree Traversal Algorithm 的实现满街都是 ... Java 领域居然一个没有 ...

    我能找到的范围里大家都在用 parent_id 递归 ... 我不是很懂 ... 是 Java 有什么黑科技优化这种大量的递归查询吗 ..?

    也罢了 ... 既然找不到只好自己写 ... 反正也不难 ...

    刚才写完一寻思 ... 我这样一直闭门造车也不是个事儿 ... 只能停留在实现 ... 实现方式是对是错也没人能指导 ...

    新写的这个东西第一 CURD 都占了 ... 第二是我真的没找到类似的东西 ... 第三我对 Java 的方式和思想确实不是很熟 ...

    正好借这个契机放代码来求个 Code Review ... 主要是想知道以我这样的方式写一个 Java 的 Web 项目是否是正确的 ...

    我没写任何基础算法之外的东西 ... 比如 parent_id 或者 depth 的组合一类的 ... 就是一个纯粹的 MPTT 算法 ...

    [ github.com/qex/Springboot-MPTTA ] ... 代码量很小 ... 望占用您些许时间斧正一二 ...
    28 条回复    2019-12-17 15:00:15 +08:00
    huijiewei
        1
    huijiewei  
       2019-12-16 21:28:45 +08:00
    直接取出所有记录根据 parentId 生成树放缓存就是了

    https://github.com/huijiewei/agile-boot

    里面的 agile-core-shop ShopCategory 就是个树
    Sunyanzi
        2
    Sunyanzi  
    OP
       2019-12-16 23:59:34 +08:00
    @huijiewei 感谢回复 ... 我也读了一下代码 ...

    我大概理解的意思是 ... 因为在 Spring 里有 Cacheable 与 CacheEvict 的存在 ...

    所以 SQL 其实可以随便写 ... 优化算法并不是很重要 ... 因为只有第一次才连库之后就走缓存了 ...

    以及我顺带一问因为看您在用 JPA 操作数据库 ... 选择用 JPA 而不是别的方式的理由是 ..?
    gz911122
        3
    gz911122  
       2019-12-17 00:00:40 +08:00
    怎么一搜"无限级分类"出来的都是 php 的...
    gz911122
        4
    gz911122  
       2019-12-17 00:05:11 +08:00
    看了一下大致明白了,但是我这搬砖这几年从来没用到过这个啊?
    是在 php 上会很常用么?使用场景是什么啊?
    secondwtq
        5
    secondwtq  
       2019-12-17 00:23:35 +08:00
    这个中文名字里面的”预排序“是翻译夹的私货么
    inhzus
        6
    inhzus  
       2019-12-17 01:59:39 +08:00 via Android
    (都是英文省略号看得真心有些心累
    arYUWang
        7
    arYUWang  
       2019-12-17 02:41:49 +08:00
    Preorder Traversal 应该是前序遍历把。。
    Cbdy
        8
    Cbdy  
       2019-12-17 08:00:49 +08:00 via Android
    接口首字母 I,你以前是写 C#的吗?
    Sunyanzi
        9
    Sunyanzi  
    OP
       2019-12-17 08:53:52 +08:00
    @gz911122 我也很意外... Java 关于无限级分类的资料就很少 ... 我能找到的都是用 parent_id 的 ...

    以及我没明白「常用」是对什么而言的 ... 是无限级分类是否常用 ... 还是这个左右值实现是否常用 ..?

    @secondwtq @eugenewangfw 讲道理这个算法的中文名应该叫「改进的前序树遍历算法」 ...

    但我用 MPTT 作为关键字搜百度的时候 ... 得到的中文资料都翻译为「预排序遍历树」 ... 我就 ... 随大流了 ...
    Sunyanzi
        10
    Sunyanzi  
    OP
       2019-12-17 09:01:29 +08:00
    @Cbdy 我认真想了一下 ... 大概是个不知道什么时候养成的习惯 ... 也不知道是好是坏 ...

    Service 层我一般有两种写法 ... 一种是直接一个类搞定不区分接口和实现 ... 那样直接就叫 ooService ...

    另一种就是例子这样 ... 对外写接口 ... Impl 只对内 ... 这时候就区分 IooService 和 ooServiceImpl 了 ...

    我也不知道这样是不是对的 ... 还是说 Java 并不推荐在接口名字里标记当前对象是个接口 ..?
    huijiewei
        11
    huijiewei  
       2019-12-17 09:06:46 +08:00 via iPhone
    @Sunyanzi jpa 是 Spring 的默认解决方案
    nobt
        12
    nobt  
       2019-12-17 09:12:22 +08:00
    @Sunyanzi 其实大概是看你使用什么代理,比如默认使用接口+实现类模式的 CGLIB 代理,那这样的话就不必对接口名特殊标记了,比如 package.user.service 下面放的是接口 UserService 就是接口,package.user.service.impl 下面放的是实现类 UserServiceImpl

    如果使用的 JDK 动态代理就不采用接口+实现类了了,也就是不需要写接口,注入的是实现类,原理是注入了个子类吧
    gz911122
        13
    gz911122  
       2019-12-17 09:38:09 +08:00
    @Sunyanzi #9 无限级分类是否常用
    Cbdy
        14
    Cbdy  
       2019-12-17 09:45:14 +08:00
    @Sunyanzi 为什么不参考一下 JDK 是怎么写的呢?
    glaucus
        15
    glaucus  
       2019-12-17 09:48:58 +08:00
    @Sunyanzi #10 我以为薛之谦搞 IT 了
    Sunyanzi
        16
    Sunyanzi  
    OP
       2019-12-17 10:09:15 +08:00
    @huijiewei 那那 ... 那我有个关于 JPA 的问题 ... 因为我也是用了好久的 JPA 才接触的 MyBatis ...

    在使用 JPA 的时候 ... 是推荐能用 JPQL 完成的内容不使用 nativeQuery 还是反之 ..? 或者只是随个人喜好 ..?

    以及 ... 在 JPA 里面所有 Modifying 操作都要标 Transactional ... 不管用不用得到事务都需要标 ...

    想要优化掉这个在我知道的范围里就只能引 EntityManager ... 这样是正确的做法吗 ..?

    @nobt 以我现在的水平 ... 还完全读不懂这段话 ... 我唯一用过 CGLIB 的地方就是个 Bean Map 转换 ...

    以及代理的话 ... 我知道 Spring 的代理对象 ... 但也仅限于知道而已 ... 我如何能知道我用的是 ... 什么代理 ..?
    Sunyanzi
        17
    Sunyanzi  
    OP
       2019-12-17 10:19:01 +08:00
    @gz911122 无限级分类超常用 ... 基本上需求里涉及「要个分类」就等同于「要做个无限级分类」 ...

    如果之前从没用到过这个的话 ... 那比如有需求说做个文章管理 ... 要能够添加总分类和子分类 ...

    就类似于目录的感觉 ... 层级递进可以很深 ... 这样的需求你会如何做 ..?
    huijiewei
        18
    huijiewei  
       2019-12-17 10:39:16 +08:00
    @Sunyanzi 尽量少手写 SQL 语句就是了。你看我的项目里面,基本除了批量操作,都没有手写 SQL 语句的
    gz911122
        19
    gz911122  
       2019-12-17 11:28:07 +08:00
    @Sunyanzi #17 了解了,没做过这种层级很深的分类,做的都是层级固定的... :P
    不过感觉这种用 mysql 这种存储怪怪的... 无限追溯 pid 效率感觉不怎么好啊.

    不过工作这 4,5 年还真没做过这种无限极分类的需求,挺有意思的
    nobt
        20
    nobt  
       2019-12-17 11:38:08 +08:00
    @Sunyanzi
    你先定义一个接口,然后用实现类实现具体逻辑
    public class CategoryServiceImpl implements ICategoryService

    随后在 Controller 中这样写:
    @Autowired
    ICategoryService cSvc;

    注入的是接口,而不是实现类 CategoryServiceImpl,故使用的是 CGLIB 代理
    Sunyanzi
        21
    Sunyanzi  
    OP
       2019-12-17 11:43:34 +08:00
    @nobt 原来如此 ... 原来这就叫 CGLIB 代理 ... 学到了 ...

    也就是如果我换个写法 ... 用 Autowired 直接注 CategoryServiceImpl var; 这样 ... 就是 JDK 动态代理了 ..?

    那么这两种代理在实际应用中 ... 我应该根据什么来选择使用哪种呢 ... 总不能是根据自己想不想懒吧 ..?
    Takamine
        22
    Takamine  
       2019-12-17 11:58:31 +08:00 via Android
    @gz911122 电商多级分类,基本上电商网站点开首页都是。
    Takamine
        23
    Takamine  
       2019-12-17 12:09:13 +08:00 via Android
    @nobt ……你是认真的吗。
    默认是使用 JDK 对接口进行代理的吧。
    Takamine
        24
    Takamine  
       2019-12-17 12:22:01 +08:00 via Android
    @nobt JDK 代理接口是继承了 Proxy 类,Cglib 代理才是字节码增强生成子类,我觉得你是记反了。:doge:
    Takamine
        25
    Takamine  
       2019-12-17 12:28:38 +08:00 via Android
    看到楼主私有变量前面的_,猜下之前应该写过 php 或者 python。:doge:
    fangzy
        26
    fangzy  
       2019-12-17 12:30:21 +08:00 via Android   ❤️ 1
    无限分级,我习惯用 path,配合 root 和 parent,查询方便,便于缓存
    nobt
        27
    nobt  
       2019-12-17 14:53:12 +08:00
    @Takamine 言之有理,手动狗头


    @Sunyanzi 不可随意,内含玄机,随意用它,还需深入
    nobt
        28
    nobt  
       2019-12-17 15:00:15 +08:00
    @Takamine
    @Sunyanzi

    对不起,正式纠正一下描述错误。

    0.如果目标类有实现的接口就使用动态代理
    1.如果目标类没有实现的接口就使用 cglib 代理
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4309 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 05:27 · PVG 13:27 · LAX 22:27 · JFK 01:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.