目录
(一)项目概述
(二)开始阶段
问题1:如何创建一个可以连接数据库的JavaWeb项目?
问题2:如何向前端传输想要的信息?
问题3:如何读取数据库中想要得到的数据?
问题4:如何将后端的信息发送给前端页面?
问题5:前端如何向服务端发送带有行为特征的信息?
(三)整合阶段
问题1:如何在不同的IP地址共用同一个数据库?
问题2:如何实现MQTT云端与管理员和硬件功能块之间的互通?
预备方案:
(四)完善阶段
问题1:如何实现jsp页面局部的实时刷新?
(五)结语
(一)项目概述
该项目以三个功能块(Javaweb(智能家居数据显示)、管理员UI(家居设备管理)、Ardurino(家居本居))和两个传输块(Mysql、Mqtt)实现。 笔者在此项目中主要负责编辑JavaWeb前后端语句以及实现JavaWeb功能块与Mysql的数据传输接收。笔者在下文将以整个项目分为三个阶段展开,主要谈谈在各个阶段中遇到的问题和解决的方案。
发开工具:IDEA
(二)开始阶段
笔者所在团队分为三个小组,分别对应实现三个功能块的内容,以下针对JavaWeb功能块提出一些问题。
问题1:如何创建一个可以连接数据库的JavaWeb项目?
在安装完IDEA后,我们新建一个Web模块项目。之后,我们需要添加mysql-connector-java-8.0.25.jar在项目lib中,再编辑Tomcat本地服务器(笔者使用的是8.0.70版本,如果版本过低好像会对项目产生影响)以方便测试项目。
问题2:如何向前端传输想要的信息?
搭建Servlet服务端,在web.xml文件中进行映射匹配。
<servlet>
<servlet-name>loginServlet</servlet-name>
<servlet-class>jspservlet.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>loginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
问题3:如何读取数据库中想要得到的数据?
必须先建立类与其产生连接,之后在服务端后台利用sql语句对数据库进行查询。
package jspservlet.db;
import java.sql.Connection;
import java.sql.DriverManager;
public class DBConnect {
private final String DBDRIVER = "com.mysql.cj.jdbc.Driver" ;
private final String DBURL = "jdbc:mysql://localhost:3306/smarthome2?useSSL=false&serverTimezone=UTC" ;
private final String DBUSER = "本地账号" ;
private final String DBPASSWORD = "密码" ;
private Connection conn = null ;
public DBConnect() {
try{
Class.forName(DBDRIVER) ;
this.conn = DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD) ;
}catch (Exception e){
System.out.println(e.getMessage());
}
}
public Connection getConnection(){
return this.conn ;
}
public void close(){
try{
this.conn.close() ;
}catch (Exception e){ }
}
}
String sql = "需要的sql语句";
PreparedStatement pstmt = null ;
DBConnect dbc = null;
try{
dbc = new DBConnect() ;
pstmt = dbc.getConnection().prepareStatement(sql) ;//运行sql语句
ResultSet rs = pstmt.executeQuery();
while(rs.next()){
rs.getString("根据自己的填写"));//得到select语句对应的内容
}
rs.close() ;
pstmt.close() ;
}
catch (SQLException e){
System.out.println(e.getMessage());
}
finally{
dbc.close() ;
}
问题4:如何将后端的信息发送给前端页面?
在服务端读取到数据库的信息后,完善服务器的doGet方法(页面打开时会自动执行)——利用session.setAttribute或request.setAttribute的方法提供一个类似键值对的信息,之后再重定向以显示指定的某个jsp页面。在该jsp中,可以使用<% %>隐藏域去接收服务器的响应,再以<%= %>的形式打印在jsp的任何位置(可以实现在循环中呈现多个设备,即html语句嵌套在域中)。
后端语句----------------------------------------------------------------------------
public void doGet(HttpServletRequest req,HttpServletResponse res) {
LightDAO dao=new LightDAOImpl();//创建后台实现类,为实现数据查询功能
HttpSession session=req.getSession();
ArrayList<Light> linfo=new ArrayList<Light>();
ArrayList<String> ltype=new ArrayList<>();
try {
session.setAttribute("随便取个名字", 想传的内容);
linfo=dao.allLightid();//后端实现的方法(自己写去)
ltype=dao.allLightType();
session.setAttribute("linfo", linfo);
session.setAttribute("ltype",ltype);
res.sendRedirect("./light.jsp");//将响应交给前端页面
}
catch(Exception e){
e.printStackTrace();
}
}
前端语句----------------------------------------------------------------------------
<% ArrayList<Light> l = (ArrayList<Light>)session.getAttribute("linfo"); %>
<% int i=0;%>
<% while (i<l.size()){ %>
<% Light k=l.get(i); %>
<% String cl=k.getType(); %>
<% for (j=0;j<type.size();j++){ %>
<% String t=type.get(j); %>
<% if (cl.equals(t)){ %>
<div class= "col-lg-4 col-md-6 col-sm-12 single_project cat<%=j+1%>">
<div class="grid_item">
<div class="deneb_img">
<img src=<%= l.get(i).getImg() %> class="img-fluid" alt="">
</div>
<div class="deneb_info">
<h4><a href="./inf?action=light&id=<%=k.getId() %>"><%= l.get(i).getType() %></a></h4>
<p id="zz">brightness:<%= l.get(i).getState() %></p>
</div>
</div>
</div>
<% }}i++;} %>
问题5:前端如何向服务端发送带有行为特征的信息?
这个问题主要是因为想要查询某个具体设备(在Mysql中的id唯一)的响应信息,所以该请求必须携带带有特征的信息,让服务器进行响应。实现方法是使用<a href=""?id=xxx&...>这种形式的超链接请求语句,然后再服务端利用req.getParameter("id")的形式获取id(当然可以取别的名字)后的内容。
<a href="./inf?action=light&id=<%=k.getId() %>"><%= l.get(i).getType() %></a>
String judge=req.getParameter("action");
if (judge!=null) {
if (judge.equals("changeon")) {
}
if (judge.equals("changeoff")) {
}
if (judge.equals("delete")) {
}
}
(三)整合阶段
在笔者JavaWeb功能块与本地Mysql连接可以单独实现之后,笔者开始与另外两个功能块的内容进行对接:首先就是与管理员功能块统一数据库的内容形式(如何利用ip共用同一数据库在后面会讲到),然后就是了解了硬件功能块的运作形式(Arduino单开线程发送和接收信息),最后组员提供了利用WIFI模块实现云端服务器通讯(MQTT传输块)的方法。以下是在讨论时提出的几个问题和相应的解决方式。
问题1:如何在不同的IP地址共用同一个数据库?
在提供数据库的设备上以管理员身份打开Mysql控制台,输入use mysql连接用户表,利用create函数创建一个新的用于连接的用户(记得用flush privileges是操作有效),最后利用grant函数给用户授权,@后的内容可以用‘%’,即任何IP都可以连接。
之后将运行JavaWeb的设备中connect的语句中的localhost替换为提供数据库设备的无线互联网v4IP地址并更改下方用户名和密码的字符串。(运行项目可能会有一个数据库无法连接的报错,在IP地址后加上&allowPublicKeyRetrieval=true可以实现联通。)
注意:在测试之前先用cmd ping下对应的IP,如果ping不进可能要关掉防火墙!!
问题2:如何实现MQTT云端与管理员和硬件功能块之间的互通?
在讨论这个问题之前需要先弄清楚各个功能块之间的逻辑关系,JavaWeb只需要读取Mysql中的内容,管理员需要不断接收云端上发布的内容并将内容写进共用的Mysql中,硬件需要不断向云端发送各个智能家居的传感器当前的数据,可见管理员后台和硬件方面是两条不同的线程,以云端为连接的桥梁。
所以只需要解决桥梁的搭建问题就能实现互通——
在硬件方面,利用WIFI模块和Arduino的语句可以开辟一条线程向MQTT服务器发送带有topic标签的内容(订阅MQTT后可以在网页上实时看到发送过来的数据噢^^),发送的消息会被存在一个broker(类似地址的感觉,互联网这块知识不怎么了解)里。管理员通过MQTT官网提供的资源包在新建Maven架构项目里添加依赖项,然后官方提供的java连接的API,再开辟一条保持连接服务器的新线程(注意要将broker改成自己的),就可以定时获取MQTT云端上的数据啦!
效果图:
重新连接的问题可能是运行太快导致的,可以用sleep减慢速度。
预备方案:
利用蓝牙模块的蓝牙配对与主机产生关联,相当于管理员直接与硬件进行沟通,不经过云端。(存在问题:蓝牙距离较短,大概只有10米远)
做法:
家居启动后,蓝牙会进入持续配对的状态(红灯闪烁),打开管理员设备蓝牙与家居进行配对,之后利用java的bluetooth类在后台发送和接收语句(原理和云端连接相同)。
实物效果图(这里还是用手机连的):发送指令BlueLight_5后的结果
(四)完善阶段
在以上阶段有序进行完之后,笔者想到Mysql数据库实时更新会对JavaWeb的前端会产生一定的影响(数据无法统一)。
问题1:如何实现jsp页面局部的实时刷新?
笔者上网查阅很多的资料(其实是踩了很多坑)之后,钻研出了一个属实有点生硬的方法:
想到js语言可以在后台不断反复运行,那么就可以利用js语句向服务器定时发送请求并接收响应,果然在网上发现了js下的ajax功能,该功能与jsp+servlet原理类似,但是是在js环境下运行,不需要时刻手动点击刷新页面产生事件令页面发生反应。最后只需要在成功收到一次响应的语句后修改一次当前jsp页面标签内的内容,再将整个过程放到一个时刻运行的循环里就可以实现实时刷新啦!wuhu~~!
目前还在修改这部分的代码,所以没法提供源码,见谅。
(五)结语
如果大家在做这个项目的时候遇到什么新的问题,或者对某些问题有更好的更快捷的做法,可以在文章下方评论或者直接私信笔者,文章还会持续更新,谢谢大家!