前阵子空闲的时候写了一个关于全国省市区树形结构相关的项目,由于技术文中途难产了,因此直接贴代码吧(该博主很懒,什么都没留下)
树形泛型父类,子类可以继承该父类,一般一个树形结构的面向对象建模是这样的:id,parentId(hibernate等持久化框架有规定id是要实现可序列化接口),childs(自身引用)。
import java.io.Serializable; import java.util.LinkedList; import java.util.List; public abstract class Treebean<PK extends Serializable,T extends Treebean<PK,?>> implements Serializable { /** * */ private static final long serialVersionUID = 1878885497744083173L; protected PK id; protected PK parentId; protected LinkedList<T> childs = new LinkedList<T>(); public PK getId() { return id; } public void setId(PK id) { this.id = id; } public PK getParentId() { return parentId; } public void setParentId(PK parentId) { this.parentId = parentId; } protected Treebean<PK,T> addChild(T child) { this.childs.add(child); return this; } protected void buildTree(List<Treebean<PK,T>> sourceList, List<Treebean<PK,T>> childs, PK parentId) { for (Treebean<PK,T> tree : sourceList) { if (checkParent(tree, parentId)) { childs.add(tree); buildTree(sourceList, (List<Treebean<PK, T>>) tree.childs,tree.id); } } } public LinkedList<T> getChilds() { return childs; } protected abstract boolean checkParent(Treebean<PK,T> tree, PK parentId); protected boolean hasChild() { return !this.childs.isEmpty(); } }
相关的表数据如下,一般的树形表设计,博主是建议要有root节点的,类似省市区这种结构,根节点可以是中国。
相关的实体类如下:
public class Province extends Treebean<Integer,Province> { /** * */ private static final long serialVersionUID = 8505185101020732530L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public static Province getRoot(){ Province root = new Province(); root.setId(1); root.setName("中国"); return root; } @Override public String toString() { return "Province [name=" + name + ", id=" + id + ", parentId=" + parentId + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Province other = (Province) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } @Override protected boolean checkParent(Treebean<Integer, Province> tree, Integer parentId) { Integer pId = tree.parentId; return pId != null && parentId.intValue() == pId; } }
外部调用,首先是dao,使用的jdbctemplate,把地区表的数据查出(这里使用了groovy):
import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import com.tcsoft.treedata.bean.Province; @Repository class ProvinceDao extends AbstractJdbcTemplateDao { public List<Province> getAllList() { String sql = "select * from tbl_province"; return this.jdbcTemplate.query(sql, new ProvinceRowMapper()); } private class ProvinceRowMapper implements RowMapper<Province> { @Override public Province mapRow(ResultSet rs, int rowNum) throws SQLException { // TODO Auto-generated method stub Province provice = new Province( id:rs.getInt("id"), name:rs.getString("name"), parentId:rs.getInt("parent_id") ); return provice; } } }
业务类,主要用于构造所必须的树形结构(备注,像省市区这种基本不会变动的数据,一般都需要加入缓存,目前缓存框架使用的是ehcache)
import org.springframework.beans.factory.annotation.Autowired import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service import com.tcsoft.treedata.bean.Province import com.tcsoft.treedata.dao.ProvinceDao @Service class ProvinceServiceImpl implements ProvinceService { @Autowired private ProvinceDao provinceDao; @Cacheable("PROVINCE_Cache") public Province getAllProvinceTree() { Province root = Province.getRoot(); List<Province> sourceList = this.provinceDao.getAllList(); root.buildTree(sourceList, root.getChilds(), 1); return root; } }
ehcache配置:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false" monitoring="autodetect" dynamicConfig="true"> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /> <!-- DEFAULT CACHE --> <cache name="DEFAULT_CACHE" maxElementsInMemory="10000" maxElementsOnDisk="10000" eternal="false" timeToIdleSeconds="60" timeToLiveSeconds="60" memoryStoreEvictionPolicy="LFU"> </cache> <cache name="PROVINCE_Cache" maxElementsInMemory="1000" maxElementsOnDisk="0" eternal="false" timeToIdleSeconds="120000" timeToLiveSeconds="120000" memoryStoreEvictionPolicy="LFU"> </cache> <!--terracottaConfig url="localhost:9510"/ --> </ehcache>
后台递归展示测试类:
import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.tcsoft.treedata.bean.Province; import com.tcsoft.treedata.service.ProvinceService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-context.xml") public class TreeTest { @Autowired private ProvinceService provinceService; @Test public void testGetAllList() { Province root = this.provinceService.getAllProvinceTree(); showTreeList_2(root.getChilds(), "┣"); } private void showTreeList_2(Collection<Province> topList, String prefix) { for(Province top : topList){ // 顶点 System.out.println( prefix + top.getName()); // 子树 showTreeList_2(top.getChilds(), " " + prefix); } } }
前台使用ztree展示数型结构(相当强大的树形控件)
<script type="text/javascript"> $(function(){ var setting = { data: {key: {children: "childs"}} }; $.post("http://127.0.0.1:8080/treedata/tree",function(data){ $.fn.zTree.init($("#provinceTree"), setting, data); },"json"); }); </script>
后台使用spring mvc responsebody返回:
@RequestMapping("/tree") @ResponseBody public Province getTree(){ return this.provinceService.getAllProvinceTree(); }