这篇文章,我将 把一个 spring+hibernate+shiro+Ehcache 完整的集成流程记录下来,源码亦将分享出来(项目源码地址: http://download.youkuaiyun.com/download/u011998835/10038869 点击打开链接)。但是好像csdn传资源必须要指定积分的,所以我不得已选择了2分。但这里不要在意细节了。我主要是记录,遇到问题和开发心得。
那么我们开始使用工具编写项目了。
因为DAdmin这个项目我已经新建过了,所以接下来有些演示 就用DDAdmin来替代了。
先打开File,找到Project Structure 先设置一些东西
新建java文件夹
指定java作为java代码的源码目录
新建test目录
指定成为测试目录
不要忘了设定jdk7了,默认是用jdk5编译的,哪怕你提供了jdk环境,但编译用jdk5,那么有些泛型新写法就不能使用了。
然后OK或者apply
然后看到的项目结构如下。
写配置pom.xml吧(这个 过程 就是 在把相关的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.dashuai</groupId>
<artifactId>DAdmin</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<properties>
<java_version>1.7</java_version>
<java-encoding_version>UTF-8</java-encoding_version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring_version>4.3.12.RELEASE</spring_version>
<hibernate_version>4.3.11.Final</hibernate_version>
<shiro_version>1.3.2</shiro_version>
<mysql_connector_version>5.1.44</mysql_connector_version>
<druid-version>1.0.31</druid-version>
</properties>
<repositories>
<repository>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<dependencies>
<!-- Junit 4 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Common libs start -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<!-- Common libs end -->
<!--启动CGlib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
</dependency>
<!-- log日志 start -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!-- log日志 end -->
<!-- Spring相关jar包 start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.11</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>
<!-- Spring相关jar包 end -->
<!--mysql 驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql_connector_version}</version>
</dependency>
<!-- 数据源 druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid-version}</version>
</dependency>
<!--json GSON 包 -->
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.5</version>
</dependency>
<!-- jsp,servlet,jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--工具包,加密算法 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.4</version>
</dependency>
<!-- Hibernate start -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate_version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>${hibernate_version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
</dependency>
<!-- Hibernate END -->
<!--Hibernate与spring 集成所依赖的 -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.20.0-GA</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro_version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro_version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro_version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro_version}</version>
</dependency>
</dependencies>
<build>
<finalName>DAdmin</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
<charset>UTF-8</charset>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
先看一下未来呈现的项目结构
我们一样一样的来写。
先新建java的文件夹,就是各种package。
然后新建resource目录下的文件,暂时先新建着空文件。然后再填东西。
好,接下来,我们先从web.xml入手。
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Spring-Hibernate Web Application</display-name>
<welcome-file-list>
<welcome-file></welcome-file>
</welcome-file-list><!--暂不指定欢迎页面-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-shiro.xml,
classpath:spring/applicationContext.xml
</param-value>
</context-param>
<!--spring与shiro配置的spring-shiro.xml一定要指定,不然shiro不一定发挥作用-->
<!-- // Hibernate4 延迟加载策略 -->
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 防止发生java.beans.Introspector内存泄露,应将它配置在ContextLoaderListener的前面 -->
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<!-- /Spring MVC支持配置 ///-->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- /Spring 解决乱码问题/// -->
<filter>
<filter-name>SpringEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SpringEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- //阿里巴巴数据连接池 Druid的监控/// -->
<filter>
<filter-name>druidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<init-param>
<param-name>exclusions</param-name>
<param-value>
/css/*,/style/*,/js/*,*.js,*.css,/druid*,/attached/*,*.jsp
</param-value>
</init-param>
<init-param>
<param-name>principalSessionName</param-name>
<param-value>sessionUser</param-value>
</init-param>
<init-param>
<param-name>profileEnable</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>druidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- //druid监控页面,使用${pageContext.request.contextPath}/druid/index.html访问/ -->
<servlet>
<servlet-name>druidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>druidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
<!-- shiroFilter 不能放在 Hibernate的filter之前,或者druid之前,建议放在最后面-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<session-config>
<session-timeout>120</session-timeout>
</session-config>
</web-app>
ehcache.xml
<ehcache name="shiro_ehcache" updateCheck="false">
<diskStore path="../DAdmin_Ehcache"/>
<!-- hibernate ehcache 和 shiro_ehcache 必须放在同一个xml中配置
不要分开放,不然只会有一个生效。name="shiro_ehcache",可以不用配置。(我当时分开使用了)
-->
<!-- hibernate ehcache-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="true"/>
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxElementsInMemory="10000"
eternal="false"
timeToLiveSeconds="120"
overflowToDisk="true"/>
<!-- shiro ehcache-->
<!-- 登录记录缓存 锁定10分钟 -->
<cache name="passwordRetryCache"
maxElementsInMemory="2000" eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<cache name="authorizationCache"
maxElementsInMemory="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<cache name="authenticationCache"
maxElementsInMemory="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<cache name="shiro-activeSessionCache"
maxElementsInMemory="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<cache name="shiro_cache"
maxElementsInMemory="2000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
maxElementsOnDisk="0"
overflowToDisk="true"
memoryStoreEvictionPolicy="FIFO"
statistics="true">
</cache>
</ehcache>
log4j.properties:
log4j.rootLogger=INFO,CONSOLE,DAILY_ALL
#console log
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %l - %m%n
log4j.appender.CONSOLE.Target=System.out
#all log
log4j.appender.DAILY_ALL=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DAILY_ALL.layout=org.apache.log4j.PatternLayout
log4j.appender.DAILY_ALL.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %l - %m%n
log4j.appender.DAILY_ALL.File=../logs/DAdmin.log
hibernate的log配置
#log4j.logger.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
#log4j.loggerorg.hibernate.type.descriptor.sql.BasicExtractor=TRACE
#log4j.logger.org.hibernate.engine.QueryParameters=DEBUG
#log4j.logger.org.hibernate.engine.query.HQLQueryPlan=DEBUG
#log4j.logger.org.hibernate.cache=DEBUG
database.properties
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.dialect=org.hibernate.dialect.MySQLDialect
driverClassName=com.mysql.jdbc.Driver
jdbc_url=jdbc\:mysql\://localhost\:3306/dadmin?useUnicode\=true&characterEncoding\=UTF-8
jdbc_username=root
jdbc_password=123456
hibernate.hbm2ddl.auto=update
hibernate.query.substitutions=true 1, false 0
hibernate.default_batch_fetch_size=16
hibernate.max_fetch_depth=2
hibernate.generate_statistics=false
hibernate.enable_lazy_load_no_trans=true
initialSize=3
maxActive=50
minIdle=1
maxWait=60000
validationQuery=SELECT 1
testOnBorrow=false
testOnReturn=false
testWhileIdle=true
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=25200000
removeAbandoned=false
removeAbandonedTimeout=1800
logAbandoned=true
filters=mergeStat
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<context:component-scan base-package="com.dashuai.service,com.dashuai.dao,com.dashuai.shiro"/>
<!-- //强制启用CGliB 替换JDK的动态代理 在反射动态生成时 效率更高/// -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<!-- 读取数据库基本连接配置 -->
<context:property-placeholder location="classpath:database.properties" />
<!-- 阿里巴巴的Druid数据库连接池日志过滤 下面 baseDataSource会使用到的 -->
<bean id="statFilter" class="com.alibaba.druid.filter.stat.StatFilter"
lazy-init="true">
<property name="logSlowSql" value="true" />
<property name="mergeSql" value="true" />
</bean>
<!-- 数据库连接池的配置base dataSource -->
<bean name="baseDataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<!-- 初始化连接大小 -->
<property name="initialSize" value="${initialSize}" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="${maxActive}" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="${minIdle}" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="${maxWait}" />
<property name="validationQuery" value="${validationQuery}" />
<property name="testOnBorrow" value="${testOnBorrow}" />
<property name="testOnReturn" value="${testOnReturn}" />
<property name="testWhileIdle" value="${testWhileIdle}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
<!-- 打开removeAbandoned功能 -->
<!-- 配置removeAbandoned对性能会有一些影响,建议怀疑存在泄漏之后再打开。 在上面的配置中,如果连接超过30分钟未关闭, 就会被强行回收,并且日志记录连接申请时的调用堆栈。 -->
<property name="removeAbandoned" value="${removeAbandoned}" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="${logAbandoned}" />
<!-- 合并多个DruidDataSource的监控数据 -->
<property name="useGlobalDataSourceStat" value="true" />
<property name="proxyFilters">
<list>
<ref bean="statFilter" />
</list>
</property>
</bean>
<bean name="master-dataSource" parent="baseDataSource"
init-method="init">
<property name="name" value="master" />
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_username}" />
<property name="password" value="${jdbc_password}" />
<property name="filters" value="${filters}" />
</bean>
<!-- 配置sessionFactory 并配置二级缓存 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="master-dataSource" />
<property name="packagesToScan">
<list>
<value>com.dashuai.entity</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="javax.persistence.validation.mode">none</prop>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.query.substitutions">${hibernate.query.substitutions}</prop>
<prop key="hibernate.default_batch_fetch_size">${hibernate.default_batch_fetch_size}</prop>
<prop key="hibernate.max_fetch_depth">${hibernate.max_fetch_depth}</prop>
<prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
<prop key="hibernate.enable_lazy_load_no_trans">${hibernate.enable_lazy_load_no_trans}</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">classpath:ehcache.xml</prop>
<!-- 开启查询缓存 -->
<prop key="hibernate.cache.use_query_cache">true</prop>
<!-- 开启二级缓存 -->
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<!-- 高速缓存提供程序 -->
<!-- 由于spring也使用了Ehcache, 保证双方都使用同一个缓存管理器 -->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
</props>
</property>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:ehcache.xml</value>
</property>
<!-- 由于hibernate也使用了Ehcache, 保证双方都使用同一个缓存管理器 -->
<property name="shared" value="true"/>
</bean>
<!-- 开启事务 配置transactionManager -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* com.dashuai.service..*Impl.*(..))" />
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
</aop:config>
<!-- 设置所有方法都使用事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" propagation="REQUIRED"/><!--之前是NOT_SUPPORT-->
<tx:method name="list*" read-only="true" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="REQUIRED"/>
<tx:method name="query*" read-only="true" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<!--默认其他方法都是REQUIRED-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
</beans>
applicationContext-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.dashuai.controller"/>
<!-- 启用springmvc 的注解 -->
<mvc:annotation-driven />
<!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<!-- Model与View 的切换配置 -->
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="contentType" value="text/html"/>
<property name="prefix" value="/WEB-INF/page/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 不拦截静态请求,如css,js -->
<mvc:default-servlet-handler />
<!-- 上传文件的配置 虽然本项目没有用到上传文件-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
<mvc:view-controller path="/" view-name="front/index"/>
<mvc:view-controller path="/admin" view-name="admin/index"/>
<mvc:view-controller path="/admin/" view-name="admin/index"/>
<mvc:view-controller path="/user" view-name="front/user/index"/>
<mvc:view-controller path="/user/" view-name="front/user/index"/>
</beans>
spring-shiro.xml 这个复杂,我们一点点的看。 首先web.xml中配置了shiroFilter,所以一定有shiroFilter。然后就是spring和shiro集成之后的配置。
<!-- 安全认证过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="filters">
<map>
<!--自定义两个 登录 过滤器 第一个是 后台登陆认证过滤,第二个是前台 登录认证过滤 -->
<entry key="authsys" value-ref="systemAuthFilter"></entry>
<entry key="authnor" value-ref="normalAuthFilter"></entry>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/res_admin/**=anon
/res_front/**=anon
/plugins/**=anon
/login.html=anon
/admin/login.*=anon
/admin/**=authsys
/user/**=authnor
/**=anon
</value>
</property>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
这两个必须要有的。
然后就是一点点看了。<!-- 定义Shiro安全管理配置 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
<property name="authenticator" ref="defineModularRealmAuthenticator" />
<!-- 这里主要是设置自定义的单Realm应用,若有多个Realm,可使用'realms'属性代替 -->
<!-- <property name="realm" ref="loginRealm"/> -->
<property name="realms" >
<list> <!-- 前台和后台的登录认证和权限授权认证 -->
<ref bean="systemUserRealm" />
<ref bean="normalUserRealm"/>
</list>
</property>
<property name="sessionManager" ref="sessionManager" />
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
我们在接着看memoryConstrainedCacheManer。
<bean id="memoryConstrainedCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<bean id="defineModularRealmAuthenticator" class="com.dashuai.shiro.DefautModularRealm">
<property name="definedRealms">
<map>
<entry key="systemAuthorizingRealm" value-ref="systemUserRealm" />
<entry key="normalAuthorizingRealm" value-ref="normalUserRealm" />
</map>
</property>
<property name="authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
</property>
</bean>
<!-- Realm 域 授权和认证的判断 -->
<bean id="systemUserRealm" class="com.dashuai.shiro.SystemUserRealm" />
<bean id="normalUserRealm" class="com.dashuai.shiro.NormalUserRealm" />
// 下面的java代码有的我注释了,是因为注释的代码 是需要根据不同需求自行编写的。
package com.dashuai.shiro;
import org.apache.log4j.Logger;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.util.CollectionUtils;
import java.util.Collection;
import java.util.Map;
/**
* 管理多个Realms
*/
public class DefautModularRealm extends ModularRealmAuthenticator {
private static final Logger log = Logger.getLogger(DefautModularRealm.class);
private Map<String, Object> definedRealms;
/**
* 多个realm实现
*/
@Override
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {
return super.doMultiRealmAuthentication(realms, token);
}
/**
* 调用单个realm执行操作
*/
@Override
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm,AuthenticationToken token) {
log.info("DefautModularRealm doSingleRealmAuthentication Shiro 认证执行的第 3 步 " +token);
// 如果该realms不支持(不能验证)当前token
if (!realm.supports(token)) {
throw new ShiroException("token错误!");
}
AuthenticationInfo info = realm.getAuthenticationInfo(token);
log.info("DefautModularRealm doSingleRealmAuthentication info " +info);
return info;
}
/**
* 判断登录类型执行操作
*/
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)throws AuthenticationException {
log.info("DefautModularRealm doAuthenticate Shiro 认证执行的第二步 ");
this.assertRealmsConfigured();
Realm realm = null;
CustomUsernamePasswordToken token = (CustomUsernamePasswordToken) authenticationToken;
//判断是否是后台用户
if (token.getLoginType()==CustomUsernamePasswordToken.LOGIN_ADMIN_TYPE) {
realm = (Realm) this.definedRealms.get("systemAuthorizingRealm");// 查看shiro的配置
}
else if (token.getLoginType()==CustomUsernamePasswordToken.LOGIN_FRONT_TYPE){
realm = (Realm) this.definedRealms.get("normalAuthorizingRealm");
}
log.info("doAuthenticate ==> "+realm);
return this.doSingleRealmAuthentication(realm, authenticationToken);
}
/**
* 判断realm是否为空
*/
@Override
protected void assertRealmsConfigured() throws IllegalStateException {
this.definedRealms = this.getDefinedRealms();
if (CollectionUtils.isEmpty(this.definedRealms)) {
throw new ShiroException("值传递错误!");
}
}
public Map<String, Object> getDefinedRealms() {
return this.definedRealms;
}
public void setDefinedRealms(Map<String, Object> definedRealms) {
this.definedRealms = definedRealms;
}
}
package com.dashuai.shiro;
import org.apache.shiro.authc.UsernamePasswordToken;
public class CustomUsernamePasswordToken extends UsernamePasswordToken {
public static final int LOGIN_ADMIN_TYPE=1000; // 后台
public static final int LOGIN_FRONT_TYPE=999; // 前台
private int loginType; // 是否是系统后台用户还是前台用户
public int getLoginType() {
return loginType;
}
public void setLoginType(int loginType) {
this.loginType = loginType;
}
public CustomUsernamePasswordToken (String username, String password, boolean rememberMe, int loginType) {
super(username, password, rememberMe);
this.loginType = loginType;
}
}
package com.dashuai.shiro;
import com.dashuai.entity.system.SystemRole;
import com.dashuai.entity.system.SystemUser;
import com.dashuai.service.system.SystemRoleService;
import com.dashuai.service.system.SystemUserRoleService;
import com.dashuai.service.system.SystemUserService;
import com.dashuai.util.ConstantUtil;
//import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* 系统用户领域(后台用户操作)
*/
public class SystemUserRealm extends AuthorizingRealm {
private static final Logger log = LoggerFactory.getLogger(SystemUserRealm.class);
// @Autowired
// private SystemUserService systemUserService;
// @Autowired
// private SystemRoleService systemRoleService;
// @Autowired
// private SystemUserRoleService systemUserRoleService;
/**
* Shiro权限认证,即权限授权
* 会进入授权方法一共有三种情况!
1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
2、@RequiresRoles("admin") :在方法上加注解的时候;
3、[@shiro.hasPermission name = "admin"][/@shiro.hasPermission]:在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("SystemUserRealm Shiro请求授权 ");
// SystemUser systemUser = (SystemUser) SecurityUtils.getSubject().getPrincipal();
// SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// if(systemUser.getIsRoot().equals(ConstantUtil.IS_ROOT)){
// Collection<String> roles = new ArrayList<>();
// List<SystemRole> list = systemRoleService.getRootAllRoles();
// for(SystemRole systemRole:list){
// roles.add(systemRole.getRoleName());
// }
// info.addRoles(roles);
// log.info("SystemUserRealm Root 获取角色:{} ", Arrays.toString(info.getRoles().toArray()));
// }else{
// // 非root用户
// info.addRoles(systemUserRoleService.getAllRolesNameByUserId(systemUser.getUuid()));
// log.info("SystemUserRealm 后台 非ROOT 获取角色: {}", Arrays.toString(info.getRoles().toArray()));
// }
// return info;
return null;
}
/**
* 用户认证,登录认证
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws UnknownAccountException,IncorrectCredentialsException {
// log.info("SystemUserRealm doGetAuthenticationInfo Shiro 认证执行的第 4 步 " );
// CustomUsernamePasswordToken token = (CustomUsernamePasswordToken) authenticationToken;
// String username = token.getUsername();
// log.info("SystemUserRealm token username: {}, password: {}",username,new String(token.getPassword()));
// SystemUser systemUser = systemUserService.findUserByUsername(username);
// log.info("SystemUserRealm systemUser: {}",systemUser);
// if(null==systemUser){
// throw new UnknownAccountException("SystemUserRealm 提示: 用户名不存在");
// }else if(!systemUser.getPassword().equals(new String(token.getPassword()))){
// throw new IncorrectCredentialsException("SystemUserRealm 提示:密码错误");
// }else if(systemUser.getStatus()==1){
// throw new AccountException("SystemUserRealm 提示: 账号被禁用");
// }else{
// return new SimpleAuthenticationInfo(systemUser,systemUser.getPassword(),getName());
// }
return null;
}
}
package com.dashuai.shiro;
import com.dashuai.entity.system.SystemUser;
import com.dashuai.service.system.SystemUserRoleService;
import com.dashuai.service.system.SystemUserService;
import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Arrays;
/**
* 这里验证多个 Realm,前后台 分开验证
*/
public class NormalUserRealm extends AuthorizingRealm {
private static final Logger log = Logger
.getLogger(SystemUserRealm.class);
// @Autowired
// private SystemUserService systemUserService;
// @Autowired
// private SystemUserRoleService systemUserRoleService;
/**
* Shiro权限认证,即权限授权
*
* 关于多个Realm授权的问题。对于本项目配置而言是这样的。
* 本项目配置了两个: SystemUserRealm;NormalUserRealm;
* 它会按配置顺序执行。先SystemUserRealm;
* 如果在先SystemUserRealm发现了想要的角色或者权限,那么不执行NormalUserRealm;
* 如果在SystemUserRealm没有找到,那么继续执行NormalUserRealm中授权方法。
*
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("前台用户 NormalUserRealm Shiro 请求授权");
// SystemUser systemUser = (SystemUser) SecurityUtils.getSubject().getPrincipal();
// SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addRoles(systemUserRoleService.getAllRolesNameByUserId(systemUser.getUuid()));
// log.info("NormalUserRealm 前台用户 获取角色: "+ Arrays.toString(info.getRoles().toArray()));
// return info;
return null;
}
/**
* 用户认证,登录认证
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("前台用户 NormalUserRealm 用户认证 Shiro 认证执行的第 4 步 " );
// CustomUsernamePasswordToken token = (CustomUsernamePasswordToken) authenticationToken;
// String username = token.getUsername();
// log.info("NormalUserRealm token username: "+username+", password: "+new String(token.getPassword()));
// SystemUser systemUser = systemUserService.findUserByUsername(username);
// log.info("NormalUserRealm systemUser: "+systemUser);
// if(null==systemUser){
// throw new UnknownAccountException("NormalUserRealm tips: 用户名不存在");
// }else if(!systemUser.getPassword().equals(new String(token.getPassword()))){
// throw new IncorrectCredentialsException("NormalUserRealm tips: 密码错误,请重试");
// }else if(systemUser.getStatus()==1){
// throw new AccountException("NormalUserRealm tips: 账号被禁用");
// }else{
// return new SimpleAuthenticationInfo(systemUser,systemUser.getPassword(),getName());
// }
return null;
}
}
这样 如下部分我们都看完了。
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
<property name="authenticator" ref="defineModularRealmAuthenticator" />
<!-- 这里主要是设置自定义的单Realm应用,若有多个Realm,可使用'realms'属性代替 -->
<!-- <property name="realm" ref="loginRealm"/> -->
<property name="realms" >
<list> <!-- 前台和后台的登录认证和权限授权认证 -->
<ref bean="systemUserRealm" />
<ref bean="normalUserRealm"/>
</list>
</property>
还有未看完的。
<property name="sessionManager" ref="sessionManager" />
<property name="rememberMeManager" ref="rememberMeManager"/>
现在看sessionManager
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionDAO" ref="sessionDAO" />
<!-- 会话超时时间,单位:毫秒 -->
<property name="globalSessionTimeout" value="1800000" />
<!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话 -->
<property name="sessionValidationInterval" value="1800000" />
<property name="sessionValidationSchedulerEnabled" value="true" />
<property name="sessionIdCookie" ref="simpleCookie" />
<property name="sessionIdCookieEnabled" value="true" />
</bean>
<!-- 会话Cookie模板 -->
<bean id="simpleCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg name="name" value="shiro.sesssion"/>
<property name="path" value="/"/>
</bean>
<bean id="sessionDAO"
class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="cacheManager" ref="shiroCacheManager" />
</bean>
<!-- 定义授权缓存管理器 -->
<bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
</bean>
<!-- rememberMe管理器 -->
<bean id="rememberMeManager"
class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cipherKey"
value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
<property name="cookie" ref="rememberMeCookie"/>
</bean>
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="604800"/><!-- 7天 -->
</bean>
最后在看shiroFilter 中 filter的配置:
<bean id="systemAuthFilter" class="com.dashuai.shiro.SystemFormAuthFilter" >
<property name="loginUrl" value="/admin/login.html" />
<property name="successUrl" value="/admin/index.html" />
</bean>
<bean id="normalAuthFilter" class="com.dashuai.shiro.NormalFormAuthFilter" >
<property name="loginUrl" value="/login.html" />
<property name="successUrl" value="/index.html" />
</bean>
package com.dashuai.shiro;
import org.apache.log4j.Logger;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 后台的用户登录认证
*/
public class SystemFormAuthFilter extends FormAuthenticationFilter {
private static final Logger log = Logger
.getLogger(SystemFormAuthFilter.class);
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
log.info("SystemFormAuthFilter onAccessDenied Shiro 认证执行的第一步 ");
return super.onAccessDenied(request, response);
}
/*@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
log.info("SystemFormAuthFilter onLoginSuccess ");
//我们登录成功之后要跳到一个固定的页面,通常是跳到首页,
WebUtils.getAndClearSavedRequest(request);
WebUtils.redirectToSavedRequest(request,response,"admin/index.html");
return super.onLoginSuccess(token, subject, request, response);
}*/
}
package com.dashuai.shiro;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 前台 用户登录的验证
*/
public class NormalFormAuthFilter extends FormAuthenticationFilter {
private static final Logger log = LoggerFactory
.getLogger(SystemFormAuthFilter.class);
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
log.info("NormalFormAuthFilter onAccessDenied Shiro 前台 认证执行的第一步 ");
return super.onAccessDenied(request, response);
}
}
这样shiro的配置就完成了。
项目中 总有存在5张表
/**
* 用户表。(通用用户,能否访问后台,看是否有后台角色)
*/
@Entity
@Table(name="system_user")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class SystemUser implements Serializable {
@Id
@GenericGenerator(name = "systemUUID", strategy = "uuid")
@GeneratedValue(generator = "systemUUID")
@Column(name = "uuid",updatable = false, nullable = false)
private String uuid; // 表主键
@Column
private String username; // 登录用户名
@Column
private String password; // 登录密码
@Column
private String isRoot; // 最高权限管理员; "1":root "0": notRoot
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column
private Date registerDate;
@Column
private Integer status; // 账号状态: 0:正常;1:禁用
@Entity
@Table(name="system_role")
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class SystemRole implements Serializable{
@Id
@GenericGenerator(name = "systemUUID", strategy = "uuid")
@GeneratedValue(generator = "systemUUID")
@Column(name = "uuid",updatable = false, nullable = false)
private String uuid; // 表主键
@Column(name="role_name")
private String roleName;
@Column(name="role_desc")
private String roleDesc;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "create_date",updatable=false)
private Date createDate;// 创建日期
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "update_date")
private Date updateDate;// 更新日期
public Date getCreateDate() {
return createDate;
}
/**
* 菜单表,你可以理解为 权限表
*/
@Entity
@Table(name="system_menu")
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class SystemMenu implements Serializable {
@Id
@GenericGenerator(name = "systemUUID", strategy = "uuid")
@GeneratedValue(generator = "systemUUID")
@Column(name = "uuid",updatable = false, nullable = false)
private String uuid;
// 父类ID
@Column(name = "pId", length = 64)
private String pid;
// 资源名称
@Column(name = "name", length = 32)
private String name;
// 资源类型
@Column(name="type")
private String type;//权限类型,0.表示目录 1,表示菜单.2,表示按扭..
// 资源排序
@Column(name = "seq", length = 4)
private int seq;
// 资源的URL
@Column(name = "resUrl", length = 128)
private String resUrl;//URL地址.例如:/videoType/query 不需要项目名和http://xxx:8080
// 资源状态
@Column(name = "status", length = 2)
private int status;// 这个变量含义定义为: 0:可修改删除 1:不可修改删除
//层级
@Column(name="level", length = 4)
private int level;
// 资源说明
@Column(name = "descript", length = 128)
private String descript;
// 资源图标
@Column(name = "iconCls", length = 128)
private String iconCls;
@Entity
@Table(name="system_user_role")
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class SystemUserRole implements Serializable{
@Id
@GenericGenerator(name = "systemUUID", strategy = "uuid")
@GeneratedValue(generator = "systemUUID")
@Column(name = "uuid",updatable = false, nullable = false)
private String uuid; // 表主键
@Column
private String sysUserId;
@Column
private String sysRoleId;
@Entity
@Table(name="system_role_menu")
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class SystemRoleMenu implements Serializable{
@Id
@GenericGenerator(name = "systemUUID", strategy = "uuid")
@GeneratedValue(generator = "systemUUID")
@Column(name = "uuid",updatable = false, nullable = false)
private String uuid;
@Column
private String sysRoleId;
@Column
private String sysMenuId;
具体的太长了,所以我讲讲项目中的心得体会吧。
1. shiro+hibernate+ehcache 这三个用的时候配置需要注意,ehcache.xml要合到一起。
2. log4j和slf4j的依赖是
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
为什么我要说这个,因为 我被别人误导了,别人导入了 log4j,slf4j相关依赖,就是没有上面的,导致我只能用 log4j显示日志,slf4j却没有效果,郁闷半天。
3. hibernate的二级缓存用法
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">classpath:ehcache.xml</prop>
<!-- 开启查询缓存 -->
<prop key="hibernate.cache.use_query_cache">true</prop>
<!-- 开启二级缓存 -->
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<!-- 高速缓存提供程序 -->
<!-- 由于spring也使用了Ehcache, 保证双方都使用同一个缓存管理器 -->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:ehcache.xml</value>
</property>
<!-- 由于hibernate也使用了Ehcache, 保证双方都使用同一个缓存管理器 -->
<property name="shared" value="true"/>
</bean>
@Entity @Table(name="system_role") @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY) public class SystemRole implements Serializable{
实体类加上注解,表示该实体类需要使用二级缓存。模式可选 read_write也可以是 read_only。但据说 read_only效率高些。 list()方法的调用,如果要用缓存,需要setCacheable(true)
@Override public GridPageResult<SystemRole> getPageRoles(Pager pager) { List<SystemRole> rows = getByHQL(true,"from SystemRole order by updateDate desc",null,pager.getPage(),pager.getRows()); long total = countByHQL(true,"select count(*) from SystemRole",null); return new GridPageResult<>(total,rows); }
public List<T> getByHQL(boolean withCache, String hql, Map<String, Object> params) {
Query q = getCurrentSession().createQuery(hql);
if (params != null && !params.isEmpty()) {
for (String key : params.keySet()) {
q.setParameter(key, params.get(key));
}
}
return q.setCacheable(withCache).list();
}
public List<T> getByHQL(boolean withCache, String hql, Map<String, Object> params, int page, int rows) {
Query q = getCurrentSession().createQuery(hql);
if (params != null && !params.isEmpty()) {
for (String key : params.keySet()) {
q.setParameter(key, params.get(key));
}
}
return q.setCacheable(withCache).setFirstResult((page - 1) * rows)//
.setMaxResults(rows)//
.list();
}
public long countByHQL(boolean withCache, String hql, Map<String, Object> params) {
Query q = getCurrentSession().createQuery(hql);
if (params != null && !params.isEmpty()) {
for (String key : params.keySet()) {
q.setParameter(key, params.get(key));
}
}
return (Long) q.setCacheable(withCache).uniqueResult();
}
public long countBySQL(boolean withCache, String sql, Map<String, Object> params) {
SQLQuery q = getCurrentSession().createSQLQuery(sql);
if (params != null && !params.isEmpty()) {
for (String key : params.keySet()) {
q.setParameter(key, params.get(key));
}
}
return (Long) q.setCacheable(withCache).uniqueResult();
}
public List<Map<String, Object>> getResultMapBySql(String sql, Map<String, Object> params) {
SQLQuery q = getCurrentSession().createSQLQuery(sql);
if (params != null && !params.isEmpty()) {
for (String key : params.keySet()) {
q.setParameter(key, params.get(key));
}
}
return q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
}
public List<Map<String, Object>> getResultMapBySql(String sql, Map<String, Object> params, int page, int rows) {
SQLQuery q = getCurrentSession().createSQLQuery(sql);
if (params != null && !params.isEmpty()) {
for (String key : params.keySet()) {
q.setParameter(key, params.get(key));
}
}
// 这种的不能使用 setCacheable(withCache),会报错。
return q.setFirstResult((page - 1) * rows)//
.setMaxResults(rows)//
.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)//
.list();
}
使用二级缓存也会有很多不便的地方:
public void updateRole(SystemRole role) {
//update(role); // 不能这样更新,cache是read-only模式
String hql = "update SystemRole set " +
"roleName=:roleName,roleDesc=:roleDesc,updateDate=:updateDate " +
" where uuid=:uuid";
Map<String,Object> params = new HashMap<>();
params.put("roleName",role.getRoleName());
params.put("roleDesc",role.getRoleDesc());
params.put("updateDate",role.getUpdateDate());
params.put("uuid",role.getUuid());
executeHQL(hql,params);
}
不能直接调用update方法,更新对象,要采用执行sql或者hql语句方式,直接操作数据库。经测试,直接操作数据库的更新,缓存也会失效,也会更新的。
第二个就是使用sql语句查询的时候,如果使用cache为true的情况,不能返回List<Map>类型,必须指定成对象。当然setcache为false,顺利执行。 那么如果是true,代码应这样写。
@Override
public List<DtoRoleMenu> getRoleHasAndNotHasMenus(String roleId) {
String sql="select m.uuid as id, " +
"m.pId as pId, " +
"m.name as name, " +
"m.resUrl as resUrl, " +
"m.type as type, " +
"srm.sysRoleId as roleId " +
"from system_menu m " +
"left join system_role_menu srm " +
"on m.uuid = srm.sysMenuId " +
"and srm.sysRoleId =:roleId ";
return getCurrentSession()
.createSQLQuery(sql)
.addScalar("id", StringType.INSTANCE)
.addScalar("pId", StringType.INSTANCE)
.addScalar("name", StringType.INSTANCE)
.addScalar("resUrl", StringType.INSTANCE)
.addScalar("type", StringType.INSTANCE)
.addScalar("roleId", StringType.INSTANCE)
.setParameter("roleId",roleId)
.setResultTransformer(Transformers.aliasToBean(DtoRoleMenu.class))
.setCacheable(false).list();
}
贴上项目运行效果图吧。
项目运行
因为项目已经集成了 tomcat插件了。所以我们配置一下就行了,不需要额外的tomcat包了
点击OK之后,你会看到右侧就会出现你新建的东西。然后点击那两个绿色的按钮任意一个运行。
然后就可以运行了。
本项目做了一些初始化工作,所以,后台的管理员 用户名 root,密码 123456。
本项目下载地址:下载地址
如果你使用IDEA 直接打开,可能需要配置一下,因为 你的F盘可能没有maven软件,而且系统自带的maven肯定会下载jar包到你的c盘。所以最好配置一下。
还有就是 源码处,有一个无关痛痒的小错误,源码位置
我为了证明 能不能使用 cache 返回 List<Map>,测试的结果是不能使用 cache为true,否则报错。