功能实现及开发过程可能遇到的问题
- 发送聊天的消息和实时显示聊天的消息一定要分开处理!
- 发送聊天消息如何实现?
①在多行文本框上面绑定“按下键盘[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(){
var finalMessageId = 0;
askForNew();
function askForNew() {
$.post("ServletAsk",{"finalMessageId":finalMessageId},function(hasNew){
if(hasNew == "true") {
getNew();
}
},"text");
setTimeout(askForNew, 1000);
}
function getNew() {
var $showMessage = $("#showMessage");
$.post("ServletGetNew",{"finalMessageId":finalMessageId},function(newMessage){
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>";
$showMessage.append(htmlStr);
finalMessageId = messageId;
}
$showMessage[0].scrollTop = 10000000;
},"json");
}
$("#sendMessage").keypress(function(event){
if(event.keyCode == 13) {
var message = $.trim(this.value);
$.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;
Connection connection = JDBCUtils.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
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 {
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());
}
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);
}
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 {
String msg = request.getParameter("message");
Message message = new Message(null, msg, new Date());
System.out.println(message);
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 {
String finalMessageId = request.getParameter("finalMessageId");
MessageDao messageDao = new MessageDao();
boolean hasNew = messageDao.hasNew(finalMessageId);
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 {
String finalMessageId = request.getParameter("finalMessageId");
MessageDao messageDao = new MessageDao();
List<Message> newMessage = messageDao.getNewMessage(finalMessageId);
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源代码