1. 根據靜態頁面完成JavaBean設計
在上一節中,我們完成了文章封面的制作,這些都屬於靜態頁面的部分。
從圖片中可以看到,一篇文章的主要信息有:文章標題,文章名稱,作者,還有摘要描述。
在《用大白話聊聊JavaSE -- 如何理解Java Bean(一)》中,我們已經討論關於JavaBean的一些問題。
一般來說,JavaBean分為必要字段和輔助字段,文章標題,文章名稱,作者,還有摘要描述,還有文章內容這些,應該屬於必要字段的范疇。
至於輔助字段,我就不搞那么復雜了,簡單設置幾個吧,比如發布時間,最后更新時間,是否發布,是否刪除。
當然,我們還需要知道這篇文章是誰寫的,所以還要再加一個userid字段,這樣的話才能和user表關聯起來。
最后,還需要有一個分類字段,一篇文章,肯定是屬於某一個類別的,所以這個也需要加上。
嗯,就添加這幾個輔助字段吧,我們弄簡單一點。
我們在bean包里面新建一個Article類。
設置屬性如下:
package bean;
import java.util.Date;
import annotation.Column;
import annotation.Table;
@Table(tableName = "t_article")
public class Article {
@Column(field = "id" , type = "varchar(100)" , primaryKey = true)
private String id; //主鍵
@Column(field = "header" , type = "varchar(100)")
private String header; //標題
@Column(field = "name" , type = "varchar(60)")
private String name; //文章名稱
@Column(field = "content" , type = "text")
private String content; //文章內容
@Column(field = "author" , type = "varchar(30)")
private String author; //作者
@Column(field = "description" , type = "varchar(100)")
private String description; //概要
@Column(field = "is_published" , type = "int(1)")
private Integer isPublished; //是否發布 0 未發布 1 發布
@Column(field = "is_delete" , type = "int(1)")
private Integer isDelete; //是否刪除 0 未刪除 1 已刪除
@Column(field = "create_time" , type = "datetime")
private Date createTime;//創建時間
@Column(field = "update_time" , type = "timestamp" , defaultNull = false)
private Date updateTime;//最后更新時間
@Column(field = "user_id" , type = "varchar(100)" , defaultNull = false)
private String userId;//作者id
@Column(field = "category_id" , type = "int(2)" , defaultNull = false)
private Integer categoryId;//分類ID
}
然后,別忘了生成get,set以及toString方法。
2. Mysql建表
2.1 文章表
在TestMain方法中再生成一下sql語句。
package test;
import bean.Article;
import util.TableUtils;
public class TestMain {
public static void main(String[] args) {
String sql = TableUtils.getCreateTableSQl(Article.class);
System.out.println(sql);
}
}
運行
這是生成出來的sql語句
DROP TABLE IF EXISTS t_article;
DROP TABLE IF EXISTS t_article;
create table t_article(
id varchar(100) DEFAULT NULL,
header varchar(100) DEFAULT NULL,
name varchar(60) DEFAULT NULL,
content text DEFAULT NULL,
author varchar(30) DEFAULT NULL,
description varchar(100) DEFAULT NULL,
is_published int(1) DEFAULT NULL,
is_delete int(1) DEFAULT NULL,
create_time datetime DEFAULT NULL,
update_time timestamp NOT NULL,
user_id varchar(100) NOT NULL,
category_id int(2) NOT NULL,
) DEFAULT CHARSET=utf8
因為 update_time 是timestamp類型,也就是時間戳,那么我們給他一個默認值,默認就是當前時間。
改成:
update_time timestamp NOT NULL
DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
在mysql數據庫里面運行一下,發現報錯了
[Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ') DEFAULT CHARSET=utf8' at line 13
哦,原來在屬性的最后一行不能有逗號。
看來之前寫的方法還有點問題,這邊我們先把逗號去掉吧。
再次運行sql語句,OK,成功建表了。
3. 制造測試數據,JUint初探
接下來,我們來虛擬一些數據。
我們在test包下新建一個類,叫做TestInsertOperation,就是測試INSERT操作的意思。
我們用JUint來測試。
JUnit是一個基於Java語言的單元測試框架,用起來比較方便。它的源代碼很輕巧,而且優雅地運用了多種設計模式,應該來說,這是一個非常優秀的框架。
首先在這個TestInsertOperation類中添加一個方法
/**
* 測試:給文章插入數據
*/
@Test
public void insertArticle(){
}
@Test是一個注解,加上它以后,才會被JUint測試框架識別。
把光標放在@Test上面,ctrl + 1
這個東西就跳出來了,點擊第一項,JUint的依賴包就被加載進來了。
接下來,在測試方法 insertArticle 中寫上測試代碼:
String sql = "INSERT INTO t_article(id,header,name,content,author,"
+ "description,is_published,is_delete,create_time,update_time"
+ ",user_id,category_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?) ";
String id = UUID.randomUUID().toString(); //主鍵
String header = "Java Web實用技術";
String name = "如何將MyEclipse項目導入eclipse";
String content = "我們經常會在網上下載一些開源項目,或者從別的地方遷移一些項目進來,但經常會發現導入后各種報錯。這是初學java肯定會遇到的問題,本文對一些常見的處理方案做一個總結。(本文將MyEclipse項目導入eclipse的過程為例,其他情況也可參考這個流程)";
String author = "Jack";
String description = "解決項目導入的沖突問題...";
int isPublished = 1 ;
int isDelete = 0;
String create_time = "2016-10-19 10:43:10";
String update_time = "2016-10-19 10:43:10";
String userId = "319600c3-550a-4f9f-80cf-deebe2376528";
int categoryId = 2;
DataBaseUtils.update(sql, id,header,name,content,author,description,isPublished,isDelete,create_time,update_time,userId,categoryId);
System.out.println("新增成功!");
鼠標雙擊方法名
按一下F11,開始測試(如果F11不起作用,那么就右鍵,Run As, JUnit Test)
測試結果:
OK,沒有錯誤。
控制台也沒有報錯,而且成功打印了 "新增成功!" 這幾個字。
我已經在庫里查到這條數據了,現在,用jdbc的方式將剛剛插入的數據查詢出來。
在庫里看到它的 ID 為 2145ed72-164a-401c-af29-248625a775b8。
好的,現在新寫一個方法來獲取這條數據:
public void getArticle(){
String sql = "select * from t_article where id = ?";
Article article = DataBaseUtils.queryForBean(sql, Article.class, "2145ed72-164a-401c-af29-248625a775b8");
System.out.println(article);
}
測試結果:
Article [ id = 2145ed72-164a-401c-af29-248625a775b8,
header = Java Web實用技術,
name = 如何將MyEclipse項目導入eclipse,
content = 我們經常會在網上下載一些開源項目,或者從別的地方遷移一些項目進來,但經常會發現導入后各種報錯。這是初學java肯定會遇到的問題,本文對一些常見的處理方案做一個總結。(本文將MyEclipse項目導入eclipse的過程為例,其他情況也可參考這個流程),
author = Jack,
description = 解決項目導入的沖突問題...,
isPublished = 1,
isDelete = 0,
createTime = Wed Oct 19 10:43:10 CST 2016,
updateTime = Wed Oct 19 10:43:10 CST 2016,
userId = 319600c3-550a-4f9f-80cf-deebe2376528,
categoryId = 2 ]
這樣就成功了。
2.2 分類表
分類表的話比較簡單,為了簡單起見,我們就不寫JavaBean了,直接在mysql中建表吧。
建表語句:
DROP TABLE IF EXISTS `t_category`;
CREATE TABLE `t_category` (
`category_id` int(11) NOT NULL AUTO_INCREMENT,
`category_name` varchar(20) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
ID是自增長的。
制造數據:
INSERT INTO `t_category` VALUES ('1', '連載小說');
INSERT INTO `t_category` VALUES ('2', '編程代碼類');
INSERT INTO `t_category` VALUES ('3', '生活感悟');
insert 操作可以直接在mysql中進行操作,也可以用jdbc來操作。
jdbc操作的代碼如下
/**
* 插入分類數據
*/
@Test
public void insertCategory(){
DataBaseUtils.update("insert into t_category set category_name = ?", "連載小說");
DataBaseUtils.update("insert into t_category set category_name = ?", "編程代碼類");
DataBaseUtils.update("insert into t_category set category_name = ?", "生活感悟");
}
測試一下就行了。
好的,插入完畢后,我們新建一個測試方法來查詢一下。
/**
* 獲取分類列表
*/
@Test
public void getCategoryList(){
String sql = "select * from t_category where 1 = 1";
List list = DataBaseUtils.queryForList(sql);
System.out.println(list);
}
結果:
[ {category_name=連載小說, category_id=1},
{category_name=編程代碼類, category_id=2},
{category_name=生活感悟, category_id=3} ]
嗯,OK了。
4. service層開發
上面的測試代碼主要是dao部分的,但因為本項目省去了dao層,所以,有什么操作的話,我們直接在service層解決掉算了。
新建一個 ArticleService 類
首頁的文章列表:
從靜態頁面中,我們可以看到,文章被分為幾個不同的類別,比如連載小說,就是一個單一的類別,我們應該是通過類別去加載相應的文章。
在數據庫表中,連載小說的分類ID為1,那么我們如果想要查詢出該分類下的文章,就會寫出這樣的sql語句:
select * from t_article where category_id = 1;
我們先不管到底怎么和首頁對接的,先把后台邏輯寫好再說。
在 ArticleService 類中定義一個查詢方法
/**
* 通過類別獲取文章列表
* @param categoryId
* @param start
* @param end
*/
public List<Map<String,Object>> getArticlesByCategoryId(Integer categoryId,Integer start,Integer end){
String sql = "select id,header,name,author,"
+ "description from t_article where 1 = 1 "
+ " and is_delete = 0"
+ " and is_published = 1"
+ " and category_id = ?"
+ " order by update_time desc limit ?,?";
return DataBaseUtils.queryForList(sql, categoryId,start,end);
}
測試代碼:
ArticleService ArticleService = new ArticleService();
List list = ArticleService.getArticlesByCategoryId(2,0,10);
System.out.println(list);
我測試了一下,應該沒問題。sql查詢的話,我做了一個簡單的排序,就是根據最后更新時間倒序排序。
相信你也已經看出來了,因為我們已經有了 DataBaseUtils 這個工具類,所以大大減少了我們的java代碼。
不然的話,我們還需要手動去獲取連接,然后生成 PreparedStatement 的實例,一大堆 try catch ,最后還要關閉連接。
有了 DataBaseUtils ,這些重復的代碼就可以省略了。
在這個 getArticlesByCategoryId 方法中,我故意沒有把文章內容查詢出來。
原因很簡單,因為文章內容不需要展示在首頁,我就是查詢出來也沒用。
我把id查出來了,這樣的話,當用戶通過點擊文章封面,進入詳情頁的時候,就可以獲取文章id,有了這個id,我們是不是就可以去數據庫把文章內容給查出來了呢?
所以,我們肯定還需要一個方法,就是通過文章id查詢出文章內容的方法。
代碼:
/**
* 通過文章id獲取文章內容
* @param id
* @return
*/
public List<Map<String,Object>> getContentByArticleId(String id){
String sql = "select content from t_article where id = ?";
return DataBaseUtils.queryForList(sql,id);
}
測試了一下,也是沒問題的哈。
5. 與前台頁面對接
好的,后台已經寫好了,我們現在和前台對接一下。
打開index.jsp
找到編程代碼類:
<div class='category'>
<div class='title'>編程代碼類</div>
<ul class='items'>
<li class='item'></li>
<li class='item'></li>
<div style='clear:both'></div>
</ul>
</div>
現在,我們要把它變成動態的。
首先,在index.jsp頁面頂部的地方,導入必要的包。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page language="java" import="service.ArticleService" pageEncoding="UTF-8"%>
然后,新建一個 ArticleService 的實例。
<% ArticleService articleService = new ArticleService(); %>
接下來,在 編程代碼類 的div上方獲取 list 數據。
<%
//查詢出編程代碼類的相關文章
List<Map<String,Object>> articles2 = articleService.getArticlesByCategoryId(2, 0, 6);
pageContext.setAttribute("articles2", articles2);
%>
${articles2}
<div class='category'>
<div class='title'>編程代碼類</div>
<ul class='items'>
<li class='item'></li>
<li class='item'></li>
<div style='clear:both'></div>
</ul>
</div>
pageContext是JSP九大隱式對象的一員,顧名思義,它的作用域就是當前頁面。
${articles2}表示在html代碼中顯示articles2的具體信息,注意,這個信息是Java代碼獲取的。
我們只要刷新一下頁面,這些代碼邏輯就會被執行。
好的,我們刷新一下。
報錯了。
沒關系,看看它說什么。
錯誤信息:
message /index.jsp (line: 2, column: 1) Page directive must not have multiple occurrences of pageencoding
哦,它說我們must not have,一定不能有。
一定不能有什么呢?繼續往下看。
multiple occurrences of pageencoding(多個pageencoding出現)
哦,一定不能出現多個 pageencoding 。
我們來看看頁面。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page language="java" import="service.ArticleService" pageEncoding="UTF-8"%>
嗯,我們真的定義了多個pageEncoding。
好的,我們刪掉多余的pageEncoding。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page language="java" import="java.util.*"%>
<%@ page language="java" import="service.ArticleService"%>
再來一次,刷新頁面。
效果出來了。
${articles2}變成了:
[{id=2145ed72-164a-401c-af29-248625a775b8, author=Jack, description=解決項目導入的沖突問題..., name=如何將MyEclipse項目導入eclipse, header=Java Web實用技術}]
然后,我們需要用JSTL標簽庫中的一個叫做 c:forEach 標簽。
它的作用是循環遍歷,我們直接上代碼吧。
<%
//查詢出編程代碼類的相關文章
List<Map<String,Object>> articles2 = articleService.getArticlesByCategoryId(2, 0, 6);
pageContext.setAttribute("articles2", articles2);
%>
<div class='category'>
<div class='title'>編程代碼類</div>
<ul class='items'>
<c:forEach items="${articles2}" var="item">
<li class='item'>
<div class='item-banner'>
<div class='item-header'>${item.header}</div>
<div class='item-name'>${item.name}</div>
<div class='item-author'>@${item.author} 著</div>
</div>
<div class='item-description'>${item.description}</div>
</li>
</c:forEach>
<div style='clear:both'></div>
</ul>
</div>
我們用了一個forEach標簽,將${articles2}進行了遍歷。因為${articles2}是一個list,所以是可以遍歷的。
var="item" 是遍歷出來的每一個對象。
效果:
因為字數太多,加上行高的關系,整個封面被擠下來了。
嗯,我手動來調一下css樣式吧。
讓文章名稱強制不換行,溢出部分用 ... 顯示
.item-name {
font-size: 22px;
line-height: 74px;
white-space:nowrap;
overflow:hidden;
text-overflow: ellipsis;
}
鼠標划上去的時候,顯示一個 tip 提示
<div class='item-name' title = "${item.name}">${item.name}</div>
這樣就OK了。
好的,與前台對接完畢了。
我又弄了幾條測試數據。
假模假式的,稍微有那么點樣子了。
嗯,今天就到這里了,下一節咱們繼續。
源碼:點擊下載