入门案例
MVVM(Model-View-ViewModel):数据双向绑定:数据和视图同时变化
//在线cdn
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
-
创建一个vue对象,监听一个id=app的组件
-
v-model =“username” = 让vue监听我这个名为username的模型的数据.
-
当vue发现username模型中的数据变化时,data()中的username发生变化,并将新值返回,
-
最后渲染给插件表达式
<div id="app">
<input v-model="username">
<!-- 插值表达式 -->
{{username}}
</div>
<!--放在下面先加载好页面再解析js-->
<script>
<!--创建一个vue对象,监听id=app的组件,-->
new Vue({
el:"#app",
//vue对象中全部都是键值对类型的对象,这是对下面代码的简写
data(){
return{
username:""
}
}
/*data:function (){
return{
username:""
}
}*/
})
</script>
常用方法
<div id="app">
<input v-model="username">
<!-- 动态绑定 -->
<!--写()用来传参-->
<a v-bind:href="username" v-on:click="show()">aaaa</a>
<div v-if="username == 1">1</div>
<div v-else-if="username == 2">2</div>
<div v-else>3</div>
<div v-show="username == 4">style=display: none</div>
<!--把这个div标签重复四遍-->
<div v-for="(addr,index) in address" :key="index">
索引{{index}} --> 元素{{addr}}
</div>
</div>
<script>
new Vue({
el:"#app",
data(){
return{
username:"",
address:["长安","洛阳","海口","关东"]
}
},
//所有的函数都写在这里面
methods:{
show(){
alert("胡杰是个s级");
}
}
})
</script>
生命周期
钩子函数
methods:{
show(){
alert("胡杰是个s级");
}
},
//DOM对象和Vue对象加载完成后执行
mounted(){
alert("Hello World!");
}
案例:数据添加和遍历
springMVC启动之后默认打开的首页是webapp/index.jsp。位置和页面名称都不能错,否则将报404错误。
<div id="app">
<!--表单数据双向绑定-->
<input v-model:name="name" value="name">
<input v-model:name="id" value="id">
<input v-model:name="age" value="age">
<input v-model:name="money" value="money">
<!--向vue提交单击函数-->
<input type="submit" value="提交" @click="show()">
</div>
<script>
new Vue({
el:"#app",
data(){
return{
//接收view的双向绑定数据
name:"",
id:"",
age:"",
money:""
}
},
methods:{
show(){
axios.post("http://localhost:8080/students",{name:this.name,id:this.id,age:this.age,money:this.money})
.then(resp=>{
alert(resp.data.msg);
location.href = "html/showList.html"
})
}
}
})
</script>
<div id="app">
<table>
<!--html标签写错,访问不到数据-->
<tr v-for="(stu,index) in students" :key="index">
<th>{{index}}</th>
<td>{{stu.name}}</td>
<td>{{stu.id}}</td>
<td>{{stu.age}}</td>
<td>{{stu.money}}</td>
</tr>
</table>
</div>
<script>
new Vue({
el:"#app",
data(){
return{
students:[]
}
},
mounted(){
axios.get("http://localhost:8080/students").then(resp=>{
this.students = resp.data.data;
console.log('y',this.students);
})
}
})
</script>
Element-ui
在线cdn
<!--element基于vue,vue.js一定要在element.js前面-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<!-- 引入样式:表示它是样式表 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
布局
<!-- layout每行24个格子-->
<el-row>
<el-col :span="12"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="4"><div class="grid-content bg-purple-light"></div></el-col>
<el-col :span="4"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="4"><div class="grid-content bg-purple-light"></div></el-col>
</el-row>
return {
//将item充满Array(20)这个集合
tableData: Array(20).fill(item)
}
表格
//每列去掉后就是width:auto
width="180"
//居中对齐
align="center"
综合案例
SqlSessionFactoryUtils
package com.itheima.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class SqlSessionFactoryUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
//静态代码块会随着类的加载而自动执行,且只执行一次
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
}
查询所有
mapper层
@Select("select * from tb_brand")
//将xml中的规定的数据库返回的结果集绑定给实体类
@ResultMap("brandResultMap")
List<Brand> selectAll();
字段映射
<resultMap id="brandResultMap" type="brand">
<result property="brandName" column="brand_name" />
<result property="companyName" column="company_name" />
</resultMap>
servive层
public class BrandServiceImpl implements BrandService {
//工具类获取slqSession工厂
private SqlSessionFactory factory = SqlSessionFactoryUtils.getSqlSessionFactory();
@Override
public List<Brand> selectAll() {
SqlSession sqlSession = factory.openSession();
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
try {
return mapper.selectAll();
}finally {
//关闭资源
sqlSession.close();
}
}
}
//调用Service层
List<Brand> brands = brandService.selectAll();
//转成json
String json = JSON.toJSONString(brands);
//返回前端
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(json);
mysql8.0在连接数据库时需要配置时区引用
useSSL=false&serverTimezone=UTC"
前端接收数据并展示
//页面加载完成后,查询所有数据
mounted(){
axios.get("http://localhost:8080/brand-case/selectAllServlet").then(resp=>{
//将返回的json数据绑定给tableData数据
this.tableData = resp.data;
})
},
新增用户
@Insert("insert tb_brand value(null,#{brandName},#{companyName},#{ordered},#{description},#{status}) ")
int insertBrand(Brand brand);
@Override
public boolean insertBrand(Brand brand) {
SqlSession sqlSession = factory.openSession();
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
try {
return mapper.insertBrand(brand) > 0;
}finally {
//提交事务
sqlSession.commit();
sqlSession.close();
}
}
insertServlet
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取前端传来的参数
BufferedReader reader = request.getReader();
String s = reader.readLine();
Brand brand = JSON.parseObject(s, Brand.class);
boolean b = brandService.insertBrand(brand);
response.getWriter().write(String.valueOf(b));
}
前端
将查询所有的方法拆分出来
//重置新增弹框的表单数据
resultForm(){
this.brand = {};
},
//页面加载完成后,查询所有数据
mounted(){
this.selectAll();
},
// 查询全部数据
selectAll(){
axios.get("http://localhost:8080/brand-case/selectAllServlet").then(resp=>{
//将返回的json数据绑定给tableData数据
this.tableData = resp.data;
})
},
添加数据
// 添加数据
addBrand() {
axios.post("http://localhost:8080/brand-case/brand/insert",this.brand).then(resp=>{
//注意,最后返回来的是个对象
if(resp.data.toString() == "true"){
//关闭弹窗
this.dialogVisible = false;
//弹出提示框
this.$message({
message: '恭喜你,添加成功',
type: 'success'
});
}else if(resp.data.toString() == "false"){
this.$message.error("添加失败,请检查数据");
}else {
//程序报错时:比如输入的字符串长度大于表的设计长度,就会出现系统级异常
this.$message.error("系统级异常");
}
//必定执行
}).finally(()=>{
//重新刷新数据
this.selectAll();
this.resultForm();
})
},
优化Servlet代码
将原先HttpServlet根据请求方式进行方法分发,变成根据请求路径进行方法分发
service() 方法是 Servlet 的核心。
每当一个客户请求一个HttpServlet 对象,该对象的service() 方法就要被调用,而且传递给这个方法一个"请求"(ServletRequest)对象和一个"响应"(ServletResponse)对象作为参数。
**在 HttpServlet 中已存在 service()方法.**缺省的服务功能是调用与 HTTP 请求的方法相应的 do 功能。
例如,如果 HTTP 请求方法为 GET,则缺省情况下就调用 doGet()。
https://www.cnblogs.com/bdqczhl/p/7086970.html#:~:text=service%20%28%29%20%E6%96%B9%E6%B3%95%E6%98%AF%20Servlet%20%E7%9A%84%E6%A0%B8%E5%BF%83%E3%80%82%20%E6%AF%8F%E5%BD%93%E4%B8%80%E4%B8%AA%E5%AE%A2%E6%88%B7%E8%AF%B7%E6%B1%82%E4%B8%80%E4%B8%AAHttpServlet%20%E5%AF%B9%E8%B1%A1%2C%E8%AF%A5%E5%AF%B9%E8%B1%A1%E7%9A%84service%20%28%29,%E6%96%B9%E6%B3%95%E5%B0%B1%E8%A6%81%E8%A2%AB%E8%B0%83%E7%94%A8%2C%E8%80%8C%E4%B8%94%E4%BC%A0%E9%80%92%E7%BB%99%E8%BF%99%E4%B8%AA%E6%96%B9%E6%B3%95%E4%B8%80%E4%B8%AA%22%E8%AF%B7%E6%B1%82%22%20%28ServletRequest%29%E5%AF%B9%E8%B1%A1%E5%92%8C%E4%B8%80%E4%B8%AA%22%E5%93%8D%E5%BA%94%22%20%28ServletResponse%29%E5%AF%B9%E8%B1%A1%E4%BD%9C%E4%B8%BA%E5%8F%82%E6%95%B0%E3%80%82%20%E5%9C%A8%20HttpServlet%20%E4%B8%AD%E5%B7%B2%E5%AD%98%E5%9C%A8%20service%20%28%29%E6%96%B9%E6%B3%95.
根据请求路径进行方法分发
package com.itheima.web.servlets;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BaseServlet extends HttpServlet {
//tomcat会调用这个方法来判断应该走get还是post
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//url是全路径,uri是从项目名开始的短路径
String requestURI = req.getRequestURI();
int index = requestURI.lastIndexOf("/");
//会把index处的字符也截取进去
String substring = requestURI.substring(index + 1);
//谁调用this就是谁,这里最后是BrandServlet调用,this就是BrandServlet
Class<? extends BaseServlet> aClass = this.getClass();
try {
//子类的所有方法的传参全部统一
Method method = aClass.getMethod(substring, HttpServletRequest.class, HttpServletResponse.class);
try {
//注意这里,子类的对应的方法要是public修饰的,不然会报找不到方法错误
method.invoke(this,req,resp);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
浏览器监听到访问/brand下的servlet时,会先调用它的父类BaseServlet
package com.itheima.web.servlets;
import com.alibaba.fastjson.JSON;
import com.itheima.pojo.Brand;
import com.itheima.service.BrandService;
import com.itheima.service.impl.BrandServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;
@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {
private BrandService brandService = new BrandServiceImpl();
public void selectAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Brand> brands = brandService.selectAll();
String json = JSON.toJSONString(brands);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(json);
}
public void insert(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
BufferedReader reader = request.getReader();
String s = reader.readLine();
Brand brand = JSON.parseObject(s, Brand.class);
boolean b = brandService.insertBrand(brand);
response.getWriter().write(String.valueOf(b));
}
}
批量删除
/**
* 批量删除
* @param ids
* @return
*/
int deleteByIds(@Param("ids") int[] ids);
<delete id="deleteByIds">
delete from tb_brand where id in
<!-- item是元素迭代时的别名,必写
把service传过来的名为ids集合迭代,每个元素叫id,然后按条件拼接sql语句
-->
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
public void deleteByIds(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
BufferedReader reader = request.getReader();
String s = reader.readLine();
//前台传来的是[id:"",id:""]
int[] ids = JSON.parseObject(s, int[].class);
boolean b = brandService.deleteByIds(ids);
response.getWriter().write(String.valueOf(b));
}
前端
// 被选中的id数组
selectedIds:[],
// 复选框选中数据集合
multipleSelection: [],
// 复选框选中后执行的方法:这个val就是所有被选中的复选框的数据
handleSelectionChange(val) {
this.multipleSelection = val;
},
// 批量删除
deleteByIds(){
//确认操作弹出框
this.$confirm('此操作将永久删除, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
//确认删除
}).then(() => {
//得到所有的id this.multipleSelection中存了所有被选中的复选框信息
for (let i = 0; i < this.multipleSelection.length; i++) {
//会自动增加长度
this.selectedIds[i] = this.multipleSelection[i].id;
}
axios.post("http://localhost:8080/brand-case/brand/deleteByIds",this.selectedIds).then(resp=>{
if(resp.data.toString() == "true"){
this.$message.success("删除成功");
}else if(resp.data.toString() == "false"){
this.$message.error("删除失败,请检查数据");
}else {
this.$message.error("系统级异常");
}
}).finally(()=>{
this.selectAll();
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
分页查询
返回前端的数据除了集合外还有总条数
//创建对象时告诉说list集合要存入什么样的对象
@Data
@AllArgsConstructor
public class PageBean<T> {
private int totalCount;
private List<T> rows;
}
/**
* 分页查询
* @param
* @return
*/
//从哪里开始查,查多少个
@Select("select * from tb_brand limit #{startSize},#{pageSize}")
//千万记住这里要和数据库字段映射
@ResultMap("brandResultMap")
List<Brand> selectPage(@Param("startSize") int startSize,@Param("pageSize")int pageSize);
/**
* 总条数
* @param
* @return
*/
@Select("select count(*) from tb_brand ")
int totalSize();
service层返回结果集
//当前页与查询几个
@Override
public PageBean<Brand> selectPage(int currentPage, int pageSize) {
SqlSession sqlSession = factory.openSession();
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
//算出起始索引
int startSize = (currentPage - 1) * pageSize;
//调用mapper层
List<Brand> brands = mapper.selectPage(startSize, pageSize);
int count = mapper.totalSize();
//返回结果集
PageBean<Brand> pageBean = new PageBean<Brand>(count, brands);
return pageBean;
}
servlet层接收参数得到pageBean
public void selectPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int currentPage = Integer.parseInt(request.getParameter("currentPage"));
int pageSize = Integer.parseInt(request.getParameter("pageSize"));
PageBean<Brand> pageBean = brandService.selectPage(currentPage,pageSize);
String json = JSON.toJSONString(pageBean);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(json);
}
前端
更改查询所有为按条件查询分页
// 查询分页数据
selectAll(){
axios.get("http://localhost:8080/brand-case//brand/selectPage?currentPage="+this.currentPage+"&pageSize="+this.pageSize)
.then(resp=>{
console.log(resp.data.rows)
this.tableData = resp.data.rows;
this.totalCount = resp.data.totalCount;
})
},
模型层新增相应数据
//总页数默认100
totalCount:100,
//默认每页显示条数为5
pageSize:5,
//默认当前页为1
currentPage:1,
分页表单更改属性为我们自己新增的模型数据
<!--分页工具条-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5, 10, 15, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalCount">
</el-pagination>
修改对应的当前页和每页条数变化后的方法
//每页显示条数变化之后
handleSizeChange(val) {
this.pageSize = val;
this.selectAll();
},
//当前页数变化之后
handleCurrentChange(val) {
this.currentPage = val;
this.selectAll();
},
如果这行在项目运行后会莫名其妙自动添加,那就是在导入element-ui,直接注释
// import sl from "./element-ui/src/locale/lang/sl";
按条件查询
改造分页查询的后端代码
mapper查询时增加条件
/**
* 按条件分页查询
* @param
* @return
*/
//从哪里开始查,查多少个,按什么条件
List<Brand> selectPageAndCondition(@Param("startSize") int startSize
,@Param("pageSize")int pageSize
,@Param("brand") Brand brand);
/**
* 按条件查询总条数
* @param
* @return
*/
int totalSizeAndCondition(@Param("brand")Brand brand);
sql变成有条件的动态sql
<sql id="brandCondition">
<where>
<if test="brand.brandName != null and brand.brandName != '' ">
<!--注意这里要写的是数据库字段名-->
and brand_name like concat('%',#{brand.brandName},'%')
</if>
<if test="brand.companyName != null and brand.companyName != ''">
and company_name like concat('%',#{brand.companyName},'%')
</if>
<if test="brand.status != null">
and status = #{brand.status}
</if>
</where>
</sql>
<select id="selectPageAndCondition" resultMap="brandResultMap">
select * from tb_brand
<include refid="brandCondition"></include>
limit #{startSize},#{pageSize}
</select>
<select id="totalSizeAndCondition" resultType="int">
select count(*) from tb_brand
<include refid="brandCondition"></include>
</select>
service原本要处理brand数据,但是直接由mapper的动态sql来处理了
@Override
public PageBean<Brand> selectPageAndCondition(int currentPage,int pageSize,Brand brand) {
SqlSession sqlSession = factory.openSession();
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
//算出起始索引
int startSize = (currentPage - 1) * pageSize;
/*//处理brand的条件
String brandName = brand.getBrandName();
String companyName = brand.getCompanyName();
if(brandName != "" && brandName.length()>0){
brand.setBrandName("%"+brandName+"%");
}*/
//调用mapper层
List<Brand> brands = mapper.selectPageAndCondition(startSize, pageSize,brand);
int count = mapper.totalSizeAndCondition(brand);
//返回结果集
PageBean<Brand> pageBean = new PageBean<Brand>(count, brands);
sqlSession.close();
return pageBean;
}
servlet需要额外处理前端传来的brand数据
public void selectPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int currentPage = Integer.parseInt(request.getParameter("currentPage"));
int pageSize = Integer.parseInt(request.getParameter("pageSize"));
//处理前端传来的数据中的brand对象
BufferedReader reader = request.getReader();
String s = reader.readLine();
Brand brand = JSON.parseObject(s, Brand.class);
PageBean<Brand> pageBean = brandService.selectPageAndCondition(currentPage,pageSize,brand);
String json = JSON.toJSONString(pageBean);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(json);
}
前端
前端页面中默认已经绑定好了数据
// 查询方法
onSubmit() {
this.selectAll();
},
selectAll()变成post,多提交一个brand对象的json数据
axios.post("http://localhost:8080/brand-case//brand/selectPage?currentPage="+this.currentPage+"&pageSize="+this.pageSize
,this.brand)
.then(resp=>{
this.tableData = resp.data.rows;
this.totalCount = resp.data.totalCount;
})
brand实体中有个statusStr()将1,0变成了对应的"启用,禁用"字符串
prop="statusStr"
align="center"
label="当前状态">
前端代码优化
//钩子函数中会判断this属于谁,这里代表的vue对象
.then(resp=>{
this.tableData = resp.data.rows;
this.totalCount = resp.data.totalCount;
})