通過自動回復機器人學Mybatis--基礎(chǔ)版

java.jpg

第一章 案例簡介

設(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的需求

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

Mybatis中提供給Dao層的處理對象為SqlSession

SqlSession的作用 :

  1. 向SQL語句傳入?yún)?shù)
  2. 執(zhí)行SQL語句
  3. 獲取執(zhí)行SQL 語句的結(jié)果
  4. 事務的控制

如何獲得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

Paste_Image.png

Paste_Image.png

配置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'>![](" + $("#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);
}

/**
 * 獲取當前系統(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. 亂碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容