利用SpringBoot的简单性和易集成特点,与内存服务器Redis和搜索服务器Solr集成完成项目案例,题目要求查询菜单记录需要从Solr中检索,并且高亮显示查询关键字,菜单使用的材料需要使用Redis内存服务器缓存。
案例要求:
1、安装Redis内存服务器
2、安装Solr全文检索服务器
本项目案例使用MyBatisPlus存储数据库,可以为开发者简化部分代码的编写,自动生成部分代码,并且完全兼容MyBatis,但MyBatisPlus的缺点是需要为每个实体类提供一个Mapper接口,Service接口和Service实现类。
POM.xml配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.testgroupId>
<artifactId>Proj_FtlSolrRedisartifactId>
<packaging>warpackaging>
<version>0.0.1-SNAPSHOTversion>
<name>Proj_FtlSolrRedis Maven Webappname>
<url>http://maven.apache.orgurl>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.6.RELEASEversion>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>3.8.1version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-solrartifactId>
<version>2.1.11.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
<scope>truescope>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.6version>
<exclusions>
<exclusion>
<groupId>com.alibabagroupId>
<artifactId>jconsoleartifactId>
exclusion>
<exclusion>
<groupId>com.alibabagroupId>
<artifactId>toolsartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-jasperartifactId>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
dependency>
<dependency>
<groupId>taglibsgroupId>
<artifactId>standardartifactId>
<version>1.1.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatisplus-spring-boot-starterartifactId>
<version>1.0.5version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plusartifactId>
<version>2.1.8version>
dependency>
<dependency>
<groupId>tk.mybatisgroupId>
<artifactId>mapperartifactId>
<version>3.4.5version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>1.1.2version>
dependency>
<dependency>
<groupId>commons-beanutilsgroupId>
<artifactId>commons-beanutils-coreartifactId>
<version>1.8.3version>
dependency>
dependencies>
<build>
<finalName>Proj_FtlSolrRedisfinalName>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<fork>truefork>
configuration>
plugin>
plugins>
build>
project>
application.yml配置
#SpringBoot启动端口和项目路径
server:
port: 8080
context-path: /
#SpringMVC中JSP视图配置
spring:
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
# freemarker:
# charset: UTF-8
# template-loader-path: /WEB-INF/ftl/
# suffix: .ftl
# content-type: text/html
# settings:
# number_format: '0.##'
#数据源配置
datasource:
name: w1
url: jdbc:mysql://127.0.0.1:3306/w1?useUnicode=true&characterEncoding=utf8
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
#Http编码配置
http:
encoding:
force: true
charset: UTF-8
enabled: true
#Rabbitmq消息服务器配置
rabbitmq:
host: 192.168.25.10
port: 5672
username: admin
password: admin
virtual-host: /
# Redis数据库索引(默认为0)
redis:
database: 0
host: 192.168.25.30
port: 6379
password: redis
pool.max-active: 8
pool.max-wait: -1
pool.max-idle: 8
pool.min-idle: 0
timeout: 0
#Solr配置
data:
solr:
host: http://localhost:8984/solr/new_core
#Mybatis实体类配置
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
#pagehelper分页插件
pagehelper:
helperDialect: mysql
reasonable: true
#日志配置
logging:
file: d:/springboot.log
level:
com.gufang.oa.mapper: DEBUG
菜单实体类
package com.test.bean;
import java.io.Serializable;
import java.sql.Date;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
//菜单表实体类
@TableName("t_menu")
public class MenuInfo implements Serializable{
//定义主键策略
@TableId(type=IdType.AUTO)
private Integer id = null;
private String name = null;
private Date dt = null;
private Float price = null;
//定义非数据库字段,默认exist=true,代表是数据库字段
@TableField(exist=false)
private String tid = null;
@TableField(exist=false)
private String tname = null;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDt() {
return dt;
}
public void setDt(Date dt) {
this.dt = dt;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
public String getTid() {
return tid;
}
public void setTid(String tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
}
菜单分类实体类
package com.test.bean;
import java.io.Serializable;
import java.sql.Date;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
//分类表实体类
@TableName("t_menutype")
public class TypeInfo implements Serializable{
@TableId
private Integer id = null;
private String name = null;
@TableField(exist=false)
private String checked = "";
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getChecked() {
return checked;
}
public void setChecked(String checked) {
this.checked = checked;
}
}
中间表实体类
package com.test.bean;
import java.io.Serializable;
import java.sql.Date;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
//菜单与材料关联表,实现多对多关系
//MybatisPlus使用注解定义表名
@TableName("t_m2t")
public class M2TInfo implements Serializable{
private Integer mid = null;
private Integer tid = null;
public Integer getMid() {
return mid;
}
public void setMid(Integer mid) {
this.mid = mid;
}
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
}
菜单Mapper接口
package com.test.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.test.bean.MenuInfo;
public interface MenuMapper extends BaseMapper<MenuInfo>{
}
菜单Mapper.xml文件
<mapper namespace="com.test.mapper.MenuMapper">
mapper>
菜单服务接口
package com.test.service;
import com.baomidou.mybatisplus.service.IService;
import com.test.bean.MenuInfo;
public interface IMenuService extends IService<MenuInfo>{
}
菜单服务实现类
package com.test.service.impl;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.test.bean.MenuInfo;
import com.test.mapper.MenuMapper;
import com.test.service.IMenuService;
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper,MenuInfo>
implements IMenuService
{
}
我们发现使用MyBatisPlus设计代码非常简洁,实现和集成几个接口便可提交数据库表的基本操作,用户也可以在Mapper.xml中定义SQL,在Mapper接口中实现SQL的方法,MyBatisPlus完全兼容MyBatis。唯一的缺点就是需要为每个实体类都定义一套接口和实现类。
分类表Mapper接口
package com.test.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.test.bean.MenuInfo;
import com.test.bean.TypeInfo;
public interface TypeMapper extends BaseMapper{
}
分类表服务接口
package com.test.service;
import com.baomidou.mybatisplus.service.IService;
import com.test.bean.MenuInfo;
import com.test.bean.TypeInfo;
public interface ITypeService extends IService{
}
分类表的服务实现类
package com.test.service.impl;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.test.bean.MenuInfo;
import com.test.bean.TypeInfo;
import com.test.mapper.MenuMapper;
import com.test.mapper.TypeMapper;
import com.test.service.IMenuService;
import com.test.service.ITypeService;
@Service
public class TypeServiceImpl extends ServiceImpl
implements ITypeService
{
}
中间表的Mapper接口
package com.test.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.test.bean.M2TInfo;
import com.test.bean.MenuInfo;
import com.test.bean.TypeInfo;
public interface M2TMapper extends BaseMapper{
}
中间表的服务接口
package com.test.service;
import com.baomidou.mybatisplus.service.IService;
import com.test.bean.M2TInfo;
import com.test.bean.MenuInfo;
import com.test.bean.TypeInfo;
public interface IM2TService extends IService{
}
中间表的服务实现类
package com.test.service.impl;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.test.bean.M2TInfo;
import com.test.bean.MenuInfo;
import com.test.bean.TypeInfo;
import com.test.mapper.M2TMapper;
import com.test.mapper.MenuMapper;
import com.test.mapper.TypeMapper;
import com.test.service.IM2TService;
import com.test.service.IMenuService;
import com.test.service.ITypeService;
@Service
public class M2TServiceImpl extends ServiceImpl
implements IM2TService
{
}
菜单Controller跳转类
package com.test.ctrl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.test.bean.M2TInfo;
import com.test.bean.MenuInfo;
import com.test.bean.TypeInfo;
import com.test.service.IM2TService;
import com.test.service.IMenuService;
import com.test.service.ITypeService;
import com.test.util.FreeMarkerUtil;
import com.test.util.PageUtil;
import com.test.util.RedisTool;
import com.test.util.ResultInfo;
import com.test.util.SolrTool;
@Controller
@CacheConfig(cacheNames="menu")
public class MenuCtrl {
@Autowired
private IMenuService menuServ;
@Autowired
private ITypeService typeServ;
@Autowired
private IM2TService m2tServ;
@Autowired
private SolrTool solr;
@Autowired
private RedisTool redis;
/**
* 通过数据库查询结果集数据,并且提供分页支持
* @param page 当前页码
* @param rows 每页显示记录数
* @param name 查询的关键字
* @return
*/
private ModelAndView initListDataDb(Integer page,Integer rows,String name)
{
//如果没有定义分页参数,初始化
if(page == null)
page = 1;
if(rows == null)
rows = 4;
if(name == null)
name = "";
ModelAndView mv = new ModelAndView();
//定义MybatisPlus查询封装对象
Wrapper wrapper = new EntityWrapper();
//定义查询条件
wrapper.like("name", "%"+name+"%");
//定义分页对象
Page p = new Page(page,rows);
//查询分页对象
Page pageObj = menuServ.selectPage(p,wrapper);
List menuList = pageObj.getRecords();
for(MenuInfo mi:menuList)
{
//定义MyBatisPlus查询封装对象
Wrapper wrapper2 = new EntityWrapper();
//定义查询条件
wrapper2.eq("mid", mi.getId());
//查询结果集
List m2tLst = m2tServ.selectList(wrapper2);
String tname = "";
for(M2TInfo m2t:m2tLst)
{
TypeInfo ti = typeServ.selectById(m2t.getTid());
tname = tname + ti.getName() + ",";
}
mi.setTname(tname);
}
//将查询结果集存放到ModelAndView对象中,以便前台页面读取
mv.addObject("menuList", menuList);
//定义返回的页面名称
mv.setViewName("menu");
//分页对应的URL
String url = "/init?name="+name;
//查询菜单记录总数
Integer total = menuServ.selectCount(wrapper);
PageUtil pu = new PageUtil(url,page,rows,total);
//保存分页Html代码
mv.addObject("pagediv", pu.toHtml());
return mv;
}
/**
* 从Solr中查询符合条件的数据
* @param page 当前页码
* @param rows 每页显示记录数
* @param name 查询的关键字
* @return
*/
private ModelAndView initListDataSolr(Integer page,Integer rows,String name)
{
//如果没有定义分页参数,初始化
if(page == null)
page = 1;
if(rows == null)
rows = 4;
if(name == null)
name = "";
String query = "*:*";//"name:"+name;
if(name != null && !"".equals(name))
query = "name:"+name;
ModelAndView mv = new ModelAndView();
//转化当前页码为开始记录数
Integer starts = (page-1)*rows;
//从Solr中查询符合条件的数据
ResultInfo result = solr.queryList(MenuInfo.class, query, starts, rows, "name");
mv.addObject("menuList", result.getList());
mv.setViewName("menu");
String url = "/init?name="+name;
Integer total = result.getTotal().intValue();
PageUtil pu = new PageUtil(url,page,rows,total);
mv.addObject("pagediv", pu.toHtml());
return mv;
}
//初始化URL
@RequestMapping("/init")
public ModelAndView init(Integer page,Integer rows,String name)
{
return initListDataSolr(page,rows,name);
}
//显示菜单页面,传人菜单ID
@RequestMapping("/addmenu")
public ModelAndView addmenu(Integer mid)
{
MenuInfo mi = new MenuInfo();
if(mid != null)
{
mi = menuServ.selectById(mid);
}
ModelAndView mv = new ModelAndView();
//定义MyBatisPlus查询封装对象
Wrapper wrapper = new EntityWrapper();
//查询分类列表
List typeList = typeServ.selectList(wrapper);
if(mid != null)
{
//定义MyBatisPlus查询封装对象
Wrapper wrapper2 = new EntityWrapper();
//定义查询条件
wrapper2.eq("mid", mid);
List m2tLst = m2tServ.selectList(wrapper2);
//循环赋值分类选中状态,对应前台CheckBox选中状态,可以简化前台代码处理
for(TypeInfo ti:typeList)
{
for(M2TInfo m2t:m2tLst)
{
if(ti.getId() == m2t.getTid())
ti.setChecked("checked");
}
}
}
//将查询结果集存放到ModelAndView对象中,以便前台页面读取
mv.addObject("menu", mi);
mv.addObject("typeList", typeList);
//返回页面名称
mv.setViewName("addmenu");
return mv;
}
//定义菜单保存方法,从前台接收参数封装为MenuInfo对象
@RequestMapping("/savemenu")
public ModelAndView savemenu(HttpServletRequest req,MenuInfo mi)
{
//首先从包装对象中取出分类ID,中间以逗号分隔的字符串,如果执行保存操作后,此属性的值清空
String tid = mi.getTid();
//调用MyBatisPlus的保存与更新方法
menuServ.insertOrUpdate(mi);
//根据菜单ID,删除中间表数据
Map delMap = new HashMap();
delMap.put("mid", mi.getId());
m2tServ.deleteByMap(delMap);
String tname = "";
if(tid != null)
{
//分割菜单分类ID
String[] dim = tid.split(",");
for(String tid2:dim)
{
//创建中间表对象
M2TInfo m2t = new M2TInfo();
m2t.setMid(mi.getId());
m2t.setTid(Integer.parseInt(tid2));
m2tServ.insert(m2t);
//记录分类名称,中间以逗号分隔,最后为菜单对象赋值,前台页面方可显示分类名称
TypeInfo ti = typeServ.selectById(tid2);
tname = tname + ti.getName() + ",";
}
}
//定义数据对象,一定需要定义ID键
Map m = new HashMap();
m.put("id",mi.getId());
m.put("name",mi.getName());
m.put("dt",mi.getDt());
m.put("price",mi.getPrice());
m.put("tname",tname);
//生成静态页面
FreeMarkerUtil.generateHtml(req.getServletContext(), "html", "menu.html", m);
//数据添加到Solr
solr.addMap(m);
return initListDataSolr(1,4,null);
}
/**
* 根据前台选中的菜单ID,删除菜单数据与中间表数据
* @param ids
* @return
*/
@ResponseBody
@RequestMapping("/deletemenu")
public boolean deletemenu(Integer[] ids)
{
if(ids != null)
{
for(Integer id:ids)
{
menuServ.deleteById(id);
Map delMap = new HashMap();
delMap.put("mid", id);
m2tServ.deleteByMap(delMap);
solr.delete(id+"");
}
}
return true;
}
}
根据模板生成静态页面的工具类FreeMarkerUtil
package com.test.util;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.cache.URLTemplateLoader;
import freemarker.cache.WebappTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
public class FreeMarkerUtil {
/**
* 此方法根据模板和数据在指定的目录生成静态页面,数据对象Map必须存在键值id,它对应的值是生成静态页面的文件名
* @param servletContext Servlet上下文对象,可以通过request获得
* @param staticHtmlPath 生成静态网页的目录,它位于webapp目录下,通过浏览器地址可以访问到
* @param template freemarker模板名称,它必须位于webapp/ftl目录下
* @param map 数据模型
*/
public static void generateHtml(ServletContext servletContext,
String staticHtmlPath,String template,Map map)
{
try
{
//声明配置对象
Configuration conf = new Configuration(Configuration.VERSION_2_3_23);
conf.setEncoding(Locale.getDefault(), "UTF-8");
//声明模板加载器
WebappTemplateLoader wtl = new WebappTemplateLoader(servletContext, "/ftl");
//绑定到配置对象
conf.setTemplateLoader(wtl);
Template tmplt = conf.getTemplate(template);
//根据模板与数据模型生成静态网页
String path = servletContext.getRealPath("/"+staticHtmlPath);
String file = path+"/"+map.get("id")+".html";
FileOutputStream fos = new FileOutputStream(file);
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
tmplt.process(map, bw);
fos.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
分页工具类
package com.test.util;
/**
* 处理分页的工具类
* 每翻页都请求后台加载分页数据
*/
public class PageUtil {
private Integer page = 1;//默认显示第一页
private Integer rows = 4;//每页显示记录数
private Integer total = null;//总行数
private String url = null;//点击页码跳转url
/**
* 分页工具类构造方法
* @param url 分页对应的URL,可以包含查询参数
* @param page 当前页码
* @param rows 每页显示的记录数
* @param total 数据库表总行数
*/
public PageUtil(String url,Integer page,Integer rows,Integer total)
{
this.url = url;
this.page = page;
this.rows = rows;
this.total = total;
}
/**
* 生成静态HTML分页代码片段,通过EL语言加载到JSP页面中
*/
public String toHtml()
{
StringBuffer sb = new StringBuffer();
//计算总页数
int pages = 0;
if(total % rows == 0)
pages = total / rows;
else
pages = (total / rows) + 1;
sb.append("\r\n");
String firstUrl = null;
if(url.indexOf("?")>0)
firstUrl = url + "&page=1&rows="+rows;
else
firstUrl = url + "?page=1&rows="+rows;
sb.append("首页\r\n");
String backUrl = null;
if(url.indexOf("?")>0)
backUrl = url + "&page="+(page==1?1:(page-1))+"&rows="+rows;
else
backUrl = url + "?page="+(page==1?1:(page-1))+"&rows="+rows;
sb.append("上一页\r\n");
for(int i=1;i<=pages;i++)
{
String pageUrl = null;
if(url.indexOf("?")>0)
pageUrl = url + "&page="+i+"&rows="+rows;
else
pageUrl = url + "?page="+i+"&rows="+rows;
if(i == page)
sb.append(""+i+"\r\n");
else
sb.append(""+i+"\r\n");
}
String nextUrl = null;
if(url.indexOf("?")>0)
nextUrl = url + "&page="+(page==pages?pages:(page+1))+"&rows="+rows;
else
nextUrl = url + "?page="+(page==pages?pages:(page+1))+"&rows="+rows;
sb.append("下一页\r\n");
String lastUrl = null;
if(url.indexOf("?")>0)
lastUrl = url + "&page="+pages+"&rows="+rows;
else
lastUrl = url + "?page="+pages+"&rows="+rows;
sb.append("尾页\r\n");
sb.append(" 第"+page+"/"+pages+"页\r\n");
sb.append(" 共"+total+"条记录\r\n");
sb.append("\r\n");
return sb.toString();
}
}
Redis的工具类提供两两个,一个适应代码缓存对象,一个适应使用注解缓存对象
使用代码缓存对象
package com.test.util;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisAccessor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;
@Service
public class RedisTool {
//注入Redis模板对象
@Autowired
//@Qualifier("redisTemplate")
private RedisTemplate redis;
//保存对象类型到Redis内存服务器
public void saveObject(String key,Object obj)
{
redis.opsForValue().set(key,obj);
}
//根据Key获取Redis内存对象
public Object getObject(String key)
{
return redis.opsForValue().get(key);
}
//保存Map对象到Redis内存服务器
public void saveMap(String key,Map map)
{
redis.opsForHash().putAll(key,map);
}
//根据Key获取Redis Map对象
public Map getMap(String key)
{
return redis.opsForHash().entries(key);
}
//保持List对象到Redis内存服务器
public void saveList(String key,List list)
{
redis.opsForList().leftPushAll(key, list);
}
//根据Key获取Redis List对象
public List getList(String key)
{
return redis.opsForList().range(key, 0, -1);
}
}
使用注解缓存对象
package com.test.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 通过注解使用Redis内存服务器
*/
@Configuration
@EnableCaching
public class RedisUtil extends CachingConfigurerSupport{
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
/**
* 生成Redis的键值,根据类名+方法名+参数值的形式
* @return
*/
@Bean(name = "keyGenerator")
public KeyGenerator keyGenerator() {
return new KeyGenerator(){
@Override
public Object generate(Object target, java.lang.reflect.Method method,
Object... params) {
StringBuffer sb = new StringBuffer();
sb.append(target.getClass().getName());
sb.append(method.getName());
for(Object obj:params){
if(obj != null)
{
sb.append(obj.toString());
}
}
return sb.toString();
}
};
}
/**
* 生成CacheManager对象
* @param redisTemplate
* @return
*/
@Bean
public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(10000);
return cacheManager;
}
/**
* 生成Redis内存管理模板,注解@Primary代表此对象是同类型对象的主对象,优先被使用
* @param factory
* @return
*/
@Primary
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory){
RedisTemplate template = new RedisTemplate();
template.setKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(factory);
setSerializer(template);
template.afterPropertiesSet();
return template;
}
/**
* 序列化Redis的键和值对象
* @param template
*/
private void setSerializer(RedisTemplate template){
@SuppressWarnings({ "rawtypes", "unchecked" })
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
}
}
Solr数据管理工具类
package com.test.util;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Solr管理的工具类,提供添加数据到Solr,删除Solr数据和查询Solr数据
*/
@Service
public class SolrTool {
@Autowired
private SolrClient client;
/**
* 根据记录ID删除Solr中数据
* @param id
*/
public void delete(String id)
{
try
{
client.deleteById(id.toString());
UpdateResponse up = client.commit();
System.out.println(up);
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
* 删除Solr中全部数据
*/
public void deleteAll()
{
try
{
SolrQuery q = new SolrQuery();
q.set("q", "*:*");
q.set("fl", "id");
QueryResponse resp = client.query(q);
SolrDocumentList lst = resp.getResults();
for(SolrDocument doc:lst)
{
String id = (String)doc.getFieldValue("id");
client.deleteById(id);
}
client.commit();
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
* 更新Solr中的数据,数据模型Map中必须包含id
* @param map
*/
public void update(Map map)
{
try
{
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", map.get("id"));
for(Object o:map.keySet())
{
String key = (String)o;
if(!"id".equals(key))
{
Map m = new HashMap();
m.put("set", map.get(key));
doc.addField(key, m);
}
}
UpdateResponse resp = client.add(doc);
client.commit();
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
* 添加数据对象Map到Solr
* 如果Solr中没有Field域,自动创建这些Field
* @param map
*/
public void addMap(Map map)
{
try
{
SolrInputDocument doc = new SolrInputDocument();
Set keys = map.keySet();
for(String k:keys)
{
doc.addField(k, map.get(k));
}
client.add(doc);
UpdateResponse up = client.commit();
System.out.println(up);
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
* 查询Solr中符合条件的数据并高亮显示
* @param clz 查询的Solr记录转化为对象的类型
* @param query 查询条件,以field:value的形式,可以包含and,or
* @param starts 开始记录数
* @param rows 查询的记录数
* @param flCol 高亮显示的字段列表,以逗号分隔
* @param 对象类型
* @return
*/
public ResultInfo queryList(Class clz,String query,
Integer starts,Integer rows,String flCol)
{
try
{
SolrQuery q = new SolrQuery();
q.set("q",query);//定义查询条件,使用属性名:属性值的形式,查询全部使用*
q.set("fl", "*");//定义返回属性列表,返回全部以*代替
// 分页显示功能
q.setStart(starts);
q.setRows(rows);
q.setHighlight(true);
q.setHighlightSimplePre("");
q.setHighlightSimplePost("");
q.setParam("hl.fl", flCol);
QueryResponse response = client.query(q);
String hl = response.getHighlighting().toString();
Map>> hlMap = response.getHighlighting();
SolrDocumentList list = response.getResults();
System.out.println("total = "+list.getNumFound());
List lst = getBeans(clz, list,flCol,hlMap);
ResultInfo result = new ResultInfo();
result.setList(lst);
result.setTotal(list.getNumFound());
return result;
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* 将查询的SolrDocument转换为对象
* @param clazz 查询的Solr记录转化为对象的类型
* @param solrDocumentList 记录列表
* @param flCol 高亮显示的字段列表,以逗号分隔
* @param hlMap 高亮显示字符串,需要解析
* @param 对象类型
* @return
* @throws Exception
*/
public final List getBeans(Class clazz,
SolrDocumentList solrDocumentList,String flCol,
Map>> hlMap) throws Exception{
List list = new ArrayList();
T t = null;
for (SolrDocument solrDocument : solrDocumentList)
{
//反射出实例
t = clazz.newInstance();
//获取所有属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields)
{
Object val = solrDocument.get(field.getName());
if(val != null)
{
String val2 = val.toString();
if(val2.startsWith("[") && val2.endsWith("]"))
val2 = val2.substring(1,val2.length()-1);
if(field.getType() == java.sql.Date.class)
{
java.util.Date dt = new java.util.Date(val2);
val2 = new java.sql.Date(dt.getTime()).toString();
}
if(field.getType() == java.sql.Timestamp.class)
{
java.util.Date dt = new java.util.Date(val2);
val2 = new java.sql.Timestamp(dt.getTime()).toString();
}
if(flCol.indexOf(field.getName())>=0)
{
List lstHigh= hlMap.get(solrDocument.get("id")).get(field.getName());
if(lstHigh!=null && lstHigh.size()>0)
val2=lstHigh.get(0);
}
BeanUtils.setProperty(t, field.getName(), val2);
}
}
list.add(t);
}
return list;
}
public final T getBean(Class clazz,
SolrDocument solrDocument) throws Exception {
//反射出实例
T t = clazz.newInstance();
//获取所有属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 如果注解为默认的 采用此属性的name来从solr中获取值
BeanUtils.setProperty(t, field.getName(),
solrDocument.get(field.getName()));
}
return t;
}
public static void main(String[] args)
{
String name = "七匹狼短袖T恤";
name = name.replaceAll("短袖", "短袖");
System.out.println(name);
}
}
package com.test.util;
import java.util.List;
/**
* 查询Solr返回的对象,对象类型为T的集合,还包含Solr中符合条件记录总数
* @param
*/
public class ResultInfo {
private List list = null;
private Long total = null;
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
}
}
SpringBoot启动类
package com.test.util;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@MapperScan("com.test.mapper")
@ComponentScan("com.test.ctrl,com.test.service.impl,com.test.util")
public class Starter {
public static void main(String[] args) {
SpringApplication.run(Starter.class, args);
}
}
菜单页面
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="/js/base.css">
<script type="text/javascript" src="/js/jquery.min.js">script>
<title>菜单列表title>
<script>
function checkall()
{
$('input[name="chkid"]').each(function(){
if(this.checked)
this.checked = false;
else
this.checked = true;
});
}
function doadd()
{
window.open('/addmenu','_self');
}
function doedit(id)
{
window.open('/addmenu?mid='+id,'_self');
}
function dodeletes()
{
var ids = '';
$('input[name="chkid"]:checked').each(function(){
ids = ids + this.value + ',';
});
$.ajax({
url:'/deletemenu?ids='+ids,
success:function(data){
window.open('/init','_self');
}
})
}
function dodelete(id)
{
$.ajax({
url:'/deletemenu?ids='+id,
success:function(data){
window.open('/init','_self');
}
})
}
function doquery()
{
var q = $('#query').val();
window.open('/init?name='+q,'_self');
}
function dohtml(id)
{
var url = '/html/'+id+'.html';
window.open(url,'_blank');
}
script>
head>
<body>
<div>
<table>
<tr>
<lable>名称查询:lable>
<input id="query" name="query" type="text" value="${query}"/>
<input type="button" onclick="doquery()" value="查询"/>
<input type="button" onclick="doadd()" value="新增"/>
<input type="button" onclick="dodeletes()" value="批量删除"/>
tr>
table>
<table>
<tr>
<th width="50"><input type="checkbox" id="chkall" name="chkall" onclick="checkall()"/>全选th>
<th>编号th>
<th>名称th>
<th>菜系th>
<th>价格th>
<th>上架时间th>
<th>操作th>
tr>
<c:forEach items="${menuList}" var="m">
<tr>
<td><input type="checkbox" id="chkid" name="chkid" value="${m.id}"/>td>
<td>${m.id}td>
<td>${m.name}td>
<td>${m.tname}td>
<td>${m.price}td>
<td>${m.dt}td>
<td>
<input type="button" value="编辑" onclick="doedit(${m.id})"/>
<input type="button" value="删除" onclick="dodelete(${m.id})"/>
<input type="button" value="查看" onclick="dohtml(${m.id})"/>
td>
tr>
c:forEach>
table>
${pagediv}
div>
body>
html>
添加菜单页面
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="/js/base.css">
<script type="text/javascript" src="/js/jquery.min.js">script>
<title>菜单列表title>
<script>
function dosave()
{
addfrm.submit();
}
function doback()
{
window.open('/init','_self');
}
script>
head>
<body>
<div>
<form name="addfrm" action="/savemenu" method="post">
<table>
<input type="hidden" id="id" name="id" value="${menu.id}"/>
<tr>
<td>名称td>
<td><input type="text" id="name" name="name" value="${menu.name}" }/>td>
tr>
<tr>
<td>价格td>
<td><input type="text" id="price" name="price" value="${menu.price}" />td>
tr>
<tr>
<td>上架时间td>
<td><input type="text" id="dt" name="dt" value="${menu.dt}" />td>
tr>
<tr>
<td>菜系td>
<td>
<c:forEach items="${typeList}" var="t">
<input type="checkbox" id="tid" name="tid" value="${t.id}" ${t.checked }>${t.name}
c:forEach>
td>
tr>
table>
<table>
<tr>
<input type="button" onclick="dosave()" value="保存"/>
<input type="button" value="返回" onclick="doback()"/>
tr>
table>
form>
div>
body>
html>
菜单模板
模板必须位于webapp/ftl目录下
<html>
<head>
<meta charset="UTF-8">
<title>菜单title>
head>
<body>
<h3>菜名:${name}h3>
<h3>价格:${price}h3>
<h3>菜系:${tname}h3>
<h3>上架时间:${dt}h3>
body>
html>
代码下载
https://github.com/qixiangchen/Proj_FtlSolrRedis.git