Author:相忠良
Email: ugoood@163.com
起始於:May 29, 2018
最後更新日期:June 6, 2018javascript
聲明:本筆記依據傳智播客方立勳老師 Java Web 的授課視頻內容記錄而成,中間加入了本身的理解。本筆記目的是強化本身學習所用。如有疏漏或不當之處,請在評論區指出。謝謝。
涉及的圖片,文檔寫完後,一次性更新。html
JDBC(Java Data Base Connectivity) 是 java 數據庫鏈接,主要有接口組成。
JDBC的做用:能夠經過java程序操做數據庫。java
組成JDBC的2個包:mysql
除導入JDBC外,還需導入相應數據庫JDBC接口的實現,即數據庫驅動。咱們用的是mysql-connector-java-5.0.8-bin.jar
針對mysql的數據庫驅動。程序員
作JDBC試驗前的準備工做:
user.sql文件,代碼以下:web
create database day14 character set utf8 collate utf8_general_ci; use day14; create table users( id int primary key, name varchar(40), password varchar(40), email varchar(60), birthday date ); insert into users(id,name,password,email,birthday) values(1,'zs','123456','zs@sina.com','1980-12-04'); insert into users(id,name,password,email,birthday) values(2,'lisi','123456','lisi@sina.com','1981-12-04'); insert into users(id,name,password,email,birthday) values(3,'wangwu','123456','wangwu@sina.com','1979-12-04');
而後創建普通java工程day14,並創建lib文件夾,將mysql-connector-java-5.0.8-bin.jar
複製到lib,並把該包變爲奶瓶。spring
創建Demo1.java,操做 day14 數據庫的 users 表,代碼以下:sql
package cn.wk.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class Demo1 { public static void main(String[] args) throws SQLException { /* jdbc:mysql 表示協議 //localchost:3306 本機3306端口,是mysql服務器端口 day14 要鏈接的數據庫 */ String url = "jdbc:mysql://localhost:3306/day14"; String username = "root"; String password = "root"; // 1. 加載驅動 DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // 2. 獲取鏈接 Connection conn = DriverManager.getConnection(url, username, password); // 3. 獲取向數據庫發sql語句的statement對象 Statement st = conn.createStatement(); // 4. 向數據庫發sql,獲取數據庫返回的結果集 ResultSet rs = st.executeQuery("select * from users"); // 5. 從結果集中獲取數據 while (rs.next()) { System.out.print("id = " + rs.getObject("id") + " "); System.out.print("name = " + rs.getObject("name") + " "); System.out.print("password = " + rs.getObject("password") + " "); System.out.print("email = " + rs.getObject("email") + " "); System.out.print("birthday = " + rs.getObject("birthday") + " "); System.out.println(); } // 6. 釋放資源(釋放連接) rs.close(); st.close(); conn.close(); } }
依據上面所說,修改了上節的代碼,代碼片斷以下:數據庫
// localhost簡寫,?後接各類參數,不管mysql用何種字符集,均推薦字符顯式設定 String url = "jdbc:mysql:///day14" + "?user=root&password=root" + "&useUnicode=true&characterEncoding=UTF-8"; Class.forName("com.mysql.jdbc.Driver"); // <---- 加載類,同時註冊 Connection conn = DriverManager.getConnection(url);
Connection對象:
Jdbc程序中的Connection,它用於表明數據庫的連接,Collection是數據庫編程中最重要的一個對象,客戶端與數據庫全部交互都是經過connection對象完成的,這個對象的經常使用方法:apache
Statement對象:
Statement對象用於向數據庫發送SQL語句, Statement對象經常使用方法:
ResultSet對象:
ResultSet用於表明Sql語句的執行結果。
下圖表達了,mysql 表中字段類型與 ResultSet 方法對照表:
最後必定要釋放 conn,由於mysql的連接資源不多很寶貴,釋放技巧以下:
資源釋放代碼放入finally裏。
模板代碼 以下:
package cn.wk.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import cn.wk.domain.User; public class Demo2 { public static void main(String[] args) throws SQLException, ClassNotFoundException { String url = "jdbc:mysql:///day14"; String username = "root"; String password = "root"; Connection conn = null; Statement st = null; ResultSet rs = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(url, username, password); st = conn.createStatement(); // throw rs = st.executeQuery("select * from users"); while (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setPassword(rs.getString("password")); user.setEmail(rs.getString("email")); user.setBirthday(rs.getDate("birthday")); } } finally { if (rs != null) { try { rs.close(); // throw new } catch (Exception e) { e.printStackTrace(); } rs = null; } if (st != null) { try { st.close(); } catch (Exception e) { e.printStackTrace(); } st = null; } if (conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } } } }
順便建了個User類,作爲javabean對象,代碼以下:
package cn.wk.domain; import java.util.Date; public class User { private int id; private String name; private String password; private String email; private Date birthday; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
案例:
Demo3.java
實現crud方法;db.properties
資源文件,達到靈活切換數據庫驅動、url、username 和 password;cn.wk.utils.JdbcUtils
工具類,實現驅動加載、創建連接和釋放連接這種公共代碼。Demo3.java,以下:
package cn.wk.demo; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import org.junit.Test; import cn.wk.domain.User; import cn.wk.utils.JdbcUtils; public class Demo3 { @Test public void insert() throws SQLException { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "insert into users(id,name,password,email,birthday)" + "values (4,'eee','123','ee@sina.com','1980-11-11')"; int num = st.executeUpdate(sql); if (num > 0) { System.out.println("插入成功!!!"); } } finally { JdbcUtils.release(conn, st, rs); } } @Test public void update() throws SQLException { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "update users set name = 'fff' where id = '4'"; int num = st.executeUpdate(sql); if (num > 0) { System.out.println("更新成功!!!"); } } finally { JdbcUtils.release(conn, st, rs); } } @Test public void delete() throws SQLException { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "delete from users where id='4'"; int num = st.executeUpdate(sql); if (num > 0) { System.out.println("刪除成功!!!"); } } finally { JdbcUtils.release(conn, st, rs); } } @Test public void find() throws SQLException { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "select * from users where id='2'"; rs = st.executeQuery(sql); if (rs != null) { System.out.println("查詢成功!!!"); } } finally { JdbcUtils.release(conn, st, rs); } } @Test public void getAll() throws SQLException { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "select id,name,password,email,birthday from users"; rs = st.executeQuery(sql); List list = new ArrayList(); while (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setPassword(rs.getString("password")); user.setEmail(rs.getString("email")); user.setBirthday(rs.getDate("birthday")); list.add(user); } System.out.println(list); } finally { JdbcUtils.release(conn, st, rs); } } }
db.properties
資源文件:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/day14 username=root password=root
cn.wk.utils.JdbcUtils
工具類:
package cn.wk.utils; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class JdbcUtils { private static Properties config = new Properties(); static { try { // 讀配置文件 db.properties config.load(JdbcUtils.class.getClassLoader().getResourceAsStream( "db.properties")); Class.forName(config.getProperty("driver")); } catch (Exception e) { throw new ExceptionInInitializerError(e); // 異常轉換成錯誤 } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(config.getProperty("url"), config.getProperty("username"), config.getProperty("password")); } public static void release(Connection conn, Statement st, ResultSet rs) { // 模板代碼 if (rs != null) { try { rs.close(); } catch (Exception e) { e.printStackTrace(); } rs = null; } if (st != null) { try { st.close(); } catch (Exception e) { e.printStackTrace(); } st = null; } if (conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } } }
更換db.properties
文件信息爲 oracle 的,幾乎不用改程序,就能夠用oracle作crud。
爲保證程序有移植性,即想作到不依賴具體的數據庫管理系統,那麼,Connection 等對象就用 sun 公司的接口,而不能用具體數據庫的相應對象!如:咱們導入的是java.sql.Connection
。
準備工做:
複製 day09_user 工程,命名爲 day14_user,並點擊該工程屬性,選 web 選項, 將 Web Context-root
改成/day09_user
。
UserDaoJdbcImpl
代碼以下:
package cn.wk.dao.impl; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import cn.wk.dao.UserDao; import cn.wk.domain.User; import cn.wk.utils.JdbcUtils; public class UserDaoJdbcImpl implements UserDao { @Override public void add(User user) { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); // conn.preparedment() 用佔位符替換位置 String sql = "insert into users(id,username,password,email,birthday,nickname)" + "values('" + user.getId() + "','" + user.getUsername() + "','" + user.getPassword() + "','" + user.getEmail() + "','" + user.getBirthday().toLocaleString() + "','" + user.getNickname() + "')"; int num = st.executeUpdate(sql); if (num < 1) { throw new RuntimeException("註冊用戶失敗!!!"); } } catch (Exception e) { throw new RuntimeException(e); } finally { JdbcUtils.release(conn, st, rs); } } @Override public User find(String username, String password) { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "select * from users where username='" + username + "' and password='" + password + "'"; rs = st.executeQuery(sql); if (rs.next()) { User user = new User(); user.setBirthday(rs.getDate("birthday")); user.setEmail(rs.getString("email")); user.setId(rs.getString("id")); user.setNickname(rs.getString("nickname")); user.setPassword(rs.getString("password")); user.setUsername(rs.getString("username")); return user; } return null; } catch (Exception e) { throw new RuntimeException(e); } finally { JdbcUtils.release(conn, st, rs); } } @Override public boolean find(String username) { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "select * from users where username='" + username + "'"; rs = st.executeQuery(sql); if (rs.next()) { return true; } return false; } catch (Exception e) { throw new RuntimeException(e); } finally { JdbcUtils.release(conn, st, rs); } } }
其餘的不寫了,好累。(2018年5月30日 23:00 於 濰坊科技學院軟件學院 315 辦公室)
爲使 service 層的 UserDao 與 實際的實現解耦,需專門創建 dao工廠類,由該工廠讀取dao.properties
這個配置文件,實現調用具體的 UserDao 實現,如調用UserDaoJdbcImpl
或UserDaoXmlImpl
等其餘實現,以下圖所示:
因爲有可能有不少工廠,故創建cn.wk.factory
包來存放工廠類。此時,咱們在該包內創建DaoFactory
類,以下:
package cn.wk.factory; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class DaoFactory { private Properties daoConfig = new Properties(); // 工廠是單例的,即全部的dao只由一個工廠來生產 // new 對象同時,加載配置文件 private DaoFactory() { InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream( "dao.properties"); try { daoConfig.load(in); } catch (IOException e) { throw new RuntimeException(e); } } private static DaoFactory instance = new DaoFactory(); public static DaoFactory getInstance(){ return instance; } // 未來可能產生多個 不一樣類型的 dao,故此處用泛型 // 若給 UserDao.class,本方法就產生UserDao的dao // DepartmentDao.class 同上 // 你給我一個接口類型, 我給你返回一個接口實現 // 爲避免調用者強轉,此處應該用泛型 // 泛型的學習,重點: // Class<T> : 人家傳進來什麼類型, T就表明什麼類型 // <T> T : <T> 是類型聲明, T 是返回的類型 public <T> T createDao(Class<T> clazz){ // 1. 獲得傳進來的接口名稱 // clazz.getName() //cn.wk.dao.UserDao String name = clazz.getSimpleName(); String className = daoConfig.getProperty(name); try { // 加載類,併產生實例 T dao = (T)Class.forName(className).newInstance(); return dao; } catch (Exception e) { throw new RuntimeException(e); } } }
還需建dao.properties
配置文件,以下:
UserDao=cn.wk.dao.impl.UserDaoJdbcImpl
修改BusinessServiceImpl
以下:
// private UserDao dao = new UserDaoJdbcImpl(); // 可用工廠模式 或 spring 解耦,之後解耦 // 利用工廠,使得業務類,無代碼依賴於具體實現,已徹底解耦 private UserDao dao = DaoFactory.getInstance().createDao(UserDao.class);
通過改造,系統結構很是好,好靈活,以下圖:
關於異常:
應該每一層都自定義異常,以便拋出異常時,能快速定位是哪層出現異常。
對待異常的態度,方立勳遵循了 Spring 做者的理念,即:
你是否但願上層處理本異常,若但願,則拋出 checked 異常,不然拋出 unchecked 異常。有時,咱們就想對上層造麻煩,擔憂上層忘記處理本層某異常,那就不用猶豫,拋 checked 異常便可!
statement 和 preparedStatement 的區別:
preparedStatement 使用的代碼片斷,通常就用 preparedStatement 而不用 statement,以下:
String url = "jdbc:mysql:///day14"; String username = "root"; String password = "root"; Connection conn = null; preparedStatement st = null; ResultSet rs = null; Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(url, username, password); String sql = "select * from users where username=?"; // ?佔位符 st = conn.preparedStatement(sql); st.setString(1, username); // 填補?號 rs = st.executeQuery(); if(rs.next()){ rs.close(); st.close(); conn.close(); return true; } rs.close(); st.close(); conn.close(); return false;
要實現一個客戶關係管理系統,客戶信息 customer 表以下:
字段名 | 類型 |
---|---|
id | varchar(40) |
name | varchar(20) |
gender | varchar(4) |
birthday | date |
cellphone | varchar(20) |
varchar(40) | |
preference | varchar(100) |
type | varchar(40) |
description | varchar(255) |
1.搭建環境 1.1 導開發包 mysql驅動 beanUtils log4j開發 jstl開發包 1.2 建立組織程序的包 cn.wk.domain cn.wk.dao cn.wk.dao.impl cn.wk.service cn.wk.service.impl cn.wk.web.controller cn.wk.web.UI cn.wk.utils cn.wk.exception junit.test WEB-INF/jsp 1.3 爲應用建立相應庫和表 create database day14_customer character set utf8 collate utf8_general_ci; use day14_customer; create table customer ( id varchar(40) primary key, name varchar(40) not null, gender varchar(4) not null, birthday date, cellphone varchar(20), email varchar(40), preference varchar(255), type varchar(100) not null, description varchar(255) ); 2.建實體 3.寫dao 4.寫service 5.寫web
根據應用建立數據庫及表,根據表建立 javabean,以下:
package cn.wk.domain; import java.util.Date; public class Customer { private String id; private String name; private String gender; private Date birthday; private String cellphone; private String email; private String preference; private String type; private String description; public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getCellphone() { return cellphone; } public void setCellphone(String cellphone) { this.cellphone = cellphone; } public String getPreference() { return preference; } public void setPreference(String preference) { this.preference = preference; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
要鏈接數據庫,則建立連數據庫的工具類,這算是一種模板代碼,cn.wk.utils.JdbcUtils
,以下:
package cn.wk.utils; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class JdbcUtils { private static Properties config = new Properties(); static { try { // 讀配置文件 db.properties config.load(JdbcUtils.class.getClassLoader().getResourceAsStream( "db.properties")); Class.forName(config.getProperty("driver")); } catch (Exception e) { throw new ExceptionInInitializerError(e); // 異常轉換成錯誤 } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(config.getProperty("url"), config.getProperty("username"), config.getProperty("password")); } public static void release(Connection conn, Statement st, ResultSet rs) { // 模板代碼 if (rs != null) { try { rs.close(); } catch (Exception e) { e.printStackTrace(); } rs = null; } if (st != null) { try { st.close(); } catch (Exception e) { e.printStackTrace(); } st = null; } if (conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } } }
連數據庫時,需用到配置文件db.properties
,它在 src 目錄下,代碼以下:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/day14_customer username=root password=root
再建立操做數據庫進行 crud 的實現類cn.wk.dao.impl.CustomerDaoImpl
,以下:
package cn.wk.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import cn.wk.dao.CustomerDao; import cn.wk.domain.Customer; import cn.wk.exception.DaoException; import cn.wk.utils.JdbcUtils; public class CustomerDaoImpl implements CustomerDao { @Override public void add(Customer c) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "insert into customer (id,name,gender,birthday,cellphone,email,preference,type,description) values (?,?,?,?,?,?,?,?,?)"; st = conn.prepareStatement(sql); st.setString(1, c.getId()); st.setString(2, c.getName()); st.setString(3, c.getGender()); st.setDate(4, new java.sql.Date(c.getBirthday().getTime())); st.setString(5, c.getCellphone()); st.setString(6, c.getEmail()); st.setString(7, c.getPreference()); st.setString(8, c.getType()); st.setString(9, c.getDescription()); st.executeUpdate(); } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } } @Override public void update(Customer c) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "udate customer set name=?,gender=?,birthday=?,cellphone=?,email=?,preference=?,type=?,description=? where id=?"; st = conn.prepareStatement(sql); st.setString(1, c.getName()); st.setString(2, c.getGender()); st.setDate(3, new java.sql.Date(c.getBirthday().getTime())); st.setString(4, c.getCellphone()); st.setString(5, c.getEmail()); st.setString(6, c.getPreference()); st.setString(7, c.getType()); st.setString(8, c.getDescription()); st.setString(9, c.getId()); st.executeUpdate(); } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } } @Override public void delete(String id) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "delete from customer where id=?"; st = conn.prepareStatement(sql); st.setString(1, id); st.executeUpdate(); } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } } @Override public Customer find(String id) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "select * from customer where id=?"; st = conn.prepareStatement(sql); st.setString(1, id); rs = st.executeQuery(); if (rs.next()) { Customer c = new Customer(); c.setBirthday(rs.getDate("birthday")); c.setCellphone(rs.getString("cellphone")); c.setDescription(rs.getString("description")); c.setEmail(rs.getString("email")); c.setGender(rs.getString("gender")); c.setId(rs.getString("id")); c.setName(rs.getString("name")); c.setPreference(rs.getString("preference")); c.setType(rs.getString("type")); return c; } } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } return null; } @Override public List<Customer> getAll() { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "select * from customer"; st = conn.prepareStatement(sql); rs = st.executeQuery(); List<Customer> list = new ArrayList<Customer>(); while (rs.next()) { Customer c = new Customer(); c.setBirthday(rs.getDate("birthday")); c.setCellphone(rs.getString("cellphone")); c.setDescription(rs.getString("description")); c.setEmail(rs.getString("email")); c.setGender(rs.getString("gender")); c.setId(rs.getString("id")); c.setName(rs.getString("name")); c.setPreference(rs.getString("preference")); c.setType(rs.getString("type")); list.add(c); } return list; } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } } }
再對操做數據庫的實現類cn.wk.dao.impl.CustomerDaoImpl
抽取成接口cn.wk.dao.CustomerDao
,以下:
package cn.wk.dao; import java.util.List; import cn.wk.domain.Customer; public interface CustomerDao { public abstract void add(Customer c); public abstract void update(Customer c); public abstract void delete(String id); public abstract Customer find(String id); public abstract List<Customer> getAll(); }
這時,注意到每層都應創建本身的異常類,以方便未來發生異常時,方便程序員瞭解究竟是哪層發生異常。故創建cn.wk.exception.DaoException
,並聲明該異常類爲RuntimeException
,同時繼承父類的全部方法,以下:
package cn.wk.exception; public class DaoException extends RuntimeException { public DaoException() {} public DaoException(String message) {super(message);} public DaoException(Throwable cause) {super(cause);} public DaoException(String message, Throwable cause) {super(message, cause);} public DaoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
接着,想到本工程的業務僅僅是對客戶信息進行 crud,業務邏輯超級簡單。因此,咱們能夠當即創建業務層實現cn.wk.service.impl.BusinessServiceImpl
,以下(注意:若業務邏輯很複雜,本層代碼也會很複雜):
package cn.wk.service.impl; import java.util.List; import cn.wk.dao.CustomerDao; import cn.wk.dao.impl.CustomerDaoImpl; import cn.wk.domain.Customer; import cn.wk.service.BusinessService; // 薄薄的業務層 public class BusinessServiceImpl implements BusinessService { private CustomerDao dao = new CustomerDaoImpl(); @Override public void addCustomer(Customer c){ dao.add(c); } @Override public void updateCustomer(Customer c){ dao.update(c); } @Override public void deleteCustomer(String id){ dao.delete(id); } @Override public Customer findCustomer(String id){ return dao.find(id); } @Override public List<Customer> getAllCustormer(){ return dao.getAll(); } }
而後當即進行業務層實現的抽取,造成接口cn.wk.service.BusinessService
package cn.wk.service; import java.util.List; import cn.wk.domain.Customer; public interface BusinessService { public abstract void addCustomer(Customer c); public abstract void updateCustomer(Customer c); public abstract void deleteCustomer(String id); public abstract Customer findCustomer(String id); public abstract List<Customer> getAllCustormer(); }
接下來,和 web 相關,也就是和用戶相關的層的代碼會較複雜,要當心了。
首先考慮首頁 index.jsp,本例用了分幀技術,涉及了 index.jsp 和 head.jsp,想達到以下效果:
index.jsp 以下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>首頁</title> </head> <frameset rows="20%,*"> <frame name="head" src="${pageContext.request.contextPath}/head.jsp"> <frame name="main"> </frameset> </html>
head.jsp 以下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>head</title> </head> <body style="text-align:center;"> <h1>XXX客戶關係管理系統</h1> <br> <a href="${pageContext.request.contextPath}/servlet/AddCustomerServlet" target="main">添加客戶</a> <a href="${pageContext.request.contextPath}/servlet/ListCustomerServlet" target="main">查看客戶</a> </body> </html>
注意到 head.jsp 中的2個超連接,結果顯示在 main 那個幀裏。
根據上述的2個超連接,分別編寫那2個控制器servlet程序。
先看添加客戶按鈕的超連接cn.wk.web.controller.AddCustomerServlet
,當客戶點擊「添加客戶」按鈕時,發出的是 get 請求, 由AddCustomerServlet
的 doGet 方法處理,該方法又把請求轉發給/WEB-INF/jsp/addcustomer.jsp
去顯示錶單,用戶填寫完表單後,再用action="${pageContext.request.contextPath}/servlet/AddCustomerServlet" method="post"
使用 post 請求又鏈接回cn.wk.web.controller.AddCustomerServlet
這個servlet控制器,該控制器用 doPost 方法接受請求並處理(就是把客戶填寫的表單封裝成一個 Customer 對象,並轉交給業務層處理,業務層將該對象轉交給dao,由dao負責將對 Customer 對象存入數據庫),其中涉及到 message 的信息傳送,以表達「添加成功或失敗」信息,並回顯給客戶。
cn.wk.web.controller.AddCustomerServlet
以下:
package cn.wk.web.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.wk.domain.Customer; import cn.wk.service.BusinessService; import cn.wk.service.impl.BusinessServiceImpl; import cn.wk.utils.Globals; import cn.wk.utils.WebUtils; public class AddCustomerServlet extends HttpServlet { // 給用戶提供一個添加界面 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 信息帶給jsp req.setAttribute("genders", Globals.genders); req.setAttribute("preferences", Globals.preferences); req.setAttribute("types", Globals.types); // 視圖 req.getRequestDispatcher("/WEB-INF/jsp/addcustomer.jsp").forward(req, resp); } // 處理用戶的添加請求 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { req.setCharacterEncoding("UTF-8"); // 表單校驗 // 把表單數據封裝到 customer 對象中 , 用工具類 WebUtils Customer c = WebUtils.request2Bean(req, Customer.class); c.setId(WebUtils.generateID()); BusinessService service = new BusinessServiceImpl(); service.addCustomer(c); req.setAttribute("message", "添加成功!!"); } catch (Exception e) { e.printStackTrace(); req.setAttribute("message", "添加失敗!!"); } req.getRequestDispatcher("/message.jsp").forward(req, resp); } }
控制層的cn.wk.web.controller.AddCustomerServlet
還引入了cn.wk.utils.Globals
這個工具類,該類目的是不想把genders, preferences, types
寫死,經過修改該類,達到直接控制那3個屬性的目的,代碼以下:
package cn.wk.utils; public class Globals { public static String genders[] = { "男", "女", "人妖" }; public static String preferences[] = { "唱歌", "跳舞", "桑拿", "打麻將", "看鳳姐", "夜生活", "xxx" }; public static String types[] = { "vip客戶", "重點客戶", "普通客戶" }; }
/WEB-INF/jsp/addcustomer.jsp
以下(涉及一個顯示日期的js控件/WEB-INF/js/ShowCalendar.js
):
該代碼涉及了一個makepre()
的 js 函數,功能是把客戶的多個preference組合成一個字符串,並弄出一個 隱式輸入項 送入要提交的表單中。makepre()
函數涉及了 javascript 的 DOM 編程,挺有意思的。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>添加用戶的視圖</title> <script type="text/javascript" src="${pageContext.request.contextPath }/js/ShowCalendar.js"></script> <script type="text/javascript"> function makepre(){ var pres = document.getElementsByName("pre"); var preference = ""; for(var i = 0; i < pres.length; i++){ var input = pres[i]; if(input.checked==true){ preference = preference + input.value + ","; } } // 組裝字符串 跳舞,打麻將,看鳳姐 preference = preference.substr(0, preference.length - 1); var form = document.getElementById("form"); var input = document.createElement("input"); input.type = "hidden"; input.name = "preference"; input.value = preference; form.appendChild(input); return true; } </script> </head> <body style="text-align:center;"> <br /> <form id="form" action="${pageContext.request.contextPath}/servlet/AddCustomerServlet" method="post" onsubmit="return makepre()"> <!-- js代碼,當按submit按鈕時,就調用makepre()方法 --> <table border="1" width="30%"> <tr> <td>客戶姓名</td> <td><input type="text" name="name"></td> </tr> <tr> <td>性別</td> <td><c:forEach var="gender" items="${genders}"> <input type="radio" name="gender" value="${gender}"> ${gender} </c:forEach></td> </tr> <tr> <td>生日</td> <td><input type="text" name="birthday" onClick="showCalendar(this.id)" id="birthday"></td> </tr> <tr> <td>手機</td> <td><input type="text" name="cellphone"></td> </tr> <tr> <td>郵箱</td> <td><input type="text" name="email"></td> </tr> <tr> <td>愛好</td> <td><c:forEach var="p" items="${preferences}"> <input type="checkbox" name="pre" value="${p}">${p} </c:forEach></td> </tr> <tr> <td>客戶類型</td> <td><c:forEach var="t" items="${types}"> <input type="radio" name="type" value="${t}">${t} </c:forEach></td> </tr> <tr> <td>客戶備註</td> <td><textarea rows="5" cols="100" name="description"></textarea> </td> </tr> <tr> <td><input type="reset" value="重置"></td> <td><input type="submit" value="添加客戶"></td> </tr> </table> </form> </body> </html>
/message.jsp
以下:
我也把那個顯示日期的 js 控件/WEB-INF/js/ShowCalendar.js
代碼貼出來:
// 日期選擇 // By Ziyue(http://www.web-v.com/) // 使用方法: // <script type="text/javascript" src="${pageContext.request.contextPath }/js/ShowCalendar.js"></script> // <input name="birthday" type="text" id="birthday" title="點擊選擇" onClick="showCalendar(this.id)"> var today; document .writeln("<div id='Calendar' style='position:absolute; z-index:1; visibility: hidden; filter:\"progid:DXImageTransform.Microsoft.Shadow(direction=135,color=#999999,strength=3)\"'></div>"); function getDays(month, year) { var daysInMonth = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); // 下面的這段代碼是判斷當前是不是閏年的 if (1 == month) return ((0 == year % 4) && (0 != (year % 100))) || (0 == year % 400) ? 29 : 28; else return daysInMonth[month]; } function getToday() { // 獲得今天的年,月,日 this.now = new Date(); this.year = this.now.getFullYear(); this.month = this.now.getMonth(); this.day = this.now.getDate(); } function getStringDay(str) { // 獲得輸入框的年,月,日 var str = str.split("-"); this.now = new Date(parseFloat(str[0]), parseFloat(str[1]) - 1, parseFloat(str[2])); this.year = this.now.getFullYear(); this.month = this.now.getMonth(); this.day = this.now.getDate(); } function newCalendar() { var parseYear = parseInt(document.all.Year.options[document.all.Year.selectedIndex].value); var newCal = new Date(parseYear, document.all.Month.selectedIndex, 1); var day = -1; var startDay = newCal.getDay(); var daily = 0; if ((today.year == newCal.getFullYear()) && (today.month == newCal.getMonth())) day = today.day; var tableCal = document.all.calendar; var intDaysInMonth = getDays(newCal.getMonth(), newCal.getFullYear()); for (var intWeek = 1; intWeek < tableCal.rows.length; intWeek++) for (var intDay = 0; intDay < tableCal.rows[intWeek].cells.length; intDay++) { var cell = tableCal.rows[intWeek].cells[intDay]; if ((intDay == startDay) && (0 == daily)) daily = 1; if (day == daily) // 今天,調用今天的Class { cell.style.background = '#6699CC'; cell.style.color = '#FFFFFF'; // cell.style.fontWeight='bold'; } else if (intDay == 6) // 週六 cell.style.color = 'green'; else if (intDay == 0) // 週日 cell.style.color = 'red'; if ((daily > 0) && (daily <= intDaysInMonth)) { cell.innerText = daily; daily++; } else cell.innerText = ""; } } function GetDate(InputBox) { var sDate; // 這段代碼處理鼠標點擊的狀況 if (event.srcElement.tagName == "TD") if (event.srcElement.innerText != "") { sDate = document.all.Year.value + "-" + document.all.Month.value + "-" + event.srcElement.innerText; eval("document.all." + InputBox).value = sDate; HiddenCalendar(); } } function HiddenCalendar() { // 關閉選擇窗口 document.all.Calendar.style.visibility = 'hidden'; } function showCalendar(InputBox) { var months = new Array("一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"); var days = new Array("日", "一", "二", "三", "四", "五", "六"); var x, y, intLoop, intWeeks, intDays; var DivContent; var year, month, day; var o = eval("document.all." + InputBox); var thisyear; // 真正的今年年份 thisyear = new getToday(); thisyear = thisyear.year; today = o.value; if (isDate(today)) today = new getStringDay(today); else today = new getToday(); // 顯示的位置 x = o.offsetLeft; y = o.offsetTop; while (o = o.offsetParent) { x += o.offsetLeft; y += o.offsetTop; } document.all.Calendar.style.left = x + 2; document.all.Calendar.style.top = y + 20; document.all.Calendar.style.visibility = "visible"; // 下面開始輸出日曆表格(border-color:#9DBAF7) DivContent = "<table border='0' cellspacing='0' style='border:1px solid #0066FF; background-color:#EDF2FC'>"; DivContent += "<tr>"; DivContent += "<td style='border-bottom:1px solid #0066FF; background-color:#C7D8FA'>"; // 年 DivContent += "<select name='Year' id='Year' onChange='newCalendar()' style='font-family:Verdana; font-size:12px'>"; for (intLoop = thisyear - 35; intLoop < (thisyear + 2); intLoop++) DivContent += "<option value= " + intLoop + " " + (today.year == intLoop ? "Selected" : "") + ">" + intLoop + "</option>"; DivContent += "</select>"; // 月 DivContent += "<select name='Month' id='Month' onChange='newCalendar()' style='font-family:Verdana; font-size:12px'>"; for (intLoop = 0; intLoop < months.length; intLoop++) DivContent += "<option value= " + (intLoop + 1) + " " + (today.month == intLoop ? "Selected" : "") + ">" + months[intLoop] + "</option>"; DivContent += "</select>"; DivContent += "</td>"; DivContent += "<td style='border-bottom:1px solid #0066FF; background-color:#C7D8FA; font-weight:bold; font-family:Wingdings 2,Wingdings,Webdings; font-size:16px; padding-top:2px; color:#4477FF; cursor:hand' align='center' title='關閉' onClick='javascript:HiddenCalendar()'>S</td>"; DivContent += "</tr>"; DivContent += "<tr><td align='center' colspan='2'>"; DivContent += "<table id='calendar' border='0' width='100%'>"; // 星期 DivContent += "<tr>"; for (intLoop = 0; intLoop < days.length; intLoop++) DivContent += "<td align='center' style='font-size:12px'>" + days[intLoop] + "</td>"; DivContent += "</tr>"; // 天 for (intWeeks = 0; intWeeks < 6; intWeeks++) { DivContent += "<tr>"; for (intDays = 0; intDays < days.length; intDays++) DivContent += "<td onClick='GetDate(\"" + InputBox + "\")' style='cursor:hand; border-right:1px solid #BBBBBB; border-bottom:1px solid #BBBBBB; color:#215DC6; font-family:Verdana; font-size:12px' align='center'></td>"; DivContent += "</tr>"; } DivContent += "</table></td></tr></table>"; document.all.Calendar.innerHTML = DivContent; newCalendar(); } function isDate(dateStr) { var datePat = /^(\d{4})(\-)(\d{1,2})(\-)(\d{1,2})$/; var matchArray = dateStr.match(datePat); if (matchArray == null) return false; var month = matchArray[3]; var day = matchArray[5]; var year = matchArray[1]; if (month < 1 || month > 12) return false; if (day < 1 || day > 31) return false; if ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) return false; if (month == 2) { var isleap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); if (day > 29 || (day == 29 && !isleap)) return false; } return true; }
把 request 中表單信息封裝成javabean需創建一個cn.wk.utils.WebUtils
工具類。因需把request中的一個 String 類型的參數birthday=1999-01-01
轉換成java.utils.Date
對象,需註冊一個轉換器,這玩意對我來講很陌生,代碼以下:
package cn.wk.utils; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.Converter; public class WebUtils { public static <T> T request2Bean(HttpServletRequest request, Class<T> beanClass) { try { T bean = beanClass.newInstance(); // 獲得 request 裏全部數據 Map map = request.getParameterMap(); // 填充 // map{name=aa,password=bb,birthday=1999-01-01} 填充到bean // bean{name=aa,password=bb,birthday=Date} ConvertUtils.register(new Converter() { @Override // 字符串 轉成 java.util.Date 返回 public Object convert(Class type, Object value) { if (value == null) { return null; } String str = (String) value; if (str.trim().equals("")) { return null; } SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); try { return df.parse(str); } catch (ParseException e) { throw new RuntimeException(e); } } }, Date.class); BeanUtils.populate(bean, map); return bean; } catch (Exception e) { throw new RuntimeException(e); } } public static String generateID(){ return UUID.randomUUID().toString(); } }
head.jsp 中的另外一個按鈕「查看客戶」,跳轉到一個控制層的servlet上,cn.wk.web.controller.ListCustomerServlet
代碼以下:
package cn.wk.web.controller; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.wk.service.BusinessService; import cn.wk.service.impl.BusinessServiceImpl; // 獲得全部客戶顯示 public class ListCustomerServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { BusinessService service = new BusinessServiceImpl(); List list = service.getAllCustormer(); req.setAttribute("list", list); req.getRequestDispatcher("/WEB-INF/jsp/listcustomer.jsp").forward( req, resp); } catch (Exception e) { e.printStackTrace(); req.setAttribute("message", "查看客戶失敗!!"); req.getRequestDispatcher("/message.jsp").forward(req, resp); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
經過上面的servlet,除了發生異常時發送「查看客戶失敗!!」的信息到/message.jsp
,無異常時將request轉發給/WEB-INF/jsp/listcustomer.jsp
,其代碼以下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>列出全部客戶</title> </head> <body style="text-align: center;"> <table frame="border" width="85%"> <tr> <td>客戶姓名</td> <td>性別</td> <td>生日</td> <td>手機</td> <td>郵箱</td> <td>愛好</td> <td>類型</td> <td>備註</td> <td>操做</td> </tr> <c:forEach var="c" items="#{requestScope.list}"> <tr> <td>${c.name }</td> <td>${c.gender }</td> <td>${c.birthday }</td> <td>${c.cellphone }</td> <td>${c.email }</td> <td>${c.preference }</td> <td>${c.type }</td> <td>${c.description }</td> <td> <a href="#">修改</a> <a href="#">刪除</a> </td> </tr> </c:forEach> </table> </body> </html>
上述 jsp 頁面仍會顯示在 index.jsp 中name="main"
的分幀裏,緣由是在 head.jsp 中有這樣的定義:
<a href="${pageContext.request.contextPath}/servlet/ListCustomerServlet" target="main">查看客戶</a>
即用戶點擊查看客戶
按鈕,跳轉到控制層的ListCustomerServlet
這個servlet上,而後request又由該servlet轉發到/WEB-INF/jsp/listcustomer.jsp
這個數據展現頁面上,而這個展現頁面仍受 head.jsp 中的target="main"
的控制,最終拼接在 index.jsp 頁面指定的位置上。 真夠複雜的了。