本期主要是文章保存功能,涉及到草稿、文章發佈、歷史這三個要點。javascript
項目github地址:github.com/pc859107393…html
實時項目同步的地址是國內的碼雲:git.oschina.net/859107393/m…java
個人簡書首頁是:www.jianshu.com/users/86b79…git
上一期是:[手把手教程][第二季]java 後端博客系統文章系統——No5github
實用技巧sql
- 經過面向對象建模後,咱們能夠逆向工程生成對應的bean甚至一些其餘的代碼
- mybatis可使用動態sql語句執行復雜操做
- 使用pojo包裝類型進行數據庫操做
- 使用高級映射能夠實現複雜的業務場景
- 延遲加載和緩存開發
- mybatis逆向工程工具
文章提交數據庫
上期咱們經過對數據庫的分析,仔仔細細的探索了一遍WordPress程序運行時候文章提交和草稿提交的區別和共同點,簡略的歸納下以下:json
更爲詳細的文章,建議去個人上一篇文章查看。後端
既然咱們已經大概明白了怎麼去實現,那麼如今咱們須要的就是進行實驗。設計模式
首先老規矩,先從咱們的dao層實現開始,咱們爭取把各個功能模塊解耦,那麼咱們就不能過於在dao層限制,因此咱們須要在dao層實現數據(文章、草稿、歷史記錄)插入。因此咱們先生成dao接口以下:
@Repository("postDao")
public interface PostDao extends Dao<PostCustom> {
/** * 根據擴展類來查詢文章信息,文章爲postBean * @param postCustom post的擴展類,使用註解來講明對象名稱 * @return 受影響的行數 */
@Override
int add(@Param(value = "postCustom") PostCustom postCustom);
//////////////////////////////////////分割線///////////////////////////
@Override
int del(PostCustom postBean);
@Override
int update(PostCustom postBean);
@Override
PostCustom findOneById(Serializable postId);
@Override
List<PostCustom> findAll();
List<PostCustom> findAllNew();
/** * 分頁查詢 * * @param offset 起始位置 * @param limit 每頁數量 * @return */
List<PostCustom> findAllPublish(@Param("offset") int offset, @Param("limit") int limit);
int getAllCount();
List<PostCustom> getAllPostDateCount();
}
//在上面分割線上面的,就是咱們此次提交文章的接口,下面咱們接着完成mapper
<insert id="add" parameterType="PostCustom">
<!-- selectKey是在insert語句執行的時候能夠執行的語句。 keyProperty是指定須要記錄的key order是選擇在insert執行以前仍是以後執行selectKey中的語句 resultType是返回的數據類型 SELECT LAST_INSERT_ID();是返回最近執行insert後增長的內容的自增主鍵。注意,這個語句僅僅可用於自增主鍵。 --> <selectKey keyProperty="postCustom.id" order="AFTER" resultType="java.lang.String"> SELECT LAST_INSERT_ID(); </selectKey> <choose> <when test="postCustom.id == null"> #文章id爲空說明是剛開始插入新的文章 INSERT INTO `wp_posts` (`post_author`, `post_date`, `post_date_gmt`, `post_content`, `post_title`, `post_excerpt`, `post_status`, `comment_status`, `comment_count`, `ping_status`, `post_password`, `post_name`, `to_ping`, `pinged`, `post_modified`, `post_modified_gmt`, `post_content_filtered`, `post_parent`, `guid`, `menu_order`, `post_type`, `post_mime_type`) VALUES (#{postCustom.postAuthor},#{postCustom.postDate},#{postCustom.postDateGmt},#{postCustom.postContent}, #{postCustom.postTitle}, #{postCustom.postExcerpt}, #{postCustom.postStatus},#{postCustom.commentStatus},#{postCustom.commentCount},#{postCustom.pingStatus},#{postCustom.postPassword},#{postCustom.postName}, #{postCustom.toPing},#{postCustom.pinged},#{postCustom.postModified},#{postCustom.postModifiedGmt},#{postCustom.postContentFiltered}, #{postCustom.postParent},#{postCustom.guid},#{postCustom.menuOrder},#{postCustom.postType},#{postCustom.postMimeType}) </when> <when test="postCustom.id != null"> #id不爲空說明如今是更新某個文章 # UPDATE wp_posts # SET `post_author`=#{postAuthor},`post_date`=#{postDate} # WHERE ID=#{id} </when> </choose> </insert>複製代碼
上面的語句中咱們能夠看到在mapper的 insert語句塊中咱們插入了selectKey語句塊,主要是用來返回咱們插入的條目的自增id。爲何這裏咱們非要獲取到插入的id呢?主要是文章生成後的日誌記錄都是須要文章id才能生成對應的日誌記錄。
同時在inset語句塊中,咱們使用了choose+when語句塊生成了動態sql,這樣就能動態的選擇程序執行。
同時在上面的代碼中咱們能夠看到上面的when和下面的when語句塊中關於bean的屬性獲取使用方式不同!
重點:擴展類型,要使用父類的屬性時,必須是對象名.屬性名的形式,對象名從dao的接口設置。(不然直接使用屬性的時候,會異常提示空指針,且提示對象的屬性中沒有該屬性)
小技巧:
PostDao的單元測試
上面咱們也是提到過單元測試,那麼咱們如今看看這個dao的單元測試應該如何完成呢?
首先給你們看看咱們文章的bean:
public class PostBean implements Serializable {
/** * 版本號 */
private static final long serialVersionUID = 4477235816922585138L;
private String id;
private String postAuthor;
private Date postDate;
private Date postDateGmt;
private String postContent;
private String postTitle;
private String postExcerpt;
private String postStatus;
private String commentStatus;
private String pingStatus;
private String postPassword;
private String postName;
private String toPing;
private String pinged;
private Date postModified;
private Date postModifiedGmt;
private String postContentFiltered;
private String postParent = "0";
private String guid;
private Integer menuOrder;
private String postType;
private String postMimeType;
private Long commentCount = 0L;
//省略get、set
@Override
public String toString() {
return "PostBean{" +
"id='" + id + '\'' +
", postAuthor='" + postAuthor + '\'' +
", postDate=" + postDate +
", postDateGmt=" + postDateGmt +
", postContent='" + postContent + '\'' +
", postTitle='" + postTitle + '\'' +
", postExcerpt='" + postExcerpt + '\'' +
", postStatus='" + postStatus + '\'' +
", commentStatus='" + commentStatus + '\'' +
", pingStatus='" + pingStatus + '\'' +
", postPassword='" + postPassword + '\'' +
", postName='" + postName + '\'' +
", toPing='" + toPing + '\'' +
", pinged='" + pinged + '\'' +
", postModified=" + postModified +
", postModifiedGmt=" + postModifiedGmt +
", postContentFiltered='" + postContentFiltered + '\'' +
", postParent='" + postParent + '\'' +
", guid='" + guid + '\'' +
", menuOrder=" + menuOrder +
", postType='" + postType + '\'' +
", postMimeType='" + postMimeType + '\'' +
", commentCount=" + commentCount +
'}';
}
}複製代碼
哈哈哈哈哈,是否是特別的多呢?又沒有一種蛋疼菊緊的趕腳?
說實話,一看到這裏的時候,我也很惆悵。靈機一動,感受能夠祭出這個系列教程咱們本身手寫的第一個設計模式了!開啓咱們的javaBean的建造者模式,go!
import cn.acheng1314.utils.StringUtils;
import com.sun.istack.internal.NotNull;
import java.util.Date;
/** * Description:文章上傳的擴展 * * @author acheng * @date */
public class PostCustom extends PostBean {
private Boolean isPublish;
public PostCustom() {
}
public PostCustom(String id, String postAuthor, Date postDate, Date postDateGmt, String postContent, String postTitle, String postExcerpt, String postStatus, String commentStatus, String pingStatus, String postPassword, String postName, String toPing, String pinged, Date postModified, Date postModifiedGmt, String postContentFiltered, String postParent, String guid, Integer menuOrder, String postType, String postMimeType, Long commentCount) {
setId(id);
setPostAuthor(postAuthor);
setPostDate(postDate);
setPostDateGmt(postDateGmt);
setPostContent(postContent);
setPostContentFiltered(postContentFiltered);
setPostTitle(postTitle);
setPostExcerpt(postExcerpt);
setPostStatus(postStatus);
setCommentStatus(commentStatus);
setPingStatus(pingStatus);
setPostPassword(postPassword);
setPostName(postName);
setToPing(toPing);
setPinged(pinged);
setPostModified(postModified);
setPostModifiedGmt(postModifiedGmt);
setPostParent(postParent);
setGuid(guid);
setMenuOrder(menuOrder);
setPostType(postType);
setPostMimeType(postMimeType);
setCommentCount(commentCount);
}
public Boolean getPublish() {
return isPublish;
}
public void setPublish(Boolean publish) {
isPublish = publish;
}
/** * 建造者,參數過於多的時候考慮使用建造者完成。 * 靜態內部類不使用的時候是不會被建立的。 */
public static class Builder {
private String id;
private String postAuthor;
private Date postDate;
private Date postDateGmt;
private String postContent;
private String postTitle;
private String postExcerpt;
private String postStatus;
private String commentStatus;
private String pingStatus;
private String postPassword;
private String postName;
private String toPing;
private String pinged;
private Date postModified;
private Date postModifiedGmt;
private String postContentFiltered;
private String postParent;
private String guid;
private Integer menuOrder;
private String postType;
private String postMimeType;
private Long commentCount;
public Builder id(String id) {
this.id = id;
return this;
}
public Builder postAuthor(@NotNull String postAuthor) {
this.postAuthor = postAuthor;
return this;
}
public Builder postDate(Date postDate) {
this.postDate = postDate;
return this;
}
public Builder postDateGmt(Date postDateGmt) {
this.postDateGmt = postDateGmt;
return this;
}
public Builder postContent(String postContent) {
this.postContent = StringUtils.isEmpty(postContent) ? "" : postContent;
return this;
}
public Builder postTitle(String postTitle) {
this.postTitle = StringUtils.isEmpty(postTitle) ? "" : postTitle;
return this;
}
public Builder postExcerpt(String postExcerpt) {
this.postExcerpt = postExcerpt;
return this;
}
public Builder postStatus(String postStatus) {
this.postStatus = postStatus;
return this;
}
public Builder commentStatus(String commentStatus) {
this.commentStatus = commentStatus;
return this;
}
public Builder pingStatus(String pingStatus) {
this.pingStatus = pingStatus;
return this;
}
public Builder postPassword(String postPassword) {
this.postPassword = postPassword;
return this;
}
public Builder postName(String postName) {
this.postName = postName;
return this;
}
public Builder toPing(String toPing) {
this.toPing = toPing;
return this;
}
public Builder pinged(String pinged) {
this.pinged = pinged;
return this;
}
public Builder postModified(Date postModified) {
this.postModified = postModified;
return this;
}
public Builder postModifiedGmt(Date postModifiedGmt) {
this.postModifiedGmt = postModifiedGmt;
return this;
}
public Builder postContentFiltered(String postContentFiltered) {
this.postContentFiltered = postContentFiltered;
return this;
}
public Builder postParent(String postParent) {
this.postParent = postParent;
return this;
}
public Builder guid(String guid) {
this.guid = guid;
return this;
}
public Builder menuOrder(Integer menuOrder) {
this.menuOrder = menuOrder;
return this;
}
public Builder postType(String postType) {
this.postType = postType;
return this;
}
public Builder postMimeType(String postMimeType) {
this.postMimeType = postMimeType;
return this;
}
public Builder commentCount(Long commentCount) {
this.commentCount = commentCount;
return this;
}
public PostCustom build() {
return new PostCustom(id,
postAuthor,
postDate,
postDateGmt,
postContent,
postTitle,
postExcerpt,
postStatus,
commentStatus,
pingStatus,
postPassword,
postName,
toPing,
pinged,
postModified,
postModifiedGmt,
postContentFiltered,
postParent,
guid,
menuOrder,
postType,
postMimeType,
commentCount);
}
}
}複製代碼
那麼咱們ben生成好了後,咱們就須要開始寫咱們的單元測試,以下:
@Test
public void testInsertPost() {
Date atNow = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
formatter.format(atNow);
// PostCustom postCustom = new PostCustom();
// postCustom.setId(null);
// postCustom.setPublish(true);
// postCustom.setPostAuthor("001");
// postCustom.setPostDate(atNow);
// postCustom.setPostDateGmt(atNow);
// postCustom.setPostContent("1111111");
// postCustom.setPostTitle("22222");
// postCustom.setPostExcerpt("11111289489589458");
// postCustom.setPostStatus("11111289489589458");
// postCustom.setCommentStatus("11111289489589458");
// postCustom.setPingStatus("11111289489589458");
// postCustom.setPostPassword("11111289489589458");
// postCustom.setPostName("11111289489589458");
// postCustom.setToPing("11111289489589458");
// postCustom.setPinged("11111289489589458");
// postCustom.setPostModified(atNow);
// postCustom.setPostModifiedGmt(atNow);
// postCustom.setPostContentFiltered(formatter.format(atNow));
// postCustom.setGuid(formatter.format(atNow));
// postCustom.setMenuOrder(11100111);
// postCustom.setCommentStatus();
PostCustom postCustom = PostInitUtils.setPostinfo("0", atNow, "我是一個兵999", "我是一個兵2111", "文章內容````嘛", "www.cc1tv.com");
postDao.add(postCustom);
System.out.print("id=" + postCustom.getId());
postCustom = PostInitUtils.insertPostLog(postCustom.getId(), postCustom.getPostAuthor(), postCustom.getPostTitle(), postCustom.getGuid(), postCustom.getPostContent());
postDao.add(postCustom);
}複製代碼
你們能夠看到上面咱們的PostCustom仍是從咱們本身手動封裝的PostInitUtils中獲取的,同時咱們的PostCustom仍是咱們的PostBean的子類。因此這裏就用到了傳說中的pojo包裝類型相關的一些知識(咱們這裏並無包裝,不過已經用到了擴展類),讓咱們接着看看PostInitUtils的代碼:
import cn.acheng1314.domain.PostCustom;
import java.text.SimpleDateFormat;
import java.util.Date;
/** * Description:文章上傳 * * @author acheng * @date 2017/2/27 */
public class PostInitUtils {
public final static String PUBLISH_POST_STATUS = "publish";
public final static String OPEN_COMMENT_STATUS = "open";
public final static String OPEN_PING_STATUS = "open";
public final static String POST_TYPE = "post";
public final static String INHERIT_POST_STATUS = "inherit";
public final static String CLOSED_COMMENT_STATUS = "closed";
public final static String REVISION_POST_MIME_TYPE = "revision";
/** * 發佈文章,這時候沒有ID * * @param postAuthor 文章做者 * @param postDate 文章提交日期 * @param postTitle 文章標題 * @param postName 文章名字通常類說是文章標題進行url轉碼 * @param postContent 文章內容 * @param guid 文章的guid * @return */
public static PostCustom setPostinfo(
String postAuthor
, Date postDate
, String postTitle
, String postName
, String postContent
, String guid) {
Date atNow = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
formatter.format(atNow);
return new PostCustom.Builder()
.id(null)
.postAuthor(postAuthor)
.postDate(postDate)
.postDateGmt(atNow)
.postTitle(postTitle)
.postExcerpt("")
.postStatus(PUBLISH_POST_STATUS)
.commentStatus(OPEN_COMMENT_STATUS)
.commentCount(0L)
.pingStatus(OPEN_PING_STATUS)
.postPassword("")
.postName(postName)
.toPing("")
.pinged("")
.postModified(atNow)
.postModifiedGmt(atNow)
.postContentFiltered("")
.postContent(postContent)
.postParent("0")
.guid(guid)
.menuOrder(0)
.postType(POST_TYPE)
.postMimeType("")
.build();
}
/** * 文章操做時候,插入日誌 * @param parentId 修改文章的ID * @param postAuthor 文章做者 * @param postTitle 文章標題 * @param guid 文章的訪問標記 * @param postContent 文章內容 * @return */
public static PostCustom insertPostLog(String parentId, String postAuthor, String postTitle, String guid, String postContent){
Date atNow = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
formatter.format(atNow);
return new PostCustom.Builder()
.id(null)
.postAuthor(postAuthor)
.postDate(atNow)
.postDateGmt(atNow)
.postTitle(postTitle)
.postContent(postContent)
.postExcerpt("")
.postStatus(INHERIT_POST_STATUS)
.commentStatus(CLOSED_COMMENT_STATUS)
.commentCount(0L)
.pingStatus(CLOSED_COMMENT_STATUS)
.postPassword("")
.postName(parentId+"-revision-v1")
.toPing("")
.pinged("")
.postModified(atNow)
.postModifiedGmt(atNow)
.postContentFiltered("")
.postParent(parentId)
.guid(guid)
.menuOrder(0)
.postType(REVISION_POST_MIME_TYPE)
.postMimeType("")
.build();
}
}複製代碼
這裏咱們能夠看到咱們對外開放的核心也就文章核心信息相關的一些東西了,那麼這樣咱們就能很好的控制數據。
如今咱們能夠看下數據庫,最新多出來的數據和文章首頁插入的數據以下:
咱們今天的主幹內容到此基本完成,剩下的從dao到前臺界面適配等等基本上騷年們本身均可以完成至此不在贅述。
核心總結
- mapper中的文章寫入處標準的主要數據庫操做代碼應該是insert,因此文章更新的應該單獨開放接口來完成
- pojo其實是文章的包裝類型,javaBean中不但有擴展類型(子類是父類的擴展類型)更有包裝類型,包裝類型咱們在經常使用的json輸出的時候有用到,你們能夠本身多多動腦。
- dao層應該專一於提供單一數據驅動,Service對外提供數據接口,能夠適當參與業務場景的數據關係處理。