多級菜單查詢實踐-Mybatis和JPA


在9月份面試時,面試官在一張草稿紙上出了一道省市縣聯動的題目,並提示我可以使用遞歸或循環,將數據查詢出來封裝到對象中。當時我用了循環的方式將其封裝,雖說勉強實現,但是代碼冗余度高,邏輯性特別差。

碰巧最近項目中業務需要,也是將多級菜單封裝到根菜單返回給前端,發現還是會把自己弄得頭暈,因此打算認真寫一下,當做筆記,也對之前Hibernate,Mybatis框架的復習。下面將會使用Mybatis 和 JPA 分別實現菜單封裝業務邏輯,初步規划只要將數據以JSON格式寫回前端即是測試通過。

Demo1 「使用Mybatis實現多層菜單數據查詢」

數據庫表一覽

menuid menuname parentid
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數組)。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM