前几篇讲的缓存都是后台缓存,并且思路都是与mybatis结合的sql持久化层的缓存,用的redis或者ehache缓存框架。今天我们讲一个前台页面缓存,没有任何框架,纯java代码,并且很实用和灵活。是从我们项目中抽离出来的分享给大家。
1.前台页面缓存主要用在什么地方?
我们这里主要是用在下拉框数据显示上,和前端工程师的静态页面缓存没任何关联。如果用户登录成功进入功能页面,一些预先加载的数据中,查询条件的下拉数据是绝对要先加载好的。
2.下拉数据缓存的数据结构
下拉数据的结构设计直接影响取值和级联效果的开发难以程度。非常重要。下拉的数据最合理的结构之一如下:
[{id:"真实值",name:"显示值"},{id:"真实值",name:"显示值"},...]
比如[{id:"A",name:"北京分公司"},{id:"B",name:"湖北分公司"}]
那这些数据结构如何构造出来的呢?
我们在执行查询语句的Service层,对查询出来的数据进行结构调整。下面这个可以先简单看下,和后面的具体步骤结合更好理解。
sql:SELECT DISTINCT MANAGECOMCODE AS ID,NAME,MANAGECOM FROM.....
//查询语句
List<Map<String,String>> list=appDao.queryForMapList("SOA.DataDict.getBranchByUserId",param);
//执行查询出来的数据结构为List<Map<String,String>>,比如[{"A":"北京分公司","ID":"10","NAME":"北京分支"},{"B":"湖北分公司","ID":"20","NAME":"十堰分支"},...];要让这数据拆成[{id:"A",name:"北京分公司"},{id:"B",name:"湖北分公司"}]
Map<String,List<Map<String,String>>> data=new HashMap<String,List<Map<String,String>>>();
//防止字典表数据重复,也为了拆分map值。用到HashSet
Set<String> keySet =new HashSet<String>();
for(Map<String,String> map:list){
//把所有公司码都作为key先放到set集合中,也有去重复作用。
keySet.add(map.get("MANAGECOM"));
}
//然后根据公司码遍历出公司名。重新构造数据结构
for(String key:keySet){
List<Map<String,String>> cd=new ArrayList<Map<String,String>>();
for(Map<String,String> map:list){
if(map.get("MANAGECOM").equals(key)){
Map<String,String> org=new HashMap<String,String>();
org.put("id",map.get("ID"));
org.put("name",map.get("NAME"));
cd.add(org);
}
}
data.put(key,cd);
}
return data;
3.用的什么来缓存数据?
shiro的Session来存储数据。因为用户登录用到shiro,验证通过就可以加载缓存。
4.数据缓存从后台到前台页面的流程
1.查询出数据,进行数据结构调整。—service层
2.将数据进一步区分,放到公共的缓存HashMap中—cache层,相当于service层
3.用户登录成功,创建session,并把公共的缓存setAttribute进session中。
4.到加载菜单页面的公共页面取出缓存数据,并转数据类型为json
5.在单独一个js中对json缓存数据进行按key区分,取出数据封装下拉框公共方法
6.在各个子页面引入公共方法和公共页面,展示出数据。
5.具体实现步骤(看思路,代码供参考,不具备复现价值)
项目框架:ssm
现在我们要实现一个省市区中省份的下拉数据展示,则依它来了解数据流如何从数据库到下拉菜单的。
5.1 数据库中省的单表数据结构如下图 ,表名称:soa_province,表字段有PROVINCE , NAME , ENABLED , ADDUSER , ADDTIME .
5.2 mapper文件的SQL查询语句,这就开始注意数据结构的id,name字段
<select id="getProvince" resultMap="BaseResultMap">
SELECT PROVINCE AS ID,NAME FROM SOA_PROVINCE WHERE ENABLED='Y'
</select>
5.3 service层执行查询的语句,调整数据结构
@Service(DataDictSerice.service_ID)
public class DataDictServiceImpl extends BaseServiceImpl implements DataDictSerice{
public List<Map<String,String>> getProvince(){
List<Map<String,String>> list=appDAO.queryForMapList("SOA.getProvince",new HashMap());
List<Map<String,String>> data=new ArrayList<Map<String,String>>();
Map<String,String> map;
final String ID="id";
final String NAME="name";
for(Map<String,String> map:list){
map=new HashMap<String,String>();
map.put(ID,map.get("ID"));
map.put(NAME,map.get("NAME"));
data.add(map);
}
return data;
}
}
5.4 对数据进一步处理,并存放到HashMap中。
@Service("CommonCacheMap")
public class CommonCacheMap{
@Resource(name=DataDictService.SERVICE_ID)
private DataDictService dataDictService;
public static final Map<String,Object> baseData=new HashMap<String,Object>();
//系统启动时就加载好各缓存
@PostConstruct
public void afterPropertiesSet() throws Exception{
SoaDataDictProvince();
SoaDataDictCity();
...
}
private static Map<String,String> PROVINCE=new ConcurrentHashMap<String,String>();
private void SoaDataDictProvince(){
PROVINCE.clear();
List<Map<String,String>> data=dataDictService.getProvince()
for(Map<String,String> map:data){
PROVINCE.put(map.get("id"),map.get("name"))
}
baseData.put("province",data);
}
}
5.5 在用户登录时把HashMap中的数据放到session中返回前台菜单公共页面
@Controller("mainController")
@RequestMapping(value="")
public class MainController extends BaseController{
@Resource(name=ISystemService.SERVICE_ID)
private ISystemService systemAction;
@RequestMapping("/importFunction")
public ModelAndView main(HttpServletRequest request){
LoginUser user=UserUtils.getUser();
UserBo userInfo=user.getUser();
Map<String,Object> baseData=CommonCacheMap.baseData;
Session session=SecurityUtils.getSubject().getSession();
session.setAttribute("baseData",baseData);
session.setAttribute("currentUser",user);
ModelAndVilw modelAndView=new ModelAndView();
//用modelandvilw实现转跳到main.jsp页面
modelAndView.setViewName("main");
return modelAndView;
}
}
5.6 main.jsp公共页面处理缓存数据,转换为json数据。
在main.jsp中并没有直接处理数据js代码,但是main.jsp引入了别的jsp页面来处理后台传过来的数据
![]()
在include.jsp公共页面中。确实有处理的代码。
第一步:取到baseData
<%
String baseData="";
Object base=session.getAttribute("baseData");
if(base !=null){
baseData=JSONUtils.toJOSNString(base);
}
%>
第二步 赋值给jsp变量并转换数据类型。
<script type="text/javascript">
var baseData;
var bb="<%=baseData%>";
if(bb){
baseData=eval("("+bb+")")
}
</script>
下图为强转后的数据结构:
在这个include.jsp页面处理完缓存后才能引入data.js文件。注意按先后顺序加载!
5.7 封装缓存为公共方法,供子页面调用。在data.js这个js文件中实现。
//定义全局变量
var comObj,saleCom,center,city,dept,group,branch,province,....;
if(baseData){
province=baseData.province;//省
city=baseData.city;//市
}
//为下拉显示加上全部这个字段。
function getAllProvince(){
if([]!=province){
var temp=$.extend(true,[],province);
temp.unshift({'id':'','name':'全部'});
return temp;
}
}
function getAllCity(key){
if([] !=city[key]){
var temp=$.extend(true,[],city[key]);
temp.unshift({'id':'','name':"全部"});
return temp;
}
}
5.8 在子页面引用封装的方法,easyui框架来实现下拉。
<input class="easyui-combobox" id="transProv" name="transProv"/>
//省市区下拉列表
$("#transProv").combobox({
editable:false;
data:getAllProvince(),
valueField:"id",
textFileld:"name",
onSelect:function(current){
$("#transCity").combobox({
editable:false;
data:getAllCity(current.id),
valueField:"id",
textField:"name",
onSelect:function(currentd){
$("#transDisr").combobox({
editable:false,
data:getAllDist(currentd.id),
valueField:"id",
textField:"name"
})
}
})
$("#transDistr").combobox('setValue','');
$("#transDistr").combobox('loadData',[]);
}
})
本文介绍了一种不依赖框架的前端页面缓存方法,详细展示了如何利用Java代码处理和缓存下拉框数据,包括数据结构调整、使用Shiro Session存储及从前台到后台的数据流过程。
4312

被折叠的 条评论
为什么被折叠?



