WEB后端_Day07(订单模块、Filter、ThreadLocal、Ajax)
零点读书-订单
订单模块的实现
创建订单模块的数据库表
create table t_order(
`order_id` varchar(50) primary key,
`create_time` datetime,
`price` decimal(11,2),
`status` int,
`user_id` int,
foreign key(`user_id`) references t_user(`id`)
);
create table t_order_item(
`id` int primary key auto_increment,
`name` varchar(100),
`count` int,
`price` decimal(11,2),
`total_price` decimal(11,2),
`order_id` varchar(50),
foreign key(`order_id`) references t_order(`order_id`)
);
实体创建
public class Order {
private String orderId;
private Date createTime;
private BigDecimal price;
// 0 未发货,1 已发货,2 表示已签收
private Integer status = 0;
private Integer userId;
public class OrderItem {
private Integer id;
private String name;
private Integer count;
private BigDecimal price;
private BigDecimal totalPrice;
private String orderId;
DAO
订单Dao接口
public interface IOrderDao {
int save(Order order);
}
实现类
public class OrderDaoImpl extends BaseDAO implements IOrderDao {
@Override
public int save(Order order) {
String sql = "insert into t_order(`order_id`,`create_time`,`price`,`status`,`user_id`) values(?,?,?,?,?)";
return update(sql,order.getOrderId(),order.getCreateTime(),order.getPrice(),order.getStatus(),order.getUserId());
}
}
订单项Dao接口
public interface IOrderItemDao {
int save(OrderItem item);
}
实现
public class OrderItemDaoImpl extends BaseDAO implements IOrderItemDao {
@Override
public int save(OrderItem orderItem) {
String sql = "insert into t_order_item(`name`,`count`,`price`,`total_price`,`order_id`) values(?,?,?,?,?)";
return update(sql,orderItem.getName(),orderItem.getCount(),orderItem.getPrice(),orderItem.getTotalPrice(),orderItem.getOrderId());
}
}
Service
当对购物车进行结账操作时,用户必须登陆 不登录不能结账。
订单接口
public interface IOrderService {
void createOrder(Cart cart,Integer userId);
}
public class OrderServiceImpl implements IOrderService {
private IOrderDao dao = new OrderDaoImpl();
private IOrderItemDao orderItemDao = new OrderItemDaoImpl();
@Override
public void createOrder(Cart cart, Integer userId) {
//产生一个订单ID 订单号必须唯一
String orderId = System.currentTimeMillis()+"-"+userId;
//在双十一 秒杀系统中 对于订单的处理:编写一个后台程序 专门用来生成订单ID
// 比如一次生成100个id,然后将生成的这100个ID 保存在内存型数据库redis中,
//当需要id的时候 就从redis中获取一个 当发现redis中的id不足10个的时候 此时就通知程序然后重新生成下一批的id
Date createTime = new Date();
BigDecimal price = cart.getPriceAll();
Order order = new Order(orderId,createTime,price,0,userId);
//当保存 订单的时候 同时将订单详情保存一下
for(Map.Entry<Integer, CartItem> entry : cart.getCart().entrySet()){
OrderItem orderItem = new OrderItem();
orderItem.setName(entry.getValue().getName());
orderItem.setCount(entry.getValue().getNumber());
orderItem.setTotalPrice(entry.getValue().getTotalPrice());//该类商品的总价
orderItem.setPrice(entry.getValue().getPrice());//单价
orderItemDao.save(orderItem);
orderItem.setOrderId(orderId);
}
//结算之后 将购物车清空
cart.emptyCar();
dao.save(order);
}
}
Servlet
@WebServlet("/order")
public class OrderServlet extends BaseServlet{
private IOrderService service = new OrderServiceImpl();
public void createOrder(HttpServletRequest req , HttpServletResponse resp) throws ServletException, IOException {
//判断用户是否登陆 如果登陆 则继续 如果没有登陆 则跳转到登录页 登陆
HttpSession session = req.getSession();
User user = (User)session.getAttribute("user");
if(user!=null){//用户未登陆
Cart cart = (Cart) session.getAttribute("cart");
String orderId = service.createOrder(cart ,user.getId());
req.setAttribute("orderId",orderId);
}
req.getRequestDispatcher("/pages/cart/checkout.jsp").forward(req,resp);
}
}
前端页面处理
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta charset="UTF-8">
<title>结算页面</title>
<jsp:include page="/pages/public/top.jsp"/>
<style type="text/css">
h1 {
text-align: center;
margin-top: 200px;
}
</style>
</head>
<body>
<div id="header">
<img class="logo_img" alt="" src="static/img/logo.jpg" >
<div>
<span>欢迎<span class="um_span">灵灵</span>光临零点读书</span>
<a href="order/order.html">我的订单</a>
<a href="index">注销</a>
<a href="index">返回</a>
</div>
</div>
<div id="main">
<c:if test="${ empty orderId}">
<h1>您还未登录,请登陆后继续操作。<a href="pages/user/login.jsp" style="color:red;font-size: 32px">去登陆</a></h1>
</c:if>
<c:if test="${not empty orderId}">
<h1>你的订单已结算,订单号为${orderId}</h1>
</c:if>
</div>
<div id="bottom">
<span>
零点读书.Copyright ©2020
</span>
</div>
</body>
</html>
查看我的订单
dao
List<Order> selectOrdersByUserId(Integer id);
dao的实现
@Override
public List<Order> selectOrdersByUserId(Integer id) {
String sql = "select order_id orderID,create_time CreateTime,price,status,user_id userId from t_order where user_id=?";
return queryForList(Order.class,sql,id);
}
service
List<Order> queryOrder(Integer id);
service的实现
@Override
public List<Order> queryOrder(Integer id) {
return dao.selectOrdersByUserId(id);
}
servlte
public void orderByUser(HttpServletRequest req , HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
User user = (User)session.getAttribute("user");
Integer userId = user.getId();
List<Order> userOrders = service.queryOrder(userId);
req.setAttribute("orders",userOrders);
req.getRequestDispatcher("/pages/order/order.jsp").forward(req,resp);
}
页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
<meta charset="UTF-8">
<title>我的订单</title>
<jsp:include page="/pages/public/top.jsp"/>
<style type="text/css">
h1 {
text-align: center;
margin-top: 200px;
}
</style>
</head>
<body>
<div id="header">
<img class="logo_img" alt="" src="static/img/logo.jpg" >
<div>
<span>欢迎<span class="um_span">灵灵</span>光临零点读书</span>
<a href="order?_mehtod=orderByUser">我的订单</a>
<a href="../../index.html">注销</a>
<a href="../../index.html">返回</a>
</div>
</div>
<div id="main">
<c:if test="${empty orders}">
您目前还没有订单,请前往首页选购商品。<a href="index" style="color: red;font-size: 32px">去购物</a>
</c:if>
<c:if test="${not empty orders}">
<table>
<tr>
<td colspan="4" style="font-size:28px;font-weight:800">我的订单</td>
</tr>
<tr>
<td>订单ID</td>
<td>日期</td>
<td>金额</td>
<td>状态</td>
<td>详情</td>
</tr>
<c:forEach items="${orders}" var="order">
<tr>
<td>${order.orderId}</td>
<td><fmt:formatDate value="${order.createTime}" pattern="yyyy-MM-dd"></fmt:formatDate></td>
<td>${order.price}</td>
<td>${order.status}</td>
<td><a href="order?_method=orderDetail&orderId=${order.orderId}">查看详情</a></td>
</tr>
</c:forEach>
</table>
</c:if>
</div>
<div id="bottom">
<span>
零点读书.Copyright ©2020
</span>
</div>
</body>
</html>
当生成订单之后此时可以选择去结算 结算我们就需要调用支付宝 微信等一些第三方的支付平台来完成。
Filter
1、Filter 过滤器它是JavaWeb 的三大组件之一。三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器
2、Filter 过滤器它是JavaEE 的规范。也就是接口
3、Filter 过滤器它的作用是:拦截请求,过滤响应。
package cn.lanqiao.book.filter;
import javax.servlet.*;
import java.io.IOException;
public class ManagerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("filter init。。。。。。。");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("do filter。。。。");
//放行 filterChain称为拦截器链
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("destroy。。。");
}
}
web.xml配置filter
<filter>
<filter-name>manager</filter-name>
<filter-class>cn.lanqiao.book.filter.ManagerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>manager</filter-name>
<!-- 表示拦截所有请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
Filter 的拦截路径
–精确匹配
<url-pattern>/target.jsp</url-pattern>
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp
–目录匹配
<url-pattern>/admin/*</url-pattern>
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*
–后缀名匹配
<url-pattern>*.html</url-pattern>
以上配置的路径,表示请求地址必须以.html 结尾才会拦截到
<url-pattern>*.do</url-pattern>
以上配置的路径,表示请求地址必须以.do 结尾才会拦截到
<url-pattern>*.action</url-pattern>
以上配置的路径,表示请求地址必须以.action 结尾才会拦截到
Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!
实现登陆检测
在拦截器中实现一个功能,当用户访问后台管理时,必须登陆之后才能够访问,否则不允许访问 跳转到登录页去登陆。
public class ManagerFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpSession session = req.getSession();
User user = (User)session.getAttribute("user");
System.out.println(user);
if(user==null){
resp.sendRedirect("/book/pages/user/login.jsp");
}
chain.doFilter(req,resp);
}
}
使用拦截器来实现中文乱码的统一处理
get的中文乱码的 处理
1 配置tomcat URIEcoding=UTF-8
2 在request中获取的String类型的参数 然后使用new String(name.getBytes(),“UTF-8”)
POST请求 的中文乱码:
req.setEncode(“UTF-8”);
public class CharacterFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterConfig config = this.getFilterConfig();
String encoding = config.getInitParameter("encoding");
request.setCharacterEncoding(encoding);
chain.doFilter(request,response);
}
}
注意字符集拦截器必须拦截所有请求
<filter>
<filter-name>encode</filter-name>
<filter-class>cn.lanqiao.book.filter.CharacterFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
使用注解开发Filter
@WebFilter(filterName="encoder",urlPatterns = {"/*"},initParams = {@WebInitParam(name = "encoding",value = "UTF-8")})
public class CharacterFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterConfig config = this.getFilterConfig();
String encoding = config.getInitParameter("encoding");
request.setCharacterEncoding(encoding);
chain.doFilter(request,response);
}
}
多个拦截器
当有个多个拦截器的时候 在web.xml的配置中 拦截器的执行顺序与filter-mapping的先后顺序有关
在注解形式中 是无法调整顺序的
ThreadLocal 的使用
多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。
ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题
ThreadLocal 的作用,它可以解决多线程的数据安全问题。
ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)
ThreadLocal 的特点:
1、ThreadLocal 可以为当前线程关联一个数据。(它可以像Map 一样存取数据,key 为当前线程)
2、每一个ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal 对象实例。
3、每个ThreadLocal 对象实例定义的时候,一般都是static 类型
4、ThreadLocal 中保存数据,在线程销毁后。会由JVM 虚拟自动释放。
public class ThreadLocalTest {
private static ThreadLocal local = new ThreadLocal();
public static void main(String[] args) {
new Thread(
new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
local.set("thread1");
System.out.println(local.get());
}
}
).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
local.set("thread2");
System.out.println(local.get());
}
}).start();
}
}
public class ThreadLocalTest {
public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
private static Random random = new Random();
public static class Task implements Runnable {
@Override
public void run() {
// 在Run 方法中,随机生成一个变量(线程要关联的数据),然后以当前线程名为key 保存到map 中
Integer i = random.nextInt(1000);
// 获取当前线程名
String name = Thread.currentThread().getName();
System.out.println("线程["+name+"]生成的随机数是:" + i);
// data.put(name,i);
threadLocal.set(i);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object o = threadLocal.get();
System.out.println("在线程["+name+"]快结束时取出关联的数据是:" + o);
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++){
new Thread(new Task()).start();
}
}
}
使用Filter 和ThreadLocal 组合管理事务
使用ThreadLocal 来确保所有dao 操作都在同一个Connection 连接对象中完成
package cn.lanqiao.book.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.util.JdbcUtils;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtils {
private static DruidDataSource dataSource;
private static ThreadLocal<Connection> connLocal =new ThreadLocal<>();
static {
try {
Properties properties = new Properties();
// 读取jdbc.properties 属性配置文件
InputStream inputStream =
JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
// 从流中加载数据
properties.load(inputStream);
// 创建数据库连接池
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接池中的连接
* @return 如果返回null,说明获取连接失败<br/>有值就是获取连接成功
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = dataSource.getConnection();
connLocal.set(conn);//将获取到的链接存储在ThreadLocal中
conn.setAutoCommit(false);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
public static void commitAndClose(){
Connection conn = connLocal.get();
if(conn!=null){
try {
conn.commit();//提交数据
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
if(conn !=null){
try {
conn.close();//关闭链接
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
connLocal.remove();//链接关闭之后 必须从ThreadLocal中异常链接对象
}
public static void rollBackAndClose(){
Connection conn = connLocal.get();
if(conn!=null){
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
connLocal.remove();
}
/**
* 关闭连接,放回数据库连接池
* @param conn
*/
public static void close(Connection conn){
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
baseDao
public int update(String sql, Object... args) {
Connection connection = JDBCUtils.getConnection();
try {
int i = queryRunner.update(connection, sql, args);
JDBCUtils.commitAndClose();
return i;
} catch (SQLException e) {
JDBCUtils.rollBackAndClose();
e.printStackTrace();
} finally {
JdbcUtils.close(connection);
}
return -1;
}
使用Filter 过滤器统一给所有的Service 方法都加上try-catch。来进行实现的管理。
@WebFilter("/*")
public class TransactionFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
try{
chain.doFilter(request,response);
}catch (Exception e){
JDBCUtils.rollBackAndClose();
e.printStackTrace();
}
}
}
一定要记得把BaseServlet 中的异常往外抛给Filter 过滤器
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("_method");
try {
Method method1= this.getClass().getDeclaredMethod(method,HttpServletRequest.class,HttpServletResponse.class);
method1.invoke(this,req,resp);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
//将异常转换为运行时异常 抛出给filter
throw new RuntimeException(e);
}
}
将所有异常都统一交给Tomcat,让Tomcat 展示友好的错误信息页面
<error-page>
<error-code>500</error-code>
<location>/pages/error/500.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/pages/error/404.jsp</location>
</error-page>
JSON 在java 中的使用
将后台得到的数据(集合 对象 数组 )转换为一个json字符串 传递给前台 来供前台做数据展示
此处我们借助于阿里的 fastjson来完成转换
1 导包
2 将一个list集合转换为json字符串
@Test
public void list2JsonTest(){
BookDao dao = new BookDaoImpl();
List<Book> bookList = dao.queryBooks();
String str = JSONObject.toJSONString(bookList);
System.out.println(str);
}
AJAX
- AJAX 即“Asynchronous Javascript And XML”(异步JavaScript 和XML),是指一种创建交互式网页应用的网页开发技术。
- Ajax 是一种浏览器通过js 异步发起请求,局部更新页面的技术。
- Ajax 请求的局部更新,浏览器地址栏不会发生变化,局部更新不会舍弃原来页面的内容
jQuery 中的AJAX 请求
$.ajax 方法
-
url 表示请求的地址
-
type 表示请求的类型GET 或POST 请求
-
data 表示发送给服务器的数据
-
-
格式有两种:
-
- 一:name=value&name=value
- 二:{key:value}
- 一:name=value&name=value
-
-
-
success 请求成功,响应的回调函数
-
dataType 响应的数据类型
-
-
常用的数据类型有:
-
- text 表示纯文本
- xml 表示xml 数据
- json 表示json 对象
- text 表示纯文本
-
-
$("#ajaxBtn").click(function(){
$.ajax({
url:"http://localhost:8080/16_json_ajax_i18n/ajaxServlet",
//发送给服务器的数据
data:{action:"jQueryAjax"},
type:"GET",
success:function (data) {
// alert("服务器返回的数据是:" + data);
// var jsonObj = JSON.parse(data);
$("#msg").html("编号:" + data.id + " , 姓名:" + data.name);
},
dataType : "json"//响应的数据类型
});
});
. g e t 方 法 和 .get 方法和 .get方法和.post 方法
- url 请求的url 地址
- data 发送的数据
- callback 成功的回调函数
- type 返回的数据类型
$("#getBtn").click(function(){
$.get("http://localhost:8080/16_json_ajax_i18n/ajaxServlet","action=jQueryGet",function (data) {
$("#msg").html(" get 编号:" + data.id + " , 姓名:" + data.name);
},"json");
});
// ajax--post 请求
$("#postBtn").click(function(){
$.post("http://localhost:8080/16_json_ajax_i18n/ajaxServlet","action=jQueryPost",function (data){
$("#msg").html(" post 编号:" + data.id + " , 姓名:" + data.name);
},"json");
});
$.getJSON 方法
- url 请求的url 地址
- data 发送给服务器的数据
- callback 成功的回调函数
// ajax--getJson 请求
$("#getJSONBtn").click(function(){
$.getJSON("http://localhost:8080/16_json_ajax_i18n/ajaxServlet","action=jQueryGetJSON",
function(data) {
$("#msg").html(" getJSON 编号:" + data.id + " , 姓名:" + data.name);
});
});
如何将表达数据提交给服务器
表单序列化serialize()
serialize()可以把表单中所有表单项的内容都获取到,并以name=value&name=value 的形式进行拼接。
// ajax 请求
$("#submit").click(function(){
// 把参数序列化
$.getJSON("http://localhost:8080/16_json_ajax_i18n/ajaxServlet","action=jQuerySerialize&" +
$("#form01").serialize(),function (data) {
$("#msg").html(" Serialize 编号:" + data.id + " , 姓名:" + data.name);
});
});
使用AJAX 验证用户名是否可用
在UserServlet中新增一个检查用户名是否存在的方法
//将处理的是一个ajax请求
public void checkUsername(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String username = req.getParameter("username");
User user = userService.checkUsername(username);
Map<String,String> result= new HashMap<>();
if(user!=null){
result.put("msg","true");
}else{
result.put("msg","false");
}
String resJson = JSONObject.toJSONString(result);
resp.getWriter().write(resJson);
}
前端处理
$("#username").blur(function (){
var username = this.value;
$.get(
"http://localhost:8080/book/user",
{"_method":"checkUsername","username":username},
function (data){
var msg = JSON.parse(data).msg;
alert(msg);
if(msg!="true"){
$("span.errorMsg").text("");
$("span.errorMsg").text("用户名可用");
}else{
$("span.errorMsg").text("");
$("span.errorMsg").text("用户名不可用");
}
})
})
});
加入购物车使用ajax
前端处理
$("#username").blur(function (){
var username = this.value;
$.get(
"http://localhost:8080/book/user",
{"_method":"checkUsername","username":username},
function (data){
var msg = JSON.parse(data).msg;
alert(msg);
if(msg!="true"){
$("span.errorMsg").text("");
$("span.errorMsg").text("用户名可用");
}else{
$("span.errorMsg").text("");
$("span.errorMsg").text("用户名不可用");
}
})
})
});
加入购物车使用ajax