从0开始搭建一个项目
用到的技术:springboot+vue+mybatis+elementui+mysql
1.创建springboot项目
1.引入pom依赖
<dependencies><!--模板引擎--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!--web项目依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--简化实体类,自动创建get set等方法--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--测试相关--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--mybatis依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><!--mysql依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--json数据处理--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.15</version></dependency><!--分页助手 mybatis分页查询需要的依赖--><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.2.13</version></dependency></dependencies>
2.配置文件
#项目端口server.port=8899#mysql数据库spring.datasource.hikari.max-lifetime=120000spring.datasource.url= jdbc:mysql://47.98.252.xxx:xxxx/atguigudbspring.datasource.username=数据库用户名spring.datasource.password=数据库密码spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.hikari.max_lifetime=120000#mysql字段驼峰命名 数据库中下划线的字段会被自动分隔变成大写字母mybatis.configuration.map-underscore-to-camel-case=true#指定xml文件路径mybatis.mapper-locations=classpath:mapper/*.xml#解析时间变为东8区spring.jackson.time-zone=GMT+8#时间格式spring.jackson.date-format=yyyy-MM-dd HH:mm:ss#打印sqllogging.level.com.lzp=debug#pagehelper传递分页参数 根据参数自动分页pagehelper.support-methods-arguments=true
3.创建欢迎页
此工程欢迎页就是登录页
templates文件夹下index.html文件
页面代码:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>login</title><script src="/vue/vue.js" rel="stylesheet" type="text/javascript"></script><script src="/element-ui/lib/index.js"></script><link rel="stylesheet" href="/element-ui/lib/theme-chalk/index.css"><script src="/axios/axios.min.js"></script><script src="/vue-router/vue-router.min.js"></script></head><body style="background: linen"><div id="test" ><h1 align="center">请输入账号和密码</h1><br><br><br><br><!--登录--><h5 align="center" style="color: red">{{errorMsg}}</h5><el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="800px" class="demo-ruleForm" ><h5 align="center" style="color: #C0C4CC">账号:admin 密码:任意</h5><el-form-item label="账号" prop="username"><el-input style="width:285px;" v-model="ruleForm.username" prefix-icon="el-icon-user"placeholder="请输入账号" clearable></el-input></el-form-item><el-form-item label="密码" prop="password"><el-input style="width:285px;" v-model="ruleForm.password"prefix-icon="el-icon-unlock" placeholder="请输入密码" show-password clearable></el-input></el-form-item><el-form-item><el-button type="primary" @click="submitForm('ruleForm')">立即登录</el-button></el-form-item></el-form></div><script>//定义路由,登陆成功后进行页面跳转let rt = new VueRouter({ mode: 'history',routes: [{path: "/item",name: "ok",}, {path: "/reg",}]})//创建vue实例new Vue ({el:"#test",router: rt,data: {//错误信息errorMsg:'',//表单数据 账号和密码ruleForm: {username: '',password:'',},//校验规则rules: {name: [{ required: true, message: '请输入用户名', trigger: 'blur' },{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }],}}, //方法methods: {//提交账号密码submitForm(formName) {//执行数据校验this.$refs[formName].validate((valid) => {//校验通过发送请求 通过axios.post方法if (valid) { //发送请求,处理登录 axios.post("/doLogin", this.ruleForm).then(res => { console.log(res); //登录成功判断 if(res.data===123 && res.status===200) //路径修改 this.$router.push("item"); else this.errorMsg="账号:admin~" }) // alert('submit!');} else {console.log('error submit!!');return false;}});}},//路径变了页面不跳转,路径变了强制刷新页面watch: {'$route' (to, from) {this.$router.go(0);}},})</script></body></html>
页面效果:
4.处理登录请求
正常逻辑是去数据库根据用户名获取用户信息,然后进行密码匹配,匹配正确正常返回,异常返回异常信息。
这里处理逻辑很简单,判断传过来的username是不是admin,是的话返回123,不是的话返回0。
@RequestMapping("/doLogin")@ResponseBodypublic String doLogin(@RequestBody User user){System.out.println(user);if(user.getUsername().equals("admin"))return "123";else return "0";}
5.登录成功进行页面跳转
设置路径为item,然后进行页面刷新,跳转到localhost:8899/item,展示employee信息
6.接收item请求
跳转到item.html页面
@RequestMapping("/item")public String afterLogin(){return "item";}
7.item.html
展示员工信息
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>employees</title><script src="/vue/vue.js" rel="stylesheet" type="text/javascript"></script><script src="/element-ui/lib/index.js"></script><link rel="stylesheet" href="/element-ui/lib/theme-chalk/index.css"><script src="/axios/axios.min.js"></script><script src="/vue-router/vue-router.min.js"></script></head><body><divid="item" class="item" ><h3 align="right" >你好:{{username}}</h3><h3 style="color: #a0cfff;"align="left" >员工信息展示</h3><div>入职日期:<div class="block" style="display: inline-block"><el-date-pickerstyle="width: 195px"value-format="yyyy-MM-dd"v-model="search.searchDate"type="date"placeholder="请选择入职日期起点"></el-date-picker><el-date-pickerstyle="width: 195px"value-format="yyyy-MM-dd"v-model="search.searchDateEnd"type="date"placeholder="请选择入职日期终点"></el-date-picker></div>姓名:<el-inputstyle="width: 185px"placeholder="请输入部分姓名"prefix-icon="el-icon-search"v-model="search.searchName"></el-input><el-button type="primary" round @click="getEmployees()" icon="el-icon-search">条件搜索</el-button><el-buttontype="primary" round @click="getDepartments()" icon="el-icon-menu">部门总览</el-button></div><template><el-table:data="employeesData"style="width: 100%":row-class-name="tableRowClassName"><el-table-columnprop="employeeId"label="编号"width="180"></el-table-column><el-table-columnprop="firstName"label="姓氏"width="180"></el-table-column><el-table-columnprop="lastName"label="名字"width="180"></el-table-column><el-table-columnprop="email"label="邮箱"width="180"></el-table-column><el-table-columnprop="phoneNumber"label="电话"width="180"></el-table-column><el-table-columnprop="hireDate"label="入职日期"width="180"></el-table-column><el-table-columnprop="salary"label="工资"width="180"></el-table-column><el-table-columnprop="departmentName"label="部门名称"></el-table-column><el-table-column><template slot-scope="scope"><el-button type="text" @click="getDepartmentInfo(scope.row.departmentId)">部门信息</el-button></template></el-table-column></el-table><el-dialog title="部门信息" :visible.sync="dialogTableVisible"><el-table :data="departmentData"><el-table-column prop="departmentId" label="部门编号" width="150"></el-table-column><el-table-column prop="departmentName" label="部门名称" width="200"></el-table-column></el-table></el-dialog><div class="block"><!--完整功能--><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="page.pageNum":page-sizes="[5, 10, 15, 20,25,30,35,40,45,50]":page-size="page.pageSize"layout="total, sizes, prev, pager, next, jumper":total="page.total"></el-pagination></div></template></div> <script> // 定义路由 let rt = new VueRouter({ mode: 'history', routes: [{ path: "/departments", name: "ok", }, { path: "/reg", }] })// 创建vue实例 new Vue({ el:"#item", router: rt, // 数据 data() { return { username: '', tableData: [], employeesData: [], page:{ pageNum: 1, pageSize: 10, total:0, }, search:{ searchName: '', searchDate: '', searchDateEnd:'', }, departmentData: [], dialogTableVisible: false, } }, //方法 methods: { //获取用户信息,页面展示 getUser() { axios.post("/getUser").then(res => { this.username=res.data console.log(res.data) }) }, getData(){ axios.post("/getData").then(res => { this.tableData=res.data.data console.log(this.tableData) }) }, // 获取员工信息,传入分页参数,条件参数 getEmployees(){ axios.get("/getEmployees",{ params:{ pageNum:this.page.pageNum, pageSize:this.page.pageSize, searchName:this.search.searchName, searchDate:this.search.searchDate, searchDateEnd:this.search.searchDateEnd, } }).then(res => { this.employeesData=res.data.data.list; this.page.pageNum=res.data.data.pageNum; this.page.pageSize=res.data.data.pageSize; this.page.total=res.data.data.total; console.log(res.data.data) }) }, //展示所有部门信息 getDepartments(){ this.$router.push("departments"); }, //表格项样式 tableRowClassName({row, rowIndex}) { if (rowIndex === 0) { return 'success-row'; } else if (rowIndex === 1) { return 'warning-row'; } return ''; }, //处理每页显示条数 handleSizeChange(val) { this.page.pageSize=val; this.getEmployees(); console.log(`每页 ${val} 条`); }, //处理页面切换 handleCurrentChange(val) { this.page.pageNum=val; this.getEmployees(); console.log(`当前页: ${val}`); }, //获取单个部门信息 getDepartmentInfo(depId){ console.log(depId); axios.get("/getDepartmentById",{ params:{ depId:depId, } }).then(res => { this.departmentData=res.data.data; console.log(this.departmentData) this.dialogTableVisible = true; }) } }, //页面创建完执行,获取用户,获取员工信息 created: function () { this.getUser(), this.getEmployees() }, //路径变了页面不跳转,强制刷新页面 watch: { '$route' (to, from) { this.$router.go(0); } }, }) </script></body><style>.el-table .warning-row {background: oldlace;}.el-table .success-row {background: #f0f9eb;}.el-container {padding: 0px;margin: 0px;height: 100%;}.item{margin-left: 200px;/*margin:0 auto;*/}</style></html>
页面效果:
8.接收getEmployees请求
controller:
传入分页参数和条件查询参数,调用service处理。
@GetMapping("/getEmployees")public R findEmployees(@RequestParam Map<String, Object>params) throws ParseException {System.out.println(params);PageInfo<EmployeeVo> employees = empService.findEmployees(params);return R.ok().setData(employees);}
service:
使用PageHelper进行分页查询,调用mapper方法进行查询
@Overridepublic PageInfo<EmployeeVo> findEmployees(Map<String, Object>params) throws ParseException {PageHelper.startPage(params);PageInfo<EmployeeVo> employeesPageInfo = new PageInfo<>(empMapper.findEmployees(params));return employeesPageInfo;}
mapper:
List<EmployeeVo> findEmployees(@Param("params") Map<String, Object> params);
xml:
<select id="findEmployees" resultType="com.lzp.vo.EmployeeVo">select e.*,d.department_name from employees eLEFT JOIN departments dON d.`department_id`=e.`department_id`where 1=1<if test=" params.searchName != null and params.searchName!='' "> and ( first_name like "%"#{params.searchName}"%" or last_name like "%"#{params.searchName}"%" )</if><if test=" params.searchDate != null and params.searchDate!='' ">and hire_date ]]> #{params.searchDate}</if><if test=" params.searchDateEnd != null and params.searchDateEnd!='' ">and hire_date <![CDATA[ #{params.searchDateEnd}</if></select>
9.处理部门信息
点击部门信息,根据department_id查询部门信息
controller:
根据部门id查询
@GetMapping("/getDepartmentById")public R findDepartmentById(@RequestParam("depId") Integer depId) throws ParseException {System.out.println(depId);List<DepatmentVo> depatmentVo = empService.findDepartmentById(depId);return R.ok().setData(depatmentVo);}
service:
根据id查询
@Overridepublic List<DepatmentVo> findDepartmentById(Integer depId) {List<DepatmentVo>departmentById = empMapper.findDepartmentById(depId);return departmentById;}
mapper:
List<DepatmentVo> findDepartmentById(@Param("depId") Integer depId);
xml:
<select id="findDepartmentById" resultType="com.lzp.vo.DepatmentVo">select department_id,department_name from departments where department_id=#{depId}</select>
10.查询所有部门
点击部门总览,查询部门总信息,进行页面跳转。
页面效果:
页面内容:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>departments</title><script src="/vue/vue.js" rel="stylesheet" type="text/javascript"></script><script src="/element-ui/lib/index.js"></script><link rel="stylesheet" href="/element-ui/lib/theme-chalk/index.css"><script src="/axios/axios.min.js"></script><script src="/vue-router/vue-router.min.js"></script></head><body><div id="department" class="department"><h3 align="right" >你好:{{username}}</h3><div><el-button size="small" type="primary" round @click="getLocationInfo()" icon="el-icon-menu">地址总览</el-button></div><h3 style="color: #a0cfff"align="left" >部门信息展示</h3><template><el-table:data="departmentsData"style="width: 100%":row-class-name="tableRowClassName"><el-table-columnprop="departmentId"label="部门编号"width="180"></el-table-column><el-table-columnprop="departmentName"label="部门名称"width="180"></el-table-column><el-table-columnprop="managerName"label="部门领导"width="180"></el-table-column><el-table-columnprop="streetAddress"label="部门地址"width="180"></el-table-column><el-table-column><template slot-scope="scope"><el-button type="text" @click="getLocationInfo(scope.row.locationId)">地址信息</el-button></template></el-table-column></el-table><el-dialog title="地址信息" :visible.sync="dialogTableVisible" class="pop-up"><el-table :data="locationsData"><el-table-column prop="locationId" label="地址编号" width="150"></el-table-column><el-table-column prop="streetAddress" label="部门地址" width="200"></el-table-column><el-table-column prop="postalCode" label="邮编" width="150"></el-table-column><el-table-column prop="city" label="所在城市" width="150"></el-table-column><el-table-column prop="stateProvince" label="所在州省" width="150"></el-table-column></el-table></el-dialog><div class="block"><!--完整功能--><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="page.pageNum":page-sizes="[5, 10, 15, 20,25,30,35,40,45,50]":page-size="page.pageSize"layout="total, sizes, prev, pager, next, jumper":total="page.total"></el-pagination></div></template></div><script><!--创建vue实例-->new Vue({el:"#department",//数据data() {return{username:'',departmentsData: [],locationsData: [],dialogTableVisible: false,page:{pageNum: 1,pageSize: 10,total:0,},}},//方法methods:{tableRowClassName({row, rowIndex}) {if (rowIndex === 0) {return 'success-row';} else if (rowIndex === 1) {return 'warning-row';}return '';},handleSizeChange(val) {this.page.pageSize=val;this.getDepartments();console.log(`每页 ${val} 条`);},handleCurrentChange(val) {this.page.pageNum=val;this.getDepartments();console.log(`当前页: ${val}`);},getUser() {axios.post("/getUser").then(res => {this.username=res.dataconsole.log(res.data)})},//获取部门信息getDepartments(){axios.get("/getDepartments",{params:{pageNum:this.page.pageNum,pageSize:this.page.pageSize,// searchName:this.search.searchName,// searchDate:this.search.searchDate,// searchDateEnd:this.search.searchDateEnd,}}).then(res => {this.departmentsData=res.data.data.list;this.page.pageNum=res.data.data.pageNum;this.page.pageSize=res.data.data.pageSize;this.page.total=res.data.data.total;console.log(res.data.data)})},//获取地址信息getLocationInfo(locationId){console.log(locationId);axios.get("/getLocations",{params:{locationId:locationId,}}).then(res => {this.locationsData=res.data.data;console.log(this.locationsData)this.dialogTableVisible = true;})}},created: function () {this.getUser(),this.getDepartments()},})</script></body><style>.el-table .warning-row {background: oldlace;}.el-table .success-row {background: #f0f9eb;}.department{margin-left: 200px;/*margin:0 auto;*/}/*弹窗增加滚动条*/.pop-up .el-dialog__body {padding: 3px 30px;overflow-y: auto;height: 40vh;}</style></html>
11.接收getDepartments请求
controller:
传入分页参数,调用service方法查询
@GetMapping("/getDepartments")@ResponseBodypublic R getDepartments(@RequestParam Map<String, Object> params ){PageInfo<DepartmentsVo> departments = depService.getDepartments(params);return R.ok().setData(departments);}
service:
分页查询查出所有部门信息,遍历集合,根据manager_id查询领导名字。返回PageInfo。
@Overridepublic PageInfo<DepartmentsVo> getDepartments(Map<String, Object>params) {//分页查询PageHelper.startPage(params);PageInfo<DepartmentsVo> departmentsVoPageInfo = new PageInfo<>(depMapper.getDepartments(params));//PageInfo类型的数据,实际数据都在list集合中//List departments = depMapper.getDepartments(params);//遍历list集合List<DepartmentsVo> departmentsVoList = departmentsVoPageInfo.getList().stream().map((item) -> {//获取到部门领导的名字 根据manager id 去employees表查询String managerNameById = empMapper.findManagerNameById(item.getManagerId());item.setManagerName(managerNameById);return item;}).collect(Collectors.toList());//将新的list集合放入PageInfo对象中departmentsVoPageInfo.setList(departmentsVoList);return departmentsVoPageInfo;}
mapper:
List<DepartmentsVo> getDepartments(@Param("params") Map<String, Object> params);String findManagerNameById(@Param("id") Integer id);
xml:
<select id="getDepartments" resultType="com.lzp.vo.DepartmentsVo">SELECT d.*,l.street_addressFROM departments dLEFT JOIN locations lON d.`location_id`=l.`location_id`</select><select id="findManagerNameById" resultType="String">select first_name from employees where employee_id=#{id}</select>
这里也可以使用两次关联查询
SELECT d.`department_id`,d.`department_name`,e.`first_name`,l.street_addressFROM departments dLEFT JOIN employees eON d.`manager_id`=e.`employee_id`LEFT JOIN locations lON d.`location_id`=l.`location_id`
12.地址信息和地址总览
页面效果:
地址信息:
地址总览:
这里都采用了弹窗,未新增页面。
两个查询方式采用同一个方法接收,有id的话根据id查询,没有的话查询全部
controller:
@GetMapping("/getLocations")public R getLocations(@RequestParam(required = false)Integer locationId){System.out.println(locationId);List<Location> locations = localService.getLocations(locationId);return R.ok().setData(locations);}
service:
@Overridepublic List<Location> getLocations(Integer locationId) {return localMapper.getLocations(locationId);}
mapper:
List<Location> getLocations(@Param("locationId") Integer locationId);
xml:
<select id="getLocations" resultType="com.lzp.entity.Location">select location_id,street_address,postal_code,city,state_province from locationswhere 1=1<if test=" locationId > 0 ">and location_id=#{locationId}</if></select>
至此,就完成了简单的vue整合
2.项目部署的问题
1.内存占用问题
想把项目部署到服务器,服务器内存所剩不多,引出第一个问题,怎么确定这个程序占用多大内存呢?
默认情况:最大内存为物理内存的1/4,初始内存为物理内存的1/64,这个内存指的是堆内存,由新生代和老年代两部分,比例为1:2,所以我的远程服务器内存为8g,最大内存就会占2g。所以启动时要指定内存,先设置了256m看下情况。
metaspace为元空间大小,默认为20M左右,需要增大,不然会进行fullGC.
启动程序,打开jvisualvm.exe(在jdk的bin目录下)
可以看到总数为256m。
这里看出内存使用量,可以看出新生代使用较多,老年代使用很少,所以可以确定256m内存是完全满足要求的(128m应该也能满足–)。
至此,困扰的问题就大概解决了,这个小项目的内存占用为100m左右。当然这是没有请求进来的情况下,youngGC的频率不高,大概几十秒一次(在linux下频率还要更低)。
这是在idea打开的结果,采用jar包部署的方式,新生代内存增加没这么快
下面是采用jar包部署的方式(windows)
linux
更为平缓~
2.jmeter测试
测试1s10线程,每秒进行5次youngGC。
这里是在windows部署的项目,又尝试在阿里云部署,然后做了jmeter测试,发现GC频率并不高。这是windows和linux的区别么,有待大神指教—–
生成了jmeter文件,数据有点恐怖,最长的响应时间为15s。。。,感觉应该和本地网络有很大关系,需要再做测试。(网络良好的时候又做了一次测试,每秒线程数为25时,最大响应时间在1000ms以内,每秒线程数为30时,最大响应时间就是秒级别的了,所以可以确定,在这个256m内存配置下,网络条件良好的情况下,程序最多每秒可以处理25次请求)
jmeter测试方式
在软件中设置好参数,添加好请求,然后保存测试文件
然后进到jmeter的bin目录,打开cmd
执行 jmeter -n -t HTTP请求2.jmx -l testplan/result/result.txt -e -o testplan/webreport
- HTTP请求2.jmx 测试文件的名字,我这里在bin目录下,可以单独指定文件夹
- testplan/result 请求执行记录的文件夹
- testplan/webreport 生成html报告的文件夹
3.VisualVM监控阿里云服务器部署的项目
1.linux服务器配置
1.在java bin目录下新建文件 jstatd.all.policy
文件内容:
grant codebase “file:/home/mysoftware/myjava/jdk1.8.0_171/lib/tools.jar” {
permission java.security.AllPermission;
};
2.然后启动jstatd
nohup jstatd -J-Djava.security.policy=/home/mysoftware/myjava/jdk1.8.0_171/bin/jstatd.all.policy -J-Djava.rmi.server.hostname=47.98.xxx.xxx &
3.然后netstat -lutnp |grep jstatd 查看jstad占用的端口(有两个) 防火墙添加
4.然后在xshell就可以使用 jps jmap 等命令了
2.windows下配置
打开VisualVM,添加远程主机(在jdk的bin目录下)
总结: jvm方面的知识还需要提高,加油!