2021SC@SDUSC
这一篇博客承接博客(2)的内容,来继续解读module-product-model 实体层的代码
Module-product-model 模型层
BaseProduct类图分析
在上一篇博客中提到了BaseProduct的继承树,并且对BaseProduct的代码进行了分析,代码比较简单,主要是get和set方法
Serializable接口
从继承树上面来看,父类对象Model是来自com.jfinal.plugin.activerecord的一个类,是属于jfinal框架的部分,它实现了Serializable接口,实现了序列化。
在这里复习一下Serializable接口的作用:一般情况下,我们在定义实体类时会继承Serializable接口。并且会定义serialversionUID(如果我们没有自己声明一个serialVersionUID变量,接口会默认生成一个serialVersionUID)
Serializable接口就是Java提供用来进行高效率的异地共享实例对象的机制,实现这个接口即可。接口里面什么内容都没有,我们可以将它理解成一个标识接口。通知jvm,我不对这个类做序列化了,jvm会自动对其序列化。
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
把对象转换为字节序列的过程称为对象的序列化
把字节序列恢复为对象的过程称为对象的反序列化
serialVersionUID是用来辅助对象的序列化与反序列化的,原则上序列化后的数据当中的serialVersionUID与当前类当中的serialVersionUID一致,那么该对象才能被反序列化成功。这个serialVersionUID的详细的工作机制是:在序列化的时候系统将serialVersionUID写入到序列化的文件中去,当反序列化的时候系统会先去检测文件中的serialVersionUID是否跟当前的文件的serialVersionUID是否一致,如果一致则反序列化成功,否则就说明当前类跟序列化后的类发生了变化,比如是成员变量的数量或者是类型发生了变化,那么在反序列化时就会发生crash,并且会报出错误
@SuppressWarnings({"rawtypes", "unchecked"})
public abstract class Model<M extends Model> implements IRow<M>, Serializable {
private static final long serialVersionUID = -990334519496260591L;
public static final int FILTER_BY_SAVE = 0;
public static final int FILTER_BY_UPDATE = 1;
private String configName;
/**
* Flag of column has been modified. update need this flag
*/
private Set<String> modifyFlag;
/**
* Attributes of this model
*/
private Map<String, Object> attrs = createAttrsMap()
// 剩下的内容为了节省篇幅不再copy过来
}
在代码段中可以看出,给出了serialVersionUID = -990334519496260591L
这里的serializableUID的设置是不是有什么要求,在后续应用的时候可以继续研究
SuppressWarning注解的使用
简介:java.lang.SuppressWarnings是J2SE 5.0中标准的Annotation之一。可以标注在类、字段、方法、参数、构造方法,以及局部变量上。
作用:告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。
value - 将由编译器在注释的元素中取消显示的警告集。允许使用重复的名称。忽略第二个和后面出现的名称。出现未被识别的警告名不是 错误:编译器必须忽略无法识别的所有警告名。但如果某个注释包含未被识别的警告名,那么编译器可以随意发出一个警告。
各编译器供应商应该将它们所支持的警告名连同注释类型一起记录。鼓励各供应商之间相互合作,确保在多个编译器中使用相同的名称。
示例:
@SuppressWarnings(“unchecked”) : 告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。
@SuppressWarnings(“serial”): 如果编译器出现这样的警告信息:The serializable class WmailCalendar does not declare a static final serialVersionUID field of type long,
@SuppressWarnings(“deprecation”):如果使用了使用@Deprecated注释的方法,编译器将出现警告信息。
@SuppressWarnings(value={“unchecked”, “deprecation”}):告诉编译器同时忽略unchecked和deprecation的警告信息,使用这个注释将警告信息去掉
@SuppressWarnings(value={“unchecked”, “deprecation”}) :
等同 @SuppressWarnings(“unchecked”, “deprecation”)
IRow
IRow也是com.jfinal.plugin.activerecord包下面的一个类,它提供统一的方法来处理Model和Record
以下是方法
/**
* Get column of mysql type: date, year
*/
public java.util.Date getDate(String column);
public LocalDateTime getLocalDateTime(String column);
/**
* Get column of mysql type: time
*/
public java.sql.Time getTime(String column);
/**
* Get column of mysql type: timestamp, datetime
*/
public java.sql.Timestamp getTimestamp(String column);
这里的IRow提供了获取mysql数据库中各种数据类型的方法,这里的作用大概是为了获取数据库方法使用的。
com.jfinal.plugin.activerecord.Model抽象类
在Model类中,实现了IRow接口之后,从方法中可以看出Model抽象类提供了比较完整的增删改查的功能代码
io.jboot.db.model.JbootModel类
JbootModel类继承了Model抽象类,实现了Model的一些方法
这里的方法主要也是一些关于数据库操作的方法,findListByIds、delete之类的方法都是CURD之类的操作。
io.jpress.base.BaseOptionsModel
BaseOptionsModel类是继承了JbootModel,主要是关于options的操作
这里解释一下options
在做项目时,很多时候发送一个post请求,是先发送一个option请求,然后再发送post请求,一直这么用之前也没有仔细思考,今天有时间,好好了解一下为什么会多一次请求。
疑问1:什么是options请求
OPTIONS请求方法的主要用途有两个:
1、获取服务器支持的HTTP请求方法;
2、用来检查服务器的性能。例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。
这是浏览器给我们加上的,后端并没有做任何操作。
疑问2:为什么会用到options请求
这得从浏览器同源策略和跨域说起,具体可阅读也谈谈同源策略和跨域问题和浏览器同源政策及其规避方法,这里不在赘述。
解决跨域问题的方法有很多种,CORS是比较好的解决方案,项目也是用的这种模式,这个模式会有”预检”的请求,也就是正常请求之前的options请求。
BaseProduct代码
BaseProduct的代码结构比较简单,以get和set方法为主,对应数据库中product表的各个属性
截取其中一段代码来看:
public abstract class BaseProduct<M extends BaseProduct<M>> extends BaseOptionsModel<M> implements IBean {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
public void setId(java.lang.Long id) {
set("id", id);
}
/**
* 主键ID
*/
public java.lang.Long getId() {
return getLong("id");
}
/**
* slug
*/
public void setSlug(java.lang.String slug) {
set("slug", slug);
}
/**
* slug
*/
public java.lang.String getSlug() {
return getStr("slug");
}
}
Product代码分析
@Table(tableName = "product", primaryKey = "id")
public class Product extends BaseProduct<Product>
注解@Table:io.jboot.db.annotation.Table
package io.jboot.db.annotation;
import java.lang.annotation.*;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Table {
String tableName();
String primaryKey() default "";
}
注解@Table的两个属性,一个是数据表名,一个是表的主键
这里保留一个疑问,jpress的系统是如何与数据库进行沟通的?在之后的核心代码分析可以慢慢的来解决。
Product类有STATUS_NORMAL私有变量:
private static final long serialVersionUID = 1L;
public static final int STATUS_NORMAL = 1;
public static final int STATUS_DRAFT = 2;
public static final int STATUS_TRASH = 3;
相对应的有三个判断状态的IS方法
public boolean isNormal() {
return getStatus() != null && getStatus() == STATUS_NORMAL;
}
public boolean isDraft() {
return getStatus() != null && getStatus() == STATUS_DRAFT;
}
public boolean isTrash() {
return getStatus() != null && getStatus() == STATUS_TRASH;
}
下面的三个方法:getUrl(),getUrlWithPageNumber(),getHtmlView()
分别来获取url地址,和带有页面数目的url地址,以及html的视图
public String getUrl() {
return UrlUtils.getUrl("/product/", StrUtil.isNotBlank(getSlug()) ? getSlug() : getId());
}
public String getUrlWithPageNumber(int pageNumber) {
if (pageNumber <= 1) {
return getUrl();
}
return UrlUtils.getUrl("/product/", StrUtil.isNotBlank(getSlug()) ? getSlug() : getId(), "-", pageNumber);
}
public String getHtmlView() {
return StrUtil.isBlank(getStyle()) ? "product.html" : "product_" + getStyle().trim() + ".html";
}
下面的代码的方法功能是:
getText()获取文章的文本,getImages()获取文章所有图片或者前几张图片
public String getText() {
return JsoupUtils.getText(getContent());
}
/**
* 获取文章的所有图片
*
* @return
*/
public List<String> getImages() {
return JsoupUtils.getImageSrcs(getContent());
}
/**
* 获取前面几张图片
*
* @param count
* @return
*/
public List<String> getImages(int count) {
List<String> list = getImages();
if (list == null || list.size() <= count) {
return list;
}
List<String> newList = new ArrayList<>();
for (int i = 0; 0 < count; i++) {
newList.add(list.get(i));
}
return newList;
}
下面代码主要提供了针对image、video、audio的has方法以及getFirst方法
以及能否进行评论的状态判断
public boolean hasImage() {
return getFirstImage() != null;
}
public boolean hasVideo() {
return getFirstVideo() != null;
}
public boolean hasAudio() {
return getFirstAudio() != null;
}
public String getFirstImage() {
return JsoupUtils.getFirstImageSrc(getContent());
}
public String getFirstVideo() {
return JsoupUtils.getFirstVideoSrc(getContent());
}
public String getFirstAudio() {
return JsoupUtils.getFirstAudioSrc(getContent());
}
public String getShowImage() {
String thumbnail = getThumbnail();
return StrUtil.isNotBlank(thumbnail) ? thumbnail : getFirstImage();
}
public boolean isCommentEnable() {
Boolean cs = getCommentStatus();
return cs != null && cs == true;
}
加入用户的购物车:
public UserCart toUserCartItem(Long userId, Long distUserId, String spec) {
UserCart userCart = new UserCart();
userCart.setUserId(userId);
userCart.setSellerId(this.getUserId());
userCart.setProductId(getId());
userCart.setProductType("product");
userCart.setProductTypeText("产品");
userCart.setProductPrice(this.getPrice());
userCart.setProductCount(1);
userCart.setProductTitle(getTitle());
userCart.setProductSummary(CommonsUtils.maxLength(getText(), 200));
userCart.setSelected(false);
userCart.setProductLink(getUrl());
userCart.setWithVirtual(false);//非虚拟产品
userCart.setWithRefund(true);//可以退货
userCart.setCommentPath(getUrl());
userCart.setCreated(new Date());
userCart.setProductSpec(spec);
Boolean disEnable = getDistEnable();
if (disEnable != null && disEnable) {
userCart.setDistUserId(distUserId);
}
String showImage = getShowImage();
if (StrUtil.isNotBlank(showImage)) {
userCart.setProductThumbnail(showImage);
}
return userCart;
}
加入用户的收藏:
public UserFavorite toFavorite(Long userId) {
UserFavorite favorite = new UserFavorite();
favorite.setUserId(userId);
favorite.setType(UserFavorite.FAV_TYPE_PRODUCT);
favorite.setTypeText(UserFavorite.FAV_TYPE_PRODUCT_TEXT);
favorite.setTypeId(String.valueOf(getId()));
favorite.setTitle(getTitle());
favorite.setSummary(getSummary());
favorite.setThumbnail(getShowImage());
favorite.setLink(getUrl());
favorite.setCreated(new Date());
return favorite;
}
高光Highlight的设置:
public String getHighlightContent() {
String content = getStr("highlightContent");
return StrUtil.isNotBlank(content) ? content : CommonsUtils.maxLength(getText(),100,"...");
}
public void setHighlightContent(String highlightContent) {
put("highlightContent", highlightContent);
}
public String getHighlightTitle() {
String title = getStr("highlightTitle");
return StrUtil.isNotBlank(title) ? title : getTitle();
}
public void setHighlightTitle(String highlightTitle) {
put("highlightTitle", highlightTitle);
}