C3P0是一個開源的JDBC連接池。
一、准備工作
1、下載
C3P0工具包
-
c3p0-0.9.5.5.jar (C3P0核心)
-
mchange-commons-java-0.2.19.jar (C3P0依賴)
下載地址:https://sourceforge.net/projects/c3p0/
MySQL的JDBC驅動包
mysql-connector-java-5.1.49.jar
下載地址:https://dev.mysql.com/downloads/connector/j/
DBUtils工具類庫
我們還需要使用DBUtils和C3P0一起配合使用。
下載地址:https://commons.apache.org/proper/commons-dbutils/download_dbutils.cgi
2、導入
將jar包復制到libs文件夾,將jar包添加到庫文件中(右擊jar包 --> Build Path --> Add To Build Path)
3、配置 c3p0-config.xml
在工程的 src 目錄下新建一個名為 c3p0-config 的XML文件(文件名不能自定義,必須在src目錄下)
然后開始配置:
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/db_health</property> <property name="user">root</property> <property name="password">1234</property> </default-config> </c3p0-config>
二、代碼實現
1、utils層
通常都是工具類,如數據庫連接、字符串處理、日期處理等。
編寫C3P0Utils.java工具類。
C3P0Utils類的作用是為我們訪問數據庫提供鏈接。
我們在工具類包com.sdbi.utils下創建一個C3P0Utils.java工具類,代碼如下:
package com.sdbi.utils;
import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3P0Utils { // 通過讀取c3p0-config文件獲取連接池對象 private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 提供一個DataSource數據源 public static DataSource getDataSource() { return dataSource; } // 創建一個ThreadLocal對象,以當前線程作為key private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); // 提供當前線程中的Connection public static Connection getConnection() throws SQLException { Connection conn = threadLocal.get(); // 嘗試從本地的線程中找到connection if (null == conn) { // 如果拿不到或者拿到的不可用 conn = dataSource.getConnection(); // 重新創建一個connection threadLocal.set(conn); // 存儲到ThreadLocal中 } return conn; } }
這里使用ThreadLocal(線程本地變量 / 線程本地存儲)來管理數據庫鏈接。
什么是ThreadLocal?
多線程之間的通信可以通過共享變量來實現(通常以 public static 來修飾共享變量),當有多個線程對共享變量進行操作時,為保證其安全性,我們通常需要對其進行同步處理來保證安全性。但是這又會造成程序執行效率的降低。
在某些情況下,若我們是對共享變量的副本進行操作,而非直接操作其本體,那么就可以在既保證效率的情況下又保證其安全性(如數據庫連接池獲取connection,以及getSession等場景),這個時候我們的主角ThreadLocal就出現了。
ThreadLocal,線程本地變量。ThreadLocal為共享變量在每個線程中都創建了一個副本,每個線程都可以訪問自己內部的副本變量。各個線程之間的變量互不干擾。
ThreadLocal底層相當於一個Map,key用來存儲當前線程,value用來存儲當前線程下共享的數據。常用方法如下:
1、get() 獲取ThreadLocal中當前線程共享變量的值。
2、set(T value) 設置ThreadLocal中當前線程共享變量的值。
3、remove() 移除ThreadLocal中當前線程共享變量的值。
2、service層
供外部調用,對dao,model等進行了包裝。
(1)UserService接口
負責業務邏輯(功能)的設計,是一個接口。
在com.sdbi.service包下創建UserService.java。代碼如下:
package com.sdbi.service;
import java.sql.SQLException; import java.util.List; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import com.sdbi.pojo.Area; import com.sdbi.pojo.Page; import com.sdbi.pojo.User; public interface UserService { User findUserByUsernameAndPassword(String username, String password); void saveUser(User user); List findAllUsers() throws SQLException; User findUserById(String id); void updateUser(User user); void deleteUser(String id); }
(2)UserServiceImpl實現類
對Service接口的具體實現。
在com.sdbi.service.impl包下創建UserServiceImpl.java,實現UserService接口。代碼如下:
package com.sdbi.service.impl;
import java.sql.SQLException; import java.util.List; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import com.sdbi.dao.UserDao; import com.sdbi.dao.impl.UserDaoImpl; import com.sdbi.pojo.Area; import com.sdbi.pojo.Page; import com.sdbi.pojo.User; import com.sdbi.service.UserService; import com.sdbi.utils.ExcelUtil; public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); @Override public User findUserByUsernameAndPassword(String username, String password) { User user = userDao.findUserByUsernameAndPassword(username, password); return user; } @Override public void saveUser(User user) { UserDao userDao = new UserDaoImpl(); userDao.saveUser(user); } @Override public List findAllUsers() throws SQLException { UserDao userDao = new UserDaoImpl(); return userDao.findAllUsers(); } @Override public User findUserById(String id) { UserDao userDao = new UserDaoImpl(); User user = userDao.findUserById(id); return user; } @Override public void updateUser(User user) { UserDao userDao = new UserDaoImpl(); userDao.updateUser(user); } @Override public void deleteUser(String id) { UserDao userDao = new UserDaoImpl(); userDao.deleteUser(id); } }
3、dao層
DAO:Data Acess Object,數據訪問對象。跟數據庫打交道的系統都會有這樣的DAO類,主要的作用:
封裝對數據庫的訪問,常規的增刪改查(CRUD操作)都通過DAO來實現。
(1)UserDao接口
負責數據庫操作(功能)的設計,是一個接口。
在com.sdbi.dao包下創建UserDao.java。代碼如下:
package com.sdbi.dao; import java.sql.SQLException; import java.util.List; import com.sdbi.pojo.Area; import com.sdbi.pojo.Page; import com.sdbi.pojo.User; public interface UserDao { User findUserByUsernameAndPassword(String username, String password); void saveUser(User user); List findAllUsers() throws SQLException; User findUserById(String id); void updateUser(User user); void deleteUser(String id); }
(2)UserDaoImpl實現類
對Dao接口的具體實現,具體的操作數據庫的SQL語句的編寫和執行。
在com.sdbi.dao.impl包下創建UserDaoImpl.java,實現UserDao接口。代碼如下:
package com.sdbi.dao.impl;
import java.sql.SQLException; import java.util.List; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import com.sdbi.dao.UserDao; import com.sdbi.pojo.Area; import com.sdbi.pojo.Page; import com.sdbi.pojo.User; import com.sdbi.utils.C3P0Utils; public class UserDaoImpl implements UserDao { private QueryRunner queryRunner = new QueryRunner(C3P0Utils.getDataSource()); @Override public User findUserByUsernameAndPassword(String username, String password) { String sql = "select * from tb_user where username = ? and password = ?"; Object[] params = new Object[] { username, password }; User user = null; try { user = (User) queryRunner.query(sql, new BeanHandler(User.class), params); } catch (SQLException e) { e.printStackTrace(); } return user; } @Override public void saveUser(User user) { String sql = "insert into tb_user(username,password,sex,birthday,createtime,content) values(?,?,?,?,?,?)"; Object[] params = new Object[] { user.getUsername(), user.getPassword(), user.getSex(), user.getBirthday(), "", "" }; try { queryRunner.update(sql, params); } catch (SQLException e) { e.printStackTrace(); } } @Override public List findAllUsers() { String sql = "select * from tb_user"; List<User> list = null; try { list = queryRunner.query(sql, new BeanListHandler<User>(User.class)); } catch (SQLException e) { e.printStackTrace(); } return list; } @Override public User findUserById(String id) { String sql = "select * from tb_user where id = ?"; User user = null; try { user = queryRunner.query(sql, new BeanHandler<>(User.class), id); } catch (SQLException e) { e.printStackTrace(); } return user; } @Override public void updateUser(User user) { String sql = "update tb_user set username=?,password=?,sex=?,birthday=?,createtime=?,content=? where id=?"; Object[] params = { user.getUsername(), user.getPassword(), user.getSex(), user.getBirthday(), "", "", user.getId() }; try { queryRunner.update(sql, params); } catch (SQLException e) { e.printStackTrace(); } } @Override public void deleteUser(String id) { String sql = "delete from tb_user where id =?"; try { queryRunner.update(sql, id); } catch (SQLException e) { e.printStackTrace(); } } }
我們在Dao的實現類中,使用QueryRunner來操作數據庫的增刪改查操作。QueryRunner是DBUtils的功能之一。
DBUtils是 Apache 組織提供的一個開源 JDBC工具類庫,它是對JDBC的簡單封裝,學習成本極低,並且使用DBUtils能極大簡化JDBC編碼的工作量,同時也不會影響程序的性能。我在單獨的一篇文章中介紹了DBUtils的使用,可以移步過去看看。 DBUtils詳解
4、pojo層
POJO:Plain Ordinary Java Object,簡單的Java對象,實際就是普通JavaBean。
有時pojo層也稱為model層。POJO類中有屬性和get、set方法,但是沒有業務邏輯。
名詞解釋
1、Bean:應用在Spring框架上,所有被Spring管理的類對象就可以將其稱作為Bean。它不僅僅可以包括對象的屬性以及get/set方法,還可以有具體的業務邏輯。
2、Entity:實體,即指數據庫表對應到實體類的映射。
3、POJO:普通Java對象,除了屬性和get/set方法外不包含具體的業務邏輯方法,和Entity區別在於沒有和數據表中字段一一對應。
4、Model:MVC架構中使用,Model的字段要大於Entity的字段,Model主要用作前端頁面數據展示,屬性、字段、類型都可以有改變,但Entity則必須與數據表字段一一對應。
總結: 實際上JavaBean、POJO、 Entity、Model, 都是Java 對象,只不過用於不同場合罷了。
在com.sdbi.pojo包下面創建User.java類,我們只需要定義好這個類的成員變量(屬性),與之對應的get/set方法和toString方法,我們可以使用Eclipse的快速生成代碼功能幫助我們完成,不必自己來寫。
但是一定要注意:
User類中一定要有無參的構造方法,因為commons-dbutils在創建Bean對象的過程中使用的是newInstance()方法,該方法只能調用無參構造。
如果類中沒寫無參構造方法,就不能創建對象,就會報錯。
代碼如下:
package com.sdbi.pojo; import java.util.Date; public class User { private Long id; // 編號 private String username;// 用戶名 private String password;// 登錄密碼 private String name; // 姓名 private Byte sex; // 性別 private String idCard; // 身份證號 private Integer roleId; // 角色id private Date birthday; // 生日 private Integer deptId; // 系部/部門ID private Integer classId; // 教研室/班級ID private String homeAddress; // 家庭住址 private Integer province; // 省 private Integer city; // 市 private Integer district; // 區 private Integer street; // 街道 private String dorm; // 宿舍 public Long getXXX() { return xxx; } public void setXXX(Long xxx) { this.xxx = xxx; } @Override public String toString() { return ......; } }
5、servlet層
我們在com.sdbi.servlet包下定義UserServlet.java 類。
注意,我們在這里並沒有讓UserServlet繼承HttpServlet,重寫里面的doGet()或doPost()方法,而是去繼承了我們自己定義的一個統一的父類BaseServlet。
原因我在另一篇文章中進行了說明,大家可以移步過去看看。BaseServlet詳解
package com.sdbi.servlet; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import java.sql.SQLException; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.ProgressListener; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import com.sdbi.pojo.Area; import com.sdbi.pojo.Page; import com.sdbi.pojo.User; import com.sdbi.service.UserService; import com.sdbi.service.impl.UserServiceImpl; import com.sdbi.utils.DateUtil; @WebServlet("/UserServlet") public class UserServlet extends BaseServlet { private UserService userService = new UserServiceImpl(); // 創建服務層實現類的對象 public String userLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, SQLException { // 獲取JSP提交的用戶名和密碼 String username = request.getParameter("username"); String password = request.getParameter("password"); // 調用業務層功能,根據用戶名查找用戶select * from tb_user where username = ? and password = ? // 並返回用戶 User user = userService.findUserByUsernameAndPassword(username, password); // 調用Service層實現類的方法 // 根據返回的用戶是否為空,判斷用戶是否已經存在,向客戶端響應 if (null != user) { System.out.println("UserServlet.userLogin()...user = " + user.toString()); request.getSession().setAttribute("user", user); request.getRequestDispatcher("/manage.jsp").forward(request, response); } else { request.setAttribute("error", "登錄失敗"); request.getRequestDispatcher("/loginNew.jsp").forward(request, response); } return null; } public String userLogout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, SQLException { request.getSession().removeAttribute("user"); response.sendRedirect("loginNew.jsp"); return null; } public String toAddPage(HttpServletRequest request, HttpServletResponse response) throws Exception { request.getRequestDispatcher("/adduser.jsp").forward(request, response); return null; } public String userAdd(HttpServletRequest request, HttpServletResponse response) throws Exception { User user = new User(); user.setUsername(request.getParameter("username")); user.setPassword(request.getParameter("password")); user.setSex(new Byte(request.getParameter("sex"))); user.setBirthday(DateUtil.parseToDate(request.getParameter("birthday"), DateUtil.yyyyMMdd)); userService.saveUser(user); // 調用Service層實現類的方法 List userList = userService.findAllUsers(); request.setAttribute("userList", userList); request.getRequestDispatcher("/listuser.jsp").forward(request, response); return null; } public List userList(HttpServletRequest request, HttpServletResponse response) throws Exception { try { List userList = userService.findAllUsers(); // 調用Service層實現類的方法 request.setAttribute("userList", userList); request.getRequestDispatcher("/listuser.jsp").forward(request, response); } catch (SQLException e) { e.printStackTrace(); } return null; }
public String toUpdatePage(HttpServletRequest request, HttpServletResponse response) throws Exception { String id = request.getParameter("id"); User user = userService.findUserById(id);// 調用Service層實現類的方法 request.setAttribute("user", user); request.getRequestDispatcher("/updateuser.jsp").forward(request, response); return null; } public String updateUser(HttpServletRequest request, HttpServletResponse response) throws Exception { User user = new User(); user.setId(new Long(request.getParameter("id"))); user.setUsername(request.getParameter("username")); user.setPassword(request.getParameter("password")); user.setSex(new Byte(request.getParameter("sex"))); user.setBirthday(DateUtil.parseToDate(request.getParameter("birthday"), DateUtil.yyyyMMdd)); userService.updateUser(user);// 調用Service層實現類的方法 List userList = userService.findAllUsers(); request.setAttribute("userList", userList); request.getRequestDispatcher("/listuser.jsp").forward(request, response); return null; } public String deleteUser(HttpServletRequest request, HttpServletResponse response) throws Exception { String id = request.getParameter("id"); userService.deleteUser(id); // 調用Service層實現類的方法 List userList = userService.findAllUsers(); request.setAttribute("userList", userList); request.getRequestDispatcher("/listuser.jsp").forward(request, response); return null; } }
三、注意事項:
1、實體類中的成員變量名和數據庫表中字段名要一致