前阵子空闲的时候写了一个关于全国省市区树形结构相关的项目,由于技术文中途难产了,因此直接贴代码吧(该博主很懒,什么都没留下)
树形泛型父类,子类可以继承该父类,一般一个树形结构的面向对象建模是这样的: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();
}