在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數組)。