通过自动回复机器人学Mybatis--基础版

java.jpg

第一章 案例简介

设计类似微信公众号的自动回复功能

准备工作
JSP JSTL EL JS/JQUERY
Servlet JavaBean
JDBC MySQL


第二章 实战第一部--黎明前的黑暗

2.1 黎明前的黑暗

案例分析

基本功能 :接受发送指令 根据指令自动回复对应的内容

模块划分 :

  • 回复内容维护
  • 对话功能
  • 回复内容列表
  • 回复内容删除

2.2 案例演示

2.3 页面跳转

2.4 连接数据库

2.5 数据展示


第三章 实战第二部--Mybatis来袭

3.1 Mybatis的下载并搭建核心架构

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的相关知识

Dao的需求

  1. 对象能与数据库交互
  2. 能执行SQL语句

Mybatis中提供给Dao层的处理对象为SqlSession

SqlSession的作用 :

  1. 向SQL语句传入参数
  2. 执行SQL语句
  3. 获取执行SQL 语句的结果
  4. 事务的控制

如何获得SqlSession :
1. 通过配置文件获取数据库连接相关信息
com.qezhhnjy.config.Configuration.xml




  
    
      
        
      
      
        
        
        
        

      
    
  

  
    

  

2. 通过配置信息构建SqlSessionFactory
com.qezhhnjy.db.DBAccess.getSession();

public class DBAccess {
    public SqlSession getSqlSession() throws IOException {
        //通过配置文件获取数据库连接信息
        Reader reader = Resources.getResourceAsReader("com/qezhhnjy/config/Configuration.xml");
        //通过配置信息构建一个SqlSessionFactory
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
        return ssf.openSession();
    }
}

3. 通过SqlSessionFactory打开数据库会话
com.qezhhnjy.dao.MessageListDao.mybatisQuery(...);

    public static List mybatisQuery(String command,String description){
        DBAccess dbAccess = new DBAccess();
        List 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基本配置与执行

src/test/java/org/apache/ibatis/submitted/complex_property/User.xml
将该文件复制到项目的
src/com/qezhhnjy/config/sqlxml/Message.xml


  

  
    //主键
    

    
    
  

  


Message类对应的全查询语句和实体类映射

  
    
    
    
    
  

  

第四章 实战第三部

4.1 动态SQL拼接

OGNL是一个强大的表达式语言
OGNL表达式语言详解
http://blog.csdn.net/yu102655/article/details/52179695
http://blog.csdn.net/hello_xusir/article/details/53423399

通过自动回复机器人学Mybatis--基础版_第1张图片
Paste_Image.png

通过自动回复机器人学Mybatis--基础版_第2张图片
Paste_Image.png

配置SQL拼接实现字段查询和模糊查询

1. 将用户输入的查询内容封装并传递给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

  

4.2 应用log4j调试动态SQL

在官网下载的mybatis文件目录下有log4j的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

在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;
    }
}

调试阶段设置为DEBUG级别, 可以输出最多的信息

4.3 实现单条信息删除

1. 配置Message.xml

    
        DELETE FROM message WHERE id = #{_parameter}

    

**2. 设计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对数据库增删改需要手动提交
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(sqlSession !=null)sqlSession.close();
        }
    }

3. Message维护类Service中的逻辑方法MessageMaintainService.deletOne(String);

    public static void deleteOne(String id) {
//service层中主要处理类似的参数逻辑问题
        if (id != null &&!"".equals(id.trim())) {
           MessageListDao.mybatisDeleteOne(Integer.parseInt(id));
        }
    }

4. 设计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 {
        //设置编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        String id = request.getParameter("id");
        System.out.println(id);
        //数据库查询
        //servlet中尽量不要设计对数据为null及类型错误==的判定,在service中进行.
        MessageMaintainService.deleteOne(id);
        //跳转
        System.out.println("删除成功-----");
        request.getRequestDispatcher("/list.action").forward(request, response);
    //跳转不能直接跳转到list.jsp页面.
    //因为跳转的request并没有添加messageList属性,jsp中逻辑语言会出现空指针.
    //要重新跳转到ListServlet重新查询数据再跳转到实际页面.
    }
}

5. 编写list.jsp中删除链接的href

删除

比较简单的方法是通过get方法传值.但这种方法容易泄漏信息, 且只能传递少量信息.
在类似查询指定内容或模糊内容数据后进行单条删除时, 由于无法传递查询输入框中的值. 每次删除后会刷新为空并显示全部条数据,影响用户体验.

这时可以使用js设置为post方法传递信息.

删除
  

同时在table循环输出message以外设置一个隐藏标签:


4.4 实现信息批量删除

1. 配置Message.xml

    
        DELETE FROM message WHERE ID in(
        
            #{item}
        

        )
    

2. 设计Dao层方法 MessageListDao.mybatisDeleteBatch(List idList);
3. Message维护类Service中的逻辑方法MessageMaintainService.deletBatch(String[] ids);
4. 设计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 {
        //设置编码
        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对象.查询删除后会跳转到全部数据页面.
//        request.getRequestDispatcher("/list.action").forward(request, response);
    }
}

5. list.jsp页面checkbox传值及全选全不选



删 除


4.5 实现自动回复功能

模仿微信对话页面html
WEB-INF/jsp/front/talk.jsp


<%@ page language="java" contentType="text/html;charset=UTF-8" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

    
    
    微信公众号
    
    
    
    
    
    
    
    
    
    
    
    
        
        
正在与公众号对话

talk.jsp对应的数据处理js方法
resources/js/front/talk.js

/**
 * 页面加载
 */
$(function(){
    render();
    var content = "客官,来啦,坐吧!
回复[查看]收取更多精彩内容。"; content += "
回复[帮助]可以查看所有可用的指令。"; // 添加公众号的开场白 appendDialog("talk_recordbox","公众号",content); render(); }); /** * 发送消息 * @param basePath */ function send() { var content = $("#content").val(); /** * content = ""/null/undefined/数字0 , 则判定为false. */ if(!content) { alert("请输入内容!"); return; } $.ajax({ // //通过这个标签取值, 减少参数的传递.跳转到指定Servlet url : $("#basePath").val() + "AutoReplyServlet.action", type : "POST", dataType : "text", timeout : 10000, success : function (data) { appendDialog("talk_recordboxme","My账号",content); appendDialog("talk_recordbox","公众号",data); //清空文本框内容 $("#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 = $('
').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(); } /** * 向聊天记录中添加聊天内容 * @param myClass 添内容的样式 * @param name 发送消息的账号名称 * @param content 发送的内容 */ function appendDialog(myClass,name,content) { var div = ""; div += "
"; div += "
![](" + $("#basePath").val() + "resources/images/thumbs/" + myClass + ".jpg)" + name + "
"; div += "
"; div += "
"; div += "

" + content + "

"; div += "" + getCurrentDate() + ""; div += "
"; div += "
"; $('#jp-container').children().eq(0).children().eq(0).append(div); } /** * 获取当前系统时间 * @returns {String} 当前系统时间 */ 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 messageList;
        //设置输入'帮助'返回全部数据
        //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("
"); } 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; }

第五章 实战第四部

5.1 一对多关系的配置 Ⅰ

如果回复"段子", 而机器人返回的始终是一个笑话,就很无聊了.
所以需要实现一个指令能够随机回复多条内容中的一条的功能.

重新设计数据表

command 表 : ID /commandname/description
content表: ID/content/command_ID(froeign key)

5.2 一对多关系的配置 Ⅱ

Content.xml


    
        
        
        
    

Command.xml


    
        
         
        
        
        
        
    

    

最后在Configuration.xml中添加这两个实体类的xml文件映射.

5.3 一对多关系的配置 Ⅲ

5.4 常用标签

标签
Message.xml

      SELECT id,command,description,content FROM MESSAGE WHERE 1=1
      
           AND command = #{command}
      
      
           AND description LIKE '%' #{description} '%'
      

等效于

      SELECT id,command,description,content FROM message
      
          
              AND command = #{command}
          
          
              AND description LIKE '%' #{description} '%'
          
      

where标签可以在没有可选项时去除WHERE关键字. 在有可选项时去除第一个条件前面的and或者or.

标签(与select标签同一级)

     SELECT  FROM message
     
         
             AND command = #{command}
         
         
             AND description LIKE '%' #{description} '%'
         
     

   id,command,description,content

标签


UPDATE message
      
          command=#{command},
          
          description=#{description},
          
      

标签可以不必考虑中内容的','的取舍问题.

标签与where/set同级

      

表示如果标签里有内容输出, 则 分别加上prefix/suffix指定的前缀和后缀.
prefixOverrides表示输出内容最前面有指定内容则切除. 上面就表示有and或者or时切掉and或者or.
suffixOverrides同理.

标签


    
    
    
    

类似java中的if...else及switch语句.

标签(与collection相反)
用于在子表中映射父表

功能 标签名称
定义SQL语句 insert
delete
update
select
配置java对象属性与查询结果集中列名对应关系 resultMap
控制动态SQL拼接 foreach
if
choose
格式化输出 where
set
trim
配置关联关系 collection
association
定义常量 sql
引用常量 include

第六章 战斗总结

6.1 容易混淆的概念

reslutMap resultType

resultType指直接设定结果集映射为某个工具类或实体类.
为实体类时列名与类中属性名需一一对应,忽略大小写.
为工具类, 如java.util.Map时.以列名为Key, 取值为value. 大小写敏感.

parameterMap parameterType

parameterMap用法与resultMap类似. 不推荐使用deprecated.

#{} ${}

#{}有预编译效果,在console中会以?显示,
${}没有预编译效果, 在console中会以其值显示
${段子} 显示为: WHERE command=段子 .这个sql语句片段是错误的.段子前后没有单引号.正确的用法应该是'${段子}'.
${}的用法主要在于order by ×××时. 如果用预编译则会以?替代.没有效果 .在表头onclick事件按照该列进行排列时, 通过传递该列列名然后利用${×××}编译到sql语句中进行动态排序.

#{} ognl

如果parameterType为String及8中基本数据类型. #{}可以写成#{_parameter}.也可以#{}或者中间可以随便写别的. 都不会影响.此时,这个值是确定的. 而ognl表达式则依然只能写_parameter.
但依然不推荐省略或者乱写.

6.2 常见问题解析

1. 获取自增主键值
在对command进行插入时, 由于command.id为mysql自增. 不需要进行设置. 那么在插入command后, 应该怎样获取到这个自增的id,作为content的外键添加command包含的contents.



    INSERT INTO command(command,description) VALUES(#{command},#{description})

2. 找不到namespace.id的异常效果
需要在Configuration.xml中映射各个实体类的xml文件. 否则运行就会出现这个异常.

3. sql语句编写错误

4. 不要过度使用${}

5. 乱码

你可能感兴趣的:(通过自动回复机器人学Mybatis--基础版)