通過(guò)自動(dòng)回復(fù)機(jī)器人學(xué)Mybatis--基礎(chǔ)版

java.jpg

第一章 案例簡(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的需求

  1. 對(duì)象能與數(shù)據(jù)庫(kù)交互
  2. 能執(zhí)行SQL語(yǔ)句

Mybatis中提供給Dao層的處理對(duì)象為SqlSession

SqlSession的作用 :

  1. 向SQL語(yǔ)句傳入?yún)?shù)
  2. 執(zhí)行SQL語(yǔ)句
  3. 獲取執(zhí)行SQL 語(yǔ)句的結(jié)果
  4. 事務(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

Paste_Image.png

Paste_Image.png

配置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. 亂碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茫舶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刹淌,老刑警劉巖饶氏,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異有勾,居然都是意外死亡疹启,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)柠衅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)皮仁,“玉大人,你說(shuō)我怎么就攤上這事菲宴〈恚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵喝峦,是天一觀的道長(zhǎng)势誊。 經(jīng)常有香客問(wèn)我,道長(zhǎng)谣蠢,這世上最難降的妖魔是什么粟耻? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮眉踱,結(jié)果婚禮上挤忙,老公的妹妹穿的比我還像新娘。我一直安慰自己谈喳,他們只是感情好册烈,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著婿禽,像睡著了一般赏僧。 火紅的嫁衣襯著肌膚如雪大猛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天淀零,我揣著相機(jī)與錄音挽绩,去河邊找鬼。 笑死驾中,一個(gè)胖子當(dāng)著我的面吹牛唉堪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哀卫,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼巨坊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼撬槽!你這毒婦竟也來(lái)了此改?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤侄柔,失蹤者是張志新(化名)和其女友劉穎共啃,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體暂题,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡移剪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了薪者。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纵苛。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖言津,靈堂內(nèi)的尸體忽然破棺而出攻人,到底是詐尸還是另有隱情,我是刑警寧澤悬槽,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布怀吻,位于F島的核電站,受9級(jí)特大地震影響初婆,放射性物質(zhì)發(fā)生泄漏蓬坡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一磅叛、第九天 我趴在偏房一處隱蔽的房頂上張望屑咳。 院中可真熱鬧,春花似錦弊琴、人聲如沸兆龙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)详瑞。三九已至掂林,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坝橡,已是汗流浹背泻帮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留计寇,地道東北人锣杂。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像番宁,于是被迫代替她去往敵國(guó)和親元莫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容