jeesite 学习笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/symbol1993/article/details/72820016

jeesite内容丰富,集成了大量优秀的组件,是一个值得研究的框架。它有:

1.shiro安全权限控制

2.mybatis查询缓存接口扩展

3.ecache分布式缓存整合

4.页面资源缓存优化

5.多数据源灵活切换

6.mybatismapper文件动态刷新

7.activiti工作流流程管理

8.excel注解式导入导出

9.siteMesh保持页面统一

10.异常处理机制

这还没完 ,还有很多等等吧。

1.shiro安全权限控制

参考:我之前写的 shiro学习笔记1、shiro学习笔记2、shiro学习笔记3、shiro学习笔记4

2.mybatis查询缓存接口扩展

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,我们不用去设置它,这样可以避免数据库脏读。

3.ecache 缓存整合

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:内存数量最大时是否清除。

4.页面资源缓存优化

 页面缓存主要用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压缩。

5.多数据源灵活切换

(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>

6.mybatismapper文件动态刷新

参见:http://thinkgem.iteye.com/blog/2304557

7.activiti工作流流程管理

还在计划学习整理中。。。。。。。。。

8.excel注解式导入导出

(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("<br/>登录名 "+user.getLoginName()+" 已存在; ");
                     failureNum++;
                 }
             }catch(ConstraintViolationException ex){
                 failureMsg.append("<br/>登录名 "+user.getLoginName()+" 导入失败:");
                 List<String> messageList = BeanValidators.extractPropertyAndMessageAsList(ex, ": ");
                 for (String message : messageList){
                     failureMsg.append(message+"; ");
                     failureNum++;
                 }
             }catch (Exception ex) {
                 failureMsg.append("<br/>登录名 "+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";
 }

9.siteMsh保持页面统一

一个网站,比如校内网,前端展示的页面总是有固定不变的地方,它们是:上部,左部,和底部,真正变化的内容,都是在中间产生的。

如果用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>/*</url-pattern>
</filter-mapping>

(3)配置文件
位置是WEB-INF/siteMesh.xml

<sitemesh>
    <mapping decorator="/decorators/default.jsp"/>

    <excludes>

        <pattern>/static/*</pattern>

    </excludes>
</sitemesh>

10.异常处理机制

(1)非检查性异常进行事务回滚
将检查性异常转化成非检查性异常进行事务回滚
参看异常工具类

com.thinkgem.jeesite.common.utils.Exceptions

(2)将异常转化为string

(3)判断异常是否是由于底层的异常引起的

(4)从request中获取异常,在页面上进行输出

展开阅读全文

没有更多推荐了,返回首页