1. 程式人生 > >完整JavaWeb專案筆記 第二部分-專案結構及Bean和Dao設計

完整JavaWeb專案筆記 第二部分-專案結構及Bean和Dao設計

文章目錄

一 服務端專案結構

  Java開發工具我用的是Eclipse,配置完Maven後,建立的war工程,接下來就是專案目錄結構的劃分,這一點的重要性說來可大可小,結構清晰,則開發更加順利,結構不合理則變動會比較頻繁,對於體量較小的專案而言雖然影響不大,但是出於習慣的考慮,還是對結構進行下劃分:

在這裡插入圖片描述

  1. bean包中為資料表對應的Java類;
  2. common中放置專案中其他類引用的公用設計;
  3. dao包中放置兩部分,一個是資料訪問介面設計,一個是介面實現;
  4. filter包中放置Servlet請求過濾器;
  5. service包為服務層設計,所有的業務邏輯實現都在這裡;
  6. servlet包下為所有處理請求的Servlet實現類;
  7. util包下則為各類公用函式的實現。

二 表結構設計

  資料表設計不細說了,簡簡單單幾張表,大致看一下對應的Bean就差不多,這裡不涉及任何技術問題。但是需要注意的是,表及表字段的命名必須遵守規範,合理的命名規範對於未來可能出現的重構、其他需求而言及其重要,附上Navicat的截圖:

專案表結構

三 bean設計

  這裡再提一嘴,所有的bean需要有較為嚴格的結構設計:

  1. 除和表字段對應的成員屬性(私有訪問許可權),必須有一一對應的屬性訪問器(getter、setter方法);
  2. 如果有特殊的物件建立需求,則除特殊的建構函式外,必須保留預設的無引數構造;
  3. 必須要重寫equals、hashCode方法,方法重寫一般使用表主鍵欄位作為主要判斷邏輯,重寫是為了滿足業務邏輯上的等值判斷;
  4. 儘量重寫toString方法,以便設計時進行日誌列印、打樁除錯的資訊輸出。
  5. 需要注意的是bean屬性未必需要和表字段結構一一對應,比如說自增欄位,雖然常被設定為主鍵,但是為了方便未來的拆表及資料遷移,我們還是會使用UUID作為其唯一索引,這種情況下自增id欄位就沒有必要做為bean的屬性存在,其查詢邏輯中也不應該使用。

  附上大致的設計:

Bean設計

  1. Module為帖子的分類,學習、工作、兩性什麼的;
  2. Topic為帖子的結構,包括帖子的標題、內容;
  3. TopicReply為帖子的回覆內容,包括回覆人id、回覆時間等;
  4. TopicInof則為帖子的附加資訊,回覆數量、發表時間、作者等資訊;
  5. User為使用者資訊,包括使用者的賬號、密碼等;
  6. UserSession為使用者會話資訊,包括登陸時間、token有效期等。

  以Module為例:

package com.bubbling.bean;

import java.util.Date;

public class Module
{
	private String name;
	private String description;
	private Date creatime;
	private Date updatime;

	public Module()
	{
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public String getDescription()
	{
		return description;
	}

	public void setDescription(String description)
	{
		this.description = description;
	}

	public Date getCreatime()
	{
		return creatime;
	}

	public void setCreatime(Date creatime)
	{
		this.creatime = creatime;
	}

	public Date getUpdatime()
	{
		return updatime;
	}

	public void setUpdatime(Date updatime)
	{
		this.updatime = updatime;
	}

	@Override
	public int hashCode()
	{
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj)
	{
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Module other = (Module) obj;
		if (name == null)
		{
			if (other.name != null)
				return false;
		}
		else if (!name.equals(other.name))
			return false;
		return true;
	}

	@Override
	public String toString()
	{
		return "Module [name=" + name + ", description=" + description + ", creatime=" + creatime + ", updatime="
				+ updatime + "]";
	}

}

三 Dao設計

  Dao層為資料訪問介面,我們所有的設計要儘量面向介面,雖然當下我們的實現採用的是Mysql資料庫,且實現為JDBC的方式,但是難保以後不會有實現上的改變,如果有,那麼介面的意義就大了,對應用設計來說,整體結構無需任何變化,僅介面實現改變即可。

  對Dao的設計,需要充分考慮應用對錶資料的訪問需求,不能一味的按增刪改查需求設計,而對於MVC分層結構的設計來說,對請求的業務處理應該交由服務層實現,也就是說Service應該是Dao的使用大戶,那麼Dao的介面設計就應該主要考慮Service的實現需求。

  Dao也不多說,對於這個專案而言,主要就三個資料訪問需求:

  1. Module,模組
  2. Topic,帖子
  3. User,使用者

  貼一張參考圖:

Dao設計

  以ModuleDao為例,介紹下介面定義及其實現:

package com.bubbling.dao;

import java.util.List;

import com.bubbling.bean.Module;

public interface ModuleDao
{
	public boolean addModule(String name, String description);

	public Module getModuleByUuid(String uuid);

	public List<Module> getModuleList();
}

  ModuleDaoMysqlImpl實現:

package com.bubbling.dao.impl.mysql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.bubbling.bean.Module;
import com.bubbling.dao.ModuleDao;
import com.bubbling.util.DatabaseUtil;

public class ModuleDaoMysqlImpl implements ModuleDao
{

	private static final String SqlAddModule = "INSERT INTO module ( `uuid`, `name`, `description`, `creatime` ) VALUES ( REPLACE (UUID(), '-', ''), ?, ?, NOW())";

	@Override
	public boolean addModule(String name, String description)
	{
		Connection conn = null;
		PreparedStatement st = null;
		boolean ret = false;
		int index = 1;
		try
		{
			conn = DatabaseUtil.getConnection();
			st = conn.prepareStatement(SqlAddModule);
			st.setString(index++, name);
			st.setString(index++, description);
			int i = st.executeUpdate();
			if (i > 0)
			{
				ret = true;
			}
		}
		catch (SQLException e)
		{
			e.printStackTrace();
		}
		finally
		{
			DatabaseUtil.close(conn, st);
		}
		return ret;
	}

	private static final String SqlGetModuleByUuid = "SELECT `name`, `description` FROM module WHERE uuid = ?";

	@Override
	public Module getModuleByUuid(String uuid)
	{
		Connection conn = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		Module module = null;
		int index = 1;
		try
		{
			conn = DatabaseUtil.getConnection();
			st = conn.prepareStatement(SqlGetModuleByUuid);
			st.setString(index++, uuid);
			rs = st.executeQuery();
			if (rs.next())
			{
				module = new Module();
				module.setName(rs.getString("name"));
				module.setDescription(rs.getString("description"));
			}
		}
		catch (SQLException e)
		{
			e.printStackTrace();
		}
		finally
		{
			DatabaseUtil.close(conn, st, rs);
		}
		return module;
	}

	private static final String SqlGetModuleList = "SELECT `name`, `description` FROM module";

	@Override
	public List<Module> getModuleList()
	{
		Connection conn = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		List<Module> ret = new ArrayList<>();
		try
		{
			conn = DatabaseUtil.getConnection();
			st = conn.prepareStatement(SqlGetModuleList);
			rs = st.executeQuery();
			while (rs.next())
			{
				Module module = new Module();
				module.setName(rs.getString("name"));
				module.setDescription(rs.getString("description"));
				ret.add(module);
			}
			if (ret.size() == 0)
			{
				ret = null;
			}
		}
		catch (SQLException e)
		{
			e.printStackTrace();
		}
		finally
		{
			DatabaseUtil.close(conn, st, rs);
		}
		return ret;
	}
}

  注意,上例中資料庫連線的獲取及關閉定義在了DatabaseUtil中,資料庫方面設計將在第三部分介紹,資料庫連線池採用阿里的Druid,資料來源配置採用JNDI。