精华内容
下载资源
问答
  • 一级响应和二级响应的区别
    万次阅读 多人点赞
    2020-12-02 12:07:16

    一、目的

    后台管理系统页面的整体结构如图:

    实现顶部一级菜单和左侧二级菜单进行响应,选哪个一级菜单时,左侧菜单栏有相对应下的二级菜单,产生联动效果。然后选不同的二级菜单,主体内容区域展示对应内容。

    二、效果图

     后台管理系统模板源码的 github 地址:https://github.com/hxhpg/vue-secondMenu-test

    三、具体操作实现

    1、用 vue-cli 创建的 Vue 项目后,自己再手动搭建项目的基本目录结构,如图:

    2、创建一个公共组件 Whole.vue 来实现整体页面结构的布局,通过 div 来进行区域定位和大小设定。

    使用 el-menu 组件的 mode 属性为 "horizontal" 来使导航菜单变更为水平模式。

    @select="handleSelect" 来切换不同的一级菜单展示不同的二级菜单栏和主体内容区域,用 router-view 通过路由来进行组件页面跳转,代码如下:

    <template>
      <div class="wrapper">
        <!-- 页面头部部分 -->
        <div class="header">
          <div class="logo">后台管理系统</div>
          <!-- 水平一级菜单 -->
          <div style="float:left;">
            <el-menu 
              mode="horizontal"
              text-color="#000000"
              active-text-color="#3989fa"
              :default-active="toIndex" 
              @select="handleSelect">
              <el-menu-item v-for="(item, index) in itemList" :index="item.path" :key="index">
                <span slot="title">{{ item.title }}</span>
              </el-menu-item>
            </el-menu>
          </div>
          
          <div class="header-right">
            <div class="header-user-con">
              <!-- 用户头像 -->
              <div class="user-avator">
                <img src="@/assets/img/img.jpg"/>
              </div>
              <!-- 用户名下拉菜单 -->
              <el-dropdown class="user-name" trigger="click" @command="handleCommand">
                <span class="el-dropdown-link"> {{ username }} <i class="el-icon-caret-bottom"></i></span>
                <el-dropdown-menu slot="dropdown">
                  <el-dropdown-item disabled>修改密码</el-dropdown-item>
                  <el-dropdown-item command="loginout">退出登录</el-dropdown-item>
                </el-dropdown-menu>
              </el-dropdown>
            </div>
          </div>
        </div>
    
        <!-- 页面左侧二级菜单栏,和主体内容区域部分 -->
        <el-main>
          <router-view></router-view>
        </el-main>
    
      </div>
    </template>
    
    <script>
    import bus from "@/utils/bus";
    export default {
      data(){
        return{
          itemList: [    // 水平一级菜单栏的菜单
            { path: '/Home', title: '首页' },
            { path: '/test1', title: '一级菜单1' },
            { path: '/test2', title: '一级菜单2' },
            { path: '/test3', title: '一级菜单3' },
            { path: '/permission', title: '管理员权限' },
            // { path: '/i18n', title: '国际化组件' }
          ],
        }
      },
      computed: {
        username(){
          return localStorage.getItem('ms_username') || '';
        },
        toIndex(){  // 根据路径绑定到对应的一级菜单,防止页面刷新重新跳回第一个
          return '/' + this.$route.path.split('/')[1];
        },
      },
      created() {
        
      },
      methods: {
        handleSelect(path){  // 切换菜单栏
          this.$router.push({
            path: path
          });
        },
        handleCommand(command){  // 用户名下拉菜单选择事件
          if(command == 'loginout'){
            localStorage.removeItem('ms_username');
            this.$router.push({
                path: '/Login'
            });
          }
        }
      }
    }
    </script>
    
    <style scoped>
    .wrapper {
      width: 100%;
      height: 100%;
      background: #f0f0f0;
    }
    .header {
      position: relative;
      box-sizing: border-box;
      width: 100%;
      height: 70px;
      font-size: 22px;
    }
    .header .logo {
      float: left;
      margin-left: 60px;
      margin-top: 17.5px;
      height: 29px;
      width: 160px;
      vertical-align: middle;
    }
    /* --------------- 用户头像区域的样式 ---------------- */
    .header-right {
      float: right;
      padding-right: 50px;
    }
    .header-user-con {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 70px;
    }
    .user-avator {
      margin-left: 20px;
    }
    .user-avator img {
      display: block;
      width: 40px;
      height: 40px;
      border-radius: 50%;
    }
    .user-name {
      margin-left: 10px;
    }
    .el-dropdown-link {
      cursor: pointer;
    }
    .el-dropdown-menu__item {
      text-align: center;
    }
    /* --------------- 水平一级菜单栏的样式--------------------- */
    .el-menu.el-menu--horizontal {
      border-bottom: none !important;
      float: left;
      margin-left: 50px;
      background: transparent;
    }
    .el-menu--horizontal > .el-menu-item.is-active {
      /* border-bottom: 2px solid #3989fa;
      color: #3989fa; */
      font-weight: bold;
    }
    .el-menu--horizontal > .el-menu-item {
      font-size: 16px;
      margin: 0 15px;
    }
    </style>

    3、这样就把页面顶部的一级菜单栏展现出来了。接下来要封装一个左侧二级菜单栏的组件 SideMenu.vue ,设定 props 是让调用该组件时把传递的值获取到。代码如下:

    <template>
      <!-- 左侧二级菜单栏的组件封装 -->
      <div class="sidebar">
        <el-menu
          class="sidebar-el-menu"
          :default-active="toIndex"
          background-color="#fff"
          text-color="#000000"
          router>
          <el-menu-item v-for="(item, index) in itemList" :key="index" :index="item.path">
            <!-- 需要图标的在 item 对象中加上属性 icon -->
            <!-- <i :class="item.icon"></i> -->
            <span slot="title">{{ item.title }}</span>
          </el-menu-item>
        </el-menu>
      </div>
    </template>
    
    <script>
    export default {
      props: {
        itemList: {
          type: Array,
          default: () => []
        }
      },
      data(){
        return {
    
        }
      },
      computed: {
        toIndex(){  // 根据路径绑定到对应的二级菜单,防止页面刷新重新跳回第一个
          return this.$route.path.split('/')[2];
        }
      },
      methods:{
          
      },
    }
    </script>
    
    <style lang="scss" scoped>
    /* 左侧菜单栏定位和位置大小设定 */
    .sidebar {
      display: block;
      position: absolute;
      left: 0;
      top: 70px;
      bottom: 0;
      overflow-y: scroll;
    }
    .sidebar::-webkit-scrollbar {
      width: 0;
    }
    .sidebar-el-menu {
      width: 250px;
    }
    .sidebar > ul {
      height: 100%;
    }
    
    /* 左侧二级菜单项的样式 */
    .el-menu-item{
      font-size: 14px !important;
      padding-left: 35px !important;
    }
    
    /* 左侧二级菜单选中时的样式 */
    .el-menu-item.is-active {
      color: white !important;
      background: #3989fa !important;
    }
    .el-menu-item, .el-submenu__title {
      height: 50px !important;
      line-height: 50px !important;
    }
    </style>

    4、然后在各个一级菜单下( 项目目录的 page 文件夹下的各个文件夹里的 index.vue )调用左侧二级菜单栏组件。

    每个 index.vue 会对应每个一级菜单,这个通过改变路径(@select="handleSelect")和对路由进行配置来实现跳转,通过组件之间的传值通信把二级菜单项传给左侧二级菜单栏的组件。

    再次用 router-view 通过路由来进行组件页面跳转(这样就形成两层嵌套,进行两次 router-view 路由跳转),index.vue 代码如下:

    <template>
      <div>
        
        <!-- 一级菜单下面所拥有的二级菜单 -->
        <el-aside>
          <SideMenu :itemList='itemList'></SideMenu>
        </el-aside>
        <!-- 以及二级菜单所对应的页面 -->
        <el-main>
          <router-view></router-view>
        </el-main>
    
      </div>
    </template>
    
    <script>
    import SideMenu from '@/components/sidemenu/SideMenu';
    export default {
      components: {
        SideMenu
      },
      data(){
        return{
          itemList: [
            { path: 'test1-1', title: '二级菜单1-1' },
            { path: 'test1-2', title: '二级菜单1-2' },
            { path: 'test1-3', title: '二级菜单1-3' },
            { path: 'test1-4', title: '二级菜单1-4' },
            { path: 'test1-5', title: '二级菜单1-5' }
          ]
        }
      }
    }
    </script>

    5、一二级菜单联动效果所需的组件和页面基本弄好了,最后得将这些都拼接起来走通,要对 vue-router 进行配置( router 文件夹下 index.js 文件),代码如下:

    import Vue from 'vue';
    import VueRouter from 'vue-router';
    
    Vue.use(VueRouter);
    
    const originalPush = VueRouter.prototype.push;
    VueRouter.prototype.push = function push(location) {
      return originalPush.call(this, location).catch(err => err);
    }
    
    export default new VueRouter({
      routes: [
        {
          path: '/',  // 程序启动默认路由
          component: () => import('@/components/common/Whole.vue'),
          meta: { title: '整体页面布局' },
          redirect: '/Home',  // 重定向到首页
          children: [
            {
              path: '/Home',
              component: () => import('@/page/Home.vue'),
              meta: { title: '首页' }
            },
            {
              path: '/test1',
              component: () => import('@/page/test1/index.vue'),
              meta: { title: '一级菜单1' },
              redirect: '/test1/test1-1',  // 该配置是若点击选择一级菜单时,默认选中并跳转到该一级菜单下的第一个二级菜单
              children:[
                {
                  path: 'test1-1',
                  component: () => import('@/page/test1/test1-1.vue'),
                  meta: { title: '二级菜单1-1' },
                },
                {
                  path: 'test1-2',
                  component: () => import('@/page/test1/test1-2.vue'),
                  meta: { title: '二级菜单1-2' },
                },
                {
                  path: 'test1-3',
                  component: () => import('@/page/test1/test1-3.vue'),
                   meta: { title: '二级菜单1-3' },
                },
                {
                  path: 'test1-4',
                  component: () => import('@/page/test1/test1-4.vue'),
                  meta: { title: '二级菜单1-4' },
                },
                {
                  path: 'test1-5',
                  component: () => import('@/page/test1/test1-5.vue'),
                  meta: { title: '二级菜单1-5' },
                }
              ]
            },
            {
              path: '/test2',
              component: () => import('@/page/test2/index.vue'),
              meta: { title: '一级菜单2' },
              redirect: '/test2/test2-1',     // 该配置是若点击选择父目录时,默认选中该父目录下的子路径页面
              children:[
                {
                  path: 'test2-1',
                  component: () => import('@/page/test2/test2-1.vue'),
                  meta: { title: '二级菜单2-1' },
                },
                {
                  path: 'test2-2',
                  component: () => import('@/page/test2/test2-2.vue'),
                  meta: { title: '二级菜单2-2' },
                },
                {
                  path: 'test2-3',
                  component: () => import('@/page/test2/test2-3.vue'),
                  meta: { title: '二级菜单2-3' },
                },
              ]
            },
            {
              path: '/test3',
              component: () => import('@/page/test3/index.vue'),
              meta: { title: '一级菜单3' },
              redirect: '/test3/test3-1',
              children:[
                {
                  path: 'test3-1',
                  component: () => import('@/page/test3/test3-1.vue'),
                  meta: { title: '二级菜单3-1' }
                },
                {
                  path: 'test3-2',
                  component: () => import('@/page/test3/test3-2.vue'),
                  meta: { title: '二级菜单3-2' }
                },
              ]
            },
            {
              path: '/i18n',  // 国际化组件
              component: () => import('@/components/common/I18n.vue'),
              meta: { title: '国际化' }
            },
            {
              path: '/permission',  // 权限页面
              component: () => import('@/page/Permission.vue'),
              meta: {
                title: '权限测试',
                permission: true
              }
            },
            {
              path: '/404',
              component: () => import('@/page/404.vue'),
              meta: { title: '404' }
            },
            {
              path: '/403',
              component: () => import('@/page/403.vue'),
              meta: { title: '403' }
            },
          ]
        },
        {
          path: '/Login',  // 登录页面
          component: () => import('@/page/Login.vue'),
          meta: { title: '登录' }
        },
        {
          path: '*',
          redirect: '/404'
        }
      ]
    });

    6、顶部一级菜单栏,左侧二级菜单栏的功能效果完成了,接下来就可以去开发具体的主体内容区域的页面了。例如 test1-2.vue 的代码如下:

    <template>
    <div class="content-box">
      <div class="container">
        <p>主体页面 1 - 2 </p>
        <div class="test-div">
          <i class="el-icon-edit"></i>
          <i class="el-icon-share"></i>
          <i class="el-icon-delete"></i>
        </div>
      </div>
    </div>
    </template>
    
    <script>
    export default {
      data(){
        return{
    
        }
      }
    }
    </script>
    
    <style>
    .test-div i{
      font-size: 25px;
    }
    </style>

    其中主体内容区域的定位和位置大小设定的 css 如下( 这些样式是公共,可复用的,单独写一个样式文件来全局引入使用 ):

    /* 页面主体部分 */
    .content-box {
      position: absolute;
      left: 250px;      /* 控制左侧二级菜单栏的宽度 */
      right: 0;
      top: 70px;
      bottom: 0;
      padding: 10px 20px;
      -webkit-transition: left .3s ease-in-out;
      transition: left .3s ease-in-out;
      background: #f0f0f0;
    }
    .content {
      width: auto;
      height: 100%;
      padding: 10px;
      overflow-y: scroll;
      box-sizing: border-box;
    }
    .content-collapse {
      left: 65px;
    }
    .container {
      padding: 20px;    /* 控制主体部分与主体边框的距离 */
      background: #fff;
      border: 1px solid #ddd;
      border-radius: 5px;
    }
    
    /* 表格栏样式 */
    .table-container {
      margin-top: 20px;
    }
    
    /* 分页栏样式 */
    .pagination-area {
      width: 100%;
      text-align: right;
      margin: 20px 0 10px 0;
    }
    
    /* 测试主体页面的 div 样式 */
    .test-div{
      margin: 15px;
    }

    整个后台管理系统模板的核心内容开发大致是这样的,更多具体细节可以下载完整的源码来看。

    项目实现了登录功能,一二级菜单联动选择展示不同的主体内容部分,封装了 axios 请求库,还简单配置了管理员和普通用户的权限区分。

    这是我本人在工作学习中遇到的问题,并对解决问题的方案进行了一次记录,跟小伙伴们分享出来哈 ~ 供参考学习,有什么建议也欢迎评论留言,转载请注明出处哈,感谢支持!


    后续扩展文章:

    Vue + ElementUI 后台管理系统实现主题风格切换_无解的菜鸟晖的博客-CSDN博客

    Vue + ElementUI 后台管理系统实现顶部一级菜单栏,左侧多级菜单栏(二级、三级等)_无解的菜鸟晖的博客-CSDN博客

    更多相关内容
  • 从国外收费网站翻译过来的款多种二级下拉菜单导航样式,二级导航菜单支持多种自定义内容,例如:图片列表,登陆注册信息,二级下拉导航条等等
  • 之前分享了很多纯css的导航菜单,今天介绍款基于jquerycss3的响应二级导航菜单,这款导航是传统的基于顶部,鼠标经过的时候显示二级导航,还采用了当前流行的响应式设计。需要的朋友可以参考下
  • 这是个HTML5+CSS3实现的响应二级导航菜单,资源来源于网络,侵删
  • 手机导航,手机导航,响应式导航,自适应导航,下拉导航
  • 微博weibo.com 端一级评论和二级评论接口返回的相应体,因为这个是unicode编码的,看不到中文,这里已经解码过,可以使用bs4等工具直接提取内容
  • 响应式网站二级导航菜单是款基于jquery.cbFlyout.jsjquery1.10.2.min.js制作的网站导航菜单,响应式设计,自适 应屏幕分辨率大小,兼容PC端手机移动端,页面正常显示时,网页顶部显示水平二级导航菜单,缩小...
  • 响应式网站二级导航菜单是款基于jquery.cbFlyout.jsjquery1.10.2.min.js制作的网站导航菜单,响应式设计,自适应屏幕分辨率大小,兼容PC端手机移动端,页面正常显示时,网页顶部显示水平二级导航菜单,缩小...
  • jQuery响应式侧滑二级菜单栏是款基于jQuery+CSS3+HTML5实现的右侧滑出二级菜单。
  • jQuery响应式侧滑二级菜单栏是款基于jQuery CSS3 HTML5实现的右侧滑出二级菜单。
  • 本文实例讲述了JS实现超精简...这是款精简版的二级导航菜单,响应鼠标的动作,鼠标放上即显示出菜单,常见的种菜单样式。喜欢的朋友拿去修改一下,再美化一番,就够用了。 运行效果截图如下: 在线演示地址如下: ...
  • 云平台安全响应机制 Microsoft Online Tech Forum 陈健宁 陈彬彬 微软全球技术支持中心 内容安排 云平台安全应急响应 更廉价的攻击 勒索软件: 预付$66 或者30% 的盈利 0days 价格从$5,000 到 $350,000 负载(受控...
  • 应用集中参数法建立了种计及时变啮合刚度、齿侧间隙、传动误差的二级圆柱斜齿轮传动系统弯-扭-轴-摆耦合动力学模型,推导并求解传动系统的动力学微分方程组,得到了二级圆柱斜齿轮传动系统的动力响应。基于有限元...
  • 主要介绍了jQuery实现有动画淡出效果的二级折叠菜单代码,涉及jQuery响应鼠标事件动态操作页面元素样式的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
  • 二级同轴摆线传动动态激励及响应分析,冉毅,杜雪松,环式减速机在使用过程中常出现振动、噪声大、发热等问题, 提出一种由一级渐开线传动和一级摆线传动组成的二级同轴双曲柄行星传动,
  • mybatis一级缓存 mybatis 的缓存分为2类,分别是一级缓存和二级缓存,一级缓存是默认开启的,它在一个sqlSession会话里面的所有查询操作都会保存到缓存中,一般来说一个请求中的所有增删改查操作都是在同一个...

    缓存是什么?

    缓存其实就是存储在内存中的临时数据,这里的数据量会比较小,一般来说,服务器的内存也是有限的,不可能将所有的数据都放到服务器的内存里面,所以, 只会把关键数据放到缓存中,缓存因为速度快,使用方便而出名!

    为什么需要缓存

    BS架构里面,用户的所有操作都是对数据库的增删改查,其中查询的操作是最多的,但如果用户想要某个数据时每次都去数据库查询,这无疑会增加数据库的压力,而且获取时间效率也会降低,所以为了解决这些问题,缓存应用而生,使用了缓存之后,服务器只需要查询一次数据库,然后将数据保存到服务器主机的内存中,以后读取时就直接取内存中的数据,而不需要每次都查数据库,这种方案除了降低数据库压力之外,还提高了响应速度,简直一箭双雕哇~

    哪些数据会放到缓存

    通常情况下,都会将那些变化较少且经常用到的数据会放到缓存中,比如像字典、系统参数、有固定值的状态码等等;另外将用户保存到缓存也是一种很好的策略,这样登录的时候就可以极速响应了。

    mybatis一级缓存

    mybatis 的缓存分为2类,分别是一级缓存和二级缓存,一级缓存是默认开启的,它在一个sqlSession会话里面的所有查询操作都会保存到缓存中,一般来说一个请求中的所有增删改查操作都是在同一个sqlSession里面的,所以我们可以认为每个请求都有自己的一级缓存,如果同一个sqlSession会话中2 个查询中间有一个 insert 、update或delete 语句,那么之前查询的所有缓存都会清空。

    Reader reader = Resources.getResourceAsReader("config/configuration.xml");
            //创建数据工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(reader);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
    
             // 。。。。。。
            // 这中间所走的所有查询操作都会进行缓存,一旦关闭sqlSession会话,缓存则会刷新
    
    
            //释放会话
            sqlSession.clearCache();
            // 关闭会话
            sqlSession.close();

    在mybatis 的配置文件中加入以下配置,开启sql日志,每一个sql代表请求了一次数据库,这样我们就可以根据sql来判断是否使用了缓存。

    <settings>
            <!--标准的日志工厂实现类,打印sql日志-->
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>

    示例代码

    public static void main(String[] args) throws IOException {
    
    
            // 加载mybatis配置文件
            Reader reader = Resources.getResourceAsReader("config/configuration.xml");
            //创建数据工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(reader);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
    
            // 获取mapper接口对象
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
    
            // 查询第一次
            User user = mapper.selectByPrimaryKey("3rfrf34r34");
            // 第二次查询
            User user1 = mapper.selectByPrimaryKey("3rfrf34r34");
            System.out.println("两个user对象是否相等:"+(user == user1));
            //释放会话
            sqlSession.clearCache();
            sqlSession.close();
        }

    打印结果

    baa372b6620ecf45917490b66a5839a9.png

    根据结果可以看到,代码中执行了2次查询, 但实际运行时只查询了一次数据库,第二次获取数据时直接从缓存中读取,并且2次读取的数据都是一样的,到这里,一级缓存就已经生效了;

    接下来我们来测试第二种情况 :查询 -> 修改 -> 查询

    实例代码。

    public static void main(String[] args) throws IOException {
    
    
            // 加载mybatis配置文件
            Reader reader = Resources.getResourceAsReader("config/configuration.xml");
            //创建数据工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(reader);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
    
            // 获取mapper接口对象
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            // 查询第一次
            User user = mapper.selectByPrimaryKey("3rfrf34r34");
    
    
            // 修改
            mapper.updateByPrimaryKey(user);
            // 第二次查询
            User user1 = mapper.selectByPrimaryKey("3rfrf34r34");
            System.out.println("两个user对象是否相等:"+(user == user1));
            //释放会话
            sqlSession.clearCache();
            sqlSession.close();
        }

    控制台打印了三次sql,其中第一个查询和第三个查询是一样的,但是并没有使用缓存,为什么会这样呢?因为每次增删改操作都有可能会改变原来的数据,所以必须刷新缓存;

    二级缓存

    二级缓存是全局的,也就是说;多个请求可以共用一个缓存,二级缓存需要手动开启,有2种方式配置二级缓存,

    • 缓存会先放在一级缓存中,当sqlSession会话提交或者关闭时才会将一级缓存刷新到二级缓存中;

    • 开启二级缓存后,用户查询时,会先去二级缓存中找,找不到在去一级缓存中找;

    二级缓存流程图

    ebb8b2ab02c1cfeb196f343f92ae31bf.png

    第一种配置方式

    单个mapper配置,主需要在需要开启二级缓存的mapper.xml文件中加入以下配置即可开启。

    <!-- 开启单个mapper的二级缓存,也叫全局缓存-->
      <cache />

    注意一定要加到xxMapper.xml的文件内,千万不要加到mybatis 的主配置文件里面了,会报错的。

    736ee96ae839bab973a5555b933d1b97.png

    第二种配置方式

    所有的mapper都开启二级缓存,在mybatis.xml中加入以下配置即可

    <settings>
            <!--  开启所有mapper的二级缓存 -->
            <!--<setting name="cacheEnabled" value="true" />-->
        </settings>

    ca5f3003b7b448e4c4db631b8f7014f2.png

    示例代码

    public static void main(String[] args) throws IOException {
    
    
            // 加载mybatis配置文件
            Reader reader = Resources.getResourceAsReader("config/configuration.xml");
            //创建数据工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    
    
            SqlSessionFactory sqlSessionFactory = builder.build(reader);
    
    
            // 第一个会话
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
    
            // 获取会话一的mapper接口对象
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
    
            // 第一次查询
            User user = mapper.selectByPrimaryKey("3rfrf34r34");
    
    
            //释放第一个会话
            sqlSession.clearCache();
            sqlSession.close();
            // 第二个会话
            SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
            // 获取会话二的mapper接口对象
            UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
            // 第二次查询
            User user1 = mapper2.selectByPrimaryKey("3rfrf34r34");
            // 释放第二个会话
            sqlSession2.clearCache();
            sqlSession2.close();
        }

    打印结果

    e0b98e2828fd72300d78bdb795fdd4c8.png

    打印结果很明显,2次查询,但是日志显示只查询了一次数据库, 第二次是从缓存中获取的数据,至此,二级缓存已开启!

    注意事项

    • 另外,缓存还有以下几种情况需要注意

    • 映射语句文件中的所有 select 语句的结果将会被缓存。

    • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。

    • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。

    • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。

    • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。

    • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

    ca73090f460cdaa5b067c5c5ca15e607.png

    展开全文
  • jQuery响应式下拉导航菜单项是款使用CSS3动画来在鼠标滑过时显示子菜单项,并且在视口变小时会切换到列表树结构。
  • 购物网站中通过勾选类别、价格、销售量范围等属性来对所有的商品进行筛选,筛选出满足客户需要的商品,这是种典型的组合查询。在小数据量的情况下,后台通过简单的sql语句便能够快速过滤出需要的数据,但随着数据...

    1 概述

    组合查询为多条件组合查询,在很多场景下都有使用。购物网站中通过勾选类别、价格、销售量范围等属性来对所有的商品进行筛选,筛选出满足客户需要的商品,这是一种典型的组合查询。在小数据量的情况下,后台通过简单的sql语句便能够快速过滤出需要的数据,但随着数据量的增加,继续使用sql语句,查询效率会直线下降。当数据量达到一定的量级,服务器将会不堪重负甚至面临挂掉的危险,并且大数据量的存储也成为了一个问题。本文将讨论在亿级数据的情况下,多条件组合查询秒级响应的解决方案。

    2 方案思考

    2.1 数据存储

    假定每条数据有10个字段,每个字段的大小为4Byte,共有1亿条数据。通过传统的关系型数据库mysql,使用JDBC批处理和事务混合的方式对数据进行插入,插入一亿数据大约需要半小时,字段可能会出现为空的情况,导致冗余。针对海量数据的存储,现如今使用较多的是HBase。使用HBase的好处有三:其一,它是非关系型数据库,字段为空的值只在逻辑上存在,在空间上不存在,因此解决了冗余的问题;其二,它是面向列的数据库,能够通过简单的API调用对字段进行横向扩展;其三,它是分布式数据库,表的RowKey 按照字典排序,Region按照RowKey设置split point进行shard,通过这种方式实现全局、分布式索引,通过RowKey索引数据能够在毫秒级返回。Hbase插入数据可以调用批量插入或者通过MR程序插入,实测在批量提交数据条数设置为1000,开10个线程的情况下,插入一亿数据大约需要10分钟。若需要加速插入速度,可以通过增加批量提交数、调整线程数或者使用MR程序进行Hbase的写入。Hbase本身是分布式数据库,数据存储可以存储在多个节点上,使用Zookeeper统一管理,提供数据备份和故障恢复的功能。因此使用Hbase作为数据仓库,对结构化数据进行存储。

    2.2 数据查询

    Hbase中的数据查询只有两种方式:一是使用get 'tablename', 'rowkey‘’直接通过rowkey进行查询,亿级数据的查询结果可以在毫秒内返回;二是设置过滤器对全表进行Scan扫描,该查询方式在海量数据的情况下耗时十分长,当然也和服务器的性能有关。我们的需求是秒级响应,如果使用全表扫描方式,数据量达到万级或者十万级就无法实现实时响应了,要进行这样的查询,往往是要通过类似Hive、Pig等系统进行全表的MapReduce计算,这种方式既浪费了机器的计算资源,又因高延迟使得应用黯然失色。因此我们考虑使用rowKey对数据进行查询,如果我们使用rowKey对全表进行多条件组合查询,这将对rowKey的设置要求十分高,面向业务而言这对程序员十分不友好,因此我们需要通过建立二级索引的方式,按索引的种类扫描各自独立的单索引表,最后将扫描结果merge,得到目标rowKey。HBase有原生的建立二级索引的方式,即使用HBase的coprocessor协处理器,可以根据业务进行灵活的设置,但较为复杂,本文讨论使用一种业务模式较为固定,但更加简单直接的方式创建索引——Solr。Solr是一个独立的企业级搜索应用服务器,是Apache Lucene项目的开源企业搜索平台。其主要功能包括全文检索、命中标示、分面搜索、动态聚类、数据库集成,以及富文本(如Word、PDF)的处理。Solr是高度可扩展的,并提供了分布式搜索和索引复制。我们可以直接使用Solr这一组件,通过修改配置文件以实现相关的业务需求。通过批量建立索引的方式对HBase中的一亿条数据的10个字段构建索引,耗时为3383s,约为1小时。具体代码如下:

    public class ThreadsCreateIndexWork {
        private static Logger logger = LoggerFactory.getLogger(ThreadsCreateIndexWork.class);
        public static void main(String[] args) throws IOException, SolrServerException {
            if(args.length < 3) {
                logger.info("[tableName  |  queueSize  |  threadCount]");
                logger.info("e.g.| test1 20000 20");
            }
            String tableName = args[0];
            String queueSize = args[1];
            String threadCount = args[2];
    
            long start = System.currentTimeMillis();
    
            final Configuration conf;
            Properties prop = PropertiesReaderUtils.getProperties("conf/path.properties");
            String server = prop.getProperty("solr.server");
            SolrServer solrServer = new ConcurrentUpdateSolrServer(server, Integer.parseInt(queueSize), Integer.parseInt(threadCount));
    
            conf = HBaseConfiguration.create();
            HTable table = new HTable(conf, tableName); // 这里指定HBase表名称
            Scan scan = new Scan();
            scan.addFamily(Bytes.toBytes("people")); // 这里指定HBase表的列族
            scan.setCaching(500);
            scan.setCacheBlocks(false);
            ResultScanner ss = table.getScanner(scan);
    
            try {
                for (Result r : ss) {
                    SolrInputDocument solrDoc = new SolrInputDocument();
                    solrDoc.addField("rowkey", new String(r.getRow()));
                    for (KeyValue kv : r.raw()) {
                        String fieldName = new String(kv.getQualifier());
                        String fieldValue = new String(kv.getValue());
                        if (fieldName.equalsIgnoreCase("upperClothing")
                                || fieldName.equalsIgnoreCase("lowerClothing")
                                || fieldName.equalsIgnoreCase("coatStyle")
                                || fieldName.equalsIgnoreCase("trousersStyle")
                                || fieldName.equalsIgnoreCase("sex")
                                || fieldName.equalsIgnoreCase("age")
                                || fieldName.equalsIgnoreCase("angle")
                                || fieldName.equalsIgnoreCase("bag")
                                || fieldName.equalsIgnoreCase("umbrella")
                                || fieldName.equalsIgnoreCase("featureType")){
                            solrDoc.addField(fieldName, fieldValue);
                        }
                    }
                    solrServer.add(solrDoc);
                }
                ss.close();
                table.close();
            } catch (IOException e) {
            } finally {
                ss.close();
                table.close();
            }
    
            long time = System.currentTimeMillis() - start;
            logger.info("---------- create index with thread use time " + String.valueOf(time));
        }
    }
    

    ConcurrentUpdateSolrServer类可以使用多线程向SolrCloud的多个节点发送http请求,queueSize为队列大小,即往Solr中一次性批量add的数据数目,threadCount为开启的线程数,可以根据服务器性能的不同进行自定义,以提高构建索引的速度。笔者对1000w条数据进行参数调整测试,得到如下结果:

    queueSizethreadNumTime (s)
    1000020274
    2000020250
    2000030255
    2000040254
    3000020255
    因此测试中选用的参数为queueSize:20000threadNum:20,索引构建速度为4w/s,还尝试通过修改scan.setCaching(500);的大小来提高构建速度,但是发现该缓存大小对构建速度的影响可以忽略不计,应该是索引构建速度低于HBase的Scan速度,因此暂时没有必要对HBase的Scan操作进行加速。Solr对构建索引的服务进行了上层封装,提供一个web服务的接口,可以直接通过可视化界面对结果进行查询。

    3 解决方案

    在这里插入图片描述
    综上,针对亿级数据多条件组合查询,给出的解决方案是使用HBase+Solr的方式,CDH将HBase和Solr都以组件的方式提供出来,可以使用CDH平台对HBase和Solr进行统一的管理。Hbase用于存储海量数据,Solr使用SolrCloud模式进行部署,提供索引构建和查询。索引的创建可以通过接口离线批量创建,也可以使用HBase Indexer连接HBase和Solr,提供自动化索引构建,CDH平台也集成了Hbase Indexer(Lily HBase Indexer)这一组件,具体的整合方法见 Solr+Hbase+Hbase Indexer查询方案流程整合

    4 方案测试

    笔者将一亿条包含10个字段的数据开启10个线程插入Hbase中,然后使用Solr对10个字段构建了索引,在Solr的可视化界面进行查询,查询结果如下图所示。
    查询全部数据
    组合查询1
    组合查询2
    其中,QTime为响应时间,q为查询语句,wt为请求格式(设置请求格式为xml响应速度更快),numFound为找到符合查询条件的数据条数,docs为返回的数据也就是rowKey。可以看到,组合查询都能够在秒级响应返回响应rowKey,而通过rowKey在HBase中返回该条数据的所有字段可以在毫秒级响应,如下图所示:
    在这里插入图片描述
    至此,可以证明,亿级数据多条件组合查询使用HBase+Solr的解决方案能够满足秒级响应的需求。具体流程操作可参考Solr+Hbase+Hbase Indexer查询方案流程整合

    展开全文
  • 它是个jQuery插件,其中包括个JavaScript文件CSS文件以及示例HTML。 描述:下拉菜单jQuery插件,用于响应式布局 仓库: : 演示: : Bower :jQuery响应菜单 需要:jQuery的 作者:jbowyers 版权:2015 ...
  • 款扁平风格带字体图标的CSS3响应式多级下拉菜单导航代码,兼容手机端二级下拉菜单、三级下拉菜单、四级下拉菜单代码。
  • 实现效果: jQuery响应式下拉导航菜单项,一款黑色的导航,鼠标放在一级目录会显示二级目录,很适用于企业网站或者商城网站,php中文网推荐下载!
  • 选择溶液初始pH值、反应温度及过硫酸钾/对氯苯酚物质的量比为考察因素,对氯苯酚拟一级降解速率常数(Y)为响应值,通过Box-Behnken设计方法(BBD)建立了以对氯苯酚拟一级降解速率常数为响应值的次多项式模型,分析...
  • CSS3实现的15种二级下拉导航加载动画特效源码是一段实现了15种响应二级菜单导航加载动画效果,当鼠标放在一级菜单导航时,二级菜单会响应动画加载,本段代码适应于所有网页使用,有需要的朋友们可以前来下载使用。...
  • 款扁平风格jQuery响应速度很快的二级选项卡多级选项卡,顾名思义,就是选项卡中包含了下级选项卡,支持多种样式,当然这样你去调试了。
  • 响应曲面法是优化随机过程的统计学试验方法。目标是寻找试验指标与各因子间的定量规律,找出各因子水平的最佳组合。在多元线性回归的基础上主动收集数据,以获得具有较好性质的回归方程。建立的复杂多维空间曲面较...

    数据分析学习记录(二)—响应曲面法及Design-Expert的简单使用

    注:本文参考博客链接:https://www.biomart.cn/experiment/793/2714853.htm
    

    一 引言

    响应曲面法(Response surface methodology)在百度百科上的解释:

    “响应曲面法是优化随机过程的统计学试验方法。目标是寻找试验指标与各因子间的定量规律,找出各因子水平的最佳组合。在多元线性回归的基础上主动收集数据,以获得具有较好性质的回归方程。建立的复杂多维空间曲面较接近实际情况,所需要的试验组数相对较少,在模拟和系统动力学中得到广泛应用。”

    对于我来说怎么样去更简单直观的理解它是我在自学时首要考虑的问题。第一点,我们为什么需要使用响应曲面法?这里就要引出另一个最常用的过程优化方法,单因素试验

    单因素试验是假设各因素间不存在交互作用的前提下,每次只改变一个因素,其他因素需要保持在恒定水平下,再去研究不同试验水平对响应值的影响。

    而在实际情况中过程影响因素十分复杂,并且因素与因素之间通常都会存在一定的交互作用,当试验因素很多的时候,我们需要进行数次的单因素分析以及较长的试验周期才能逐个优化各因素,这样效率未必太低。

    此时又不得不提到一个效率比单因素分析更高的过程优化方法,那就是正交试验。正交试验可以同时考虑多因素,在合理减少单因素分析的试验次数的情况下,寻找最佳的因素水平组合,通过方差分析得到影响结果的主次因素,但正交试验在处理因素间交互作用时需要设计交互作用表,当因素间的交互更为复杂时,正交试验的工作量也会随之上升。

    于是。。

    响应曲面法应运而生。 响应曲面法又叫回归设计,其实了解过响应曲面法的分析原理后对于它这个名字就不难理解了,它在多元线性回归的基础上建立一个包括各显著因素的一次项、二次项和任何两个因素之间的一级交互作用项,可以说是集统计、数学和计算机与一体的统计学范畴的过程优化方法。

    响应曲面法通过设计合理的少次数的试验,精确研究各因素和我们想得到的响应值之间的关系,快速有效的确定多因素系统的最佳条件。

    二 实例

    这里我找了一篇文献,用其中的数据分析结合Design-expert软件进行响应曲面分析。响应面常用的方法有两种:中心复合试验设计(central composite design,CCD)和 Box-Behnken 试验设计(BBD)。

    常用的响应面设计和分析软件有 Matlab、SAS 和 Design-Expert。在已经发表的有关响应面(RSM)优化试验的论文中,Design-Expert 是使用最广泛的软件。

    参考文献信息:
    [1]胡栋, 柯灵超, 张敬宇,等. 响应面法设计优化阿维菌素化学合成发酵培养基[J]. 中国抗生素杂志, 2018, 043(008):1055-1061.

    在这里插入图片描述
    首先打开软件,选择新建分析,然后选择响应曲面分析,选择第二个Box-Behnken ,如图:
    在这里插入图片描述

    在这里插入图片描述在这里插入图片描述

    我们对照文献中的数据表填写,输入对应的因素数量以及试验中的绝对因素(默认为0),然后输入因素的名称单位、最大值和最小值,点击continue进入下一个页面:

    在这里插入图片描述
    在这里插入图片描述

    在这里填写对应我们想优化的响应值,这篇文献中只有 一个响应值,为阿维菌素提高百分数,于是我们填写进去,单位为**%**,点击continue:

    在这里插入图片描述

    如上图的表后面的响应值数据是需要手动输入进去的,对应上面那一张表里的数据。
    输入好之后我们点击Analysis的R1:Transform 选项卡,一般选择默认值即可。如果有别的要求,可以根据需要和指示查找每种模式的详细介绍再选择。

    在这里插入图片描述
    FitSummary,可以看下建议的因素。

    在这里插入图片描述
    Model 选项卡取默认值即可,点击 ANOVA 选项卡,显示方差分析,方差的显著性检验,系数显著性检验回归方程。

    在这里插入图片描述在这里插入图片描述

    点击 Diagnostics 选项卡,依次点击左端选项,首先展示的是 Normal Plot,参差的正态规律分布图,图中的点越靠近直线越好。

    在这里插入图片描述
    第二个展示残差与方程预测值的对应关系图,分布越分散越无规律越好。

    在这里插入图片描述
    最后展示的是预测值和试验实际值的对应关系图,点越靠近同一条直线越好。

    在这里插入图片描述
    然后点击 Influence 后再点击 Report 进入结果界面,数据显示如图,包含试验实际测量值(左)和方程预测值(右)。

    在这里插入图片描述
    然后点击 Model Graphs 查看等高线图,等高线图考察每两个因素对因变量造成的影响,并由拟合的方程形成等高线,为二维平面图形,可经由该图找出较好的范围。

    在这里插入图片描述
    三维响应曲面图可更加直观地看出两因素的影响情况,可以很直观地找出最优范围,刚才所看到的二维等高线图即为三维响应面图在底面的投影图。

    在这里插入图片描述

    接下来是关键的优化条件选项,根据实际情况确定每个因素可以取值的范围,然后进行「响应值目标」的确定,每个试验都有不同的目的,比如此处我们想要找到能使阿维菌素最大产出量的最优培养条件,但别的试验中对目标的要求有需要最大值,有需要最小值,有时候需要把结果稳定在某个范围或者需要一个固定的数值。那么在这四种模式中就可以选择其相对应的情况。

    在这里插入图片描述
    在这里插入图片描述
    点击 Solutions 选项卡,即可看到经过分析得到的最优值,一般会列出许多方案,第一个方案就是各因素取最优值后的结果可取到的最大化的解决方案,为预测值。

    在这里插入图片描述

    以上!!!若有不对,欢迎交流指出。

    展开全文
  • jQuery响应式网站导航菜单基于jQuery.1.10.2.min.js制作,响应式设计,自适应分辨率,兼容PC端移动端,鼠标经过显示二级菜单。
  • 中英文响应式企业网站是一款采用asp模式,后台功能实用,界面大气,导航无限级分类,单篇栏目添加等的企业... 功能介绍: 【新闻文章管理】可以发布公司新闻业内最新资讯,可增加多个一级栏目 【英文信息管理】 可以
  • 基于vue的后台管理系统实现顶部一级菜单,左侧二级二级以下菜单 介绍 本人是在若依后台管理系统上修改的此功能 该功能实现的是顶部导航菜单左侧导航菜单进行响应,产生联动效果,而左侧的菜单是根据router获取...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 438,268
精华内容 175,307
热门标签
关键字:

一级响应和二级响应的区别