多级菜单查询实践-Mybatis和JPA

在9月份面试时,面试官在一张草稿纸上出了一道省市县联动的题目,并提示我可以使用递归或循环,将数据查询出来封装到对象中。当时我用了循环的方式将其封装,虽说勉强实现,但是代码冗余度高,逻辑性特别差。

碰巧最近项目中业务需要,也是将多级菜单封装到根菜单返回给前端,发现还是会把自己弄得头晕,因此打算认真写一下,当做笔记,也对之前Hibernate,Mybatis框架的复习。下面将会使用Mybatis 和 JPA 分别实现菜单封装业务逻辑,初步规划只要将数据以JSON格式写回前端即是测试通过。

Demo1 「使用Mybatis实现多层菜单数据查询」

数据库表一览

menuidmenunameparentid
0 系统菜单  
100 二级菜单A 0
101 三级菜单A 100
102 三级菜单A 100
200 二级菜单B 0
201 三级菜单B 200
202 三级菜单B 200

根据数据库表编写菜单实体类

public class Menu {
  private String menuid; //主键ID
  private String name; //菜单名称
  private String parentid; //上一级菜单
  private List<Menu> menus; //子菜单列表
  //get and set method...
}

 

针对Mybatis , 编写对应Mapper接口

public interface MenuMapper {
    public Menu getRootMenu();        //返回根菜单
    public List<Menu> findMenuByParentId(String parentid);//根据父一级菜单,返回所有子菜单
}

编写Mapper.xml , 这里是编码重点 , 此时 ,需要注意的点是,我们将要封装的数据是一个根菜单Menu对象,难点在于给子菜单menus赋值,因为数据库和实体类中的menus并没有任何关系,所以这里需要使用ResultMap形式将数据封装。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xingc.example.mapper.MenuMapper">
    <resultMap id="menuMap" type="com.xingc.example.pojo.Menu">
        <id column="menuid" property="menuid"/>
        <result column="name" property="name"/>
        <result column="parentid" property="parentid"/>
        <collection property="menus" ofType="com.xingc.example.pojo.Menu" column="menuid" select="findMenuByParentId"/>
    </resultMap>
    <select id="getRootMenu" resultMap="menuMap">
        SELECT * FROM menu WHERE parentid ='' or parentid is NULL
    </select>
    <select id="findMenuByParentId" parameterType="java.lang.String" resultMap="menuMap">
        SELECT * from menu where parentid = #{value}
    </select>
</mapper>

 

注:

​ 这里重点就是ResultMap的配置,普通字段,使用id和property即可实现实体类和数据库表字段映射。比较特殊的是menus这个字段,需要配置一对多关系,由于多的一方数据类型仍是Menu,因此ofType就是Menu类型,另外需要添加配置项 select , 将findMenuByParentId返回的值指向menus字段。

以下就是测试结果,重点关注 menuid , 第二级菜单都 100 , 200 ,300 ….第三级菜单都是 101,102,201,202,203….以此类推

{
    "menuid": "0", 
    "name": "系统菜单", 
    "parentid": "", 
    "menus": [
        {
            "menuid": "100", 
            "name": "二级菜单1", 
            "parentid": "0", 
            "menus": [
                {
                    "menuid": "101", 
                    "name": "三级菜单1", 
                    "parentid": "100", 
                    "menus": [ ]
                }, 
                {
                    "menuid": "102", 
                    "name": "三级菜单3", 
                    "parentid": "100", 
                    "menus": [ ]
                }
            ]
        }, 
        {
            "menuid": "200", 
            "name": "二级菜单2", 
            "parentid": "0", 
            "menus": [
                {
                    "menuid": "201", 
                    "name": "三级菜单2", 
                    "parentid": "200", 
                    "menus": [ ]
                }
            ]
        }
    ]
}

 

Demo2 「使用Hibernate或JPA实现多层菜单数据查询」

这个demo有两个主要看点:

1.上文中的Menu使用JPA再次实现。
2.JPA的一些解释。

第一步,jar包环境 。注,这里使用的是Springboot整合的方式,引入JPA的相关类库。

<!--JPA类库-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--数据库-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>
<!--其他必须的jar包也需要导入,但这里只是实现其基本功能,
因此重点不环境这里,斟酌配置。-->

 

实体类(使用注解形式)

@Entity
public class Menu {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private String menuid;        //菜单主键,GenerationType.IDENTITY即是主键生成模式,自增长。
    private String menuname;    //
    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name = "parentid")    //根据父级菜单ID,实现自关联(内部其实也就是一对多)
    private List<Menu> menus;
      //get and set method..
}

 

说明:

JPA使用注解形式进行开发,可以简便一些。省去写配置文件的麻烦,但也与java代码耦合。具体使用传统Hibernate配置文件形式开发还是使用简洁的JPA库开发还是要根据实际项目作出对应选择。
具体JPA注解的使用,这里不做重点解释。

开始DAO层开发

public interface MenuDao extends JpaRepository<Menu,String> {
}

 

是的,DAO层就这一句代码就可以了,JPA内部帮我们封装了很多简单的增删改查方法,省去我们很多重复代码的编写。

调用

@Service    
@Transactional  
public class MenuServiceImpl implements MenuService {
    @Autowired
    private MenuDao menuDao;
    @Override
    public Menu getRootMenu() {
        return menuDao.findOne("0");        //将最上层菜单返回即可内联查询出所有子菜单
    }
}

 

总结

​ 主要为了记录一个比较特殊,也是比较绕一种数据读取。其特点是对象之间内部关联,或者说有父子关系,当然实现方式也是多种多样,应结合具体项目,具体技术应用场景来规划技术选型。比如使用递归,循环等等,都可以实现类似业务。该demo可以给前端返回一个跟节点对象,而不是一组数据(json数组)。


posted @ 2018-01-01 11:39  星尘h  阅读(5707)  评论(1编辑  收藏  举报