第一章 案例簡(jiǎn)介
設(shè)計(jì)類(lèi)似微信公眾號(hào)的自動(dòng)回復(fù)功能
準(zhǔn)備工作
JSP JSTL EL JS/JQUERY
Servlet JavaBean
JDBC MySQL
第二章 實(shí)戰(zhàn)第一部--黎明前的黑暗
2.1 黎明前的黑暗
案例分析
基本功能 :接受發(fā)送指令 根據(jù)指令自動(dòng)回復(fù)對(duì)應(yīng)的內(nèi)容
模塊劃分 :
- 回復(fù)內(nèi)容維護(hù)
- 對(duì)話功能
- 回復(fù)內(nèi)容列表
- 回復(fù)內(nèi)容刪除
2.2 案例演示
2.3 頁(yè)面跳轉(zhuǎn)
2.4 連接數(shù)據(jù)庫(kù)
2.5 數(shù)據(jù)展示
第三章 實(shí)戰(zhàn)第二部--Mybatis來(lái)襲
3.1 Mybatis的下載并搭建核心架構(gòu)
https://github.com/mybatis/mybatis-3/releases
下載Mybatis jar包及源碼包
**演示代碼中的配置文件的詳細(xì)路徑 : **
src/test/java/org/apache/ibatis/submitted/complex_property/Configuration.xml
復(fù)制到項(xiàng)目的src/com/qezhhnjy/config/Configuration.xml
Mybatis的相關(guān)知識(shí)
Dao的需求
- 對(duì)象能與數(shù)據(jù)庫(kù)交互
- 能執(zhí)行SQL語(yǔ)句
Mybatis中提供給Dao層的處理對(duì)象為SqlSession
SqlSession的作用 :
- 向SQL語(yǔ)句傳入?yún)?shù)
- 執(zhí)行SQL語(yǔ)句
- 獲取執(zhí)行SQL 語(yǔ)句的結(jié)果
- 事務(wù)的控制
如何獲得SqlSession :
1. 通過(guò)配置文件獲取數(shù)據(jù)庫(kù)連接相關(guān)信息
com.qezhhnjy.config.Configuration.xml
<configuration>
<!-- <settings>
<setting name="useGeneratedKeys" value="false"/>
<setting name="useColumnLabel" value="true"/>
</settings>
<typeAliases>
<typeAlias alias="UserAlias" type="org.apache.ibatis.submitted.complex_property.User"/>
</typeAliases>-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/micro_message?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root" />
<!--property標(biāo)簽對(duì)應(yīng)本機(jī)數(shù)據(jù)庫(kù)連接相關(guān)信息-->
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/qezhhnjy/config/sqlxml/Message.xml"/>
<!--mapper標(biāo)簽為配置好的各個(gè)實(shí)體類(lèi)與數(shù)據(jù)表映射xml文件位置-->
</mappers>
</configuration>
2. 通過(guò)配置信息構(gòu)建SqlSessionFactory
com.qezhhnjy.db.DBAccess.getSession();
public class DBAccess {
public SqlSession getSqlSession() throws IOException {
//通過(guò)配置文件獲取數(shù)據(jù)庫(kù)連接信息
Reader reader = Resources.getResourceAsReader("com/qezhhnjy/config/Configuration.xml");
//通過(guò)配置信息構(gòu)建一個(gè)SqlSessionFactory
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
return ssf.openSession();
}
}
3. 通過(guò)SqlSessionFactory打開(kāi)數(shù)據(jù)庫(kù)會(huì)話
com.qezhhnjy.dao.MessageListDao.mybatisQuery(...);
public static List<Message> mybatisQuery(String command,String description){
DBAccess dbAccess = new DBAccess();
List<Message> messageList = new ArrayList<>();
SqlSession sqlSession = null;
try {
sqlSession = dbAccess.getSqlSession();
messageList = sqlSession.selectList("Message.find");
//Message對(duì)應(yīng)message.xml中mapper的屬性namespace
//find對(duì)應(yīng)message.xml中select標(biāo)簽的id
} catch (IOException e) {
e.printStackTrace();
}finally {
if(sqlSession !=null)sqlSession.close();
}
return messageList;
}
3.2 SQL基本配置與執(zhí)行
src/test/java/org/apache/ibatis/submitted/complex_property/User.xml
將該文件復(fù)制到項(xiàng)目的
src/com/qezhhnjy/config/sqlxml/Message.xml
<mapper namespace="Message">
<!--由于項(xiàng)目中所有select標(biāo)簽的id都不能重名.
為了加以區(qū)分, 使用namespace屬性來(lái)設(shè)置,不同namespace下的id可以相同.
類(lèi)似java中包的定義及使用-->
<resultMap type="UserAlias" id="UserResult">
<id column="id" jdbcType="INTEGER" property="id"/>//主鍵
<result column="username" jdbcType="VARCHAR" property="username"/>
<!--column對(duì)應(yīng)數(shù)據(jù)表列名 property對(duì)應(yīng)實(shí)體類(lèi)屬性名 jdbcType對(duì)應(yīng)java.sql.Types類(lèi)中的常量名-->
<result column="password" jdbcType="VARCHAR" property="password.encrypted"/>
<result column="administrator" jdbcType="BOOLEAN" property="administrator"/>
</resultMap>
<select id="find" parameterType="long" resultMap="UserResult">
SELECT * FROM user WHERE id = #{id:INTEGER}
</select>
<!--原文檔中有多個(gè)功能各異的select查詢標(biāo)簽-->
</mapper>
Message類(lèi)對(duì)應(yīng)的全查詢語(yǔ)句和實(shí)體類(lèi)映射
<resultMap type="com.qezhhnjy.bean.Message" id="MessageResult">
<id column="id" jdbcType="VARCHAR" property="id"/>
<result column="command" jdbcType="VARCHAR" property="command"/>
<result column="description" jdbcType="VARCHAR" property="description"/>
<result column="content" jdbcType="VARCHAR" property="content"/>
</resultMap>
<select id="find" resultMap="MessageResult">
SELECT id,command,description,content FROM MESSAGE WHERE 1=1
</select>
第四章 實(shí)戰(zhàn)第三部
4.1 動(dòng)態(tài)SQL拼接
OGNL是一個(gè)強(qiáng)大的表達(dá)式語(yǔ)言
OGNL表達(dá)式語(yǔ)言詳解
http://blog.csdn.net/yu102655/article/details/52179695
http://blog.csdn.net/hello_xusir/article/details/53423399
配置SQL拼接實(shí)現(xiàn)字段查詢和模糊查詢
1. 將用戶輸入的查詢內(nèi)容封裝并傳遞給mybatis
com.qezhhnjy.dao.MessageListDao.mybatisQuery(...);
...
Message temp = new Message();
temp.setCommand(command);
temp.setDescription(description);
try {
sqlSession = dbAccess.getSqlSession();
messageList = sqlSession.selectList("Message.find",temp);
...
//只截取了與全查詢不同的部分. 通過(guò)封裝對(duì)應(yīng)屬性的輸入值到實(shí)體類(lèi), 然后傳遞給mybatis,之后配置Message.xml來(lái)映射對(duì)應(yīng)屬性到sql語(yǔ)句中.
2. 完善配置Message.xml
Message.xml
<select id="find" parameterType="com.qezhhnjy.bean.Message" resultMap="MessageResult">
//parameterType屬性即表示傳遞進(jìn)來(lái)的對(duì)象類(lèi)型
SELECT id,command,description,content FROM MESSAGE WHERE 1=1
<if test="command != null && !"".equals(command.trim())">
AND command = #{command}
</if>
<!--if標(biāo)簽test屬性為OGNL語(yǔ)言, 可以識(shí)別java屬性及方法,但是""和&(&)要進(jìn)行轉(zhuǎn)義,&&也可以使用and 關(guān)鍵字代替.
if標(biāo)簽內(nèi)容為銜接前面的sql語(yǔ)句, 為mybatis來(lái)識(shí)別,且不必刻意空格來(lái)避免錯(cuò)誤.-->
<if test="description != null and !"".equals(description.trim())">
AND description LIKE '%' #{description} '%'
</if>
<!--模糊查詢的寫(xiě)法容易出現(xiàn)錯(cuò)誤,%前后要加上'',并且#{...}前后仍然要注意空格-->
</select>
4.2 應(yīng)用log4j調(diào)試動(dòng)態(tài)SQL
在官網(wǎng)下載的mybatis文件目錄下有l(wèi)og4j的jar包
Mybatis 3.4.4/mybatis-3.4.4/lib/log4j-1.2.17.jar
log4j的配置文件: log4j.properties(mybatis源碼包)
Mybatis 3.4.4/mybatis-3-mybatis-3.4.4/src/test/java/log4j.properties
配置文件默認(rèn)加載到src/根目錄下.
### Global logging configuration
log4j.rootLogger=ERROR, stdout
//配置日志輸出級(jí)別和位置,級(jí)別(debug<info<warn<error),如果設(shè)置為INFO,則會(huì)輸出info,warn,error的信息,如此.
### Uncomment for MyBatis logging
log4j.logger.org.apache=INFO
### 將org.apache包的日志輸出級(jí)別單獨(dú)設(shè)置為INFO,也可以再單獨(dú)設(shè)置輸出位置.
### Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %5p [%t] - %m%n
### 格式化輸出, %d表示當(dāng)前時(shí)間, %p(priority)輸出優(yōu)先級(jí),%t表示當(dāng)前線程, %m表示具體信息, %n換行.
在mybatis的org.apache.ibatis.logging.jdbc包中,有jdbc各大類(lèi)對(duì)象對(duì)應(yīng)的日志輸出類(lèi).
org.apache.ibatis.logging.jdbc.CollectionLogger
類(lèi)的源碼
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {
private Connection connection;
private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.connection = conn;
}
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if(Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
} else {
PreparedStatement stmt;
if("prepareStatement".equals(method.getName())) {
if(this.isDebugEnabled()) {
this.debug(" Preparing: " + this.removeBreakingWhitespace((String)params[0]), true);
}
stmt = (PreparedStatement)method.invoke(this.connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, this.statementLog, this.queryStack);
return stmt;
} else if("prepareCall".equals(method.getName())) {
if(this.isDebugEnabled()) {
this.debug(" Preparing: " + this.removeBreakingWhitespace((String)params[0]), true);
}
stmt = (PreparedStatement)method.invoke(this.connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, this.statementLog, this.queryStack);
return stmt;
} else if("createStatement".equals(method.getName())) {
Statement stmt = (Statement)method.invoke(this.connection, params);
stmt = StatementLogger.newInstance(stmt, this.statementLog, this.queryStack);
return stmt;
} else {
return method.invoke(this.connection, params);
}
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
return (Connection)Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
}
public Connection getConnection() {
return this.connection;
}
}
調(diào)試階段設(shè)置為DEBUG級(jí)別, 可以輸出最多的信息
4.3 實(shí)現(xiàn)單條信息刪除
1. 配置Message.xml
<delete id="deleteOne" parameterType="int" >
DELETE FROM message WHERE id = #{_parameter}
<!--String及基本類(lèi)型用_parameter代替, 類(lèi)似OGNL-->
</delete>
**2. 設(shè)計(jì)Dao層方法 **MessageListDao.mybatisDeleteOne(int);
public static void mybatisDeleteOne(int id){
DBAccess dbAccess = new DBAccess();
SqlSession sqlSession = null;
try {
sqlSession = dbAccess.getSqlSession();
sqlSession.delete("Message.deleteOne", id);
sqlSession.commit();
//mybatis對(duì)數(shù)據(jù)庫(kù)增刪改需要手動(dòng)提交
} catch (IOException e) {
e.printStackTrace();
}finally {
if(sqlSession !=null)sqlSession.close();
}
}
3. Message維護(hù)類(lèi)Service中的邏輯方法MessageMaintainService.deletOne(String);
public static void deleteOne(String id) {
//service層中主要處理類(lèi)似的參數(shù)邏輯問(wèn)題
if (id != null &&!"".equals(id.trim())) {
MessageListDao.mybatisDeleteOne(Integer.parseInt(id));
}
}
4. 設(shè)計(jì)Servlet類(lèi)servlet.DeleteOneServlet
@WebServlet(name = "DeleteOneServlet",value = "/deleteone.action")
public class DeleteOneServlet extends HttpServlet {
private static final long serialVersionUID = -1163248074005443694L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//設(shè)置編碼
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String id = request.getParameter("id");
System.out.println(id);
//數(shù)據(jù)庫(kù)查詢
//servlet中盡量不要設(shè)計(jì)對(duì)數(shù)據(jù)為null及類(lèi)型錯(cuò)誤==的判定,在service中進(jìn)行.
MessageMaintainService.deleteOne(id);
//跳轉(zhuǎn)
System.out.println("刪除成功-----");
request.getRequestDispatcher("/list.action").forward(request, response);
//跳轉(zhuǎn)不能直接跳轉(zhuǎn)到list.jsp頁(yè)面.
//因?yàn)樘D(zhuǎn)的request并沒(méi)有添加messageList屬性,jsp中邏輯語(yǔ)言會(huì)出現(xiàn)空指針.
//要重新跳轉(zhuǎn)到ListServlet重新查詢數(shù)據(jù)再跳轉(zhuǎn)到實(shí)際頁(yè)面.
}
}
5. 編寫(xiě)list.jsp中刪除鏈接的href
<a href="${basePath}deleteone.action?id=<%=message.getId()%>">刪除</a>
比較簡(jiǎn)單的方法是通過(guò)get方法傳值.但這種方法容易泄漏信息, 且只能傳遞少量信息.
在類(lèi)似查詢指定內(nèi)容或模糊內(nèi)容數(shù)據(jù)后進(jìn)行單條刪除時(shí), 由于無(wú)法傳遞查詢輸入框中的值. 每次刪除后會(huì)刷新為空并顯示全部條數(shù)據(jù),影響用戶體驗(yàn).
這時(shí)可以使用js設(shè)置為post方法傳遞信息.
<a href="javascript:(function(id) {
document.getElementById('deleteOneId').value = id;
var form=document.getElementById('mainForm');
form.action='${pageContext.request.contextPath}/deleteOne.action';
form.submit(); }) ('<%=message2.getId()%>')" >刪除</a>
<!--第二個(gè)括號(hào)里面的jsp代碼要加上' '.這里由于代碼的輸出是數(shù)字, 不加也不會(huì)出錯(cuò).但其他時(shí)候則會(huì)導(dǎo)致出錯(cuò), 且沒(méi)有報(bào)錯(cuò), 很難被發(fā)現(xiàn)!!-->
同時(shí)在table循環(huán)輸出message以外設(shè)置一個(gè)隱藏標(biāo)簽:
<input type="hidden" name="messageId" id="deleteOneId" value="" />
4.4 實(shí)現(xiàn)信息批量刪除
1. 配置Message.xml
<delete id="deleteBatch" parameterType="java.util.List" >
DELETE FROM message WHERE ID in(
<foreach collection="list" item="item" separator=",">
#{item}
</foreach>
<!--foreach實(shí)現(xiàn)將接收到的list對(duì)象遍歷出來(lái), 并且用separator內(nèi)的符號(hào)進(jìn)行分隔-->
)
</delete>
2. 設(shè)計(jì)Dao層方法 MessageListDao.mybatisDeleteBatch(List idList);
3. Message維護(hù)類(lèi)Service中的邏輯方法MessageMaintainService.deletBatch(String[] ids);
4. 設(shè)計(jì)Servlet類(lèi)servlet.DeleteBatchServlet
@WebServlet(name = "DeleteBatchServlet",value = "/deleteBatch.action")
public class DeleteBatchServlet extends HttpServlet {
private static final long serialVersionUID = -6881762699937663920L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//設(shè)置編碼
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String[] ids = request.getParameterValues("choose");
MessageMaintainService.deleteBatch(ids);
System.out.println("批量刪除成功");
response.sendRedirect("/list.action");
//這里使用的是請(qǐng)求重定向到list頁(yè). 不會(huì)傳遞req,resp對(duì)象.查詢刪除后會(huì)跳轉(zhuǎn)到全部數(shù)據(jù)頁(yè)面.
// request.getRequestDispatcher("/list.action").forward(request, response);
}
}
5. list.jsp頁(yè)面checkbox傳值及全選全不選
<input type="checkbox" name="choose" value="<%=message1.getId()%>" />
<!--刪除按鈕-->
<a class="btn03" href="javascript:(function() {var form=document.getElementById('mainForm');
form.action='deleteBatch.action';form.submit();})()">刪 除</a>
<!--全選checkbox-->
<input type="checkbox" id="all" onclick="(function() {
var chooses=document.getElementsByName('choose');
if(document.getElementById('all').checked) for(var i=0;i<chooses.length;i++) chooses[i].checked=true;
else for(var j=0;j<chooses.length;j++) chooses[j].checked=false;})()" />
4.5 實(shí)現(xiàn)自動(dòng)回復(fù)功能
模仿微信對(duì)話頁(yè)面html
WEB-INF/jsp/front/talk.jsp
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ page language="java" contentType="text/html;charset=UTF-8" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>微信公眾號(hào)</title>
<!--討論區(qū)滾動(dòng)條begin-->
<link rel="stylesheet" type="text/css" href="<%= basePath %>resources/css/jscrollpane1.css" />
<script src="<%= basePath %>resources/js/common/jquery-1.8.0.min.js" type="text/javascript"></script>
<!-- the mousewheel plugin -->
<script type="text/javascript" src="<%= basePath %>resources/js/common/jquery.mousewheel.js"></script>
<!-- the jScrollPane script -->
<script type="text/javascript" src="<%= basePath %>resources/js/common/jquery.jscrollpane.min.js"></script>
<script type="text/javascript" src="<%= basePath %>resources/js/common/scroll-startstop.events.jquery.js"></script>
<!--討論區(qū)滾動(dòng)條end-->
<script type="text/javascript" src="<%= basePath %>resources/js/front/talk.js"></script>
</head>
<body>
<input type="hidden" value="<%= basePath %>" id="basePath"/>
<br/>
<div class="talk">
<div class="talk_title"><span>正在與公眾號(hào)對(duì)話</span></div>
<div class="talk_record">
<div id="jp-container" class="jp-container">
</div>
</div>
<div class="talk_word">
<input class="add_face" id="facial" type="button" title="添加表情" value="" />
<input id="content" class="messages emotion" />
<input class="talk_send" onclick="send();" type="button" title="發(fā)送" value="發(fā)送" />
</div>
</div>
<div style="text-align:center;margin:50px 0; font:normal 14px/24px 'MicroSoft YaHei';"></div>
</body>
</html>
talk.jsp對(duì)應(yīng)的數(shù)據(jù)處理js方法
resources/js/front/talk.js
/**
* 頁(yè)面加載
*/
$(function(){
render();
var content = "客官怀跛,來(lái)啦畴博,坐吧!<br/>回復(fù)[查看]收取更多精彩內(nèi)容娇钱。";
content += "<br/>回復(fù)[幫助]可以查看所有可用的指令。";
// 添加公眾號(hào)的開(kāi)場(chǎng)白
appendDialog("talk_recordbox","公眾號(hào)",content);
render();
});
/**
* 發(fā)送消息
* @param basePath
*/
function send() {
var content = $("#content").val();
/**
* content = ""/null/undefined/數(shù)字0 , 則判定為false.
*/
if(!content) {
alert("請(qǐng)輸入內(nèi)容累榜!");
return;
}
$.ajax({
// <input type="hidden" value="<%= basePath %>" id="basePath"/>
//通過(guò)這個(gè)標(biāo)簽取值, 減少參數(shù)的傳遞.跳轉(zhuǎn)到指定Servlet
url : $("#basePath").val() + "AutoReplyServlet.action",
type : "POST",
dataType : "text",
timeout : 10000,
success : function (data) {
appendDialog("talk_recordboxme","My賬號(hào)",content);
appendDialog("talk_recordbox","公眾號(hào)",data);
//清空文本框內(nèi)容
$("#content").val("");
render();
},
data : {"content":content}
});
}
/**
* 渲染方法谴垫,加載滾動(dòng)條
*/
function render() {
// the element we want to apply the jScrollPane
var $el= $('#jp-container').jScrollPane({
verticalGutter : -16
}),
// the extension functions and options
extensionPlugin = {
extPluginOpts : {
// speed for the fadeOut animation
mouseLeaveFadeSpeed : 500,
// scrollbar fades out after hovertimeout_t milliseconds
hovertimeout_t : 1000,
// if set to false, the scrollbar will be shown on mouseenter and hidden on mouseleave
// if set to true, the same will happen, but the scrollbar will be also hidden on mouseenter after "hovertimeout_t" ms
// also, it will be shown when we start to scroll and hidden when stopping
useTimeout : true,
// the extension only applies for devices with width > deviceWidth
deviceWidth : 980
},
hovertimeout : null, // timeout to hide the scrollbar
isScrollbarHover: false,// true if the mouse is over the scrollbar
elementtimeout : null, // avoids showing the scrollbar when moving from inside the element to outside, passing over the scrollbar
isScrolling : false,// true if scrolling
addHoverFunc : function() {
// run only if the window has a width bigger than deviceWidth
if( $(window).width() <= this.extPluginOpts.deviceWidth ) return false;
var instance = this;
// functions to show / hide the scrollbar
$.fn.jspmouseenter = $.fn.show;
$.fn.jspmouseleave = $.fn.fadeOut;
// hide the jScrollPane vertical bar
var $vBar = this.getContentPane().siblings('.jspVerticalBar').hide();
/*
* mouseenter / mouseleave events on the main element
* also scrollstart / scrollstop - @James Padolsey : http://james.padolsey.com/javascript/special-scroll-events-for-jquery/
*/
$el.bind('mouseenter.jsp',function() {
// show the scrollbar
$vBar.stop( true, true ).jspmouseenter();
if( !instance.extPluginOpts.useTimeout ) return false;
// hide the scrollbar after hovertimeout_t ms
clearTimeout( instance.hovertimeout );
instance.hovertimeout = setTimeout(function() {
// if scrolling at the moment don't hide it
if( !instance.isScrolling )
$vBar.stop( true, true ).jspmouseleave( instance.extPluginOpts.mouseLeaveFadeSpeed || 0 );
}, instance.extPluginOpts.hovertimeout_t );
}).bind('mouseleave.jsp',function() {
// hide the scrollbar
if( !instance.extPluginOpts.useTimeout )
$vBar.stop( true, true ).jspmouseleave( instance.extPluginOpts.mouseLeaveFadeSpeed || 0 );
else {
clearTimeout( instance.elementtimeout );
if( !instance.isScrolling )
$vBar.stop( true, true ).jspmouseleave( instance.extPluginOpts.mouseLeaveFadeSpeed || 0 );
}
});
if( this.extPluginOpts.useTimeout ) {
$el.bind('scrollstart.jsp', function() {
// when scrolling show the scrollbar
clearTimeout( instance.hovertimeout );
instance.isScrolling = true;
$vBar.stop( true, true ).jspmouseenter();
}).bind('scrollstop.jsp', function() {
// when stop scrolling hide the scrollbar (if not hovering it at the moment)
clearTimeout( instance.hovertimeout );
instance.isScrolling = false;
instance.hovertimeout = setTimeout(function() {
if( !instance.isScrollbarHover )
$vBar.stop( true, true ).jspmouseleave( instance.extPluginOpts.mouseLeaveFadeSpeed || 0 );
}, instance.extPluginOpts.hovertimeout_t );
});
// wrap the scrollbar
// we need this to be able to add the mouseenter / mouseleave events to the scrollbar
var $vBarWrapper = $('<div/>').css({
position : 'absolute',
left : $vBar.css('left'),
top : $vBar.css('top'),
right : $vBar.css('right'),
bottom : $vBar.css('bottom'),
width : $vBar.width(),
height : $vBar.height()
}).bind('mouseenter.jsp',function() {
clearTimeout( instance.hovertimeout );
clearTimeout( instance.elementtimeout );
instance.isScrollbarHover = true;
// show the scrollbar after 100 ms.
// avoids showing the scrollbar when moving from inside the element to outside, passing over the scrollbar
instance.elementtimeout = setTimeout(function() {
$vBar.stop( true, true ).jspmouseenter();
}, 100 );
}).bind('mouseleave.jsp',function() {
// hide the scrollbar after hovertimeout_t
clearTimeout( instance.hovertimeout );
instance.isScrollbarHover = false;
instance.hovertimeout = setTimeout(function() {
// if scrolling at the moment don't hide it
if( !instance.isScrolling )
$vBar.stop( true, true ).jspmouseleave( instance.extPluginOpts.mouseLeaveFadeSpeed || 0 );
}, instance.extPluginOpts.hovertimeout_t );
});
$vBar.wrap( $vBarWrapper );
}
}
},
// the jScrollPane instance
jspapi = $el.data('jsp');
// extend the jScollPane by merging
$.extend( true, jspapi, extensionPlugin );
jspapi.addHoverFunc();
}
/**
* 向聊天記錄中添加聊天內(nèi)容
* @param myClass 添內(nèi)容的樣式
* @param name 發(fā)送消息的賬號(hào)名稱(chēng)
* @param content 發(fā)送的內(nèi)容
*/
function appendDialog(myClass,name,content) {
var div = "";
div += "<div class='" + myClass + "'>";
div += "<div class='user'>![](" + $("#basePath").val() + "resources/images/thumbs/" + myClass + ".jpg)" + name + "</div>";
div += "<div class='talk_recordtextbg'> </div>";
div += "<div class='talk_recordtext'>";
div += "<h3>" + content + "</h3>";
div += "<span class='talk_time'>" + getCurrentDate() + "</span>";
div += "</div>";
div += "</div>";
$('#jp-container').children().eq(0).children().eq(0).append(div);
}
/**
* 獲取當(dāng)前系統(tǒng)時(shí)間
* @returns {String} 當(dāng)前系統(tǒng)時(shí)間
*/
function getCurrentDate() {
var date = new Date();
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":" + (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes());
}
Service層queryBycommand(String);方法
com/qezhhnjy/service/MessageQueryService.java
public static String queryByCommand(String command) {
List<Message> messageList;
//設(shè)置輸入'幫助'返回全部數(shù)據(jù)
//Iconst.HELP_COMMAND為自定義幫助字符串常量
if (Iconst.HELP_COMMAND.equals(command)){
messageList = MessageListDao.mybatisQuery(null, null);
StringBuilder sb = new StringBuilder();
for (Message message : messageList) {
sb.append("回復(fù)[").append(message.getCommand()).append("],").append("查看[").append(message.getDescription()).append("]").append("<br/>");
}
return sb.toString();
}
messageList = MessageListDao.mybatisQuery(command, null);
if (messageList.size() > 0) return messageList.get(0).getContent();
//Iconst.NO_MATCHING_CONTENT為自定義無(wú)匹配的返回字符串常量.
return Iconst.NO_MATCHING_CONTENT;
}
第五章 實(shí)戰(zhàn)第四部
5.1 一對(duì)多關(guān)系的配置 Ⅰ
如果回復(fù)"段子", 而機(jī)器人返回的始終是一個(gè)笑話,就很無(wú)聊了.
所以需要實(shí)現(xiàn)一個(gè)指令能夠隨機(jī)回復(fù)多條內(nèi)容中的一條的功能.
重新設(shè)計(jì)數(shù)據(jù)表
command 表 : ID /commandname/description
content表: ID/content/command_ID(froeign key)
5.2 一對(duì)多關(guān)系的配置 Ⅱ
Content.xml
<mapper namespace="Content">
<resultMap type="com.qezhhnjy.bean.Content" id="ContentResult">
<id column="id" jdbcType="VARCHAR" property="id"/>
<result column="cid" jdbcType="VARCHAR" property="cid"/>
<result column="content" jdbcType="VARCHAR" property="content"/>
</resultMap>
</mapper>
Command.xml
<mapper namespace="Command">
<resultMap type="com.qezhhnjy.bean.Command" id="CommandResult">
<id column="c_id" jdbcType="VARCHAR" property="id"/>
<!--這里的列名對(duì)應(yīng)下面select標(biāo)簽中的結(jié)果. 不包括前面的a./b.==,可以使用別名.-->
<result column="command" jdbcType="VARCHAR" property="command"/>
<result column="description" jdbcType="VARCHAR" property="description"/>
<collection property="contents" resultMap="Content.ContentResult" />
<!--contents為Command實(shí)體類(lèi)中的content集合的屬性名-->
</resultMap>
<select id="query" parameterType="com.qezhhnjy.bean.Command" resultMap="CommandResult" >
SELECT a.id c_id,a.command,a.description,b.id,b.content
FROM command a LEFT JOIN content b ON a.id=b.cid WHERE 1=1
<if test="command != null && !"".equals(command.trim())">
AND command = #{command}
</if>
<if test="description != null and !"".equals(description.trim())">
AND description LIKE '%' #{description} '%'
</if>
</select>
</mapper>
最后在Configuration.xml
中添加這兩個(gè)實(shí)體類(lèi)的xml文件映射.
5.3 一對(duì)多關(guān)系的配置 Ⅲ
5.4 常用標(biāo)簽
<where></where>標(biāo)簽
Message.xml
SELECT id,command,description,content FROM MESSAGE WHERE 1=1
<if test="command != null && !"".equals(command.trim())">
AND command = #{command}
</if>
<if test="description != null and !"".equals(description.trim())">
AND description LIKE '%' #{description} '%'
</if>
等效于
SELECT id,command,description,content FROM message
<where>
<if test="command != null and !"".equals(command.trim())">
AND command = #{command}
</if>
<if test="description !=null and !"".equals(description.trim())">
AND description LIKE '%' #{description} '%'
</if>
</where>
where標(biāo)簽可以在沒(méi)有可選項(xiàng)時(shí)去除WHERE關(guān)鍵字. 在有可選項(xiàng)時(shí)去除第一個(gè)條件前面的and或者or.
<sql></sql>標(biāo)簽(與select標(biāo)簽同一級(jí))
SELECT <include refid="columns" /> FROM message
<where>
<if test="command != null and !"".equals(command.trim())">
AND command = #{command}
</if>
<if test="description !=null and !"".equals(description.trim())">
AND description LIKE '%' #{description} '%'
</if>
</where>
</select>
<sql id="columns">id,command,description,content</sql>
<set></set>標(biāo)簽
<update id="">
UPDATE message
<set>
<if test="command != null and !"".equals(command.trim())">command=#{command},
</if>
<if test="description != null and !"".equals(description.trim())">description=#{description},
</if>
</set>
</update>
<set>標(biāo)簽可以不必考慮<if>中內(nèi)容的','的取舍問(wèn)題.
<trim></trim>標(biāo)簽與where/set同級(jí)
<trim prefix="" suffix="" prefixOverrides="and/or" suffixOverrides=""></trim>
表示如果標(biāo)簽里有內(nèi)容輸出, 則 分別加上prefix/suffix指定的前綴和后綴.
prefixOverrides表示輸出內(nèi)容最前面有指定內(nèi)容則切除. 上面就表示有and或者or時(shí)切掉and或者or.
suffixOverrides同理.
<choose></choose>標(biāo)簽
<choose>
<when test=""></when>
<when test=""></when>
<when test=""></when>
<otherwise></otherwise>
</choose>
類(lèi)似java中的if...else及switch語(yǔ)句.
<association></association>標(biāo)簽(與collection相反)
用于在子表中映射父表
功能 | 標(biāo)簽名稱(chēng) |
---|---|
定義SQL語(yǔ)句 | insert |
delete | |
update | |
select | |
配置java對(duì)象屬性與查詢結(jié)果集中列名對(duì)應(yīng)關(guān)系 | resultMap |
控制動(dòng)態(tài)SQL拼接 | foreach |
if | |
choose | |
格式化輸出 | where |
set | |
trim | |
配置關(guān)聯(lián)關(guān)系 | collection |
association | |
定義常量 | sql |
引用常量 | include |
第六章 戰(zhàn)斗總結(jié)
6.1 容易混淆的概念
reslutMap | resultType |
---|
resultType指直接設(shè)定結(jié)果集映射為某個(gè)工具類(lèi)或?qū)嶓w類(lèi).
為實(shí)體類(lèi)時(shí)列名與類(lèi)中屬性名需一一對(duì)應(yīng),忽略大小寫(xiě).
為工具類(lèi), 如java.util.Map時(shí).以列名為Key, 取值為value. 大小寫(xiě)敏感.
parameterMap | parameterType |
---|
parameterMap用法與resultMap類(lèi)似. 不推薦使用deprecated.
#{} | ${} |
---|
#{}有預(yù)編譯效果,在console中會(huì)以?顯示,
${}沒(méi)有預(yù)編譯效果, 在console中會(huì)以其值顯示
${段子} 顯示為: WHERE command=段子 .這個(gè)sql語(yǔ)句片段是錯(cuò)誤的.段子前后沒(méi)有單引號(hào).正確的用法應(yīng)該是'${段子}'.
${}的用法主要在于order by ×××?xí)r. 如果用預(yù)編譯則會(huì)以?替代.沒(méi)有效果 .在表頭onclick事件按照該列進(jìn)行排列時(shí), 通過(guò)傳遞該列列名然后利用${×××}編譯到sql語(yǔ)句中進(jìn)行動(dòng)態(tài)排序.
#{} | ognl |
---|
如果parameterType為String及8中基本數(shù)據(jù)類(lèi)型. #{}可以寫(xiě)成#{_parameter}.也可以#{}或者中間可以隨便寫(xiě)別的. 都不會(huì)影響.此時(shí),這個(gè)值是確定的. 而ognl表達(dá)式則依然只能寫(xiě)_parameter.
但依然不推薦省略或者亂寫(xiě).
6.2 常見(jiàn)問(wèn)題解析
1. 獲取自增主鍵值
在對(duì)command進(jìn)行插入時(shí), 由于command.id為mysql自增. 不需要進(jìn)行設(shè)置. 那么在插入command后, 應(yīng)該怎樣獲取到這個(gè)自增的id,作為content的外鍵添加command包含的contents.
<!--插入一對(duì)多單方時(shí)設(shè)置實(shí)體類(lèi)獲取數(shù)據(jù)庫(kù)中單方自增生成的主鍵來(lái)設(shè)置多方的外鍵
useGeneratedKeys為true表示主鍵為mysql自增.keyProperty表示mybatis獲取的主鍵值添加到實(shí)體類(lèi)的該屬性-->
<insert id="insert" parameterType="com.qezhhnjy.bean.Command"
useGeneratedKeys="true" keyProperty="id" >
INSERT INTO command(command,description) VALUES(#{command},#{description})
</insert>
2. 找不到namespace.id的異常效果
需要在Configuration.xml中映射各個(gè)實(shí)體類(lèi)的xml文件. 否則運(yùn)行就會(huì)出現(xiàn)這個(gè)異常.
3. sql語(yǔ)句編寫(xiě)錯(cuò)誤
4. 不要過(guò)度使用${}
5. 亂碼