此案例的初衷,是在之前基础案例整合的基础之上,对使用SSM框架开发JavaWeb项目,所做的一个实战演练!
注意:执行这步操作之前,初始化项目工作已经完成
5.2.5.RELEASE
3.5.7
2.0.6
1.2.15
1.6.4
5.1.2
2.9.6
3.0.1
2.0
8.0.21
0.9.5.5
1.2
4.12
1.18.20
org.mybatis
mybatis
${mybatis.version}
org.mybatis
mybatis-spring
${mybatis.spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-aspects
${spring.version}
org.springframework
spring-jms
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-test
${spring.version}
com.github.miemiedev
mybatis-paginator
${mybatis.paginator.version}
com.github.pagehelper
pagehelper
${pagehelper.version}
com.fasterxml.jackson.core
jackson-databind
${jackson.version}
commons-io
commons-io
2.4
commons-fileupload
commons-fileupload
1.3.1
org.json
json
20170516
javax.servlet
javax.servlet-api
${servlet-api.version}
javax.servlet
jsp-api
${jsp-api.version}
jstl
jstl
${jstl.version}
mysql
mysql-connector-java
${mysql.version}
com.mchange
c3p0
${c3p0.version}
junit
junit
${junit.version}
org.projectlombok
lombok
${lombok.version}
src/main/resources
**/*.properties
**/*.xml
false
src/main/java
**/*.properties
**/*.xml
false
org.apache.maven.plugins
maven-compiler-plugin
1.8
1.8
UTF-8
接下来在该位置添加如下配置文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mimissm?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
jdbc.username=root
jdbc.password=mc123456
接下来在以下位置,修改web.xml配置文件
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceRequestEncoding
true
forceResponseEncoding
true
encodingFilter
/*
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
1
springmvc
*.action
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
classpath:spring-*.xml
15
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
LOGIN
${errmsg}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%----%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page import="java.util.*" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
参照下图,创建封装不同功能代码的包
所有功能实现的流程可参照下图:
说明:
第一步:在jsp页面的action位置先用#代填,controller层编写完毕后,在将正确路径填入
第二步:编写controller层
访问路径中有.action,是因为在web.xml配置文件中设置了路径拦截
@Controller
@RequestMapping("/admin")
public class AdminController {
// 切记:在所有的界面层,一定会有业务逻辑层的对象
// 实现登录判断,并做相应跳转
//创建业务逻辑层的对象
@Autowired
private AdminService adminService;
@RequestMapping("/login")
public String login(String name, String pwd, Model model) {
Admin admin = adminService.login(name, pwd);
if (admin != null) { // 登录成功
model.addAttribute("admin", admin);
return "main";
} else { // 登录失败
model.addAttribute("errmsg", "用户名或密码不正确!");
return "login";
}
}
}
第三步:编写service层接口
public interface AdminService {
// 完成登录判断
public Admin login(String name, String pwd);
}
第四步:编写serviceImpl接口实现类
@Service
public class AdminServiceImpl implements AdminService {
// 在业务逻辑层中,一定会有数据访问层的
@Autowired
private AdminMapper adminMapper;
@Override
public Admin login(String name, String pwd) {
// 根据传入的用户名,到DB中查询相应用户对象
Admin admin = adminMapper.selectByUserName(name);
if (admin != null) {
// 如果查询到用户对象,再进行密码的比对,注意密码是密文
/*分析:
admin.getaPass() ==> d033e22ae348aeb5660fc2140aec35850c4da997
pwd ==> admin
在进行密码对比时,要将传入的pwd进行md5加密,再与数据库中查到的对象的密码进行对比
*/
if (MD5Util.getMD5(pwd).equals(admin.getaPass())) {
return admin;
}
}
return null;
}
}
第五步:编写mapper接口
public interface AdminMapper {
Admin selectByUserName(String name);
}
第六步:编写mapper.xml文件
a_id, a_name, a_pass
在实际的开发中,编写一个加密的公共类即可
public class MD5Util {
public final static String getMD5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("SHA");//创建具有指定算法名称的摘要
md.update(str.getBytes()); //使用指定的字节数组更新摘要
byte mdBytes[] = md.digest(); //进行哈希计算并返回一个字节数组
String hash = "";
for (int i = 0; i < mdBytes.length; i++) { //循环字节数组
int temp;
if (mdBytes[i] < 0) //如果有小于0的字节,则转换为正数
temp = 256 + mdBytes[i];
else
temp = mdBytes[i];
if (temp < 16)
hash += "0";
hash += Integer.toString(temp, 16); //将字节转换为16进制后,转换为字符串
}
return hash;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
4.3.1 复杂条件情况说明:
4.3.2 普通分页
只需要传入起始页码和每页要显示的数据条数
// select * from product_info limit 起始记录数=((当前页-1)*每页的条数),每页取几条
// select* from product_info limit 10,5
@Override
public PageInfo splitPage(int pageNum, int pageSize) {
// 分页插件使用PageHelper工具类完成分页设置
PageHelper.startPage(pageNum, pageSize);
// 进行PageInfo的数据封装
// 进行有条件的查询操作,必须要创建ProductInfoExample对象
ProductInfoExample example = new ProductInfoExample();
//设置排序,按主键降序排序
// select * from product_info order by p_id desc
example.setOrderByClause("p_id desc");
// 设置完排序后,取集合,切记切记:一定在取集合之前,设置PageHelper.startPage(pageNum, pageSize);
List list = pmapper.selectByExample(example);
//将查到的集合封装进pageInfo
PageInfo pageInfo = new PageInfo<>(list);
return pageInfo;
}
4.3.3 多条件查询获得数据结果集
第一步:多这几个查询条件进行封装,将其转换成可new的对象
public class ProductInfoVo {
//封装所有页面上的查询条件
private String pname; // 商品名称
private Integer typeid; // 商品类型
private Integer lprice; // 最低价格
private Integer hprice; // 最高价格
private Integer page = 1; //设置页码
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public Integer getTypeid() {
return typeid;
}
public void setTypeid(Integer typeid) {
this.typeid = typeid;
}
public Integer getLprice() {
return lprice;
}
public void setLprice(Integer lprice) {
this.lprice = lprice;
}
public Integer getHprice() {
return hprice;
}
public void setHprice(Integer hprice) {
this.hprice = hprice;
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
}
**第二步:从前端jsp页面传递这几个参数**
这里使用了ajax技术,来传递参数,并将响应结果进行回显
function condition() {
// 取出查询条件
var pname = $("#pname").val();
var typeid = $("#typeid").val();
var lprice = $("#lprice").val();
var hprice = $("#hprice").val();
$.ajax({
url: "${pageContext.request.contextPath}/prod/ajaxsplit.action",
type: "post",
data: {
"pname": pname,
"typeid": typeid,
"lprice": lprice,
"hprice": hprice
},
success: function () {
// 刷新显示数据的容器
$("#table").load("http://localhost:8080/admin/product.jsp #table");
}
});
}
第三步:在controller中接收参数,并根据其值进行数据查询
页面传递过来的参数自动用ProductInfoVo进行了封装(如果单独想要哪个参数,直接用get方法就可以得到)
直接调用pmapper.selectCondition(vo),该方法返回的数据集合就是多条件查询后的结果集
@Override
public List selectCondition(ProductInfoVo vo) {
return pmapper.selectCondition(vo);
}
4.3.4 多条件查询接口selectCondition的SQL实现
p_id, p_name, p_content, p_price, p_image, p_number, type_id, p_date
4.3.5 多条件查询后数据的分页显示
与普通分页类似,只是起始页码是从前端传递的多条件参数中得到
@Override
public PageInfo splitPageVo(ProductInfoVo vo, int pageSize) {
//切记切记:在取集合之前,使用分页插件一定要先设置当前页和每页的个数,即PageHelper.startPage()属性
PageHelper.startPage(vo.getPage(), pageSize);
//取集合
List list=pmapper.selectCondition(vo);
return new PageInfo<>(list);
}
4.3.6 批量删除
第一步:从jsp页面传递参数,这次传递的是一个存放要删除商品id的数组
//批量删除
function deleteBatch(page) {
// 取出查询条件
var pname = $("#pname").val();
var typeid = $("#typeid").val();
var lprice = $("#lprice").val();
var hprice = $("#hprice").val();
//得到所有选中复选框的对象,根据其长度判断是否有选中商品
var cks = $("input[name='ck']:checked");
// 如果有选中的商品,则获取其value的值,进行字符串拼接
if (cks.length == 0) {
alert("请选择将要删除的商品!");
} else {
var str = "";
var pid = "";
if (confirm("您确定要删除" + cks.length + "条商品吗?")) {
//进行提交商品Id的字符串的拼接
$.each(cks, function () {
pid = $(this).val(); // 每一个被选中商品的id
if (pid != null) { //进行非空判断,避免出错
str += pid + ",";
}
});
$.ajax({
url: "${pageContext.request.contextPath}/prod/deleteBatch.action",
data: {
"pids": str,
"page": page,
"pname": pname,
"typeid": typeid,
"lprice": lprice,
"hprice": hprice
},
type: "post",
dataType: "text",
success: function (msg) {
alert(msg);//弹删除是否成功
$("#table").load("http://localhost:8080/admin/product.jsp #table");
}
});
}
}
}
……
最后一步:mapper.xml中SQL语句编写实现
p_id, p_name, p_content, p_price, p_image, p_number, type_id, p_date
delete from product_info where p_id in
#{pid}
4.4.1 再次强调,需要导入依赖
commons-io
commons-io
2.4
commons-fileupload
commons-fileupload
1.3.1
4.4.2 jsp页面
4.4.3 在controller中做处理
// 异步Ajax文件上传处理
@ResponseBody
@RequestMapping("/ajaxImg")
public Object ajaxImg(MultipartFile pimage, HttpServletRequest request) {
// 提取生成取文件名UUID+上传图片的后缀.jpg .png
saveFileName = FileNameUtil.getUUIDFileName() + FileNameUtil.getFileType(pimage.getOriginalFilename());
//得到项目中图片存储的路径
try {
String path = request.getServletContext().getRealPath("/image_big");
//转存 D:\IntelliJ IDEA\IdeaProjects\MimiSSM\web\image_big\mkxmk.jpg
pimage.transferTo(new File(path + File.separator + saveFileName));
} catch (Exception e) {
e.printStackTrace();
}
//为了在客户端显示图片,要将存储的文件名回传下去,由于是自定义的上传插件,所以此处要手工处理JSON
//返回客户端JSON对象,封装图片的路径,为了在页面实现立即回显
JSONObject object = new JSONObject();
object.put("imgurl", saveFileName);
//切记切记:JSON对象一定要toString()回到客户端
return object.toString();
}