JavaWeb-News-框架前滴最后一次“打怪升级”

话说:

各位读者,下午好!JavaWeb_news前面一直升级,升级到增加了分页和文件上传,今天在写Web框架前做最后一次“打怪升级”,整体原则是把框架底层的一些东西用之前的各种版本串联起来实现,继续实现新闻列表显示。这里主要展现新闻显示,文件上传及分页暂不重复实现。

整体思路是:大体按照MVC设计模式,实现接口编程。增加一个用户登陆判断用户是否存在的功能,但不做细化。有本书《大话设计模式》,蛮好的,不过不是以Java为案例编写的,可以借鉴。

目录


一、整体布局
二、准备工作
三、设计接口
四、实现dao层
五、实现Controler层(Servlet)
六、页面
七、总结


一、整体布局

JavaWeb-News-框架前滴最后一次“打怪升级”_第1张图片

二、准备工作

1、后台依旧使用news_db数据库和t_news数据表;增加一个数据库
my_news_db和数据表t_user,主要实现后台用户管理。读者可以根据实际情况自行设计。
2、导入的jar包还是和之前一样,导入Tomcat的lib库和jstl-1.2.0.jar
也就是我们的model。

User

package com.hmc.news.model;

/**
 * User:Meice
 * 2017/10/18
 */
public class User {
    private int id;
    private String username;
    private String password;

    public User() {}
    public User(int id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }
    public User(String username,String password) {
        this.username = username;
        this.password = password;

    }


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }


    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

News

package com.hmc.news.model;

/**
 * User:Meice
 * 2017/10/22
 */
public class News {
    private int id;
    private String title;
    private String author;
    private String pic;

    public News () {}

    public News(int id, String title, String author, String pic) {
        this.id = id;
        this.title = title;
        this.author = author;
        this.pic = pic;
    }

    public News(String title, String author, String pic) {
        this.title = title;
        this.author = author;
        this.pic = pic;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    @Override
    public String toString() {
        return "News{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", pic='" + pic + '\'' +
                '}';
    }
}

这两个实体类基本无变化。User实现后台用户管理(只有符合条件的用户才能登陆新闻管理界面)

三、设计接口

这次最核心的变化就是实现接口编程。把一些固定的方法抽象成接口,然后用BaseDao和对应的业务类(UserDao、NewsDao)实现。

IUserDao

    package com.hmc.news.dao;
import com.hmc.news.model.User;

public interface IUserDao {
    //定义获取用户登陆的接口
    public User getUser(String username,String password);

}
--------------------------------------------------------------------------------
编写UserDao,实现该接口,对后台是否存在当前用户做一些列业务逻辑判断。如果用户属于管理员,则允许登陆到新闻管理界面,否则就提示无权限。这里不做细化,仅仅只是体现这样一个接口。

IBaseDao
--------------------------------------------------------------------------------
package com.hmc.news.dao;
import java.util.List;

public interface IBaseDao {
    //这个接口可以定义很多高层次的抽象

    /**
     *  根据参数获取对象
     * 比如,你给我一个username,password这2个参数,我就能给你返回一个User对象
     * 其他对象也是可以的
     */
     T getByParam(String sql,Object... objects);

    /**
     * 获取对象列表
     */
    List  getList(String sql);

    //同样分页也可以这么封装
   //Pager getPager(int pageIndex,int pageSize);
    //把整个分页作为一个对象传入
    //Pager getPager(Pager pager);


    /**
     * 新增
     */
    int add(String sql,Object... objects);

    /**
     * 修改
     */
    int update(String sql,Object... objects);

    /**
     * 删除
     */
    int del(String sql,int id);

}

这个接口高度抽象了我们前面所能想到的所有方法:
查询单个对象、所有对象以及CUD.

INewsDao

package com.hmc.news.dao;

import com.hmc.news.model.News;

public interface INewsDao extends IBaseDao<News>  {

}

写好IBaseDao、BaseDao之后,在实现News对象就非常方便了。不论你给我什么对象,我都可以秒秒钟实现CURD.

四、实现dao层

几个Dao层类的顺序是,首先BaseDao==>JdbcDao==>UserDao==>NewsDao
其中JdbcDao和之前升级后的版本一样,BaseDao增加了最为核心的一个方法:获取泛型化参数

BaseDao

package com.hmc.news.dao;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

/**
 * User:Meice
 * 2017/10/18
 */
public class BaseDao extends JdbcDao implements IBaseDao {

  //定义获取泛型化参数的方法getCls()
   private Class getCls() {
        //获取泛型化父类
        Type type =this.getClass().getGenericSuperclass();
        System.out.println(type);
        //获取泛型化参数类型
        ParameterizedType pt = (ParameterizedType)type;
        Class cls = (Class)pt.getActualTypeArguments()[0];
        return cls;

    }

    @Override
    public T getByParam(String sql,Object... params) {
       List list = (List) executeQuery(sql,getCls(),params);
       list =(List) executeQuery(sql,getCls(),params);
       if(list != null && list.size()>0) {
           return  list.get(0);
       }
        return null;
    }

    @Override
    public List getList(String sql) {
        List list = (List) executeQuery(sql,getCls(),null);
        return list;
    }


    @Override
    public int add(String sql, Object... params) {
        return executeCUD(sql,params);

    }

    @Override
    public int update(String sql, Object... params) {
        return executeCUD(sql,params);
    }

    @Override
    public int del(String sql, int id) {
        return executeCUD(sql,id);
    }
}

JdbcDao

package com.hmc.news.dao;
import com.hmc.news.model.News;
import com.hmc.news.util.ConfigUtil;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * User:Meice
 * 2017/10/18
 */
public class JdbcDao {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    //定义静态语句块加载JDBC驱动
    static{
        try {
            Class.forName(ConfigUtil.getPro("driver"));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    //定义查询对象的方法executeQuery()
    public List executeQuery(String sql,Class cls,Object... params) {
        //定义一个object类型的集合,用于存放查询的数据
        List list = new ArrayList<>();
            conn = getConn();
            ps = getPs(sql,params);
            //执行查询
        try {
            rs = ps.executeQuery();
            while (rs.next()) {
                //解决问题1、如何获取数据库字段名?2、列名和对象属性之间关系如何建立?ResultSetMetaData
                //数据表的字段怎么获取呢?就在元表里面。元表怎么知道的呢?因为你给了SQL语句,并且已经执行了,所以可以通过这个接口拿到
                ResultSetMetaData rsmd = rs.getMetaData();
                //获取有多少字段,便于遍历为每个字段赋值
                int count =  rsmd.getColumnCount();
                //每一行数据就是一个对象
                Object obj = cls.newInstance();
                //遍历每一行数据(对象)的每个字段,获取列值
                for(int i=1;i<=count;i++) {
                   String colName = rsmd.getColumnName(i);
                    Object colVal = rs.getObject(colName);
                    /**
                     *因为表的列名和类的属性名一致,因此通过反射对象Field
                     * 设置以后就是为属性赋值了
                     * Java中数据库表的列名和类的属性就是通过这个反射机制建立起关系的
                     */
                  Field f = cls.getDeclaredField(colName);
                    /**
                     * 两个参数
                     * 第一个:对象
                     * 第二个:值
                     * 注意:类的字段一般为private,为避免报错:访问检查,所以设置一下访问权限
                     */
                    f.setAccessible(true);
                  f.set(obj,colVal);
                }
                //把一个完整对象添加到集合中
                list.add(obj);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } finally {
            closeJDBC(rs,ps,conn);
        }
        return list;
    }


    //定义查询单个对象的方法executeQueryOne()
    public Object executeQueryOne(String sql,Class cls,Object... params) {
        conn = getConn();
        ps = getPs(sql,params);
        //执行查询
        try {
            rs = ps.executeQuery();
            while (rs.next()) {
                //解决问题1、如何获取数据库字段名?2、列名和对象属性之间关系如何建立?ResultSetMetaData
                //数据表的字段怎么获取呢?就在元表里面。元表怎么知道的呢?因为你给了SQL语句,并且已经执行了,所以可以通过这个接口拿到
                ResultSetMetaData rsmd = rs.getMetaData();
                //获取有多少字段,便于遍历为每个字段赋值
                int count =  rsmd.getColumnCount();
                //每一行数据就是一个对象
                Object obj = cls.newInstance();
                //遍历每一行数据(对象)的每个字段,获取列值
                for(int i=1;i<=count;i++) {
                    String colName = rsmd.getColumnName(i);
                    Object colVal = rs.getObject(colName);
                    /**
                     *因为表的列名和类的属性名一致,因此通过反射对象Field
                     * 设置以后就是为属性赋值了
                     * Java中数据库表的列名和类的属性就是通过这个反射机制建立起关系的
                     */
                    Field f = cls.getDeclaredField(colName);
                    /**
                     * 两个参数
                     * 第一个:对象
                     * 第二个:值
                     * 注意:类的字段一般为private,为避免报错:访问检查,所以设置一下访问权限
                     */
                    f.setAccessible(true);
                    f.set(obj,colVal);
                }

              return obj;
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } finally {
            closeJDBC(rs,ps,conn);
        }
        return null;
    }




    //定义CUD方法(增、改、删)

    /**
     * 这个方法可以搞定以下形式的CUD操作
     * insert into t_news (title,author) values (?,?)
     * update t_news set title = ?, author=? where id = ?
     * delete from t_news where id = ?
     */

    public int executeCUD(String sql,Object... params) {
       conn = getConn();
       ps = getPs(sql,params);
        try {
            return   ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            closeJDBC(null,ps,conn);
        }
        return 0;
    }


    //定义获取连接的方法

    /**
     * 加载驱动和获取MySQL连接,最开始都是把几个参数写死,后面
     *修改外从文件中读取
     * 为避免每次获取连接,最好使用数据连接池
     */
    public Connection getConn() {
        try {
            String url = ConfigUtil.getPro("url");
            String user =ConfigUtil.getPro("user");
            String password = ConfigUtil.getPro("password");
            conn = DriverManager.getConnection(url,user,password);
            System.out.println("恭喜你,连接上了....");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }


    //定义关闭资源方法closeJDBC()


    public void closeJDBC(ResultSet rs,PreparedStatement ps,Connection conn ){
        try {
            if(rs != null) rs.close();
            if(ps != null) ps.close();
            if(conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //定义获取PreparedStatement对象的方法getPs()
    /**
     *发现,在预编译SQL和为SQL赋值的时候,这部分在executeCUD()和executeQuery()方法里都会用到,
     * 所以封装起来,便于调用
     */
    public PreparedStatement  getPs(String sql,Object... params) {
        try {
            ps = conn.prepareStatement(sql);
            if(params != null && params.length>0) {
                for(int i=0;i1),params[i]);

                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return ps;
    }


    public static void main(String[] args) {
        //验证连接MySQL是否成功
        JdbcDao jd = new JdbcDao();
        String sql = "select * from t_news";
        System.out.println(jd.executeQuery(sql, News.class,null));

    }


} 
  

UserDao

package com.hmc.news.dao;
import com.hmc.news.model.User;

/**
 * User:Meice
 * 2017/10/18
 */
public class UserDao extends BaseDao<User> implements IUserDao {

    @Override
    public User getUser(String username, String password) {
        String sql = "select * from t_user where username = ? and  password = ?";
        Object[] params = {username,password};
        User user = getByParam(sql,params);
        return user;
    }
}

NewsDao

package com.hmc.news.dao;
import com.hmc.news.model.News;

/**
 * User:Meice
 * 2017/10/22
 */
public class NewsDao  extends BaseDao<News> implements INewsDao {

}

NewsDao只需要继承BaseDao,实现接口INewsDao,什么方法也不用写了。这里既可以深刻体会到接口编程的便利!接口编程最核心的就是可扩展性强。

五、实现Controler层(Servlet)

BaseServlet
这个和之前一样,没有变动。最核心的就是Method方法映射,页面请求什么参数,就能主动调用和参数名相同的方法。

package com.hmc.news.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * User:Meice
 * 2017/10/16
 */
public class BaseServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置编码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        //设置方法映射
      String op = req.getParameter("op");
            if(op != null && !"".equals(op)) {
                try {
                    Method method = this.getClass().getDeclaredMethod(op,HttpServletRequest.class,HttpServletResponse.class);
                    method.invoke(this,req,resp);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }else {
                System.out.println("参数缺失....");
            }
    }

}

NewsServlet

package com.hmc.news.Servlet;
import com.hmc.news.dao.NewsDao;
import com.hmc.news.model.News;
import com.hmc.news.util.StringConvertUtil;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * User:Meice
 * 2017/10/22
 */
@WebServlet(urlPatterns = "/news.do")
public class NewsServlet extends BaseServlet{
    NewsDao nd = new NewsDao();

    //调用显示新闻方法
  public void list(HttpServletRequest req,HttpServletResponse resp) {
      //直接调用方法
      String sql = "select * from t_news";
      List list = (List) nd.executeQuery(sql, News.class,null);
      req.setAttribute("list",list);
      //页面跳转
      try {
          req.getRequestDispatcher("index.jsp").forward(req,resp);
      } catch (ServletException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

  public void add(HttpServletRequest req,HttpServletResponse resp) {
      //接受参数
    String title =  req.getParameter("title");
    String author =  req.getParameter("author");
    //调用方法
      String sql = "insert into t_news (title,author) values (?,?)";
      Object[] params = {title,author};
      nd.executeCUD(sql,params);
      //页面跳转
      try {
          req.getRequestDispatcher("news.do?op=list").forward(req,resp);
      } catch (ServletException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

  //定义显示要修改新闻的方法
  public void updateShow(HttpServletRequest req,HttpServletResponse resp) {
      //接受参数
       String strId = req.getParameter("id");
        int id =  StringConvertUtil.getStr(strId);
      //调用方法
      String sql = "select * from t_news where id = ?";
      Object[] params = {id};
     News news = nd.getByParam(sql,params);
     //设置参数
      req.setAttribute("news",news);
      //页面跳转
      try {
          req.getRequestDispatcher("newsUpdate.jsp").forward(req,resp);
      } catch (ServletException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
  }
  //定义修改新闻方法update()
    public void update(HttpServletRequest req,HttpServletResponse resp) {
      //接收参数
       String strId = req.getParameter("id");
      int id = StringConvertUtil.getStr(strId);
      String title = req.getParameter("title");
      String author = req.getParameter("author");

      //调用方法
        String sql = "update t_news set title=?,author=? where id = ?";
        Object[] params ={title,author,id};
        nd.executeCUD(sql,params);

        //页面跳转
        try {
            req.getRequestDispatcher("news.do?op=list").forward(req,resp);
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

  public void del(HttpServletRequest req,HttpServletResponse resp) {
     String strId = req.getParameter("id");
     int id = StringConvertUtil.getStr(strId);

     String sql = "delete from t_news where id = ?";
     Object[] params = {id};

     nd.executeCUD(sql,params);

      try {
          req.getRequestDispatcher("news.do?op=list").forward(req,resp);
      } catch (ServletException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }


  }


}

工具类:ConfigUtil、StringConvertUtil
工具类:ConfigUtil——数据库连接参数
StringConvertUtil——id转换

ConfigUtil

package com.hmc.news.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * User:Meice
 * 2017/10/21
 */
//为了更加方便调用,我们把它定义为静态的
public class ConfigUtil {
    //定义获取MySQL数据库参数的方法getPro()
   public static String getPro(String name) {
       Properties pro = new Properties();
       //this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
       InputStream is = ConfigUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
       try {
           pro.load(is);
          return pro.getProperty(name);
       } catch (IOException e) {
           e.printStackTrace();
       }
       return null;
   }

    public static void main(String[] args) {
       //测试getPro()方法
        System.out.println(ConfigUtil.getPro("driver"));
    }
}

StringConvertUtil

package com.hmc.news.util;

/**
 * User:Meice
 * 2017/10/22
 */
public class StringConvertUtil {
    public static int getStr(String strId) {
        int id = 0;
        if(strId != null && !"".equals(strId)) {
           id =  Integer.parseInt(strId);
        }else {
            id = 0;
        }
        return id;
    }

}

六、页面

页面就不在赘述。

七、总结


一、获取参数化类型,明白list<问号>与List<梯>的区别;

二、面向接口编程
以在下愚见,接口其实就类似战略,高度抽象化,其他的只是实现。面向接口编程,就是要有一种整体化思维,有一种高屋建瓴的思维方式,而不是被细节所吞没。

三、这次重复写了下,出现不少Bug,都是之前犯过的错误。所以,伤疤要常揭,揭伤疤不要只是看一看,不要只是同情的看一看,要实在的去重复、重复再重复。


好了,晚安!

你可能感兴趣的:(Java,--------JavaEE)