
第一章 案例簡介
設(shè)計類似微信公眾號的自動回復功能
準備工作
JSP JSTL EL JS/JQUERY
Servlet JavaBean
JDBC MySQL
第二章 實戰(zhàn)第一部--黎明前的黑暗
2.1 黎明前的黑暗
案例分析
基本功能 :接受發(fā)送指令 根據(jù)指令自動回復對應的內(nèi)容
模塊劃分 :
- 回復內(nèi)容維護
- 對話功能
- 回復內(nèi)容列表
- 回復內(nèi)容刪除
2.2 案例演示
2.3 頁面跳轉(zhuǎn)
2.4 連接數(shù)據(jù)庫
2.5 數(shù)據(jù)展示
第三章 實戰(zhàn)第二部--Mybatis來襲
3.1 Mybatis的下載并搭建核心架構(gòu)
https://github.com/mybatis/mybatis-3/releases下載Mybatis jar包及源碼包
**演示代碼中的配置文件的詳細路徑 : **
src/test/java/org/apache/ibatis/submitted/complex_property/Configuration.xml
復制到項目的src/com/qezhhnjy/config/Configuration.xml
Mybatis的相關(guān)知識
Dao的需求
- 對象能與數(shù)據(jù)庫交互
- 能執(zhí)行SQL語句
Mybatis中提供給Dao層的處理對象為SqlSession
SqlSession的作用 :
- 向SQL語句傳入?yún)?shù)
- 執(zhí)行SQL語句
- 獲取執(zhí)行SQL 語句的結(jié)果
- 事務的控制
如何獲得SqlSession :
1. 通過配置文件獲取數(shù)據(jù)庫連接相關(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標簽對應本機數(shù)據(jù)庫連接相關(guān)信息-->
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/qezhhnjy/config/sqlxml/Message.xml"/>
<!--mapper標簽為配置好的各個實體類與數(shù)據(jù)表映射xml文件位置-->
</mappers>
</configuration>
2. 通過配置信息構(gòu)建SqlSessionFactory
com.qezhhnjy.db.DBAccess.getSession();
public class DBAccess {
public SqlSession getSqlSession() throws IOException {
//通過配置文件獲取數(shù)據(jù)庫連接信息
Reader reader = Resources.getResourceAsReader("com/qezhhnjy/config/Configuration.xml");
//通過配置信息構(gòu)建一個SqlSessionFactory
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
return ssf.openSession();
}
}
3. 通過SqlSessionFactory打開數(shù)據(jù)庫會話
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對應message.xml中mapper的屬性namespace
//find對應message.xml中select標簽的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
將該文件復制到項目的
src/com/qezhhnjy/config/sqlxml/Message.xml
<mapper namespace="Message">
<!--由于項目中所有select標簽的id都不能重名.
為了加以區(qū)分, 使用namespace屬性來設(shè)置,不同namespace下的id可以相同.
類似java中包的定義及使用-->
<resultMap type="UserAlias" id="UserResult">
<id column="id" jdbcType="INTEGER" property="id"/>//主鍵
<result column="username" jdbcType="VARCHAR" property="username"/>
<!--column對應數(shù)據(jù)表列名 property對應實體類屬性名 jdbcType對應java.sql.Types類中的常量名-->
<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>
<!--原文檔中有多個功能各異的select查詢標簽-->
</mapper>
Message類對應的全查詢語句和實體類映射
<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>
第四章 實戰(zhàn)第三部
4.1 動態(tài)SQL拼接
OGNL是一個強大的表達式語言
OGNL表達式語言詳解
http://blog.csdn.net/yu102655/article/details/52179695
http://blog.csdn.net/hello_xusir/article/details/53423399


配置SQL拼接實現(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);
...
//只截取了與全查詢不同的部分. 通過封裝對應屬性的輸入值到實體類, 然后傳遞給mybatis,之后配置Message.xml來映射對應屬性到sql語句中.
2. 完善配置Message.xml
Message.xml
<select id="find" parameterType="com.qezhhnjy.bean.Message" resultMap="MessageResult">
//parameterType屬性即表示傳遞進來的對象類型
SELECT id,command,description,content FROM MESSAGE WHERE 1=1
<if test="command != null && !"".equals(command.trim())">
AND command = #{command}
</if>
<!--if標簽test屬性為OGNL語言, 可以識別java屬性及方法,但是""和&(&)要進行轉(zhuǎn)義,&&也可以使用and 關(guān)鍵字代替.
if標簽內(nèi)容為銜接前面的sql語句, 為mybatis來識別,且不必刻意空格來避免錯誤.-->
<if test="description != null and !"".equals(description.trim())">
AND description LIKE '%' #{description} '%'
</if>
<!--模糊查詢的寫法容易出現(xiàn)錯誤,%前后要加上'',并且#{...}前后仍然要注意空格-->
</select>
4.2 應用log4j調(diào)試動態(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
配置文件默認加載到src/根目錄下.
### Global logging configuration
log4j.rootLogger=ERROR, stdout
//配置日志輸出級別和位置,級別(debug<info<warn<error),如果設(shè)置為INFO,則會輸出info,warn,error的信息,如此.
### Uncomment for MyBatis logging
log4j.logger.org.apache=INFO
### 將org.apache包的日志輸出級別單獨設(shè)置為INFO,也可以再單獨設(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表示當前時間, %p(priority)輸出優(yōu)先級,%t表示當前線程, %m表示具體信息, %n換行.
在mybatis的org.apache.ibatis.logging.jdbc包中,有jdbc各大類對象對應的日志輸出類.
org.apache.ibatis.logging.jdbc.CollectionLogger類的源碼
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級別, 可以輸出最多的信息
4.3 實現(xiàn)單條信息刪除
1. 配置Message.xml
<delete id="deleteOne" parameterType="int" >
DELETE FROM message WHERE id = #{_parameter}
<!--String及基本類型用_parameter代替, 類似OGNL-->
</delete>
**2. 設(shè)計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對數(shù)據(jù)庫增刪改需要手動提交
} catch (IOException e) {
e.printStackTrace();
}finally {
if(sqlSession !=null)sqlSession.close();
}
}
3. Message維護類Service中的邏輯方法MessageMaintainService.deletOne(String);
public static void deleteOne(String id) {
//service層中主要處理類似的參數(shù)邏輯問題
if (id != null &&!"".equals(id.trim())) {
MessageListDao.mybatisDeleteOne(Integer.parseInt(id));
}
}
4. 設(shè)計Servlet類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ù)庫查詢
//servlet中盡量不要設(shè)計對數(shù)據(jù)為null及類型錯誤==的判定,在service中進行.
MessageMaintainService.deleteOne(id);
//跳轉(zhuǎn)
System.out.println("刪除成功-----");
request.getRequestDispatcher("/list.action").forward(request, response);
//跳轉(zhuǎn)不能直接跳轉(zhuǎn)到list.jsp頁面.
//因為跳轉(zhuǎn)的request并沒有添加messageList屬性,jsp中邏輯語言會出現(xiàn)空指針.
//要重新跳轉(zhuǎn)到ListServlet重新查詢數(shù)據(jù)再跳轉(zhuǎn)到實際頁面.
}
}
5. 編寫list.jsp中刪除鏈接的href
<a href="${basePath}deleteone.action?id=<%=message.getId()%>">刪除</a>
比較簡單的方法是通過get方法傳值.但這種方法容易泄漏信息, 且只能傳遞少量信息.
在類似查詢指定內(nèi)容或模糊內(nèi)容數(shù)據(jù)后進行單條刪除時, 由于無法傳遞查詢輸入框中的值. 每次刪除后會刷新為空并顯示全部條數(shù)據(jù),影響用戶體驗.
這時可以使用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>
<!--第二個括號里面的jsp代碼要加上' '.這里由于代碼的輸出是數(shù)字, 不加也不會出錯.但其他時候則會導致出錯, 且沒有報錯, 很難被發(fā)現(xiàn)!!-->
同時在table循環(huán)輸出message以外設(shè)置一個隱藏標簽:
<input type="hidden" name="messageId" id="deleteOneId" value="" />
4.4 實現(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實現(xiàn)將接收到的list對象遍歷出來, 并且用separator內(nèi)的符號進行分隔-->
)
</delete>
2. 設(shè)計Dao層方法 MessageListDao.mybatisDeleteBatch(List idList);
3. Message維護類Service中的邏輯方法MessageMaintainService.deletBatch(String[] ids);
4. 設(shè)計Servlet類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");
//這里使用的是請求重定向到list頁. 不會傳遞req,resp對象.查詢刪除后會跳轉(zhuǎn)到全部數(shù)據(jù)頁面.
// request.getRequestDispatcher("/list.action").forward(request, response);
}
}
5. list.jsp頁面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 實現(xiàn)自動回復功能
模仿微信對話頁面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>微信公眾號</title>
<!--討論區(qū)滾動條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ū)滾動條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>正在與公眾號對話</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對應的數(shù)據(jù)處理js方法
resources/js/front/talk.js
/**
* 頁面加載
*/
$(function(){
render();
var content = "客官,來啦,坐吧!<br/>回復[查看]收取更多精彩內(nèi)容。";
content += "<br/>回復[幫助]可以查看所有可用的指令。";
// 添加公眾號的開場白
appendDialog("talk_recordbox","公眾號",content);
render();
});
/**
* 發(fā)送消息
* @param basePath
*/
function send() {
var content = $("#content").val();
/**
* content = ""/null/undefined/數(shù)字0 , 則判定為false.
*/
if(!content) {
alert("請輸入內(nèi)容!");
return;
}
$.ajax({
// <input type="hidden" value="<%= basePath %>" id="basePath"/>
//通過這個標簽取值, 減少參數(shù)的傳遞.跳轉(zhuǎn)到指定Servlet
url : $("#basePath").val() + "AutoReplyServlet.action",
type : "POST",
dataType : "text",
timeout : 10000,
success : function (data) {
appendDialog("talk_recordboxme","My賬號",content);
appendDialog("talk_recordbox","公眾號",data);
//清空文本框內(nèi)容
$("#content").val("");
render();
},
data : {"content":content}
});
}
/**
* 渲染方法,加載滾動條
*/
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ā)送消息的賬號名稱
* @param content 發(fā)送的內(nèi)容
*/
function appendDialog(myClass,name,content) {
var div = "";
div += "<div class='" + myClass + "'>";
div += "<div class='user'>.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);
}
/**
* 獲取當前系統(tǒng)時間
* @returns {String} 當前系統(tǒng)時間
*/
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("回復[").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為自定義無匹配的返回字符串常量.
return Iconst.NO_MATCHING_CONTENT;
}
第五章 實戰(zhàn)第四部
5.1 一對多關(guān)系的配置 Ⅰ
如果回復"段子", 而機器人返回的始終是一個笑話,就很無聊了.
所以需要實現(xiàn)一個指令能夠隨機回復多條內(nèi)容中的一條的功能.
重新設(shè)計數(shù)據(jù)表
command 表 : ID /commandname/description
content表: ID/content/command_ID(froeign key)
5.2 一對多關(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"/>
<!--這里的列名對應下面select標簽中的結(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實體類中的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中添加這兩個實體類的xml文件映射.
5.3 一對多關(guān)系的配置 Ⅲ
5.4 常用標簽
<where></where>標簽
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標簽可以在沒有可選項時去除WHERE關(guān)鍵字. 在有可選項時去除第一個條件前面的and或者or.
<sql></sql>標簽(與select標簽同一級)
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>標簽
<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>標簽可以不必考慮<if>中內(nèi)容的','的取舍問題.
<trim></trim>標簽與where/set同級
<trim prefix="" suffix="" prefixOverrides="and/or" suffixOverrides=""></trim>
表示如果標簽里有內(nèi)容輸出, 則 分別加上prefix/suffix指定的前綴和后綴.
prefixOverrides表示輸出內(nèi)容最前面有指定內(nèi)容則切除. 上面就表示有and或者or時切掉and或者or.
suffixOverrides同理.
<choose></choose>標簽
<choose>
<when test=""></when>
<when test=""></when>
<when test=""></when>
<otherwise></otherwise>
</choose>
類似java中的if...else及switch語句.
<association></association>標簽(與collection相反)
用于在子表中映射父表
| 功能 | 標簽名稱 |
|---|---|
| 定義SQL語句 | insert |
| delete | |
| update | |
| select | |
| 配置java對象屬性與查詢結(jié)果集中列名對應關(guān)系 | resultMap |
| 控制動態(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é)果集映射為某個工具類或?qū)嶓w類.
為實體類時列名與類中屬性名需一一對應,忽略大小寫.
為工具類, 如java.util.Map時.以列名為Key, 取值為value. 大小寫敏感.
| parameterMap | parameterType |
|---|
parameterMap用法與resultMap類似. 不推薦使用deprecated.
| #{} | ${} |
|---|
#{}有預編譯效果,在console中會以?顯示,
${}沒有預編譯效果, 在console中會以其值顯示
${段子} 顯示為: WHERE command=段子 .這個sql語句片段是錯誤的.段子前后沒有單引號.正確的用法應該是'${段子}'.
${}的用法主要在于order by ×××時. 如果用預編譯則會以?替代.沒有效果 .在表頭onclick事件按照該列進行排列時, 通過傳遞該列列名然后利用${×××}編譯到sql語句中進行動態(tài)排序.
| #{} | ognl |
|---|
如果parameterType為String及8中基本數(shù)據(jù)類型. #{}可以寫成#{_parameter}.也可以#{}或者中間可以隨便寫別的. 都不會影響.此時,這個值是確定的. 而ognl表達式則依然只能寫_parameter.
但依然不推薦省略或者亂寫.
6.2 常見問題解析
1. 獲取自增主鍵值
在對command進行插入時, 由于command.id為mysql自增. 不需要進行設(shè)置. 那么在插入command后, 應該怎樣獲取到這個自增的id,作為content的外鍵添加command包含的contents.
<!--插入一對多單方時設(shè)置實體類獲取數(shù)據(jù)庫中單方自增生成的主鍵來設(shè)置多方的外鍵
useGeneratedKeys為true表示主鍵為mysql自增.keyProperty表示mybatis獲取的主鍵值添加到實體類的該屬性-->
<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中映射各個實體類的xml文件. 否則運行就會出現(xiàn)這個異常.
3. sql語句編寫錯誤
4. 不要過度使用${}
5. 亂碼