文章目录
-
- 1,搭建开发环境:
-
- 1.1创建项目:crm-project
-
- 1.1.1设置JDK.
- 1.1.2创建工程:crm
- 1.1.3补全目录结构:
- 1.1.4设置编码格式:UTF-8
- 1.1.5数据库编码设置
- 1.1.6字符集编码的设置
- 2.spring application配置文件知识点
-
- 2.1.applicationContext-datasource.xml
-
- 2.1.1.sqlsessionFactory他是一个数据库
- 2.1.2为什么使用127.0.0.1不用localhost
- 2.2 applicationContext-mvc.xml
-
- 2.2.1服务器一起东就加载servlet实例 servlet会加载mvc的配置文件
- 2.2.2拦截器
- 2.3 applicationContext.xml
-
- 2.3.1 监听器加载service层和dao层的配置文件
- 2.3.2服务器启动对配置文件的加载顺序
- 2.4 pom.xml
-
- 2.4.1 `和`
- 2.4.2maven对配置文件的编译
- 3.tomcat和idea
-
-
- 3.1idea项目是如何放在tomcat webapps下的
- 3.2使用127.0.01ip地址
- 3.3恢复代码提示的默认设置
-
- 4.jsp
-
- 4.1 jquery
-
- 4.1.1 使用标签保存数据,以便在需要的时候能够获取到这些数据:
- 4.1.2 jsp的运行原理:
- 4.1.3 ajax $.each( )的用法
- 4.1.4 submit发送同步,button通过绑定 发送异步
- 4.1.5 jquery小结
- 4.1.6 ajax
- 4.1.7 jquery事件函数的用法:
- 4.2 web
-
- 4.2.1 cookie的参数传输
- 4.2.2 cookie和session
- 4.2.3 window.location.href
- 4.2.4 jsp如何显示到浏览器上的
- 4.2.5 jsp和html的编码区别
- 4.2.6 `浏览器只能解决网页,json字符串只能由ajax和js来解决`
- 4.2.7 index.jsp在根目录下自带base的标签的
- 4.2.8 html中就加id 等html标签 就不要加js 的标签
- 4.2.9 同步请求 (页面跳转了)
- 4.2.10 整个页面加载完才执行入口函数
- 4.2.11 web的作用域
- 4.2.12
s
e
s
s
i
o
n
S
c
o
p
e
.
s
e
s
s
i
o
n
U
s
e
r
.
n
a
m
e
和
{sessionScope.sessionUser.name}和
- 4.3. bootstrpt
-
- 4.3.1 页面切割技术:`和:` ``
和:``
- 4.3.2 模态窗口
-
- 模态窗口:
- 控制模态窗口的显示与隐藏:
- 模态窗口的意义:
- 4.3.3 正则表达式
- 4.3.4 日历
- 4.3.5 日历用法
- 4.3.6 分页插件
- 4.3.7
- 4.3.1 页面切割技术:`和:` ``
1,搭建开发环境:
1)创建项目:crm-project
设置JDK.
创建工程:crm
补全目录结构:
设置编码格式:UTF-8
2)添加jar包:添加依赖—参考课件.
3)添加配置文件:参考课件.
4)添加静态页面资源:
webapps
|->stumgr
|->crm
|->.html,.css,.js,.img test.jsp
|->WEB-INF
|->web.xml
|->classes
|->lib
*web应用根目录下的内容都是不安全的,外界可以通过url直接访问;
所以,一般为了数据的安全,都会把页面放到WEB-INF下,因为WEB-INF目录下的资源是受保护的,外界不能直接访问。
1,搭建开发环境:
1.1创建项目:crm-project
1.1.1设置JDK.
1.1.2创建工程:crm
1.1.3补全目录结构:
1.1.4设置编码格式:UTF-8
1.1.5数据库编码设置
1.1.6字符集编码的设置
需要设置字节码的
- 编码
- 解码
- 传输数据的字节码
2.spring application配置文件知识点
2.1.applicationContext-datasource.xml
2.1.1.sqlsessionFactory他是一个数据库
2.1.2为什么使用127.0.0.1不用localhost
localhost只是tomcat配置的一个映射,localhost是可以修改的有风险
2.2 applicationContext-mvc.xml
2.2.1服务器一起东就加载servlet实例 servlet会加载mvc的配置文件
<load-on-startup>1</load-on-startup> 服务器一起东就加载servlet实例
<!-- Spring mvc分发servlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
2.2.2拦截器
a)提供拦截器类:implements HandlerInterceptor{
--pre
--post
--after
}
b)配置拦截器:springmvc.xml
能通过return ture 不能通过return false
配置拦截器
<mvc:interceptors>
<mvc:interceptor>
拦截的内容
<mvc:mapping path="/settings/**"/>
<mvc:mapping path="/workbench/**"/>
放行的内容 有一个东西即要放行也要拦截,优先进行放行
<mvc:exclude-mapping path="/settings/qx/user/toLogin.do"/>
<mvc:exclude-mapping path="/settings/qx/user/login.do"/>
<bean class="com.bjpowernode.crm.settings.web.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<mvc:mapping path="/settings/**"/>
拦截的是url
settings/* 拦截一层目录 settings/*/login.jsp还是没拦截到
settings/** 拦截settins下所有目录
2.3 applicationContext.xml
2.3.1 监听器加载service层和dao层的配置文件
(applicationContext.xml,applicationContext-datasource.xml)
监听器在服务器一启动是就会进行加载
2.3.2服务器启动对配置文件的加载顺序
服务器一启动就用监听器加载applicationContext.xml
applicationContext.xml
加载aplicationContext-datasource.xml
aplicationContext-datasource.xml
会加载mybatis-config.xml
dispatcherservlet
一启动就会加载application-mvc.xml
配置文件
2.4 pom.xml
2.4.1 <groupId>和<artifactId>
2.4.2maven对配置文件的编译
maven默认只会编译java的源文件,不会编译xml配置文件
加上这个就会编译xml配置文件了
3.tomcat和idea
3.1idea项目是如何放在tomcat webapps下的
tomcat
webapps
|->stumgr
|->crm
|->.html,.css,.js,.img test.jsp
|->WEB-INF
|->web.xml
|->classes
|->lib
-
maven会把
java代码,配置文件
编译好放在classes
, 把依赖jar包
放在lib
目录下 -
静态资源放在webapp下,会直接部署到tomcat webapps下的crm项目中根目录中
-
页面要放在webapp web-inf下
-
在crm根目录中能直接被访问,web-inf下的资源是受保护的不能直接访问,
-
要通过controller跳转才能访问,到了controller后我们就能掌握主动权,对用用户进行验证
-
为什么不将image,jquery放入web-inf下,这些东西放在web-inf下,因为要我们通过controller转发才能实现到页面上会非常麻烦,用一个要跳一个会非常麻烦
3.2使用127.0.01ip地址
3.3恢复代码提示的默认设置
4.jsp
4.1 jquery
4.1.1 使用标签保存数据,以便在需要的时候能够获取到这些数据:
-
给标签添加属性:
如果是表单组件标签,优先使用value属性,只有value不方便使用时,使用自定义属性;
如果不是表单组件标签,不推荐使用value,推荐使用自定义属性。
-
获取属性值时:
如果获取表单组件标签的value属性值:dom对象.value jquery对象.val()
如果自定义的属性,不管是什么标签,只能用:jquery对象.attr(“属性名”);
不加引号会认为是变量,加了才认为是字符串
var activityId='${requestScope.activity.id}';
//删除活动备注
$("#deleteActivityRemark").click(function () {
//获得自己创建的属性的值
var id=$("#deleteActivityRemark").attr("remarkId");
var activityId='${requestScope.activity.id}';
<a class="myHref" remarkId="${remark.id}" id="deleteActivityRemark" ><span class="glyphicon glyphicon-remove" style="font-size: 20px; color: #E6E6E6;"></span></a>
4.1.2 jsp的运行原理:
- xxx.jsp:
- tocmat中运行:
把xxx.jsp翻译成一个servlet,
运行servlet,运行的结果是一个html网页
把html网页输出到浏览器 - html网页在浏览器上运行:
先从上到下加载html网页到浏览器,在加载过程中,运行前端代码
当页面都加载完成,再执行入口函数和js.
- 把页面片段显示在动态显示在页面中:
- 选择器.html(htmlStr):覆盖显示在标签的内部
- 选择器.text(htmlStr):覆盖显示在标签的内部
- 选择器.append(htmlStr):追加显示在指定标签的内部的后边
4.1.3 ajax $.each( )的用法
success:function (data) {
//显示总条数
//$("#totalRowsB").text(data.totalRows);
//显示市场活动的列表
//遍历activityList,拼接所有行数据
var htmlStr="";
$.each(data.activityList,function (index,obj) {
htmlStr+="<tr class=\"active\">";
htmlStr+="<td><input type=\"checkbox\" value=\""+obj.id+"\"/></td>";
htmlStr+="<td><a style=\"text-decoration: none; cursor: pointer;\" οnclick=\"window.location.href='detail.html';\">"+obj.name+"</a></td>";
htmlStr+="<td>"+obj.owner+"</td>";
htmlStr+="<td>"+obj.startDate+"</td>";
htmlStr+="<td>"+obj.endDate+"</td>";
htmlStr+="</tr>";
});
$("#tBody").html(htmlStr);
htmlStr+=“<tr class=“active”>”; 在"" 双引号中加 双引号 要在里面的双引号中 加上
\
" 进行转义>
htmlStr+=“”+obj.owner+“”; 在"" 双引号中加
"+ +"
表示 obj.owner 不是一个字符,而是数据
4.1.4 submit发送同步,button通过绑定 发送异步
<input type="submit" value="发送同步请求">
<input type="button" id="queryBtu" value=" 通过id创建一个jquery对象绑定按钮,发送异步请求">
4.1.5 jquery小结
<script type="text/javascript">
$(function () {
$("#loginBtu").onclick(function () {
var loginAct =$.trim ($("#loginAct").val());
var loginPwd =$.trim( $("#loginPwd").val());
var isRemPwd = $("#isRemPwd").prop("checkbox");
})
})
</script>
-
trim
方法是去掉空格 -
使用jquery获取指定元素的指定属性的值:
选择器.attr("属性名")
;//用来获取那些值不是true/false的属性的值.
选择器.prop("属性名")
;//用来获取值是true/false的属性的值.例如:checked,selected,readonly,disabled等。 -
jquery中判断只有
==和!=
,因为是弱语言所以没有equel -
后台放送过去的是一个json(应该是
returnObject
对象自动变json
) -
(前端ajax定义好发送的就是json)后台接受的也是一个json
$("#msg").html(data.message);
$("#msg").text(data.message);
-
html即能写文本信息也能写标签信息
-
text 只能写标签信息
4.1.6 ajax
loginAct:loginAct,
loginPwd:loginPwd,
isRemPwd:isRemPwd
这种是不标准的未加双引号
//给"登录"按钮添加单击事件
$("#loginBtn").click(function () {
//收集参数
var loginAct=$.trim($("#loginAct").val());
var loginPwd=$.trim($("#loginPwd").val());
var isRemPwd=$("#isRemPwd").prop("checked");
//表单验证
if(loginAct==""){
alert("用户名不能为空");
return;
}
if(loginPwd==""){
alert("密码不能为空");
return;
}
//显示正在验证
//$("#msg").text("正在努力验证....");
//发送请求
$.ajax({
url:"settings/qx/user/login.do",
data:{
"loginAct":loginAct,
"loginPwd":loginPwd,
"isRemPwd":isRemPwd
},
type:"post",
dataType:"json",
async:"false",
success:function (data) {
if(data.code=="1"){
//跳转到业务主页面
window.location.href="workbench/index.do";
}else{
//提示信息
$("#msg").text(data.message);
}
},
beforeSend:function () {//当ajax向后台发送请求之前,会自动执行本函数;
//该函数的返回值能够决定ajax是否真正向后台发送请求:
//如果该函数返回true,则ajax会真正向后台发送请求;否则,如果该函数返回false,则ajax放弃向后台发送请求。
$("#msg").text("正在努力验证....");
return true;
}
});
});
4.1.7 jquery事件函数的用法:
- 选择器.click(function(){//给指定的元素添加事件
//js代码
}); - 选择器.click();//在指定的元素上模拟发生一次事件
4.2 web
4.2.1 cookie的参数传输
4.2.2 cookie和session
在默认情况下,session对象在关闭浏览器后并不是立刻被销毁,因此,为了考虑系统的安全性,在用户退出时,需要即刻清除session对象,防止他人盗用session对象中的信息
其实在浏览器关闭后,session并没有失效,正常来说一个session的存活时间是30分钟,也就是在不操作这个session的情况下,30分钟自动清除。可是实际中浏览器关闭,设置的session也会随之消失,这里的消失是他这个session的你找不到了,不是他没有了,因为在浏览器重新打开时,浏览器总会自动给你创建一个的新的session。session并不是唯一的,每个session都有自己的一个专属sessionId,这个sessionId在浏览器打开时创建,保存在浏览器的cookie中,浏览器关闭,cookie自动清除,sessionId丢失,之前的session找寻不到!
cookie不销毁也会这样
4.2.3 window.location.href
window.location.href = "settings/qx/user/toLogin.do";
4.2.4 jsp如何显示到浏览器上的
jsp就是一个servlet,先在tomcat运行,结果是一个html
4.2.5 jsp和html的编码区别
jsp默认是utf-8
html iso8859-1
4.2.6 浏览器只能解决网页,json字符串只能由ajax和js来解决
4.2.7 index.jsp在根目录下自带base的标签的
<%
String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";
%>
<base href="<%=basePath%>">
4.2.8 html中就加id 等html标签 就不要加js 的标签
4.2.9 同步请求 (页面跳转了)
同步请求的方式 有地址栏,form表单,超链接 ajax只有地址栏一个方式 window.loction.href
4.2.10 整个页面加载完才执行入口函数
也就是静态资源加载完后
<script type="text/javascript">
$(function () {
});
</script>
4.2.11 web的作用域
jsp作用域
1,把控制层(controller)代码中处理好的数据传递到视图层(jsp),使用作用域传递:
pageContext:用来在同一个页面的不同标签之间传递数据。
jsp的每个标签对应着一个类 如<c:if 就对应着一个类
request:在同一个请求过程中间传递数据。
session: 同一个浏览器窗口的不同请求之间传递数据。
或者是一个用户的范围内
application:所有用户共享的数据,并且长久频繁使用的数据。
4.2.12
s
e
s
s
i
o
n
S
c
o
p
e
.
s
e
s
s
i
o
n
U
s
e
r
.
n
a
m
e
和
{sessionScope.sessionUser.name}和
sessionScope.sessionUser.name和{sessionUser.name}区别
一个在指定的范围找 一个是在四个作用域所有的范围找
4.3. bootstrpt
4.3.1 页面切割技术:<frameset>和<frame>:
<div>和<iframe>:
1)不灵活效率低
<frameset>和<frame>:
<frameset>:用来切割页面.
<frameset cols="20%,60%,20%" rows="10%,80%,10%">
<frame>:显示页面.
<frame src="url">
<frameset cols="20%,60%,20%">
<frame src="url1" name="f1">
<frame src="url2" name="f2">
<frame src="url3" name="f3">
</frameset>
每一个<frame>标签就是一个独立的浏览器窗口。
<a href="url" target="f3">test</a>
2)<div>和<iframe>:
灵活效率高
<div>:切割页面。
<div style="height:10%;width=20%">
</div>
</div>
<iframe>:显示页面。
<div style="height:10%;width=20%">
<iframe href="url">
</div>
3,创建市场活动:
4.3.2 模态窗口
模态窗口:
模拟的窗口,本质上是<div>
的一个表单,通过设置z-index大小来实现的; z轴
初始时,z-index初始参数是<0,所以不显示;
需要显示时,z-index值设置成>0即可。
bootstrap来控制z-index的大小。
控制模态窗口的显示与隐藏:
1)方式一:通过标签的属性data-toggle
=“modal” 开启模态窗口 toggle切换 美工喜欢用
data-target=“模态窗口的id” 确定那个div是模态窗口
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#createActivityModal"><span class="glyphicon glyphicon-plus"></span> 创建</button>
2)方式二:通过js函数控制:程序员喜欢用的
选择器(选中div).modal(“show”);//显示选中的模态窗口
选择器(选中div).modal(“hide”);//关闭选中的模态窗口
$(function(){
$("#createActivityBtu").click(function () {
//这里可以编写js 进行一些初始化工作
// jquery转dom对象 $("#createActivityFrom") 转为 from标签
$("#createActivityFrom").get(0).reset();
//弹出创建市场的窗口,
$("#createActivityModal").modal("show");
});
<!-- data-toggle="modal" data-target="#createActivityModal" 没有这两个前端属性-->
<button type="button" class="btn btn-primary" id="createActivityBtu"><span class="glyphicon glyphicon-plus"></span> 创建</button>
3)方式三:通过标签的属性data-dismiss=“”
点击添加了data-dismiss=""属性的标签,自动关闭该标签所在的模态窗口。
模态窗口的意义:
window.open(“url”,“_blank”);
模态窗口本质上就是原来页面中的一个<div>
,只有一个页面;所有的操作都是在同一个页面中完成。
4.3.3 正则表达式
1.语言,语法,定义字符串的匹配模式,可以用来判断指定的具体字符串是否匹配模式
4.3.4 日历
js日历:
这一类问题:
1)实现起来比较复杂。
2)跟业务无关。
日历插件:bootstrap-datetimepicker
前端插件使用步骤:
1)引入开发包:.js,.css
`引入js` <script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>
`引入css` <link href="jquery/bootstrap_3.3.0/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
下载开发包,拷贝到项目webapp目录下
把开发包引入到jsp文件中:<link><script>
2)创建容器:<input type="text"> 或 <div>
3)当容器加载完成之后,对容器调用工具函数.
4.3.5 日历用法
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/";
%>
<html>
<head>
<base href="<%=basePath%>">
<%-- jquery框架 --%>
<script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>
<%-- 依赖于jquery的bootstrap框架--%>
<script type="text/javascript" src="jquery/bootstrap_3.3.0/js/bootstrap.min.js"></script>
<%-- <link rel="stylesheet" type="text/css" href="jquery/bootstrap_3.3.0/css/bootstrap-theme.min.css"/>--%>
<link rel="stylesheet" type="text/css" href="jquery/bootstrap_3.3.0/css/bootstrap.min.css">
<%-- 依赖于bootstrap 的 日历插件 这些都要按顺序写才行 --%>
<script type="text/javascript" src="jquery/bootstrap-datetimepicker-master/js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript" src="jquery/bootstrap-datetimepicker-master/locale/bootstrap-datetimepicker.zh-CN.js"></script>
<link rel="stylesheet" type="text/css" href="jquery/bootstrap-datetimepicker-master/css/bootstrap-datetimepicker.min.css">
<script type="text/javascript">
$(function () {
$("#myDate").datetimepicker({
language:"zh-CN",
format:"yyyy-mm-dd",
minView:"month",
initialDate:new Date(),
autoclose:"true"
});
});
</script>
<title>Title</title>
</head>
<body>
<input type="text" id="myDate">
</body>
</html>
<%-- jquery框架 --%>
<%-- 依赖于jquery的bootstrap框架–%>
<%-- 依赖于bootstrap 的 日历插件
这些都要按顺序写才行 js是有顺序的
--%>
双标签
js的引用格式
<script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>
单标签 没有自结束
css的引用
<link rel="stylesheet" type="text/css" href="jquery/bootstrap_3.3.0/css/bootstrap.min.css">
<script type="text/javascript"> $(function () { $("#myDate").datetimepicker({ language:"zh-CN", format:"yyyy-mm-dd", minView:"month", //最小的显示格式 initialDate:new Date(),//初始的时间 autoclose:"true"//选完时候自动关闭 }); }); </script>
入口函数注意用
}); }); 结尾
4.3.6 分页插件
<script type="text/javascript">
$(function () {
$("#demo_page").bs_pagination({
currentPage: 1,//当前页号,相当于pageNo
rowsPerPage: 10,//每页显示条数,相当于pageSize
totalRows: 1000,//总条数
totalPages: 100, //总页数,必填参数.
visiblePageLinks: 5,//最多可以显示的卡片数
showGoToPage: true,//是否显示"跳转到"部分,默认true--显示
showRowsPerPage: true,//是否显示"每页显示条数"部分。默认true--显示
showRowsInfo: true,//是否显示记录的信息,默认true--显示
//用户每次切换页号,都自动触发本函数;
//每次返回切换页号之后的pageNo和pageSize
onChangePage: function (event, pageObj) { // returns page_num and rows_per_page after a link has clicked
//js代码
// alert(pageObj.currentPage);//切换之后的 pageNo变化了
// alert(pageObj.rowsPerPage);//切换之后 pageSize变化了
//onchangePage是触发事件 只有页数变换了才会触发改事件 不会发生递归
queryActivityByConditionForPage(pageObj.currentPage,pageObj.rowsPerPage)
}
});
});
</script>
4.3.7 <link rel=“stylesheet” type=“text/css”
link标签
是用于当前文档引用外部文档的,其次,这个标签的rel属性用于设置对象和链接目的间的关系,说白了就是指明你链进来的对象是个什么东西的, Stylesheet
– 定义一个外部加载的样式表 type
定义类型
4.3.8 html中的src与href的区别
写代码的时候就经常把这两个属性弄混淆,到底是href还是src,href标识超文本引用
,用在link和a等元素上,href是引用和页面关联,是在当前元素和引用资源之间建立联系,src表示引用资源
,表示替换当前元素,用在img,script,iframe上,src是页面内容不可缺少的一部分。
src
是source的缩写,是指向外部资源的位置,指向的内部会迁入到文档中当前标签所在的位置;在请求src资源时会将其指向的资源下载并应用到当前文档中,例如js脚本,img图片和frame等元素。
当浏览器解析到这一句的时候会暂停其他资源的下载和处理,直至将该资源加载,编译,执行完毕,图片和框架等元素也是如此,类似于该元素所指向的资源嵌套如当前标签内,这也是为什么要把js饭再底部而不是头部。
<link href="common.css" rel="stylesheet"/>
当浏览器解析到这一句的时候会识别该文档为css文件,会下载并且不会停止对当前文档的处理,这也是为什么建议使用link方式来加载css而不是使用@import。
4.3.9 class标签
5. service
5.1.1 service层中,修改和查询的事务处理
-
一个service方法代表一个事务
-
查询的时候不要求两个方法放在同一个service方法中,
-
修改的时候要求放在一个事务中,也就是同一个service方法中
5.1.2 service层的方法名一定要用(add,save,edit,update,delete,开头)
service层的方法名一定要用(add,save,edit,update,delete,开头)
(add,save,edit,update,delete,开头) 要就能进行修改的 报异常回滚
其他
*
只读 不能进行修改
<aop:config>
<aop:pointcut expression="execution(* com.bjpowernode.crm..service.*.*(..))" id="allMethodPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allMethodPointcut"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="edit*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="do*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
6. Controller
6.0.1 controller的编写
一个新静态资源的目录对应一个新的controller 对应的controller所在位置也在java对应的目录
6.0.2 Controller接受的url都要用.do
结尾
用到一个新的静态资源,用一个新的controller
@RequestMapping("/workbench/activity/index.do")
@RequestMapping("/workbench/activity/insertActivity.do")
web.xml
<!-- Spring mvc分发servlet 除了jsp页面不会经过个controller 其他的都会经过再进行处理-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- 欢迎页,默认进入index controller -->
<welcome-file-list>
<welcome-file>/</welcome-file>
</welcome-file-list>
6.1. 传参问题
6.1.1 ajax向后台发送请求时,可以通过data提交参数,data的数据格式有三种格式:
1)data:{
k1:v1,
k2:v2,
…
}
*劣势:只能向后台提交一个参数名对应一个参数值的数据,
不能向后台提交一个参数名对应多个参数值的数据。
只能向后台提交字符串数据
优势:操作简单
2)data:k1=v1&k2:v2&…
*优势:不但能够向后台提交一个参数名对应一个参数值的数据,
还能向后台提交一个参数名对应多个参数值的数据。
劣势:操作麻烦
只能向后台提交字符串数据
3)data:FormData对象
优势:不但能提交字符串数据,
还能提交二进制数据
劣势:操作更复杂
data:FormData对象
$(function () {
//给"导入"按钮添加单击事件
$("#importActivityBtn").click(function () {
//收集参数
var activityFileName=$("#activityFile").val();
var suffix=activityFileName.substr(activityFileName.lastIndexOf(".")+1).toLocaleLowerCase();//xls,XLS,Xls,xLs,....
if(suffix!="xls"){
alert("只支持xls文件");
return;
}
var activityFile=$("#activityFile")[0].files[0];
if(activityFile.size>5*1024*1024){
alert("文件大小不超过5MB");
return;
}
//FormData是ajax提供的接口,可以模拟键值对向后台提交参数;
//FormData最大的优势是不但能提交文本数据,还能提交二进制数据
var formData=new FormData();
formData.append("activityFile",activityFile);
formData.append("userName","张三");
//发送请求
$.ajax({
url:'workbench/activity/importActivity.do',
data:formData,
processData:false,//设置ajax向后台提交参数之前,是否把参数统一转换成字符串:true--是,false--不是,默认是true
contentType:false,//设置ajax向后台提交参数之前,是否把所有的参数统一按urlencoded编码:true--是,false--不是,默认是true
type:'post',
dataType:'json',
success:function (data) {
if(data.code=="1"){
//提示成功导入记录条数
alert("成功导入"+data.message+"条记录");
//关闭模态窗口
$("#importActivityModal").modal("hide");
//刷新市场活动列表,显示第一页数据,保持每页显示条数不变
queryActivityByConditionForPage(1,$("#demo_pag1").bs_pagination('getOption', 'rowsPerPage'));
}else{
//提示信息
alert(data.message);
//模态窗口不关闭
$("#importActivityModal").modal("show");
}
}
});
});
6.1.2 controller的方法和mappersql语句
传参问题 用getKey得到参数 (参数是map
)
map中的key要和mapper sql语句参数一致
6.1.3 找到对应的activity类 对应的属性进行自动装配 用set方法进行装配的 (参数是activity实体类
)
前端
<script type="text/javascript">
$(function(){
$("#saveActivity").click(function () {
var activityOwner = $("#create-marketActivityOwner").val();
var marketActivityName = $("#create-marketActivityName").val();
var startTime = $("#create-startTime").val();
var endTime = $("#create-endTime").val();
var cost = $("#create-cost").val();
var describe = $("#create-describe").val();
if (activityOwner==""){
alert("所有者不能为空!")
return;
}
if(marketActivityName==""){
alert("项目名称不能为空!")
return;
}
$.ajax({
url:"workbench/activity/insertActivity.do",
data:{
"owner":activityOwner,
"name":marketActivityName,
"startDate":startTime,
"endDate":endTime,
"cost":cost,
"description":describe
},
dataType:"json",
type:"post",
success:function (data) {
if(data.code=="1"){
$("#msg").text(data.message);
//跳转到业务主页面
window.location.href="workbench/activity/index.do";
}else{
//提示信息
$("#msg").text(data.message);
}
}
})
})
});
</script>
controller层
@RequestMapping("/workbench/activity/insertActivity.do")
@ResponseBody
public Object insertActivity(Activity activity, HttpSession session) {
User user =(User) session.getAttribute(Contants.SESSION_USER);
activity.setId(UUIDUtils.getUUID());
activity.setCreateTime(DateUtil.toFormatDate(new Date()));
activity.setCreateBy(user.getId());
ReturnObject returnObject = new ReturnObject();
try {
int i = activityService.insertActivity(activity);
if (i>0){
returnObject.setCode(Contants.RETURN_OBJECT_CODE_SUCCESS);
}else {
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
returnObject.setMessage("系统慢,请稍后.....");
}
}catch (Exception e){
e.printStackTrace();
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
returnObject.setMessage("系统慢,请稍后.....");
}
return returnObject;
}
mapper层
<insert id="insertActivity" parameterType="com.bjpowernode.crm.workbench.domain.Activity" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Mon May 09 21:11:52 CST 2022.
-->
insert into tbl_activity (id, owner, name, start_date,
end_date, cost, description,
create_time, create_by)
values (#{id,jdbcType=CHAR}, #{owner,jdbcType=CHAR}, #{name,jdbcType=VARCHAR}, #{startDate,jdbcType=CHAR},
#{endDate,jdbcType=CHAR}, #{cost,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR},
#{createTime,jdbcType=CHAR}, #{createBy,jdbcType=VARCHAR})
</insert>
/**
*保存市场活动
*/
int insertActivity(Activity activity);
1.mapper.xml的
values (#{owner,jdbcType=CHAR}
中的owener
和Activity
的owner
对应
2.所以前端传过来的数据要能和
Activity
的属性一致进行配对 之后用set方法
自动赋值到Activity
的owner,name,startDate
属性
data:{
"owner":activityOwner,
"name":marketActivityName,
"startDate":startTime,
"endDate":endTime,
"cost":cost,
"description":describe
}
6.1.4 什么样的java对象能通过springMVC 自动转成json对象传到后端
map list 实体类 数组
在后端到前端的时候 map能生成json对象 一般使用ReturnObject 工具实体类来生成(实体类效率高)
map一般慎用,因为效率太低了(链表+数组)
6.1.5 前端传过来的json 和controller参数名 和类型的对应
前端传过来的json只要参数名是对应的
就行,不要求前端json的数据类型是String,还是int类型的
因为前端只能向后台提交字符串数据
js是弱类型的语言
String name, String owner, String startDate,
String endDate
,int pageSize
, int pageNo 前端传过来的json数据类型无论什么都可以 转换为int类型,和string类型
//String name, String owner, String startDate, String endDate, int pageSize, int pageNo 前端传过来的json对象成员可以 转换为int类型,和string类型
public Object queryActivityByConditionForPage(String name, String owner, String startDate, String endDate, int pageSize, int pageNo) {
Map<String, Object> map = new HashMap<>();
map.put("name", name);
map.put("owner", owner);
map.put("startDate", startDate);
map.put("endDate", endDate);
map.put("pageSize", pageSize);
map.put("beginNo", (pageNo - 1) * pageSize);
6.1.6 ajax和后端的传参问题 (实体类activity传参
)
6.1.7 ajax向后端传json的id=xxxx&id=xxx&.....&id=xxx
类型的数据 (string 数组传参问题
)
后端接受的参数要和json中的参数名一致
6.1.8 多条件查询封装函数
//带条件的查询
$("#queryActivityBut").click(function () {
queryActivityByConditionForPage("1","10");
});
//进入到首页的自动放在首页的查询
queryActivityByConditionForPage("1","10");
//多条件查询封装函数
function queryActivityByConditionForPage(pageNo,pageSize) {
var name = $("#query-name").val();
var owner = $("#query-owner").val();
var startTime = $("#query-startTime").val();
var endTime = $("#query-endTime").val();
/* var pageNo = 1;
var pageSize = 10;*/
$.ajax({
url: "workbench/activity/queryActivityByConditionForPage.do",
data: {
name: name,
owner: owner,
startDate: startTime,
endDate: endTime,
pageNo: pageNo,
pageSize: pageSize
},
type: "post",
dataType: "json",
success: function (data) {
//显示总条数
//$("#totalRowsB").text(data.totalRows);
//显示市场活动的列表
//遍历activityList,拼接所有行数据
var htmlStr = "";
$.each(data.activityList, function (index, obj) {
htmlStr += "<tr class=\"active\">";
htmlStr += "<td><input type=\"checkbox\" value=\"" + obj.id + "\"/></td>";
htmlStr += "<td><a style=\"text-decoration: none; cursor: pointer;\" οnclick=\"window.location.href='detail.html';\">" + obj.name + "</a></td>";
htmlStr += "<td>" + obj.owner + "</td>";
htmlStr += "<td>" + obj.startDate + "</td>";
htmlStr += "<td>" + obj.endDate + "</td>";
htmlStr += "</tr>";
});
$("#tBody").html(htmlStr);
}
});
}
//多条件查询封装函数
function queryActivityByConditionForPage(pageNo,pageSize) {在函数中直接写参数名,因为js是弱类型语言
6.1.9 ajax成功后读取封装后的实体类,要参照mapper中 (activity 传参
)
queryActivityByConditionForPage("1",$("#demo_page").bs_pagination("getOption","rowsPerPage"));
记录上次的每页显示的条数bs_pagination("getOption","rowsPerPage"));
6.1.10 在javaweb阶段 json传参
后端 UserServlet 程序中 ajaxExistsUsername 方法:
protected void ajaxExistsUsername(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
// 获取请求的参数 username
String username = req.getParameter("username");
// 调用 userService.existsUsername();
boolean existsUsername = userService.existsUsername(username);
// 把返回的结果封装成为 map 对象
Map<String,Object> resultMap = new HashMap<>();
resultMap.put("existsUsername",existsUsername);
Gson gson = new Gson();
String json = gson.toJson(resultMap);
resp.getWriter().write(json);
}
springMVC将这个方法进行自动处理一个returnObject
// 把返回的结果封装成为 map 对象
Map<String,Object> resultMap = new HashMap<>();
resultMap.put("existsUsername",existsUsername);
Gson gson = new Gson();
String json = gson.toJson(resultMap);
resp.getWriter().write(json);
前端 regist.jsp 页面中的代码
("#username").blur(function () {
//1 获取用户名
var username = this.value;
$.getJSON("http://localhost:8080/book/userServlet","action=ajaxExistsUsername&username=" +
username,function (data) {
if (data.existsUsername) {
$("span.errorMsg").text("用户名已存在!");
} else {
$("span.errorMsg").text("用户名可用!");
}
});
});
6.1.11 success:function (data) { 中的data
代表的东西
6.1.12 想要返回的 json对象更加的通用一般返回值用Object
6.1.13 全部请求还是异步请求
- 如果是刷新整个页面就用同步
- 如果是刷新部分页面就用异步
- 如果不确定是全部还是异步就用异步
7. 业务思想
7.0.1 User是系统管理功能 Activity业务管理功能
7.0.2 跟用户相关的是系统功能 要写在 setting文件目录下
7.0.3 做业务功能的先后顺序
功能选择
先做被依赖的功能,比如登录
7.0.4 为什么使用public修饰方法
8. mybatis
8.1. mapper
8.1.1 连接查询 d.value as c.
appellation 是错误的
8.1.2 构建表未使用外键
8.1.3 排除多条记录 not in 排除一条记录 !=
mapper.xml LIKE '%'#{activityName}'%'
%
要加一个单引号变成'%'
SELECT a.`id`, a.`name`, a.`start_date`, a.`end_date`, u.`name` AS OWNER
FROM `tbl_activity` a
JOIN `tbl_user` u ON u.`id` = a.`owner`
WHERE a.`name` LIKE '%'#{activityName}'%'
AND a.`id` NOT IN
(SELECT activity_id
FROM `tbl_clue_activity_relation`
WHERE clue_id=#{clueId}
)
sql语句是 LIKE '%发%'
SELECT a.`id`,a.`name`,a.`start_date`,a.`end_date`,u.`name` AS OWNER
FROM `tbl_activity` a
JOIN `tbl_user` u ON u.`id`=a.`owner`
WHERE a.`name` LIKE '%发%'
AND a.`id` NOT IN
(SELECT activity_id
FROM `tbl_clue_activity_relation`
WHERE clue_id='e9170a45c96d4a83800b2599c4f56c18'
)
8.1.4 连接表的效率比较
连接了两张表
<select id="selectActivityByActivityArrayId" parameterType="string" resultMap="BaseResultMap">
select a.id, a.name, a.start_date, a.end_date, u.name as owner
from tbl_activity a
join tbl_user u on a.owner = u.id
where a.id= in //效果就是(xx,xx,xx)//
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
连接了三张表 效率低
<select id="selectActivityForDetailByClueId" parameterType="string" resultMap="BaseResultMap">
select a.id, a.name, a.start_date, a.end_date, u.name as owner
from tbl_activity a
join tbl_user u on a.owner = u.id
join tbl_clue_activity_relation car on a.id = car.activity_id
where car.clue_id = #{clueId}
</select>
8.1.5 select添加string参数 (list
)
dao
int insertMultipleActivity (List<ClueActivityRelation> ClueActivityRelations);
mapper.xml insert插入多条数据
separator=“,” 是对 <foreach >
标签中的内容遍历一次进行一次 分割 ,
parameterType="com.bjpowernode.crm.workbench.domain.ClueActivityRelation">
insert into tbl_clue_activity_relation (id, clue_id, activity_id)
values
<foreach collection="list" item="obj" separator=",">
(#{obj.id}, #{obj.clueId}, #{obj.activityId} )
</foreach>
</insert>
sql语句 insert插入多条数据
INSERT INTO tbl_clue_activity_relation (id, clue_id, activity_id)
VALUES
(1245,21,21),
(2455,2,2)
8.1.6 内连接和外连接
内连接(自然连接)会只会显示配数据,舍弃不匹配数据,外连接不会舍弃,所以两个表都能匹配就用内连接
看外键是否为空在采取,内连接还是外联接
不为空自然链接就能查出来,为空要外联接查出可能为空的数据
8.1.6 带 in 的查询 (参数是 string数组
)
注意:where a.id in 要写a.id,要不然不知到id是谁的id
mapper.xml
<select id="selectActivityByActivityArrayId" parameterType="string" resultMap="BaseResultMap">
select a.id, a.name, a.start_date, a.end_date, u.name as owner
from tbl_activity a
join tbl_user u on a.owner = u.id
where in a.id= //效果就是(xx,xx,xx)//
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
<foreach collection="array" item="id" separator="," open="(" close=")"> #{id} </foreach>
没遍历一个id就加一个
,
的分隔符 遍历完后就 在前面后面分别加上( )
8.1.7 方法的参数数量和mapper.xml中sql语句关系
在MyBatis进行操作:
*** 1:如果操作方法只有一个简单类型或者字符串类型的参数,**
*** 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写**
*** 2:如果操作方法有一个对象类型的参数,**
*** 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)**
*** 3:如果操作方法有一个Map类型的参数,**
*** 在Mapper配置中可以直接通过#{key}获取key的指定value值**
*** 4:如果操作方法有多个参数,该如何处理呢?**
*** 通过MyBatis自带的参数 arg0 arg1 … 来获取相应的参数**
*** 也可以通过在方法的参数中加入@Param(“key”) 给参数添加注解的方式**
8.1.8 mapper.xml 查询和修改的语句返回值的区别
mapper.xml中的 修改语句压根没有定义resultType resultMap语句 加上反而会错
只有查询语句才有resultMap和resultType
<select id="selectActivityByConditionForPage" parameterType="map" resultMap="BaseResultMap">
<select id="selectCountOfActivityByCondition" parameterType="map" resultType="int">
8.2. mybatis逆向工程
8.2.1 配置文件设置
8.2.2 插件和jar包的区别
- jar只能让别人去调用
- 插件是可以自己运行的程序
9. 细节
9.0.1 业务和系统
dicValue 字典是系统的东西 但是字典内容映射到前台是业务层的controller
线索clue是业务层的东西
9.0.2 列选择模式
9.0.3 在idea中添加try/catch的快捷键
ctrl+alt+t 选中想被try/catch包围的语句,同时按下ctrl+alt+t, 出现下图
9.0.4 json对象和json数组的区分
-
json对象 外面是大括号{}
-
json数组 外面是中括号 []
9.0.5 String contains 用法
user.getAllowIps().contains(request.getRemoteAddr())
确定大的里面是否有小的
} else if (!user.getAllowIps().contains(request.getRemoteAddr())) {
//访问的ip地址未在允许的ip访问
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
returnObject.setMessage("访问的ip地址未在允许的ip访问");
9.0.6 前后端传url的时候尽量ctrl c ctrl v 不是自己手打
9.0.7 debug可以边修改边debug
9.0.8 ajax访问时,如果url没有写对,就会访问一个空的页面,因为在controller中找不到
9.0.9 为将来修改删除做准备,要查询id 能多地方都要传值要用到 id
9.0.10 mapper.xml 批量删除和多条件查询
<insert id="insertActivity" parameterType="com.bjpowernode.crm.workbench.domain.Activity" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Mon May 09 21:11:52 CST 2022.
-->
insert into tbl_activity (id, owner, name, start_date,
end_date, cost, description,
create_time, create_by)
values (#{id,jdbcType=CHAR}, #{owner,jdbcType=CHAR}, #{name,jdbcType=VARCHAR}, #{startDate,jdbcType=CHAR},
#{endDate,jdbcType=CHAR}, #{cost,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR},
#{createTime,jdbcType=CHAR}, #{createBy,jdbcType=VARCHAR})
</insert>
<select id="selectActivityByConditionForPage" parameterType="map" resultMap="BaseResultMap">
select a.id,u1.name as owner,a.name,a.start_date,a.end_date,a.cost,a.description,a.create_time,
u2.name as create_by,a.edit_time,u3.name as edit_by
from tbl_activity a
join tbl_user u1 on a.owner=u1.id
join tbl_user u2 on a.create_by=u2.id
left join tbl_user u3 on a.edit_by=u3.id
<where>
<if test="name!=null and name!=''">
and a.name like '%' #{name} '%'
</if>
<if test="owner!=null and owner!=''">
and u1.name like '%' #{owner} '%'
</if>
<if test="startDate!=null and startDate!=''">
and a.start_date>=#{startDate}
</if>
<if test="endDate!=null and endDate!=''">
and a.end_date<=#{endDate}
</if>
</where>
order by a.create_time desc
limit #{beginNo},#{pageSize}
</select>
<select id="selectCountOfActivityByCondition" parameterType="map" resultType="int">
select count(*)
from tbl_activity a
join tbl_user u1 on a.owner=u1.id
join tbl_user u2 on a.create_by=u2.id
left join tbl_user u3 on a.edit_by=u3.id
<where>
<if test="name!=null and name!=''">
and a.name like '%' #{name} '%'
</if>
<if test="owner!=null and owner!=''">
and u1.name like '%' #{owner} '%'
</if>
<if test="startDate!=null and startDate!=''">
and a.start_date>=#{startDate}
</if>
<if test="endDate!=null and endDate!=''">
and a.end_date<=#{endDate}
</if>
</where>
</select>
<delete id="deleteActivityByIds" parameterType="string">
delete from tbl_activity where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
9.0.11 $(“#updateActivityBtn”).click(function () { 失效可能的原因
//更新修改的参数 单击事件后面没有加 ); 是没有关系的
没有放在$(function())中有关系
$(“#updateActivityBtn”).click(function () {
10. 市场活动
10.1 导出市场活动:
@RequestMapping("/workbench/activity/exportPartActivity.do")
public void exportAllActivity(HttpServletResponse response,String [] id) throws IOException {
List<Activity> activityList = activityService.queryPartActivity(id);
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("市场活动列表");
HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell(0);
cell.setCellValue("id");
cell= row.createCell(1);
cell.setCellValue("拥有者");
cell= row.createCell(2);
cell.setCellValue("项目名称");
cell= row.createCell(3);
cell.setCellValue("开始时间");
cell= row.createCell(4);
cell.setCellValue("结束时间");
cell= row.createCell(5);
cell.setCellValue("花费");
cell= row.createCell(6);
cell.setCellValue("描述");
cell= row.createCell(7);
cell.setCellValue("创建时间");
cell= row.createCell(8);
cell.setCellValue("创建者");
cell= row.createCell(9);
cell.setCellValue("修改时间");
cell= row.createCell(10);
cell.setCellValue("修改者");
if (activityList.size()>0&&activityList!=null){
Activity activity=null;
for (int i = 0; i < activityList.size(); i++) {
// Activity activity = activityList.get(i);
activity = activityList.get(i);
//sheet.createRow(i+1);
row=sheet.createRow(i+1);
cell = row.createCell(0);
cell.setCellValue(activity.getId());
cell= row.createCell(1);
cell.setCellValue(activity.getOwner());
cell= row.createCell(2);
cell.setCellValue(activity.getName());
cell= row.createCell(3);
cell.setCellValue(activity.getStartDate());
cell= row.createCell(4);
cell.setCellValue(activity.getEndDate());
cell= row.createCell(5);
cell.setCellValue(activity.getCost());
cell= row.createCell(6);
cell.setCellValue(activity.getDescription());
cell= row.createCell(7);
cell.setCellValue(activity.getCreateTime());
cell= row.createCell(8);
cell.setCellValue(activity.getCreateBy());
cell= row.createCell(9);
cell.setCellValue(activity.getEditTime());
cell= row.createCell(10);
cell.setCellValue(activity.getEditBy());
}
}
//FileOutputStream os = new FileOutputStream("D:\\Desktop\\word文档\\demo.xls");
// workbook.write(os);
// os.close();
// workbook.close();
//1.设置响应类型
response.setContentType("application/octet-stream;charset=UTF-8");
//2.获取输出流
OutputStream out=response.getOutputStream();
//浏览器接收到响应信息之后,默认情况下,直接在显示窗口中打开响应信息;即使打不开,也会调用应用程序打开;只有实在打不开,才会激活文件下载窗口。
//可以设置响应头信息,使浏览器接收到响应信息之后,直接激活文件下载窗口,即使能打开也不打开
response.addHeader("Content-Disposition","attachment;filename=mystudentList.xls");
//读取excel文件(InputStream),把输出到浏览器(OutoutStream)
/* InputStream is=new FileInputStream("D:\\Desktop\\word文档\\test1.xls");
byte[] buff=new byte[256];
int len=0;
while((len=is.read(buff))!=-1){
out.write(buff,0,len);
}*/
//关闭资源
// is.close();
workbook.write(out);
workbook.close();
out.flush();
}
1)给"批量导出"按钮添加单击事件,发送导出请求
2)查询所有的市场活动
3)创建一个excel文件,并且把市场活动写到excel文件中
4)把生成的excel文件输出到浏览器(文件下载)
技术准备:
1)使用java生成excel文件:iText,apache-poi
关于办公文档插件使用的基本思想:把办公文档的所有元素封装成普通的Java类,
程序员通过操作这些类达到操作办公文档目的。
文件---------HSSFWorkbook
页-----------HSSFSheet
行-----------HSSFRow
列-----------HSSFCell
样式---------HSSFCellStyle
使用apache-poi生成excel:
a)添加依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
</dependency>
b)使用封装类生成excel文件:
2)文件下载:
filedownloadtest.jsp
ActivityController
|->fileDownload()
*所有文件下载的请求只能发送同步请求。
*所有文件下载的请求只能发送同步请求。
异步请求只能接受json的对象 不能接受文件
要想接受文件只能发送同步请求
10.1.1 用pio生成excel
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CreateExcelTest {
public static void main(String[] args) throws IOException {//用main来代替客户端 调用java代码
HSSFWorkbook hssfWorkbook = new HSSFWorkbook();
HSSFSheet sheet = hssfWorkbook.createSheet("学生表");
HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell(0);
cell.setCellValue("学号");
cell = row.createCell(1);
cell.setCellValue("姓名");
cell = row.createCell(2);
cell.setCellValue("年龄");
for (int i = 1; i <= 10; i++) {
row = sheet.createRow(i);
cell = row.createCell(0);
cell.setCellValue(1000 + i);
cell = row.createCell(1);
cell.setCellValue("zwj" + i);
cell = row.createCell(2);
cell.setCellValue(i);
}
FileOutputStream fileOutputStream = new FileOutputStream("D:\\Desktop\\word文档\\test1.xls");
hssfWorkbook.write(fileOutputStream);
fileOutputStream.close();
hssfWorkbook.close();
System.out.println(" ==========ok========");
}
}
cell =
//用main来代替客户端 调用java代码
10.1.2 浏览器对在服务器进行文件下载
1.设置响应类型
response.setContentType("application/octet-stream;charset=UTF-8");
is是我们new的 out,是respond new的 tomcat关我们就不用关了
File file=new File("D:\\Desktop\\word文档\\",originalFilename);//路径必须手动创建好,文件如果不存在,会自动创建
public void fileDownload(HttpServletResponse response) throws Exception{
//1.设置响应类型
response.setContentType("application/octet-stream;charset=UTF-8");
//2.获取输出流
OutputStream out=response.getOutputStream();
//浏览器接收到响应信息之后,默认情况下,直接在显示窗口中打开响应信息;即使打不开,也会调用应用程序打开;只有实在打不开,才会激活文件下载窗口。
//可以设置响应头信息,使浏览器接收到响应信息之后,直接激活文件下载窗口,即使能打开也不打开
response.addHeader("Content-Disposition","attachment;filename=mystudentList.xls");
//读取excel文件(InputStream),把输出到浏览器(OutoutStream)
InputStream is=new FileInputStream("D:\\course\\18-CRM\\阶段资料\\serverDir\\studentList.xls");
byte[] buff=new byte[256];
int len=0;
while((len=is.read(buff))!=-1){
out.write(buff,0,len);
}
//关闭资源
is.close();
out.flush();
}
//浏览器接收到响应信息之后,默认情况下,直接在显示窗口中打开响应信息;即使打不开,也会调用应用程序打开;只有实在打不开,才会激活文件下载窗口。
//可以设置响应头信息,使浏览器接收到响应信息之后,直接激活文件下载窗口,即使能打开也打不开
response.addHeader("Content-Disposition","attachment;filename=mystudentList.xls");
attachment 附件
10.1.3 表单的编码格式只能用:multipart/form-data
<form action="workbench/activity/fileUpload.do" method="post" enctype="multipart/form-data">
<input type="file" name="myFile"><br>
<input type="text" name="userName"><br>
<input type="submit" value="提交">
</form>
根据HTTP协议的规定,浏览器每次向后台提交参数,都会对参数进行统一编码;默认采用的编码格式是entype= "urlencoded"
,这种编码格式只能对文本数据进行编码;
浏览器每次向后台提交参数,都会首先把所有的参数转换成字符串,然后对这些数据统一进行urlencoded编码;对服务器传来的数据也通过这种方式进行解码 以前的提交的参数传输可以这么做,现在要传输文件了(传输二进制)就不能用这种编码方式
文件上传的表单编码格式只能用multipart/form-data:enctype="multipart/form-data" 功能:只是阻止默认行为
在循环体外面创建对象,提高效率 Activity activity=null;
if (activityList.size()>0&&activityList!=null){ Activity activity=null; for (int i = 0; i < activityList.size(); i++) { // Activity activity = activityList.get(i); activity = activityList.get(i); //sheet.createRow(i+1); row=sheet.createRow(i+1);
访问磁盘的效率是最低的,访问磁盘效率低尽量少访问,尽量多访问内存
wb.write(out)
将wb的内存数据放入out中然后,通过respond响应管道到前端
10.2 导入市场活动:
10.2.1 文件导入ajax的异步请求
//给"导入"按钮添加单击事件
$("#importActivityBtn").click(function () {
//收集参数
var activityFileName=$("#activityFile").val();
var suffix=activityFileName.substr(activityFileName.lastIndexOf(".")+1).toLocaleLowerCase();//xls,XLS,Xls,xLs,....
if(suffix!="xls"){
alert("只支持xls文件");
return;
}
var activityFile=$("#activityFile")[0].files[0];
if(activityFile.size>5*1024*1024){
alert("文件大小不超过5MB");
return;
}
//FormData是ajax提供的接口,可以模拟键值对向后台提交参数;
//FormData最大的优势是不但能提交文本数据,还能提交二进制数据
var formData=new FormData();
formData.append("activityFile",activityFile);
formData.append("userName","张三");
//发送请求
$.ajax({
url:'workbench/activity/importActivity.do',
data:formData,
processData:false,//设置ajax向后台提交参数之前,是否把参数统一转换成字符串:true--是,false--不是,默认是true
contentType:false,//设置ajax向后台提交参数之前,是否把所有的参数统一按urlencoded编码:true--是,false--不是,默认是true
type:'post',
dataType:'json',
success:function (data) {
if(data.code=="1"){
//提示成功导入记录条数
alert("成功导入"+data.message+"条记录");
//关闭模态窗口
$("#importActivityModal").modal("hide");
//刷新市场活动列表,显示第一页数据,保持每页显示条数不变
queryActivityByConditionForPage(1,$("#demo_pag1").bs_pagination('getOption', 'rowsPerPage'));
}else{
//提示信息
alert(data.message);
//模态窗口不关闭
$("#importActivityModal").modal("show");
}
}
});
});
@RequestMapping("/workbench/activity/importActivity.do")
@ResponseBody
public Object importActivity(MultipartFile activityFile,HttpSession session){
User user = (User) session.getAttribute(Contants.SESSION_USER);
List<Activity> activityList=new ArrayList<>();
ReturnObject returnObject = new ReturnObject();
try {
/*String originalFilename = activityFile.getOriginalFilename();
File file = new File("D:\\Desktop\\word文档\\",originalFilename);
activityFile.transferTo(file);
FileInputStream fileInputStream = new FileInputStream("D:\\Desktop\\word文档\\"+originalFilename);*/
//增加
InputStream inputStream = activityFile.getInputStream();
HSSFWorkbook workbook = new HSSFWorkbook(inputStream);
HSSFSheet sheet = workbook.getSheetAt(0);
Activity activity=null;
HSSFRow row =null;
HSSFCell cell =null;
for (int i = 1; i <= sheet.getLastRowNum() ; i++) {
row = sheet.getRow(i);
activity=new Activity();
activity.setId(UUIDUtils.getUUID());
activity.setOwner(user.getId());
for (int j = 0; j < row.getLastCellNum(); j++) {
cell = row.getCell(j);
String cellValue = HSSFUtils.getCellValueForStr(cell);
if(j==0){
activity.setName(cellValue);
}else if(j==1){
activity.setStartDate(cellValue);
}else if(j==2){
activity.setEndDate(cellValue);
}else if(j==3){
activity.setCost(cellValue);
}else if(j==4){
activity.setDescription(cellValue);
}
}
activityList.add(activity);
}
int i = activityService.saveMultipleActivity(activityList);
returnObject.setMessage("成功插入"+i+"数据");
returnObject.setCode(Contants.RETURN_OBJECT_CODE_SUCCESS);
} catch (IOException e) {
returnObject.setMessage("插入失败");
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
e.printStackTrace();
}
return returnObject;
}
10.2.2 flush方法
为了提高IO吞吐效率,一般的IO服务都带缓冲区,当缓冲区满了再输出一次,这样减少IO次数。
flush() 则要求立即将缓冲区的数据输出到接收方。
在java开发中,有时我们会进行流的操作,所以可能会经常遇到这样一段代码
out.flush();
out.close();
有时我们只是大概看懂这些,却不知道其中的原理性东西,下面就来理解一下:
flush()这个函数是清空的意思,用于清空缓冲区的数据流,进行流的操作时,数据先被读到内存中,然后再用数据写到文件中,那么当你数据读完时,我们如果这时调用close()方法关闭读写流,这时就可能造成数据丢失,为什么呢,因为,读入数据完成时不代表写入数据完成,一部分数据可能会留在缓存区中,为了理解这个问题,我们举一个例子:
比如,在农村,几乎每家都有抽水机,抽水机的作用是什么呢,就是把水井里的水抽到水缸中,这时我们就会用水管连接抽水机和水缸(水管就好比是缓冲区),当我们想把水井中的水都抽到水缸中时,我们就让抽水机工作抽水,如果我们发现水井里的水刚好抽完时,我们就会关掉抽水机的开关停止抽水,那么这时,管道里就会遗留一部分水,抽水就是读数据,水缸进水就是写数据,水管充当缓存区的角色,不知道这样是不是具象化了呢
那么这样一来我们如果中途调用close()方法,输出区也还是有数据的,就像水缸里有水,只是在缓冲区遗留了一部分,这时如果我们先调用flush()方法,就会强制把数据输出,缓存区就清空了,最后再关闭读写流调用close()就完成了。
10.2.3 MultipartFile myFile
前端,传参数myFile
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n3cIqwt0-1652762340017)(C:/Users/86199/AppData/Roaming/Typora/typora-user-images/image-20220513152000643.png)]
把post请求体中所有的数据封装到 参数myFile中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nJFOZVjB-1652762340019)(C:/Users/86199/AppData/Roaming/Typora/typora-user-images/image-20220513151919584.png)]
调用这个 类 然后参数从 请求体中拿数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QfewWJnO-1652762340022)(C:/Users/86199/AppData/Roaming/Typora/typora-user-images/image-20220513152320814.png)]
必须配置这个类 接受到文件上传请求后自动调用这个类
配置文件上传解析器 id:必须是multipartResolver 要不然会找不到
11. web
11.0.1 post和get详解
文件上传的表单三个条件:
1.表单组件标签必须用:
<input type="file">
<input type="text|password|radio|checkbox|hidden|button|submit|reset|file">
<select>,<textarea>等
2.请求方式只能用:post
get:参数通过请求头提交到后台,参数放在URL后边;只能向后台提交文本数据;对参数长度有限制;数据不安全;效率高
post:参数通过请求体提交到后台;既能能提交文件数据,又能够提交二进制数据;理论上对参数长度没有限制;相对安全;效率相对较低
get效率高,因为get能使用缓存将静态页面,图片缓存起来 post只能一个个向后端请求 重新传一遍无法进行缓存,
get因为有缓存,改静态页面要清理缓存才有效果,所以get对开发不友好
所以开发时能用post就用post
get长度有限制是因为 浏览器对地址栏长度有限制,所以对get参数有限制
11.0.2 web中的同步和异步:
(1)同步请求:顺序处理,即当我们向服务器发出一个请求时,在服务器没返回结果给客户端之前,我们要一直处于等待状态直至服务器将结果返回到客户端,我们才能执行下一步操作。例如普通的B/S模式就是同步请求(注:B/S模式 也即服务器与浏览器通信主要采用HTTP协议;通信方式为“请求——响应”,浏览器发出请求;服务器做出响应。)
(2)异步请求:并行处理,当我们向服务器发出一个请求时,在服务器没返回结果之前,我们还是可以执行其他操作。例如AJAX技术就是异步请求。
同步请求一般返回一个网页,异步请求一般返回一个data数据
同步请求不要返回json因为解析不了
看同步还是异步看是否会刷新整个页面
在循环体外加一个变量
?ClueActivityRelation clueActivityRelation=null; 加一个变量效率更高
ClueActivityRelation clueActivityRelation=null;
for (int i = 0; i < activityId.length; i++) {
clueActivityRelation = new ClueActivityRelation();
clueActivityRelation.setId(UUIDUtils.getUUID());
clueActivityRelation.setActivityId(activityId[i]);
clueActivityRelation.setClueId(clueId);
clueActivityRelationList.add(clueActivityRelation);
}
j1){
activity.setStartDate(cellValue);
}else if(j2){
activity.setEndDate(cellValue);
}else if(j3){
activity.setCost(cellValue);
}else if(j4){
activity.setDescription(cellValue);
}
}
activityList.add(activity);
}
int i = activityService.saveMultipleActivity(activityList);
returnObject.setMessage("成功插入"+i+"数据");
returnObject.setCode(Contants.RETURN_OBJECT_CODE_SUCCESS);
} catch (IOException e) {
returnObject.setMessage("插入失败");
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
e.printStackTrace();
}
return returnObject;
}
#### 10.2.2 flush方法
> 为了提高IO吞吐效率,一般的IO服务都带缓冲区,当缓冲区满了再输出一次,这样减少IO次数。
flush() 则要求立即将缓冲区的数据输出到接收方。
在java开发中,有时我们会进行流的操作,所以可能会经常遇到这样一段代码
out.flush();
out.close();
有时我们只是大概看懂这些,却不知道其中的原理性东西,下面就来理解一下:
flush()这个函数是清空的意思,用于清空缓冲区的数据流,进行流的操作时,数据先被读到内存中,然后再用数据写到文件中,那么当你数据读完时,我们如果这时调用close()方法关闭读写流,这时就可能造成数据丢失,为什么呢,因为,读入数据完成时不代表写入数据完成,一部分数据可能会留在缓存区中,为了理解这个问题,我们举一个例子:
比如,在农村,几乎每家都有抽水机,抽水机的作用是什么呢,就是把水井里的水抽到水缸中,这时我们就会用水管连接抽水机和水缸(水管就好比是缓冲区),当我们想把水井中的水都抽到水缸中时,我们就让抽水机工作抽水,如果我们发现水井里的水刚好抽完时,我们就会关掉抽水机的开关停止抽水,那么这时,管道里就会遗留一部分水,抽水就是读数据,水缸进水就是写数据,水管充当缓存区的角色,不知道这样是不是具象化了呢
那么这样一来我们如果中途调用close()方法,输出区也还是有数据的,就像水缸里有水,只是在缓冲区遗留了一部分,这时如果我们先调用flush()方法,就会强制把数据输出,缓存区就清空了,最后再关闭读写流调用close()就完成了。
#### 10.2.3 MultipartFile myFile
前端,传参数myFile
[外链图片转存中...(img-n3cIqwt0-1652762340017)]
把post请求体中所有的数据封装到 参数myFile中
[外链图片转存中...(img-nJFOZVjB-1652762340019)]
调用这个 类 然后参数从 请求体中拿数据
[外链图片转存中...(img-QfewWJnO-1652762340022)]
必须配置这个类 接受到文件上传请求后自动调用这个类
配置文件上传解析器 id:必须是multipartResolver 要不然会找不到
[外链图片转存中...(img-xDWy5S8Y-1652762340025)]
## 11. web
#### 11.0.1 post和get详解
文件上传的表单三个条件:
1.表单组件标签必须用:
```html
<input type="file">
<input type="text|password|radio|checkbox|hidden|button|submit|reset|file">
<select>,<textarea>等
2.请求方式只能用:post
get:参数通过请求头提交到后台,参数放在URL后边;只能向后台提交文本数据;对参数长度有限制;数据不安全;效率高
post:参数通过请求体提交到后台;既能能提交文件数据,又能够提交二进制数据;理论上对参数长度没有限制;相对安全;效率相对较低
get效率高,因为get能使用缓存将静态页面,图片缓存起来 post只能一个个向后端请求 重新传一遍无法进行缓存,
get因为有缓存,改静态页面要清理缓存才有效果,所以get对开发不友好
所以开发时能用post就用post
get长度有限制是因为 浏览器对地址栏长度有限制,所以对get参数有限制
11.0.2 web中的同步和异步:
(1)同步请求:顺序处理,即当我们向服务器发出一个请求时,在服务器没返回结果给客户端之前,我们要一直处于等待状态直至服务器将结果返回到客户端,我们才能执行下一步操作。例如普通的B/S模式就是同步请求(注:B/S模式 也即服务器与浏览器通信主要采用HTTP协议;通信方式为“请求——响应”,浏览器发出请求;服务器做出响应。)
(2)异步请求:并行处理,当我们向服务器发出一个请求时,在服务器没返回结果之前,我们还是可以执行其他操作。例如AJAX技术就是异步请求。
同步请求一般返回一个网页,异步请求一般返回一个data数据
同步请求不要返回json因为解析不了
看同步还是异步看是否会刷新整个页面
在循环体外加一个变量
?ClueActivityRelation clueActivityRelation=null; 加一个变量效率更高
ClueActivityRelation clueActivityRelation=null;
for (int i = 0; i < activityId.length; i++) {
clueActivityRelation = new ClueActivityRelation();
clueActivityRelation.setId(UUIDUtils.getUUID());
clueActivityRelation.setActivityId(activityId[i]);
clueActivityRelation.setClueId(clueId);
clueActivityRelationList.add(clueActivityRelation);
}