Ajax实现在线聊天室

来源:互联网 发布:买大班模型 淘宝店 编辑:程序博客网 时间:2024/04/29 12:39

功能实现及开发过程可能遇到的问题

  • 发送聊天的消息和实时显示聊天的消息一定要分开处理!
  • 发送聊天消息如何实现?
    ①在多行文本框上面绑定“按下键盘[keypress]”事件,在用户按回车键时发送Ajax请求
    ②接收到Ajax请求后,保存聊天记录信息
  • 实时显示聊天记录如何实现?
    ①困难:
    [1]服务器端不会主动的告诉浏览器有新的聊天记录了,而是只能被动的响应浏览器的请求
    [2]浏览器不知道服务器端什么时候产生了新的聊天记录
    ②在浏览器端不间断的发送请求,询问服务器现在是否有新的聊天记录了
    如何实现?setTimeout(回调函数的引用,毫秒值);
    ③服务器端接收到浏览器询问后,检查当前是否有新的聊天记录,如果有,返回true
    ④如果询问的结果是true,再发送请求,从服务器端获取新的聊天记录内容
    ⑤如何知道哪些记录是新的呢?
    • 在浏览器端维护一个全局变量,保存本地最新的记录的ID值
    • 在询问服务器时,将本地ID值发送过去
    • 服务器端根据ID值判断哪些记录是最新的

1. 新建AjaxChat,代码目录图如图所示

这里写图片描述

2.主界面index.jsp的代码如下,代码中已带有注释

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title><base href="http://${pageContext.request.serverName }:${pageContext.request.serverPort }${pageContext.request.contextPath }/" /><link rel="stylesheet" type="text/css" href="style/css.css" /><script type="text/javascript" src="script/jquery-1.7.2.js"></script><script type="text/javascript" src="script/dateFormate.js"></script><script type="text/javascript">    $(function(){        //用于保存当前本地最新的聊天记录ID值,初始值为0,目的是第一次加载时获取全部聊天记录        var finalMessageId = 0;        askForNew();        //声明函数:询问服务器端是否存在新的聊天记录        function askForNew() {            $.post("ServletAsk",{"finalMessageId":finalMessageId},function(hasNew){                //注意:这里hasNew是字符串,即使服务器返回的是"false",在if中判断也为true                if(hasNew == "true") {                    //给服务器端发送请求,获取最新的聊天记录                    getNew();                }            },"text");            //注意:一定要使用函数的引用,不能加()            setTimeout(askForNew, 1000);        }        //声明函数:获取新的聊天记录内容        function getNew() {            var $showMessage = $("#showMessage");            $.post("ServletGetNew",{"finalMessageId":finalMessageId},function(newMessage){                /* private Integer messageId;                private String message;                private Date messageTime; */                for(var i = 0; i < newMessage.length; i++) {                    var messageId = newMessage[i].messageId;                    var message = newMessage[i].message;                    var messageTime = newMessage[i].messageTime;                    messageTime = new Date(messageTime).Format("yyyy年MM月dd日 hh:mm:ss");                    var htmlStr = "<div>" + messageTime + " " + message + "</div>";                    //console.log(htmlStr);                    $showMessage.append(htmlStr);                    finalMessageId = messageId;                }                //获取#showMessage对应的DOM对象,通过scrollTop属性设置滚动条的显示位置                $showMessage[0].scrollTop = 10000000;            },"json");        }        //给多行文本框绑定键盘按下事件        $("#sendMessage").keypress(function(event){            //在用户按下回车键时,发送聊天消息            //通过事件对象的keyCode属性获取当前按下的键对应的ASCII码            if(event.keyCode == 13) {                //获取聊天消息的内容                var message = $.trim(this.value);                //使用Ajax技术将聊天消息发送到服务器端                $.post("ServletSay",{"message":message});                //清空多行文本框                this.value = "";            }        });    });</script></head><body>    <img src="image/logo.gif" />    <div id="showMessage"></div>    <textarea id="sendMessage"></textarea></body></html>

3.Message类的代码如下

package com.atguigu.liaotian.bean;import java.util.Date;public class Message {    private Integer messageId;    private String message;    private Date messageTime;    public Message() {    }    public Message(Integer messageId, String message, Date messageTime) {        super();        this.messageId = messageId;        this.message = message;        this.messageTime = messageTime;    }    @Override    public String toString() {        return "Message [messageId=" + messageId + ", message=" + message                + ", messageTime=" + messageTime + "]";    }    public Date getMessageTime() {        return messageTime;    }    public void setMessageTime(Date messageTime) {        this.messageTime = messageTime;    }    public Integer getMessageId() {        return messageId;    }    public void setMessageId(Integer messageId) {        this.messageId = messageId;    }    public String getMessage() {        return message;    }    public void setMessage(String message) {        this.message = message;    }}

4.Dao基类使用反射机制实现对象的增删改查

package com.atguigu.liaotian.dao;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.List;import org.apache.commons.dbutils.DbUtils;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.atguigu.liaotian.utils.JDBCUtils;/** * Dao基类 * 增删改查 * @author Administrator * */public class BaseDao<T> {    private QueryRunner runner = new QueryRunner();    private Class<T> beanType = null;    public BaseDao() {        Class clazz = this.getClass();        Class superclass = clazz.getSuperclass();        Type type = clazz.getGenericSuperclass();        if(type instanceof ParameterizedType) {            ParameterizedType pt = (ParameterizedType) type;            Type[] typeArguments = pt.getActualTypeArguments();            Type realType = typeArguments[0];            if(realType instanceof Class) {                beanType = (Class<T>) realType;            }        }    }    public Integer insertWithId(String sql, Object ... param) {        Integer id = null;        //1.获取数据库连接        Connection connection = JDBCUtils.getConnection();        //2.获取PreparedStatement对象        PreparedStatement ps = null;        //3.获取ResultSet对象用来保存返回的自增ID的值        ResultSet rs = null;        try {            //在获取PreparedStatement对象时,通过附加另外一个参数的方式将PreparedStatement对象            //设置为返回自增主键的模式            ps = connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);            for (int i = 0; i < param.length; i++) {                ps.setObject(i+1, param[i]);            }            ps.execute();            //自增的主键是以结果集形式返回的            rs = ps.getGeneratedKeys();            if(rs.next()) {                id = rs.getInt(1);            }        } catch (SQLException e) {            e.printStackTrace();        } finally {            //4.释放资源            JDBCUtils.releaseConnection(connection);            try {                DbUtils.close(ps);            } catch (SQLException e) {                e.printStackTrace();            }            try {                DbUtils.close(rs);            } catch (SQLException e) {                e.printStackTrace();            }        }        return id;    }    /**     * 执行批量更新操作     * @param sql     * @param params 执行批量操作时的SQL语句的参数,是二维数组类型     * 在非批量处理的情况下,SQL语句的参数是一维数组,对应SQL语句的一次执行     * 在批量处理的情况下,SQL语句的参数是二维数组,对应SQL语句的多次执行     *      其中每一个一维数组和一条SQL语句是对应的     *      第二维数组的每一个元素分别对应SQL语句中的一个参数     */    public void batchUpdate(String sql, Object [] ... params ) {        Connection connection = JDBCUtils.getConnection();        try {            runner.batch(connection, sql, params);        } catch (SQLException e) {            e.printStackTrace();        }        JDBCUtils.releaseConnection(connection);    }    /**     * 获取单一值的方法,声明的泛型参数是根据接收返回值的变量的类型传入的     * 如果执行的是COUNT()函数需要注意它返回的是Long包装类型     * @param sql     * @param params     * @return     */    public <E> E getSingleValue(String sql, Object ... params) {        E e = null;        Connection connection = JDBCUtils.getConnection();        try {            e = (E) runner.query(connection, sql, new ScalarHandler(), params);        } catch (SQLException e1) {            e1.printStackTrace();        }        JDBCUtils.releaseConnection(connection);        return e;    }    /**     * 查询数据库返回实体类对象的集合     * @param sql     * @param params     * @return实体类对象的集合     */    public List<T> getBeanList(String sql, Object ... params) {        Connection connection = JDBCUtils.getConnection();        List<T> list = null;        try {            list = runner.query(connection, sql, new BeanListHandler<T>(beanType), params);        } catch (SQLException e) {            e.printStackTrace();        }        JDBCUtils.releaseConnection(connection);        return list;    }    /**     * 返回单一对象的查询方法     * @param sql     * @param params     * @return 将数据库查询结果封装得到的对象     */    public T getBean(String sql, Object ... params) {        T t = null;        Connection connection = JDBCUtils.getConnection();        try {            t = runner.query(connection, sql, new BeanHandler<T>(beanType), params);        } catch (SQLException e) {            e.printStackTrace();        }        JDBCUtils.releaseConnection(connection);        return t;    }    /**     * 执行增删改的通用方法     * @param sql     * @param params     */    public void update(String sql, Object ... params) {        Connection connection = JDBCUtils.getConnection();        try {            runner.update(connection, sql, params);        } catch (SQLException e) {            e.printStackTrace();        }        JDBCUtils.releaseConnection(connection);    }}

5.BaseDao基类的实现类MessageDao类,实现Message对象的增删改查

package com.atguigu.liaotian.dao;import java.util.List;import com.atguigu.liaotian.bean.Message;public class MessageDao extends BaseDao<Message>{    //将聊天记录信息保存到数据库中    public void saveMessage(Message message) {        String sql = "INSERT INTO MESSAGE (MESSAGE_CONTENT,MESSAGE_TIME) VALUES(?,?)";        this.update(sql, message.getMessage(), message.getMessageTime());    }    //根据浏览器传入的本地最新消息ID查询比本地消息还要新的聊天记录    public List<Message> getNewMessage(String finalMessageId) {        String sql = "SELECT `MESSAGE_ID` messageId,`MESSAGE_CONTENT` message,`MESSAGE_TIME` messageTime FROM `message` WHERE MESSAGE_ID>? ORDER BY MESSAGE_ID";        return this.getBeanList(sql, finalMessageId);    }    //根据浏览器传入的本地最新消息ID查询是否存在新的聊天记录    public boolean hasNew(String finalMessageId) {        String sql = "SELECT COUNT(*) FROM `message` WHERE `MESSAGE_ID`>?";        long count = this.getSingleValue(sql, finalMessageId);        return count > 0;    }}

6.将聊天记录的值保存到数据库中

public class ServletSay extends HttpServlet {    public void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        // 1. 获取聊天记录消息的值        String msg = request.getParameter("message");        Message message = new Message(null, msg, new Date());        System.out.println(message);        // 2. 将聊天记录的值保存到数据库中        MessageDao messageDao = new MessageDao();        messageDao.saveMessage(message);    }}

7.询问是否有新消息的ServletAsk类代码如下

public class ServletAsk extends HttpServlet {    private static final long serialVersionUID = 1L;    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        //1.获取请求参数:finalMessageId        String finalMessageId = request.getParameter("finalMessageId");        //2.根据finalMessageId查询是否存在最新的聊天记录        MessageDao messageDao = new MessageDao();        boolean hasNew = messageDao.hasNew(finalMessageId);        //3.将布尔类型的返回值以Ajax响应的形式返回给浏览器        response.setContentType("text/html;charset=UTF-8");        PrintWriter writer = response.getWriter();        writer.write(hasNew+"");    }}

8.获取新聊天记录内容,并转换为json格式数据传递到前台

public class ServletGetNew extends HttpServlet {    private static final long serialVersionUID = 1L;    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        //1.获取finalMessageId        String finalMessageId = request.getParameter("finalMessageId");        //2.根据finalMessageId获取新聊天记录内容        MessageDao messageDao = new MessageDao();        List<Message> newMessage = messageDao.getNewMessage(finalMessageId);        //3.转换为JSON字符串返回给浏览器        Gson gson = new Gson();        String json = gson.toJson(newMessage);        System.out.println(json);        response.setContentType("text/json;charset=UTF-8");        PrintWriter writer = response.getWriter();        writer.write(json);    }}

运行界面效果图

这里写图片描述
这里写图片描述
这里写图片描述

项目源代码(点击下面链接)

AjaxChat源代码

0 0
原创粉丝点击