springboot+shiro+jpa整合redis缓存

本文介绍Spring Boot对Redis非关系型数据库的操作,对比了Redis与关系型数据库的差异,阐述了Redis常用数据类型和使用场景。还详细讲述了Spring Boot集成Shiro和Redis的步骤,分享了集成过程中遇到的问题及解决办法,最后提及了JPA封装和前端Layui相关内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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使用场景:

  1. 配合关系型数据库做高速缓存

  • 缓存高频次访问的数据,降低数据库io

  • 分布式架构,做session共享

  1. 可以持久化特定数据。

  • 利用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去展示多级列表

<think>首先,用户的问题是:"我想解决Vue2项目中Networkunavailable的问题请问Vue2Networkunavailable解决方案"。这是一个关于Vue.js开发中常见的问题,其中在运行项目时,Network显示为unavailable,意味着无法通过本地网络访问项目。从系统级指令,我需要:-所有行内数学表达式用$...$格式(但在这个问题中,可能不涉及数学,所以可能不需要)。-独立公式用$$...$$(同样,可能不适用)。-LaTeX语法正确。-使用中文回答。-生成相关问题。-在回答中引用的段落末尾添加引用标识,如[^1]。用户可见层的要求:-回答结构清晰,逐步解决问题。-保证回答真实可靠。参考的站内引用提供了几个解决方案:-引用[1]:提到了三种情况:多个网卡、配置host和public、系统环境变量问题。给出了在vue.config.js中配置devServer的方法。-引用[2]:类似,强调配置public字段和检查IP地址。-引用[3]:专注于系统环境变量,添加C:\Windows\System32\Wbem到Path。-引用[4]:再次提到在devServer中添加public。-引用[5]:建议在devServer中配置public和disableHostCheck。关键点总结:1.**多个网卡问题**:禁用多余的网络适配器。2.**配置host和public**:在vue.config.js文件中设置devServer的host和public。3.**系统环境变量问题**:添加C:\Windows\System32\Wbem到Path环境变量。我的回答应该结构清晰:-先解释问题:为什么Network显示unavailable?-然后分步给出解决方案。-确保解决方案可靠,基于引用。结构建议:1.**引言**:简述问题。2.**常见原因**:列出可能导致问题的原因。3.**解决方案**:分步骤详细说明。4.**验证**:如何确认问题解决。5.**相关问题**:生成几个相关问题。生成相关问题:-基于主题,例如:1.如何查找本机IP地址?2.Vue2项目中devServer的其他配置选项?3.如果环境变量修改无效,还有什么其他方法?确保使用中文回答。最终回答框架:-**问题描述**:Vue2项目运行npmrunserve后,Network显示unavailable,无法通过IP访问。-**原因分析**:通常由于网络配置、VuedevServer设置或系统环境问题。-**解决方案**:a.**检查网络适配器**:禁用多余网卡。b.**配置vue.config.js**:设置devServer的host和public。c.**修改环境变量**:添加Wbem路径到Path。-**步骤详解**:每一步给出具体操作。-**相关问题**:在末尾添加。引用标识:在引用内容后添加[^1],[^2]等,但引用是来自用户提供的,所以我应该引用那些内容。</think>### Vue2项目中Network: unavailable解决方案 在Vue2项目中运行`npm run serve`后出现**Network: unavailable**问题,表示项目无法通过本地网络IP访问(只能通过`localhost`访问)。这通常由网络配置冲突、Vue开发服务器设置不当或系统环境变量问题引起。以下是基于可靠实践的分步解决方案(参考社区常见修复方法[^1][^2][^3][^4][^5])。 --- ### 原因分析 - **多个网络适配器冲突**:电脑有多个网卡(如Wi-Fi、以太网、虚拟机网卡),导致Vue无法正确绑定IP[^1][^2]。 - **devServer配置缺失**:Vue的`devServer`未指定`public`字段或IP地址错误[^1][^4][^5]。 - **系统环境变量异常**:缺少`C:\Windows\System32\Wbem`路径,影响网络管理功能[^1][^3][^5]。 ### 分步解决方案 按照以下步骤操作,问题通常可解决(优先尝试方法1,若无效再逐步排查)。 #### 方法1: 配置Vue项目的devServer(推荐) 此方法直接修改Vue开发服务器设置,确保绑定正确IP。 1. **查找本机IP地址**: - 方法A:按`Win+R`,输入`cmd`打开命令提示符,执行`ipconfig`,找到`IPv4地址`(如`192.168.1.2`)[^2][^4]。 - 方法B:在“控制面板 > 网络和共享中心 > 网络连接”中查看活动网络的IPv4地址[^2]。 2. **修改vue.config.js文件**: -Vue项目根目录下创建或编辑`vue.config.js`文件。 - 添加以下配置(替换`public`值为你的IP和端口): ```javascript module.exports = { devServer: { host: '0.0.0.0', // 允许所有主机访问 public: '192.168.1.2:8080', // 改为你的IP和端口(如192.168.1.2:8080) port: '8080', // 端口号 https: false, // 禁用HTTPS disableHostCheck: true, // 关闭主机检查 open: false // 不自动打开浏览器 } }; ``` - **注意**:`public`字段必须与`ipconfig`显示的IP一致;如果网络切换(如从Wi-Fi换到热点),需更新IP[^2][^4][^5]。 3. **重启项目**: - 在VSCode终端运行: ```bash npm run serve ``` - 检查终端输出,Network应显示可用IP(如`http://192.168.1.2:8080`)。 #### 方法2: 禁用多余网络适配器 如果多个网卡导致冲突,精简网络配置。 1. 打开“控制面板 > 网络和共享中心 > 更改适配器设置”。 2. 禁用所有未使用的网络连接(如虚拟机网卡、蓝牙网络),只保留当前活动的连接(如Wi-Fi)[^1][^2]。 3. 重启VSCode并重新运行`npm run serve`。 #### 方法3: 修复系统环境变量 若上述无效,可能是系统Path变量缺失关键路径。 1. 打开环境变量设置: - `Win+R`输入`sysdm.cpl` → 高级 → 环境变量。 - 或在“设置 > 系统 > 高级系统设置”中找到[^3]。 2. 编辑系统变量: - 在“系统变量”列表中选择`Path` → 点击“编辑”。 - 添加新条目:`C:\Windows\System32\Wbem`(确保无拼写错误)[^1][^3][^5]。 3. 保存并重启: - 保存所有更改,重启电脑(或至少重启VSCode)。 - 重新运行项目,查看Network状态。 --- ### 验证问题解决 - **成功标志**:终端运行`npm run serve`后,Network显示类似`Network: http://192.168.1.2:8080`。 - **测试访问**:在同一局域网的手机或其他电脑,浏览器输入`http://<你的IP>:8080`,应能访问项目- **常见失败原因**: - IP地址错误:确保`public`字段IP与`ipconfig`一致[^4]。 - 防火墙阻拦:临时禁用防火墙测试。 - Vue版本问题:Vue CLI需更新至最新(运行`npm update -g @vue/cli`)。 > **注意**:这些方法基于Vue社区常见实践,成功率较高。如果问题持续,建议检查项目依赖(`npm install`)或操作系统更新[^1][^3][^5]。 --- ### 相关问题 1. **如何查找本机的准确IP地址?** 2. **Vue2的devServer还有哪些常用配置选项?** 3. **如果环境变量修复无效,还有什么其他解决方案?** 4. **Network: unavailable问题是否在Vue3中也存在?如何解决?** [^1]: 多个网卡原因或环境变量问题可能导致此错误,需禁用多余网络或添加Wbem路径到Path变量。 [^2]: 配置devServer的public字段是关键,IP地址需通过ipconfig或网络中心获取。 [^3]: 系统环境变量中添加C:\Windows\System32\Wbem可修复网络管理功能缺失。 [^4]: public字段必须与当前网络IP一致,切换网络后需更新。 [^5]: 在devServer中设置disableHostCheck: true有助于绕过主机检查。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值