第十五篇学习总结
一、购物车的实现
购物车在数据库中需要单独一张表,字段有id、userId、goodsId、num、createTime。所有用户的购物车数据都在这张表中,对购物车的增删改查也在这张表中。
1、购物车Dao层
购物车Dao层需要实现的操作有添加购物车、根据用户id和商品id查询记录、修改购物车商品的数量、删除购物车、查询某个用户所有的购车车记录
import com.qianfeng.shop.bean.Cart;
import com.qianfeng.utils.DBUtils;
import com.qianfeng.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
/**
* 购物车
*/
public class CartDao {
/**
* 添加一条购物车记录
* @param cart
* @return
*/
public int saveCart(Cart cart){
Connection cn = null;
PreparedStatement ps = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("insert into cart values(default ,?,?,?,?)");
ps.setInt(1, cart.getUserId());
ps.setInt(2, cart.getGoodsId());
ps.setInt(3, cart.getNum());
ps.setLong(4, cart.getCreateTime());
int num = ps.executeUpdate();
return num;
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps,cn);
}
return 0;
}
/**
* 根据用户id和商品id查询购物车记录的id值
* @param cart
* @return
*/
public int getCartByUserIdAndGoodsId(Cart cart){
Connection cn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("select id from cart where userId=? and goodsId = ?");
ps.setInt(1, cart.getUserId());
ps.setInt(2, cart.getGoodsId());
rs = ps.executeQuery();
if(rs.next()){
return rs.getInt(1);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs,ps,cn);
}
return 0;
}
/**
* 根据用户id和商品id修改购物车记录的数量
* @param cart
* @return
*/
public int editCartNumByUserIdAndId(Cart cart){
Connection cn = null;
PreparedStatement ps = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("update cart set num=num+? where userId=? and id=? ");
ps.setInt(1, cart.getNum());
ps.setInt(2, cart.getUserId());
ps.setInt(3, cart.getId());
int num = ps.executeUpdate();
return num;
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps,cn);
}
return 0;
}
/**
* 根据用户id和商品id删除购物车记录的数量
* @param cart
* @return
*/
public int deleteCartByUserIdAndId(Cart cart){
Connection cn = null;
PreparedStatement ps = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("delete from cart where userId=? and id=? ");
ps.setInt(1, cart.getUserId());
ps.setInt(2, cart.getId());
int num = ps.executeUpdate();
return num;
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps,cn);
}
return 0;
}
/**
* 根据用户id查询购物车中的所有记录
* @param cart
* @return
*/
public List<Cart> getCarts(Cart cart){
Connection cn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("select c.*,g.img,g.price ,g.name as goodsName ,t.name as typeName from cart c left join goods g on g.id = c.goodsId " +
" left join types t on t.id = g.typeId " +
"where c.userId=? order by c.createTime DESC");
ps.setInt(1, cart.getUserId());
rs = ps.executeQuery();
List<Cart> list = DBUtils.selectMore(Cart.class,rs);
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs,ps,cn);
}
return null;
}
}
2、前端页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="/resource/pages/header.jsp"%>
<html>
<head>
<title>我的购物车</title>
<style>
.goods{
border:1px black solid;
margin: 10px 0px;
}
.goods img{
height: 100px;
}
#sumPrice{
color: red;
font-size: 30px;
font-weight: 600;
}
</style>
</head>
<body>
<div>
<div>
<input id="all" type="checkbox"> 全选
</div>
<c:forEach items="${list}" var="item">
<div class="goods">
<input class="one" type="checkbox">
<img src="${item.img}">
<span>${item.goodsName}</span>
<span>${item.typeName}</span>
<span class="price">${item.price*item.num}</span>
<button onclick="miNum(${item.id})">-</button><span>${item.num}</span><button onclick="addNum(${item.id})">+</button>
<a href="/shop/deleteCart?id=${item.id}">删除</a>
</div>
</c:forEach>
<div>
<div id="sumPrice">0.00</div>
</div>
</div>
</body>
<script>
//使用js实现全选与反选,总价格是由js计算出来然后通过修改html来显示的
$("#all").click(function () {
var flg = $(this).prop("checked");
$(".one").each(function () {
$(this).prop("checked",flg);
})
sumPrice(); //每点击一个全选按钮都要重新计算
})
$(".one").each(function () {
$(this).click(function () {
var flg = $(this).prop("checked");
if(flg){
if($(".one:checked").length==$(".one").length){
$("#all").prop("checked",flg);
}
}else{
$("#all").prop("checked",flg);
}
sumPrice(); //每点击一次子选框也要重新计算
});
})
//计算出总价格
function sumPrice(){
var sum = 0.00;
$(".price").each(function(){
//先获取价格区域的父级,然后再找子级的多选框
var one = $(this).parent().children(".one");
if(one.prop("checked")){//如果多显框时勾上的,就计算总价格
var price = $(this).text();
sum+=parseInt(price);
}
})
$("#sumPrice").text(sum);
}
//减少
function miNum(id){
location.href="/shop/editCartNum?num=-1&id="+id;
}
//增加
function addNum(id){
location.href="/shop/editCartNum?num=1&id="+id;
}
</script>
</html>
二、订单实现
订单在数据库中需要单独一张表来存储,一个订单中有多件商品,一件商品可以在多个订单中,所以这两者是多对多的关系。需要第三张表订单-商品映射表来存储订单与商品的关系,还要存储商品的数量。对于订单的操作同样有增删改查,但是此时比较麻烦,因为要同时对三张表进行操作。提单提交完成之后需要清空购物车。
import com.qianfeng.shop.bean.Cart;
import com.qianfeng.shop.bean.Goods;
import com.qianfeng.shop.bean.OrderBean;
import com.qianfeng.utils.DBUtils;
import com.qianfeng.utils.JDBCUtils;
import javax.management.Query;
import javax.xml.transform.Result;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
public class OrderDao {
/**
* 根据用户id和订单id删除订单信息
* @param orderBean
* @return
*/
public int deleteOrderByOrderIdAndUserId(OrderBean orderBean){
Connection cn = null;
PreparedStatement ps = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("delete from orders where id= ? and userId=?");
ps.setInt(1, orderBean.getId());
ps.setInt(2, orderBean.getUserId());
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps,cn);
}
return 0;
}
/**
* 根据订单id删除映射商品信息
* @param orderId
* @return
*/
public int deleteMappingByOrderIdAndUserId(int orderId){
Connection cn = null;
PreparedStatement ps = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("delete from order_goods_mapping where orderId=?");
ps.setInt(1, orderId);
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps,cn);
}
return 0;
}
/**
* 根据订单id查询映射商品的详细信息
* @param orderId
* @return
*/
public List<Goods> getGoodsByOrderId(int orderId){
Connection cn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("select ogm.num,ogm.price ,g.name,t.name as typeName,g.img " +
"from order_goods_mapping ogm " +
"left join goods g on g.id = ogm.goodsId " +
"left join types t on t.id = g.typeId " +
"where ogm.orderId=?");
ps.setInt(1,orderId);
rs = ps.executeQuery();
List<Goods> list = DBUtils.selectMore(Goods.class,rs);
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps,cn);
}
return null;
}
/**
* 根据用户id查询当前用户的所有订单
* @param userId
* @return
*/
public List<OrderBean> getOrdersByUserId(int userId) {
Connection cn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("select o.id,o.sumPrice,o.status,o.createTime,ad.content" +
" from orders o " +
"left join address ad on ad.id = o.addressId " +
"where o.userId=?");
ps.setInt(1,userId);
rs = ps.executeQuery();
List<OrderBean> list = DBUtils.selectMore(OrderBean.class,rs);
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps,cn);
}
return null;
}
/**
* 保存订单商品映射信息
* @param carts
* @param orderId
* @return
*/
public int[] saveMapping(List<Cart> carts,int orderId){
Connection cn = null;
PreparedStatement ps = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("insert into order_goods_mapping values(?,?,?,?)");
for (Cart cart : carts) {
ps.setInt(1,orderId);
ps.setInt(2,cart.getGoodsId());
ps.setInt(3,cart.getNum());
ps.setDouble(4,cart.getPrice());
ps.addBatch();
}
int[] num = ps.executeBatch();
return num;
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps,cn);
}
return null;
}
/**
* 保存订单,并且返回订单的id
* @param orderBean
* @return
*/
public int saveOrder(OrderBean orderBean){
Connection cn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
cn = JDBCUtils.getConnection();
ps = cn.prepareStatement("insert into orders values(default ,?,?,?,?,?)",PreparedStatement.RETURN_GENERATED_KEYS);
ps.setInt(1,orderBean.getUserId());
ps.setInt(2, orderBean.getAddressId());
ps.setDouble(3, orderBean.getSumPrice());
ps.setInt(4,orderBean.getStatus());
ps.setLong(5, orderBean.getCreateTime());
int num = ps.executeUpdate();
rs = ps.getGeneratedKeys();
if(rs.next()){
return rs.getInt(1);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(ps,cn);
}
return 0;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="/resource/pages/header.jsp"%>
<html>
<head>
<title>我的订单</title>
<style>
.order{
border: 1px solid black;
}
.goods img{
height: 100px;
}
</style>
</head>
<body>
<c:forEach items="${orders}" var="item">
<div class="order">
<div>
<span>编码 : ${item.id}</span>
<span>${item.time}</span>
<span>${item.sumPrice}</span>
<c:if test="${item.status==1}">
<span>已支付</span>
</c:if>
<c:if test="${item.status==0}">
<span>未支付</span>
</c:if>
<a href="/shop/deleteOrder?id=${item.id}">删除</a>
<br>
<span>${item.content}</span>
</div>
<div>
<c:forEach items="${item.goods}" var="goods">
<div class="goods">
<img src="${goods.img}">
<span>${goods.name}</span>
<span>${goods.typeName}</span>
<span>${goods.price} * ${goods.num}</span>
</div>
</c:forEach>
</div>
</div>
</c:forEach>
</body>
</html>
三、枚举
枚举可以理解为有限制的多例,在当前类中定义多个实例供别人使用。我们之前学过单例模式,在程序运行期间只允许一个实例存在,如果要允许多个实例存在的话就需要使用多例模式。
1、枚举类的注意事项
- 定义枚举类都要使用关键字enum
- 所有枚举类都是Enum的子类
- 枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,建议不要省略
- 枚举类可以有构造器,但必须是private,默认就是private
- 灭聚类也可以有抽象方法,但是枚举项必须重写该方法
- switch语句可以使用枚举
//枚举: 多例的简化形式
public enum Message {
//将零散的公共静态常量进行了分装, 将他们约束成一组
REQUEST_SUCCESS (1,"请求成功"){
public void method() {
}
},
REQUEST_FAIL(-1,"请求失败"){
public void method() {
}
},
LGOIN_SUCCESS(2,"登陆成功"){
public void method() {
}
},
LGOIN_FAIL(-2,"登陆失败"){
public void method() {
}
};
private int code ;
private String message;
Message(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
//抽象方法必须在枚举项中重写
public abstract void method();
}
2、常用方法
- int ordinal() 获取枚举项的序号
- int compareTo(E o) 比较两个枚举项
- String name() 获取枚举枚举项的名称
- String toString() 获取枚举项的字符串表现形式
- <T> T valueOf(Class<T> type,String name) 使用字节码和名称获取枚举项
- values() 它遍历枚举类的所有枚举值非常方便
四、类加载
1、定义
如果程序要使用某个类,系统会通过加载、连接、初始化三步来实现对这个类进行初始化
- 加载就是将.class文件读入内存,并为之创建一个Class对象
- 连接:验证、准备、解析
- 初始化:执行类的静态代码块,为类的静态成员初始化
2、加载的时机
只有当类真正被使用时,类才会被加载
- 创建类的实例
- 访问类的静态变量,或者为静态变量赋值
- 调用类的静态方法
- 使用反射方式强制创建某个类或接口对象的java.lang.Class对象
- 加载某个类的子类
- 直接使用java.exe命令来运行某个主类
3、类加载器
类加载器负责将.class文件加载到内存中,并为之生成对应的Class对象
3.1 类加载器的作用
- BootstrapClassLoader 根类加载器
- 也被称为引导类加载器,负责Java核心类的加载
- 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
- ExtensionClassLoader 扩展类加载器
- 负责JRE的扩展目录中jar包的加载。
- 在JDK中JRE的lib目录下ext目录
- SysetmClassLoader 系统类加载器
- 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
- AppClassLoader 加载其他类
- 负载一些非核心类和程序猿自己写的类
3.2 双亲委派模型
类加载器总是先让父类加载器去加载,如果父类加载器加载不了,一直到跟类加载器,如果父类加载器加载不了,则会让子类加载器加载。这样做的好处有两个:安全性,避免用户自己编写的类动态替换Java的一些核心类;同时也避免了类的重复加载。
五、反射
Java的反射是通过一个类的字节码获取这个类的所有信息,还可以进行操作
1、获取字节码的三种方式
- 对象.getClass()
- 类名.class
- Class.forName("类名")
2、反射获取构造函数
Class类的newInstance()方法是使用该类无参的构造函数创建对象,如果要使用有参构造,可以调用Class类的getConstructor(String.class,int.class)方法获取一个指定的构造函数然后再调用Constructor类的newInstance("张三",20)方法创建对象
3、反射获取成员变量
- Class.getField(String)方法可以获取类中的指定字段(可见的)
- 如果是私有的可以用getDeclaredField("name")方法获取
- 通过get(obj) 和set(obj, "李四")方法可以获取和设置指定对象上该字段的值, obj指的是这个类的对象
- 如果是私有的需要先调用setAccessible(true)设置访问权限放开
4、反射获取成员方法
- Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以获取类中的指定方法
- 调用invoke(Object, Object...)可以调用对象的这个方法
六、动态代理
动态代理让我们在不破坏代码的情况下增加新的功能,常用于Java项目检测代码运行效率。
1、Java中动态代理
利用对象的类的字节码接口,写出一个新的类到本地区,通过编译器直接编译成.class文件,再通过类加载器加载进来。但是这样做有一个缺点,就是代理的对象必须实现接口。
- 在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象
- public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
- 最终会调用InvocationHandler的方法InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
2、cglib
非Java原生的动态代理,效率更高,限制更小,并且可以代理没有接口的类,它的原理是生成一个原始类的子类对象
- 要想使用cglib必须要导入四个包:ant、ant-launcher、asm、cglib。
public static void main(String[] args) {
//创建运行器
MethodInterceptor mi = new MethodInterceptor() {
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("运行前");
arg3.invokeSuper(arg0, arg2);
System.out.println("运行后");
return null;
}
};
//获取代理类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(Demo.class);
//运行任务
enhancer.setCallback(mi);
//创建代理对象
Demo d = (Demo)enhancer.create();
d.method();
}
网友评论