springboot之前已经介绍整合过了,今天来讲一下springboot对redis非关系型数据库的操作。redis非关系型数据库,完全不同于Oracle,MySQL,sqlserver 这种关系型数据库。
NOSQL(redis)与关系型数据库:
Oracle,MySQL,sqlserver是关系型数据库,Redis是K/V数据库,就是一个key,一个value,类似于Java里面的map类型,两者各有不同的应用领域,完全的说那个好也是不准确的,NoSQL虽然性能比RDMS要高,但它同时牺牲了很多有用的功能,所以要完全用NoSQL替换关系型数据库是非常困难的,但两者可以共存使用互补不足。以下简单的介绍一下redis数据类型,具体的可以看Redis官方API
Redis最为常用的数据类型主要有以下五种:
- String --字符串类型
- Hash --哈希
- List --集合
- Set
在这样看起来是不是很熟悉,Java里面操作的 list,set,String只要你明白,基本Redis的数据类型也就能够理解。
在常见的业务系统开发中,redis一般应用于,session缓存,数据高并发的读写,海量数据的读写,对扩展性要求高的数据,缓解关系型数据库高负荷的操作,防止因为高负荷的工作而使系统崩溃。
Redis使用场景:
-
配合关系型数据库做高速缓存
-
缓存高频次访问的数据,降低数据库io
-
分布式架构,做session共享
-
可以持久化特定数据。
-
利用zset类型可以存储排行榜
-
利用list的自然时间排序存储最新n个数据
主要就介绍这么多,下面开始上干货:
第一步:springboot shiro集成 pom.xml(里面也包含了Redis与shiro的集成jar包)
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inventory</groupId>
<artifactId>inventory</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>inventory</name>
<description>com.inventory</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>
<properties>
<guava.version>20.0</guava.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<redis-version>1.5.9.RELEASE</redis-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--Redis缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- ojdbc -->
<!-- <dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc</artifactId>
<version>6</version>
</dependency> -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- Json包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.16</version>
</dependency>
<!-- 为了监控数据库 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.25</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.14</version>
</dependency>
<!-- Junit 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--poi-Excel -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!-- gson包 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!-- shiro+redis缓存插件 -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>2.4.2.1-RELEASE</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
</plugins>
</build>
<repositories>
<repository>
<id>ali</id>
<name>ali Repository</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
第二步:shiro登录认证
public class MyShiroRealm extends AuthorizingRealm {
@Resource
@Lazy
private ISysUserService userInfoService;
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
SysUser userInfo = (SysUser) principals.getPrimaryPrincipal();
for (SysRole role : userInfo.getRoles()) {
authorizationInfo.addRole(role.getRoleCode());
for (SysPermission p : role.getPermissions()) {
authorizationInfo.addStringPermission(p.getPermissionCode());
}
}
return authorizationInfo;
}
/* 主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。 */
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("=============================================【开始认证["+SecurityUtils.getSubject().getSession().getId()+"]】==============================================");
// 获取用户的输入的账号.
String username = (String) token.getPrincipal();
// 通过username从数据库中查找 User对象,如果找到,没找到.
// 实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
SysUser userInfo = userInfoService.findByUsername(username);
if (userInfo == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userInfo, //用户名
userInfo.getPassword(), //密码
ByteSource.Util.bytes(userInfo.getSalt()),
getName() //realm name
);
return authenticationInfo;
}
}
第三步:加载config配置shiro类,里面主要是对shiro的配置代码以及redis作为session操作的缓存
@Configuration
public class ShiroConfig {
@SuppressWarnings("unchecked")
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("============ShiroConfiguration.shirFilter()已启动============");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/businessjs/**","anon");
filterChainDefinitionMap.put("/extjs/**","anon");
filterChainDefinitionMap.put("/fonts/**","anon");
filterChainDefinitionMap.put("/images/**","anon");
filterChainDefinitionMap.put("/jquery/**","anon");
filterChainDefinitionMap.put("/layui/**","anon");
filterChainDefinitionMap.put("/lodop/**","anon");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
//<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");
@SuppressWarnings("rawtypes")
Map map = new LinkedHashMap<>();
map.put("authc", new MyFormAuthenticationFilter());
shiroFilterFactoryBean.setFilters(map);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 凭证匹配器
* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
* )
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return hashedCredentialsMatcher;
}
@Bean
public MyShiroRealm myShiroRealm(){
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
/**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver
createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
mappings.setProperty("UnauthorizedException","403");
r.setExceptionMappings(mappings); // None by default
r.setDefaultErrorView("error"); // No default
r.setExceptionAttribute("ex"); // Default is "exception"
//r.setWarnLogCategory("example.MvcLogger"); // No default
return r;
}
/**
* ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
* @return
*/
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myShiroRealm());
// 自定义缓存实现 使用redis
securityManager.setCacheManager(cacheManager());
// 自定义session管理 使用redis
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* cacheManager缓存 redis实现
* 使用的是shiro-redis开源插件
* @return
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/**
* 配置shiro redisManager
* 使用的是shiro-redis开源插件
* @return
*/
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(PropUtil.getStr("spring.redis.host"));
redisManager.setPort(PropUtil.getInt("spring.redis.port"));
redisManager.setExpire(1800);// 配置缓存过期时间
redisManager.setTimeout(PropUtil.getInt("spring.redis.timeout"));
redisManager.setPassword(PropUtil.getStr("spring.redis.password"));
return redisManager;
}
/**
* Session Manager
* 使用的是shiro-redis开源插件
*/
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
/*sessionManager.setGlobalSessionTimeout(1800);
SecurityUtils.getSubject().getSession().setTimeout(-1000l);*/
return sessionManager;
}
/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
* 使用的是shiro-redis开源插件
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* 限制同一账号登录同时登录人数控制
*
* @return
*/
/* @Bean
public KickoutSessionControlFilter kickoutSessionControlFilter() {
KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
kickoutSessionControlFilter.setCacheManager(cacheManager());
kickoutSessionControlFilter.setSessionManager(sessionManager());
kickoutSessionControlFilter.setKickoutAfter(false);
kickoutSessionControlFilter.setMaxSession(1);
kickoutSessionControlFilter.setKickoutUrl("/auth/kickout");
return kickoutSessionControlFilter;
}*/
/**
* Shiro生命周期处理器
*
*/
@Bean
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
在这里提醒一下,springboot集成shiro可能会遇到很多问题。我列一下我在集成时遇到的问题
1.登录页面,不设置自己的登录页面,shiro会自动创建登录页面,所以在配置登录的权限的时候,要设置自己的登录页面以及action,还有就是静态资源问题,我在集成时候,在360安全浏览器静态资源可以正常显示,但在谷歌以及IE的时候,静态资源就会被拦截,其原因就是shiro权限框架拦截了静态资源,导致无法加载静态资源,所以在配置的时候要注意放开自己需要的静态资源
2.登陆成功,页面跳转不正确问题,有些人可能会遇到,我明明在shiro配置类设置了跳转成功的页面,咋就没有生效了,网上有很多种说法
经过上网查询:
1. http://www.cnblogs.com/yeming/p/5480639.html
2. http://www.bubuko.com/infodetail-1421844.html (有解释)
我重写了重写 FormAuthenticationFilter 父类的 issueSuccessRedirect 方法,设置了登录成功跳转的url
@Filter(name = "MyFormAuthenticationFilter")
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
// 制定session跳转url
private final String successUrl = "/index";
protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {
WebUtils.issueRedirect(request, response, successUrl, null, true);
}
}
第四步:redis的集成:
@Configuration
@EnableCaching
public class RedisConfiguration extends CachingConfigurerSupport {
/**
* Logger
*/
private static final Logger lg = LoggerFactory.getLogger(RedisConfiguration.class);
@Autowired
private JedisConnectionFactory jedisConnectionFactory;
//缓存管理器
@Bean
public CacheManager cacheManager() {
// 初始化缓存管理器,在这里我们可以缓存的整体过期时间什么的,我这里默认没有配置
lg.info("初始化 -> [{}]", "CacheManager RedisCacheManager Start");
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(jedisConnectionFactory);
return builder.build();
}
@Bean
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(method.getName());
for (Object obj : params) {
sb.append(":" + String.valueOf(obj));
}
String rsToUse = String.valueOf(sb);
return rsToUse;
};
}
/**
* 配置StringRedisTemplate实体
* @return
*/
@Bean
public StringRedisTemplate stringRedisTemplate(JedisConnectionFactory jedisConnectionFactory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(jedisConnectionFactory);
return stringRedisTemplate;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory ) {
//设置序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
@SuppressWarnings("rawtypes")
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer); // key序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // value序列化
redisTemplate.setHashKeySerializer(stringSerializer); // Hash key序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); // Hash value序列化
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@SuppressWarnings("deprecation")
@Bean
JedisConnectionFactory jedisConnectionFactory() {
lg.info("Create JedisConnectionFactory successful");
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(PropUtil.getStr("spring.redis.host"));
factory.setPort(PropUtil.getInt("spring.redis.port"));
factory.setTimeout(PropUtil.getInt("spring.redis.timeout"));
factory.setPassword(PropUtil.getStr("spring.redis.password"));
return factory;
}
@Bean
public JedisPool redisPoolFactory() {
lg.info("JedisPool init successful,host -> [{"+PropUtil.getStr("spring.redis.host")+"}];port -> [{"+PropUtil.getInt("spring.redis.port")+"}]");
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(PropUtil.getInt("spring.redis.jedis.pool.max-idle"));
jedisPoolConfig.setMaxWaitMillis(PropUtil.getInt("spring.redis.jedis.pool.max-wait"));
JedisPool jedisPool = new JedisPool(jedisPoolConfig, PropUtil.getStr("spring.redis.host"), PropUtil.getInt("spring.redis.port"),
PropUtil.getInt("spring.redis.timeout"), PropUtil.getStr("spring.redis.password"));
return jedisPool;
}
@Bean
public CacheErrorHandler errorHandler() {
// 异常处理,当Redis发生异常时,打印日志,但是程序正常走
lg.info("初始化 -> [{}]", "Redis CacheErrorHandler");
CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
lg.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
}
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
lg.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
}
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
lg.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
}
public void handleCacheClearError(RuntimeException e, Cache cache) {
lg.error("Redis occur handleCacheClearError:", e);
}
};
return cacheErrorHandler;
}
在这边也需要提醒下:针对redis与shiro集成遇到的问题,我使用的是jpa数据访问,会出现登录session存入Redis缓存中,其他的查询缓存失效,这个原因是因为需要登录认证需要设置一下懒加载,这样就不会出现查询缓存失效,在第二步的
这都是我在集成遇到的一些奇奇怪怪的问题,启用缓存,需要在service实现类里面写上spring缓存注解
@Service
@CacheConfig(cacheNames = "user")
public class SysUserServiceImpl implements ISysUserService{
@Resource
public SysUserDao userDao;
@Cacheable(value ="findUserByName")
public List<SysUser> findUserByName(String name) {
@SuppressWarnings("unchecked")
List<SysUser> list = userDao.findAll(new SimpleSpecificationBuilder<Object>("name","=",name).generateSpecification());
return list;
}
@Cacheable(value="PageUserList")
public PageTag<SysUser> queryUserList(int currentNum,String displayName, Long isStop, Long sex) {
String hql = "select user from SysUser user where 1=1";
if (displayName!=null&&!"".equals(displayName)) {
hql+=" and displayName = "+displayName;
}
if (isStop!=null&&!"".equals(isStop)) {
hql+=" and isdel = "+isStop;
}
if (sex!=null&&!"".equals(sex)) {
hql+=" and sex = "+sex;
}
PageTag<SysUser> pageList = userDao.findPage(currentNum, hql, null);
return pageList;
}
@Cacheable(key ="#p0")
public SysUser findUserInfo(Long Id) {
SysUser user = userDao.findUserById(Id);
return user;
}
@Caching(evict = { @CacheEvict(key="#p0",allEntries=true,beforeInvocation=true),
@CacheEvict(value="PageUserList",allEntries=true,beforeInvocation=true),
@CacheEvict(value="userTotalCount",allEntries=true,beforeInvocation=true),
@CacheEvict(value="userList",allEntries=true,beforeInvocation=true)})
public void deleteUser(Long Id) {
userDao.deleteById(Id);
}
@Caching(evict = {@CacheEvict(value="PageUserList",allEntries=true,beforeInvocation=true),
@CacheEvict(value="userTotalCount",allEntries=true,beforeInvocation=true),
@CacheEvict(value="userList",allEntries=true,beforeInvocation=true)},
put={@CachePut(key="#p0.id")})
public SysUser saveUser(SysUser user) {
userDao.save(user);
return user;
}
@Caching(evict = {@CacheEvict(value="PageUserList",allEntries=true,beforeInvocation=true),
@CacheEvict(value="userTotalCount",allEntries=true,beforeInvocation=true),
@CacheEvict(value="userList",allEntries=true,beforeInvocation=true)},
put={@CachePut(key="#p0.id")})
public SysUser updateUser(SysUser user) {
userDao.save(user);
return user;
}
@Cacheable(value ="userList")
public List<SysUser> findUserList() {
List<SysUser> list =userDao.findAll();
return list;
}
public SysUser findByUsername(String username) {
SysUser user = userDao.findByUsername(username);
return user;
}
@Cacheable(value="userTotalCount")
public Long queryUserTotalCount(String displayName, Long isStop, Long sex) {
String sql = "select count(su.user_id) count from sys_user su where 1=1 ";
if (displayName!=null&&!"".equals(displayName)) {
sql+=" and su.display_name like '%"+displayName+"%'";
}
if (isStop!=null&&!"".equals(isStop)) {
sql+=" and su.isdel ="+isStop;
}
if (sex!=null&&!"".equals(sex)) {
sql+=" and su.sex ="+sex;
}
return userDao.queryCountBySQL(sql);
}
还有就是对jpa的封装,这次只讲一下分页查询封装,我比较习惯于自定义查询,使用hql语句,所以我写的查询封装也是基于hql的查询封装。当然也有基于sql语句的封装:
@NoRepositoryBean
public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID> {
/**
* sql 返回实体对象
* @param sql
* @param cls
* @return
*/
public List<T> listBySQL(String sql, Class<T> cls);
/**
* sql 返回字符串,单个字段
* @param sql
* @return
*/
public String StringBySQL(String sql);
/**
* sql查询返回map类型的list
* @param sql
* @return
*/
public List findListBySql(String sql);
/**
* sql 返回字符串,单个字段
* @param sql
* @return
*/
public Long queryCountBySQL(String sql);
/**
* 执行ql语句,封装自定义 分页page
* @param page
* @param hql qlString 基于jpa标准的ql语句
* @param countHql 额外查询获取总记录数,使用默认则填null
* @return
*/
public PageTag<T> findPage(int currentNum,String hql,String countHql);
/**
* list 查询
* @param hql
* @return
*/
public List<T> findListByhql(String hql);
}
实现类:
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T,ID>
implements BaseRepository<T,ID> {
private final EntityManager entityManager;
//父类没有不带参数的构造方法,这里手动构造父类
public BaseRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
}
//通过EntityManager来完成查询
public List<T> listBySQL(String sql, Class<T> cls) {
Query query =entityManager.createNativeQuery(sql,cls);
return query.getResultList();
}
public String StringBySQL(String sql) {
Query query =entityManager.createNativeQuery(sql);
return query.getResultList().get(0).toString();
}
public List findListBySql(String sql) {
Query query = entityManager.createNativeQuery(sql);
query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List list = query.getResultList();
return list;
}
public Long queryCountBySQL(String sql) {
Query query =entityManager.createNativeQuery(sql);
return Long.valueOf(query.getResultList().get(0).toString());
}
public PageTag<T> findPage(int currentNum,String hql, String countHql) {
Assert.notNull(hql, "findPage() hql must not be null!");
PageTag<T> page = new PageTag<T>();
Query query = entityManager.createQuery(hql);
Long totalCount = (long) -1;
if(countHql == null || countHql ==""){
totalCount = (long) query.getResultList().size();
}else{
Query countQuery = this.entityManager.createQuery(countHql,Long.class);
totalCount = (Long) countQuery.getSingleResult();
}
if (page != null) { // 分页
// firstResult的序号从0开始
page.setPageNo(currentNum);
query.setFirstResult(page.getFirst() - 1);
if (page.getPageSize() != -1)
query.setMaxResults(page.getPageSize());
}
page.setResult(query.getResultList());
page.setTotalCount(totalCount);
return page;
}
public List<T> findListByhql(String hql) {
Assert.notNull(hql, "findListByhql hql must not be null!");
PageTag<T> page = new PageTag<T>();
Query query = entityManager.createQuery(hql);
List<T> list = query.getResultList();
return list;
}
public class BaseRepositoryFactoryBean<R extends JpaRepository<T, I>, T,I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
public BaseRepositoryFactoryBean(Class<? extends R> repositoryInterface) {
super(repositoryInterface);
// TODO Auto-generated constructor stub
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
return new BaseRepositoryFactory(em);
}
//创建一个内部类,该类不用在外部访问
private static class BaseRepositoryFactory<T, I extends Serializable>
extends JpaRepositoryFactory {
private final EntityManager em;
public BaseRepositoryFactory(EntityManager em) {
super(em);
this.em = em;
}
//设置具体的实现类是BaseRepositoryImple
protected Object getTargetRepository(RepositoryInformation information) {
return new BaseRepositoryImpl<T, I>((Class<T>) information.getDomainType(), em);
}
//设置具体的实现类的class
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseRepositoryImpl.class;
}
}
创建一个自定义的工厂,在这个工厂中注册我们自己定义的BaseRepositoryImpl的实现。
写完这些基本对jpa的封装已经简单初步实现,后期可以自己进行封装
下面需要在到dao层去继承写好的封装类
@Repository
public interface SysUserDao extends BaseRepository<SysUser,Long>, JpaSpecificationExecutor<SysUser>{
/**
* 通过用户名查找用户信息
* @param username
* @return
*/
SysUser findByUsername(String username);
SysUser findUserById(Long id);
}
最最重要的一步就是启用你写好的封装类,在启动类application上加上注解:@EnableJpaRepositories(basePackages = {"com.inventory"},
repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class//指定自己的工厂类
)
这样就基本搭建好一个springboot+shiro+jpa+thymeleaf+整合redis缓存
前端我自己学习的layui语法,给大家展示一下页面效果,如果有需要前端架构的话,我下期会讲一下layui的一些语法,以及自己使用的心得体会,包括怎么最大化的简化前端代码,layui封装,layui弹出框封装以及皮肤制作,thymeleaf语法的介绍,以及怎么使用thymeleaf去展示多级列表