抛弃框架:基于MVC模式的CRUD和Servlet(最简单的JavaWeb程序)

来源:互联网 发布:美容院调整型内衣 知乎 编辑:程序博客网 时间:2024/06/06 01:41

对于一些小项目模块时,很多时候我们使用繁琐的框架实在有些麻烦(导包多还可能版本不兼容,搭建麻烦),当离开框架后我们如何更好的撸代码呢?本文首先感谢我的老师龙sir的授业指导,文中部分代码和思想来源于我的老师,我只是结合自己的编程经验和实际开发对其进行了总结和修改整理,希望同大家分享,如果有不足之处望大家指出,希望大家能够共同学习和进步!

先上图,基于MVC模式的包结构:


然后聊聊增删改查,开始上Util包代码:

首先数据库配置文件的加载——JdbcUtil

1、(.properties文件的加载方式很多,大家可以上论坛搜索下)

2、单例模式此处建议使用饿汉式减少线程冲突,不建议以下模式,设计模式大家可以参考我上传的 “Java常用设计模式源码”

http://download.csdn.net/detail/zyp689/9828252

package com.zyp168.util;import java.io.IOException;import java.io.InputStream;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;/* * 数据库访问JDBC工具类 */public class JdbcUtil {// ---单例模式---private static JdbcUtil jdbcUtil;private static Properties properties = new Properties();private static String jdbc_driver;private static String jdbc_url;private static String jdbc_user;private static String jdbc_pwd;//懒汉式-单例模式private JdbcUtil() {}public static JdbcUtil getInstance() {if (jdbcUtil == null) {jdbcUtil = new JdbcUtil();}return jdbcUtil;}// ---静态代码块---static {InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("mysqlConfig.properties");try {properties.load(inputStream);} catch (IOException e) {e.printStackTrace();}jdbc_driver = properties.getProperty("driver");jdbc_url = properties.getProperty("url");jdbc_user = properties.getProperty("user");jdbc_pwd = properties.getProperty("pwd");try {Class.forName(jdbc_driver);} catch (ClassNotFoundException e) {e.printStackTrace();}}// ---获取连接---public Connection getConnection() {Connection connection = null;try {connection = DriverManager.getConnection(jdbc_url, jdbc_user, jdbc_pwd);} catch (SQLException e) {e.printStackTrace();}return connection;}// ---关闭资源---public void close(Connection connection, Statement stmt, ResultSet rs) {if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}}

注:单例模式

package com.zyp168.single;/** * 单例模式 *  * 饿汉式的优势在于能够有效解决线程并发问题; static修饰的变量会被作为GC的root根节点不能被回收;static * 字段(类的成员变量)在类的所有实例中只存在一次;static会将被修饰者与类直接产生引用关系,而非与类的实例。 * final修饰的变量值不会改变可节省空间,修饰的类不能被继承。 *  */public class SingleTest {public static void main(String[] args) {// 由于private修饰并重写了构造方法,故不能使用new LSingle()创建对象LSingle.getInstance();ESingle.getInstance();}}/* * 懒汉式 */class LSingle {private static LSingle lSingle = null;private LSingle() {//拒绝外部类new LSingle()方式创建对象}public static LSingle getInstance() {if (lSingle == null) {lSingle = new LSingle();}return lSingle;}}/* * 饿汉式 */class ESingle {// 此处final是为了节省空间,可以不要private static final ESingle eSingle = new ESingle();private ESingle() {//拒绝外部类new ESingle()方式创建对象}public static ESingle getInstance() {return eSingle;}}
注:不同数据库的配置文件格式:(下面是mysql版、 oracle XE版、sqlserver2008 XE版)



其次CRUD模版——JdbcTemplate

package com.zyp168.util;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.ArrayList;import java.util.List;/* * Jdbc的模板:提取出更新和查询方法封装 */public class JdbcTemplate {// 单例模式:static修饰的字段可以直接建立引导关系(不需要使用new其类再调用,可以直接用类.方法的形式调用),且只会被创建一次,位于gc根目录不被回收,会造成内存泄露private static JdbcTemplate jdbcTemplate;private JdbcTemplate() {// 默认构造方法}public static JdbcTemplate getInstance() {if (jdbcTemplate == null) {jdbcTemplate = new JdbcTemplate();}return jdbcTemplate;}// 封装更新操作(增、删、改)public boolean update(String sql, Object[] objArr) {// 初始化成员变量boolean flag = false;Connection con = null;PreparedStatement pstmt = null;// 注册并链接con = JdbcUtil.getInstance().getConnection();try {// pstmtpstmt = con.prepareStatement(sql);if (objArr!=null) {for (int i = 1; i <= objArr.length; i++) {pstmt.setObject(i, objArr[i - 1]);}}// 执行int result = pstmt.executeUpdate();// 受影响行数大于零返回tureflag = (result > 0);} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtil.getInstance().close(con, pstmt, null);}return flag;}// 封装单条查询操作public Object findOneInfo(String sql, Object[] objArr, JdbcMapper mapper) {// 初始化成员变量Connection con = null;PreparedStatement pstmt = null;ResultSet rs = null;Object rsObject = null;// pstmttry {con = JdbcUtil.getInstance().getConnection();pstmt = con.prepareStatement(sql);if (objArr!=null) {for (int i = 1; i <= objArr.length; i++) {pstmt.setObject(i, objArr[i - 1]);}}// 执行rs = pstmt.executeQuery();if (rs.next()) {rsObject = mapper.mappinng(rs);}} catch (SQLException e) {e.printStackTrace();} finally {// 查询JdbcUtil.getInstance().close(con, pstmt, rs);}return rsObject;}// 封装查询多条操作public List<? extends Object> findAllInfo(String sql, Object[] objArr,JdbcMapper mapper) {List<Object> list = new ArrayList<Object>();// 初始化变量Object rsObject = null;// Connection.createStatement() 创建一个 Statement 对象来将 SQL 语句发送到数据库。Connection con = null;// PreparedStatement表示预编译的 SQL 语句的对象,Statement 对象的子接口PreparedStatement pstmt = null;// ResultSet查询获得的数据表,next方法将光标移动到下一行对象(对应数据库表中的行),没有下一行时返回 false;ResultSet rs = null;try {// 注册并链接con = JdbcUtil.getInstance().getConnection();// 创建PreparedStatement对象pstmt = con.prepareStatement(sql);if (objArr!=null) {for (int i = 1; i <= objArr.length; i++) {pstmt.setObject(i, objArr[i - 1]);}}// 执行rs = pstmt.executeQuery();while (rs.next()) {rsObject = mapper.mappinng(rs);list.add(rsObject);}} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtil.getInstance().close(con, pstmt, rs);}return list;}}

然后ORM映射文件——JdbcMapper (想下Hibernate或Mabatis中的xml映射文件是否感觉特别熟悉?大笑

package com.zyp168.util;import java.sql.ResultSet;/* * 对象映射接口 */public interface JdbcMapper {public abstract Object mappinng(ResultSet rs);}

实例:

package com.zyp168.model.mapper;import java.sql.ResultSet;import java.sql.SQLException;import com.zyp168.model.User;import com.zyp168.util.JdbcMapper;public class UserMapper implements JdbcMapper {@Overridepublic Object mappinng(ResultSet rs) {User user = new User();try {//ORM映射 user.setId(rs.getInt("id"));user.setName(rs.getString("name"));user.setPwd(rs.getString("pwd"));} catch (SQLException e) {// 实体类属性和数据库表中字段的映射关系不匹配e.printStackTrace();}return user;}}

实例:原生的CRUD \ 第三方DbUtil包\ JdbcTemplate比较

package com.zyp168.dao.impl;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.ArrayList;import java.util.List;import java.util.Map;import org.apache.commons.dbutils.DbUtils;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanListHandler;import com.zyp168.dao.UserDao;import com.zyp168.model.User;import com.zyp168.util.JdbcTemplate;import com.zyp168.util.JdbcUtil;import com.zyp168.util.PageModel;public class UserDaoImpl implements UserDao {/* * 单例模式--饿汉式 */private final static UserDaoImpl userDaoImpl=new UserDaoImpl();private UserDaoImpl() {}public static UserDao getInstance() {return userDaoImpl;}@Overridepublic boolean register(User user) {/** * TODO 原生Jdbc实现更新操作 */boolean flag = false;Connection connection = null;PreparedStatement pstmt = null;// 注册驱动,连接数据库try {Class.forName("com.mysql.jdbc.Driver");connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}// 编写SqlString sql = "INSERT INTO user VALUES(NULL,?,?)";// 预编译Sqltry {pstmt = connection.prepareStatement(sql);pstmt.setObject(1, user.getName());pstmt.setObject(2, user.getPwd());} catch (SQLException e) {e.printStackTrace();}// 执行Sql并返回结果try {int result = pstmt.executeUpdate();// 受影响条数flag = result > 0;} catch (SQLException e) {e.printStackTrace();}// 关闭资源JdbcUtil.getInstance().close(connection, pstmt, null);return flag;}@Overridepublic User login(User user) {/** * TODO 原生Jdbc实现查询操作 */User model = new User();Statement stmt = null;ResultSet rs = null;Connection connection = JdbcUtil.getInstance().getConnection();StringBuffer sbf = new StringBuffer();sbf.append("SELECT * FROM user WHERE name='").append(user.getName()).append("' AND pwd='").append(user.getPwd() + "'");String sql = sbf.toString();// 编译执行Sql并返回结果try {stmt = connection.createStatement();// 返回的是第一行的各属性的Map键值对(键分类存储),其中next()方法将游标移动到下一行位置,如果下一行为空返回falsers = stmt.executeQuery(sql);if (rs.next()) {model.setId(rs.getInt("id"));model.setName(rs.getString("name"));model.setPwd(rs.getString("pwd"));}} catch (SQLException e) {e.printStackTrace();}JdbcUtil.getInstance().close(connection, stmt, rs);return model;}@Overridepublic boolean repwd(User user) {/** * TODO 使用自定义封装的JdbcTemplate+JdbcMapper模板进行操作 */String sql = "UPDATE user SET pwd=? WHERE name=?";Object[] objArr = { user.getPwd(), user.getName() };return JdbcTemplate.getInstance().update(sql, objArr);}@Overridepublic List<User> findByCondition(Map<String, String> condition,PageModel<User> page) {/** * TODO 使用第三方工具包DbUtils实现,API地址如下 * http://commons.apache.org/proper/commons-dbutils/apidocs/ */List<User> list = new ArrayList<User>();Connection connection = JdbcUtil.getInstance().getConnection();StringBuffer sb = new StringBuffer();/* 查询条件集合 */List<Object> objects = new ArrayList<Object>();// 查询所有if (condition == null) {sb.append("SELECT * FROM user WHERE 1 = 1 ");}// 模糊查询if (condition != null) {if (condition.get("name") != null || condition.get("name") != "") {sb.append(" AND name LIKE CONCAT('%',?,'%')");objects.add(condition.get("name"));}}// 分页查询if (page != null) {sb.append("LIMIT ?, ?");objects.add((page.getPageNum() - 1) * page.getPageSize());objects.add(page.getPageSize());}//QueryRunner queryRunner = new QueryRunner();try {list = queryRunner.query(connection, sb.toString(),new BeanListHandler<User>(User.class), objects);} catch (SQLException e) {e.printStackTrace();}DbUtils.closeQuietly(connection);return list;}}

简单说说分页实例(不完整,按实际情况灵活处理)——PageModel、PageTag

package com.zyp168.util;import java.io.Serializable;import java.util.List;/** * 分页要求:根据页码获取该页的数据集合(注:每页数据行数大小已知)  * 分页实体类: pageNum 页码/当前页 pageSize 每页大小 pageCount 总页数 recordCount 总记录数   List<T> 分页查询获得的数据集合  * 主要方法:首页 上一页 下一页 尾页 查询所有获取总记录数 分页查询获取当前页码数据集合 */public class PageModel<T> implements Serializable {// Java序列化private static final long serialVersionUID = 1L;// 分页相关参数private Integer pageNum;private Integer pageSize; //常量private Integer recordCount; //BaseDaoImpl.findAll()查询private Integer pageCount; //可根据上述三参数计算获得: 改写 PageModel<T>.getPageCount() 方法private List<T> pageList; //BaseDaoImpl<T>.queryByPage(PageModel page)查询// 自定义成员方法 :首页 上一页 下一页 尾页  /** * 获取首野 * @return 1 */public Integer getFirstPage() {return 1;}/** * 获取上一页 * @return */public Integer getPreviousPage() {if (pageNum <= 1) {return 1;} else {return pageNum - 1;}}/** * 获取下一页 * @return */public Integer getNextPage() {if (pageNum >= this.getPageCount()) {return this.getPageCount();} else {return pageNum + 1;}}/** * 获取末页 * @return */public Integer getLastPage() {return this.getPageCount();}// 构造函数public PageModel() {super();}public PageModel(Integer pageNum, Integer pageSize, Integer recordCount,Integer pageCount, List<T> pageList) {super();this.pageNum = pageNum;this.pageSize = pageSize;this.recordCount = recordCount;this.pageCount = pageCount;this.pageList = pageList;}// getter 和 setter方法 public Integer getPageNum() {return pageNum;}public void setPageNum(Integer pageNum) {this.pageNum = pageNum;}public Integer getPageSize() {return pageSize;}public void setPageSize(Integer pageSize) {this.pageSize = pageSize;}public Integer getRecordCount() {return recordCount;}public void setRecordCount(Integer recordCount) {this.recordCount = recordCount;}/** * 改写默认getPageCount方法 * @return */public Integer getPageCount() {//方法一:if (recordCount % pageSize == 0) {pageCount= recordCount / pageSize;} else {pageCount= recordCount / pageSize + 1;}return pageCount;//方法二://returnpageCount = (recordCount + pageSize - 1) / pageSize; }public void setPageCount(Integer pageCount) {this.pageCount = pageCount;}public List<T> getPageList() {return pageList;}public void setPageList(List<T> pageList) {this.pageList = pageList;}public static long getSerialversionuid() {return serialVersionUID;}}

package com.zyp168.util;import java.io.IOException;import javax.servlet.jsp.JspWriter;import javax.servlet.jsp.tagext.SimpleTagSupport;/** * 分页标签:拼写要输出到页面的HTML文本 请求地址 url 分页相关参数 page */public class PageTag<T> extends SimpleTagSupport {// 此处可以继承TagSupport或SimpleTagSupport接口(注意:Java单继承,只能继承一个)private String url; // 请求URIprivate PageModel<T> page; // 分页相关参数public void doTag() {JspWriter out = getJspContext().getOut();// SimpleTagSupport接口// PrintWriter out = response.getWriter();StringBuffer buffer = new StringBuffer();// StringBuilder sbl = new StringBuilder();/** * //拼写要输出到页面的HTML文本 */// 拼接“第一页”和“上一页”if (page.getPageNum()==1) {buffer.append("第一页  上一页");}else {buffer.append("<a href='");buffer.append(url);buffer.append("&pageNum=1'>第一页</a>");buffer.append("<a href='");buffer.append(url);buffer.append("&pageNum=");buffer.append(page.getPageNum()-1);buffer.append("'>上一页</a>");}// 循环拼接页码for (int i = 1; i <= page.getPageCount(); i++) {buffer.append("<a href='");buffer.append(url);buffer.append("&pageNum=");buffer.append(i);buffer.append("'>");buffer.append(i);buffer.append("</a>");}// 拼接“下一页”和“最后一页”if (page.getPageNum()>=page.getPageCount()) {buffer.append("下一页  最后一页");}else {buffer.append("<a href='");buffer.append(url);buffer.append("&pageNum=");buffer.append(page.getPageNum()+1);buffer.append("&'>下一页</a>");buffer.append("<a href='");buffer.append(url);buffer.append("&pageNum=");buffer.append(page.getPageCount());buffer.append("'>最后一页</a>");}// 拼接共*页buffer.append("  共");buffer.append(page.getPageCount());buffer.append("页");// 向页面输出分页的内容try {out.print(buffer.toString());} catch (IOException e) {e.printStackTrace();}}//public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public PageModel<T> getPage() {return page;}public void setPage(PageModel<T> page) {this.page = page;}}

Web中servlet部分实例:

(1)建议大家将所有jsp页面都放入WEB-INFO下,编写URL过滤器类,并将密码采用MD5加密保证安全,此处不详叙。

(2)大家可以将字符集部分做成过滤器,没必要每次设置字符集(想下spring的字符过滤配置)

(3)建议大家使用注解方式,减少web.xml配置(想下SpringMVC的注解)

(4)截取url地址交给不同方法处理的策略模式,是否让大家想起struct 和 springmvc的风格?

(5)Java反射机制学的比较好的同志们,不如尝试下反向生成对象?(想下Spring的依赖注入和控制反转)

package com.zyp168.web.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import com.zyp168.model.User;import com.zyp168.service.UserService;import com.zyp168.service.impl.UserServiceImpl;@WebServlet(name = "userServlet", urlPatterns = { "/user/*" })public class UserServlet extends HttpServlet {// 成员变量private static final long serialVersionUID = 1L;private UserService userService = new UserServiceImpl();/** * Servlet中main方法:service() doGet() doPost()等 * service()方法没有用static修饰,可以直接调用类内的非静态方法,尽量少使用static; */@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 字符集配置(建议放入字符集过滤器进行设置)request.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");// 解析请求信息URLString uri = request.getRequestURI();// 获取的地址为"/SOSO/user/execute.action"String action = uri.substring(uri.lastIndexOf("/") + 1, uri.lastIndexOf("."));// 匹配逻辑处理方法if ("execute".equals(action)) {execute(request, response);}if ("register".equals(action)) {register(request, response);}if ("login".equals(action)) {// 第一域名相同的单点登陆SSO(Single Sign On)login(request, response);}if ("repwd".equals(action)) {repwd(request, response);}if ("findByCondition".equals(action)) {findByCondition(request, response);}}// 逻辑处理方法private void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {/** * 访问WEB-INF的入口方法 */request.getRequestDispatcher("/WEB-INF/jsp/" + request.getParameter("uri")).forward(request, response);}private String register(HttpServletRequest request, HttpServletResponse response) {return null;}private void login(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {String name = request.getParameter("name");String password = request.getParameter("password");User model = new User();model.setName(name);model.setPwd(password);if (name != null && password != null) {User user = userService.login(model);if (model != null) {if (name.equals(user.getName()) && password.equals(user.getPwd())) {HttpSession session = request.getSession();session.setAttribute("user", user);// SSO相关操作:写入cookieCookie cookie = new Cookie("sso", user.getName());cookie.setMaxAge(3600);// 一小时cookie.setDomain(".zyp168.com");// www.bbs.zyp168.com 和// www.news.zyp168.comcookie.setPath("/"); // 如果为/demo_01表示仅仅demo_01项目可以获得response.addCookie(cookie);response.sendRedirect("/WEB-INF/jsp/index.jsp");}}}String msg = "您输入的帐户或密码有误,请重新登陆!";request.setAttribute("msg", msg);request.getRequestDispatcher("/redirect.jsp").forward(request, response);}private String repwd(HttpServletRequest request, HttpServletResponse response) {return null;}private String findByCondition(HttpServletRequest request, HttpServletResponse response) {return null;}}

本文先写到这里,不足之处希望大家指出,文中实例代码仅供参考,希望大家重思想,不要纠结代码,本文有待完善,希望大家提供宝贵意见,谢谢!

0 0
原创粉丝点击