jeesite内容丰富,集成了大量优秀的组件,是一个值得研究的框架。它有:
1.shiro安全权限控制
2.mybatis查询缓存接口扩展
3.ecache分布式缓存整合
4.页面资源缓存优化
5.多数据源灵活切换
6.mybatismapper文件动态刷新
7.activiti工作流流程管理
8.excel注解式导入导出
9.siteMesh保持页面统一
10.异常处理机制
这还没完 ,还有很多等等吧。
参考:我之前写的 shiro学习笔记1、shiro学习笔记2、shiro学习笔记3、shiro学习笔记4
mybatis 提供查询一级缓存和二级缓存,减轻数据库的压力,提高性能。
(1)一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。一级缓存的作用
域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession
中的一级缓存也就不存在了。Mybatis默认开启一级缓存。
(2)二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存是多个SqlSession
共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获
取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
(3)二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作,注意如果存在父类、成员pojo都需要实现序列化接口。pojo类实现序列化接口是为了将缓存数据取出执行反序列化操作,因为二级缓存数据存
储介质多种多样,不一定在内存有可能是硬盘或者远程服务器。
(4)mybatis整合redis要点
a.配置spring-context-redis.xml,管理redis数据库
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="300" /> <!-- 最大能够保持idel状态的对象数 -->
<property name="maxTotal" value="60000" /> <!-- 最大分配的对象数 -->
<property name="testOnBorrow" value="true" /> <!-- 当调用borrow Object方法时,是否进行有效性检查 -->
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg index="0" ref="jedisPoolConfig" />
<constructor-arg index="1" value="${redis.host}" />
<constructor-arg index="2" value="${redis.port}" type="int" />
<!-- <constructor-arg index="3" value="${redis.timeout}" type="int" />
<constructor-arg index="4" value="${redis.password}"/>
<constructor-arg index="5" value="${redis.database}" type="int" />
<constructor-arg index="6" value="${redis.clientName}"/> -->
</bean>
b.配置mybatis-config.xml,开启mabtis二级缓存
<!-- 使全局的映射器启用或禁用缓存。 -->
<setting name="cacheEnabled" value="true"/>
c.把model序列化
public class UserModel implements Serializable{
private static final long serialVersionUID = 4673186153813605228L;
}
d.在对应的statement 上开启缓存,默认是关闭的
<select id="selectByLoginname" resultType="com.**.Model" useCache="true">
e.mybatis刷新缓存(就是清空缓存)
在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。
设置statement配置中的flushCache=‘true’ 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
如下:
<insertid='insertUser' parameterType='com.**.User' flushCache='true'>
一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存默认情况下为true,我们不用去设置它,这样可以避免数据库脏读。
EhCache 是一个纯Java的进程内缓存框架。Hibernate中使用它做的默认缓存。
Element、Cache、CacheManager是Ehcache最重要的API。
Element:缓存的元素,它维护着一个键值对。
Cache:它是Ehcache的核心类,它有多个Element,并被CacheManager管理。它实现了对缓存的逻辑行为。
CacheManager:Cache的容器对象,并管理着Cache的生命周期。
Cache最重要的两个方法就是put和get,分别用来添加Element和获取Element。
Cache还提供了一系列的get、set方法来设置或获取缓存参数,这里不一一列举,更多API操作可参考官方API开发手册。
ehcache.xml配置参数说明:
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
timeToIdleSeconds:置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:缓存数据的生存时间(TTL),也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
maxEntriesLocalDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
overflowToDisk:内存不足时,是否启用磁盘缓存。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否在VM重启时存储硬盘的缓存数据。默认值是false。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
页面缓存主要用Filter过滤器对请求的url进行过滤,如果该url在缓存中出现。那么页面数据就从缓存对象中获取,并以gzip压缩后返回。其速度是没有压缩缓存时速度的3-5倍,效率相当之高!其中页面缓存的过滤器有CachingFilter,一般要扩展filter或是自定义Filter都继承该CachingFilter。
<!-- 简单页面缓存 -->
<cache name="pageCachingFilter"
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
memoryStoreEvictionPolicy="LFU"
statistics="true">
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=false,
replicateRemovals=true "/>
</cache>
CachingFilter功能可以对HTTP响应的内容进行缓存。这种方式缓存数据的粒度比较粗,例如缓存整张页面。它的优点是使用简单、效率高,缺点是不够灵活,可重用程度不高。
EHCache使用SimplePageCachingFilter类实现Filter缓存。该类继承自CachingFilter,有默认产生cache key的calculateKey()方法,该方法使用HTTP请求的URI和查询条件来组成key。也可以自己实现一个Filter,同样继承CachingFilter类,然后覆写calculateKey()方法,生成自定义的key。
CachingFilter输出的数据会根据浏览器发送的Accept-Encoding头信息进行Gzip压缩。
(1)修改application.properties文件
注释掉:
#jdbc.driver=com.mysql.jdbc.Driver
#jdbc.url=jdbc:mysql://127.0.0.1:3306/jeesite?useUnicode=true&characterEncoding=utf-8
#jdbc.username=root
#jdbc.password=123456
去掉注释:
jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
jdbc.username=jeesite
jdbc.password=123456
(2)修改所有Entity文件的Id字段,修改文件包括:
src/main/java/com/thinkgem/jeesite/modules/cms/entity/*.java
注释掉:
//@GeneratedValue(strategy = GenerationType.IDENTITY)
去掉注释:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_cms_article") @SequenceGenerator(name = "seq_cms_article", sequenceName = "seq_cms_article")
注意:去掉注释后需要给SequenceGenerator添加类引用,如下:
import javax.persistence.SequenceGenerator;
(3)执行bin\refresh-db\refresh-db.bat刷新数据库(导入表结构及数据)。
就是要建一套oracle数据表
(4)不同的数据库也就是有少量的查询语句不同,如myslq 分页用limit;oracle用rownum
他用了方言接口,不同数据库不同方言的具体实现
在mapper.xml中做了匹配,如:
<if test="office != null and office.id != null and office.id != ''">
AND (o.id = #{office.id} OR o.parent_ids LIKE
<if test="dbName == 'oracle'">'%,'||#{office.id}||',%')</if>
<if test="dbName == 'mssql'">'%,'+#{office.id}+',%')</if>
<if test="dbName == 'mysql'">CONCAT('%,', #{office.id}, ',%'))</if>
</if>
参考:http://thinkgem.iteye.com/blog/2304557
还在计划整理中。。。。。。。。。
(1)excel注解定义:
package com.thinkgem.jeesite.common.utils.excel.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel注解定义
* @author ThinkGem
* @version 2013-03-10
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelField {
/**
* 导出字段名(默认调用当前字段的“get”方法,如指定导出字段为对象,请填写“对象名.对象属性”,例:“area.name”、“office.name”)
*/
String value() default "";
/**
* 导出字段标题(需要添加批注请用“**”分隔,标题**批注,仅对导出模板有效)
*/
String title();
/**
* 字段类型(0:导出导入;1:仅导出;2:仅导入)
*/
int type() default 0;
/**
* 导出字段对齐方式(0:自动;1:靠左;2:居中;3:靠右)
*/
int align() default 0;
/**
* 导出字段字段排序(升序)
*/
int sort() default 0;
/**
* 如果是字典类型,请设置字典的type值
*/
String dictType() default "";
/**
* 反射类型
*/
Class<?> fieldType() default Class.class;
/**
* 字段归属组(根据分组导出导入)
*/
int[] groups() default {};
}
(2)导出实体对象中的annotation的定义:
@Entity
@Table(name = "sys_user")
public class User extends BaseEntity {
private Long id; // 编号
private List<Role> roleList = Lists.newArrayList(); // 拥有角色列表
@Id
@ExcelField(title="ID", type=1, align=2, sort=1)
public Long getId() {
return id;
}
@ManyToOne
@ExcelField(title="所属区域", align=2, sort=10)
public Area getArea() {
return area;
}
@ManyToOne
@ExcelField(title="所属部门", align=2, sort=20)
public Office getOffice() {
return office;
}
@Length(min=1, max=100)
@ExcelField(title="姓名", align=2, sort=40)
public String getName() {
return name;
}
@Length(min=0, max=100)
@ExcelField(title="用户类型", align=2, sort=80, dictType="sys_user_type")
public String getUserType() {
return userType;
}
@ExcelField(title="创建时间", type=0, align=1, sort=90)
public Date getCreateDate() {
return createDate;
}
@ExcelField(title="最后登录日期", type=1, align=1, sort=110)
public Date getLoginDate() {
return loginDate;
}
@ManyToMany
@ExcelField(title="拥有角色", align=1, sort=800, fieldType=RoleListType.class)
public List<Role> getRoleList() {
return roleList;
}
}
(3)Excel导出示例:
public String exportFile(User user) {
try {
String fileName = "用户数据"+DateUtils.getDate("yyyyMMddHHmmss")+".xlsx";
// 查询数据
Page<User> page = systemService.findUser(new Page<User>(request, response, -1), user);
// 1:创建Excel导出对象;2:设置数据;3:写入输出流;4:临时数据销毁
new ExportExcel("用户数据", User.class)
.setDataList(page.getList())
.write(response, fileName)
.dispose();
return null;
} catch (Exception e) {
addFlashMessage("导出用户失败!失败信息:"+e.getMessage());
}
return "redirect:"+BaseController.ADMIN_PATH+"/sys/user/?repage";
}
(4)Excel 导入示例:
public String importFile(MultipartFile file) {
try {
int successNum = 0;
int failureNum = 0;
StringBuilder failureMsg = new StringBuilder();
// 创建导入Excel对象
ImportExcel ei = new ImportExcel(file, 1, 0);
// 获取传入Excel文件的数据,根据传入参数类型,自动转换为对象
List<User> list = ei.getDataList(User.class);
// 遍历数据,保存数据
for (User user : list){
try{
if ("true".equals(checkLoginName("", user.getLoginName()))){
user.setPassword(SystemService.entryptPassword("123456"));
BeanValidators.validateWithException(validator, user);
systemService.saveUser(user);
successNum++;
}else{
failureMsg.append("
登录名 "+user.getLoginName()+" 已存在; ");
failureNum++;
}
}catch(ConstraintViolationException ex){
failureMsg.append("
登录名 "+user.getLoginName()+" 导入失败:");
List<String> messageList = BeanValidators.extractPropertyAndMessageAsList(ex, ": ");
for (String message : messageList){
failureMsg.append(message+"; ");
failureNum++;
}
}catch (Exception ex) {
failureMsg.append("
登录名 "+user.getLoginName()+" 导入失败:"+ex.getMessage());
}
}
if (failureNum>0){
failureMsg.insert(0, ",失败 "+failureNum+" 条用户,导入信息如下:");
}
addFlashMessage("已成功导入 "+successNum+" 条用户"+failureMsg);
} catch (Exception e) {
addFlashMessage("导入用户失败!失败信息:"+e.getMessage());
}
return "redirect:"+BaseController.ADMIN_PATH+"/sys/user/?repage";
}
一个网站,比如校内网,前端展示的页面总是有固定不变的地方,它们是:上部,左部,和底部,真正变化的内容,都是在中间产生的。
如果用jsp实现这种效果当然是可以的,到处是指令标签
<%@includefile="header.jsp" %> <%@includefile="footer.jsp" %>
这样写起来分麻烦,而且一旦改动哪里,为了全局保持统一,到处都要改。
为此,需要用一种好的解决方法,这就是SiteMesh框架,SiteMesh是基于Java、J2EE和XML的开源框架,依赖于从Servlet 2.3版本里引入的新功能——过滤器(Filters),它的主要思想是装饰设计模式,把变化的和不变的分离开来,用不变的
去修饰各种变化的内容。
(1)工作原理
SiteMesh是基于Servlet的filter的,即过滤流。它是通过截取response,并进行装饰后再交付给客户。
其中涉及到两个名词: 装饰页面(decorator page)和 “被装饰页面(Content page)” , 即 SiteMesh通过对Content Page的装饰,最终得到页面布局和外观一致的页面,并返回给客户.
(2)过滤器
位置是WEB-INF/ web.xml
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>org.sitemesh.config.ConfigurableSiteMeshFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*
(3)配置文件
位置是WEB-INF/siteMesh.xml
<sitemesh>
<mapping decorator="/decorators/default.jsp"/>
<excludes>
<pattern>/static/*
(1)非检查性异常进行事务回滚
将检查性异常转化成非检查性异常进行事务回滚
参看异常工具类
com.thinkgem.jeesite.common.utils.Exceptions
(2)将异常转化为string
(3)判断异常是否是由于底层的异常引起的
(4)从request中获取异常,在页面上进行输出