Mybatis学习

Posted 呦,名字不赖嘛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis学习相关的知识,希望对你有一定的参考价值。

 一 Mybatis搭建核心架构

配置文件

 1 <?xml version="1.0" encoding="UTF-8" ?>
19 <!DOCTYPE configuration
20     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
21     "http://mybatis.org/dtd/mybatis-3-config.dtd">
22 
23 <configuration>
33   <environments default="development">
34     <environment id="development">
35       <transactionManager type="JDBC">
36         <property name="" value=""/>
37       </transactionManager>
38       <!-- 加载驱动和连接 -->
39       <dataSource type="UNPOOLED">
40         <property name="driver" value="com.mysql.jdbc.Driver"/>
41         <property name="url" value="jdbc:mysql://127.0.0.1:3306/micro_message"/>
42         <property name="username" value="xxx"/>
43         <property name="password" value="xxx"/>
44       </dataSource>
45     </environment>
46   </environments>
54 </configuration>

 

 在java代码中读配置文件,读mybatis配置文件的代码应该写在Dao层

Mybatis相关知识

   Dao层需求

    对象能与数据库交互

    能执行SQL语句

   Mybatis中向Dao层提供对象的名字叫做SqlSession(核心对象)

   SqlSession的作用

    向SQL语句传入参数

    执行SQL语句

    获取执行SQL语句结果的能力

    事务的控制

  如何得到SqlSession

    通过配置文件获取数据库连接相关信息

    通过配置信息构建SqlSessionFactory

    通过SqlSessionFactory打开数据库会话

创建一个可以获取SqlSession的类

 1 /**
 2  * 访问数据库类
 3  */
 4 public class DBAccess {
 5     /**
 6      * 
 7      * @return
 8      * @throws IOException 出现异常先往外抛,抛给DAO层去处理,因为DAO层需要捕获异常并在finally中关闭拿到的SqlSession
 9      * 
10      * mybatis的SqlSession也是提供的供数据库的会话,其实是对JDBC的二次封装,因此拿到数据后也是需要关闭的
11      */
12     public SqlSession getSqlSession() throws IOException {
13         //通过配置文件获取数据库连接信息    路径从src根路径下面开始算起
14         Reader reader = Resources.getResourceAsReader("com/imooc/config/Configuration.xml");
15         //通过配置信息构建一个SqlSessionFactory
16         SqlSessionFactory SqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
17         //通过SqlSessionFactory打开一个数据库会话
18         SqlSession sqlSession = SqlSessionFactory.openSession();
19         return sqlSession;
20     }
21 }

 

 第一次测试getSqlSession()方法在line16处报错

public class MessageDao {
    /**
     * 根据查询条件查询消息列表
     * @param command
     * @param description
     * @return
     */
    public List<Message> queryMessageList(String command,String description) {
        DBAccess dbAccess = new DBAccess();
        SqlSession sqlSession = null;
        //通过sqlSession执行SQL语句
        
        try {
            sqlSession = dbAccess.getSqlSession();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
        
        
        return null;
    }
    public static void main(String[] args) {
        MessageDao messageDao = new MessageDao();
        //测试queryMessageList是否能够否文数据库
        messageDao.queryMessageList("test","test");
    }
}

 

 报错信息:Exception in thread "main" java.lang.NullPointerException

 原因:在mybatis的配置文件中没有注释掉<mappers>,尚未编写Message.xml

  <mappers>
    <mapper resource="com/imooc/config/sqlxml/Message.xml"/>
  </mappers>

二 SQL基本配置与执行

SQL配置文件的目的:将配置文件中的SQL语句提供给SqlSession,使其能读到并执行.

SQL语句标签的id属性就是给这段SQL语句起了一个名字,以便java代码通过该名称去调用.多个SQL配置文件的id都不能重名.因此可以通过namespace为每一个配置文件起一个不同的名称,因为不同的namespace下相同的id是可以存在的,不会引起冲突.配置文件中的resultMap标签能够配置java对象和数据库字段的映射关系.

三 动态SQL拼接

怎么向SQL语句传参

  将查询条件封装为message对象,将查询条件传入SQL语句中  

public List<Message> queryMessageList(String command,String description) {
        DBAccess dbAccess = new DBAccess();
        List<Message> messageList = new ArrayList<Message>();
        SqlSession sqlSession = null;
        try {
            Message message = new Message();
            message.setCommand(command);
            message.setDescription(description);
            sqlSession = dbAccess.getSqlSession();
            // 通过sqlSession执行SQL语句
            messageList = sqlSession.selectList("Message.queryMessageList", message);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(sqlSession != null) {
                sqlSession.close();
            }
        }
        return messageList;
    }

配置文件怎么接收参数

  使用parameterType="com.imooc.bean.Message"指定传入的参数类型,用于接收参数,如果是lang包下面的则可以直接写类型,例如在传递String类型的时候就直接写String,而并非写全路径的类名

<select id="queryMessageList" parameterType="com.imooc.bean.Message" resultMap="MessageResult">
    select ID,COMMAND,DESCRIPTION,CONTENT from MESSAGE
    <where>
        <if test="command != null and !&quot;&quot;.equals(command.trim())">
            and COMMAND=#{command}
        </if>
        <if test="description != null and !&quot;&quot;.equals(description.trim())">
            and DESCRIPTION like \'%\' #{description} \'%\'
        </if>
    </where>
  </select>

 OGNL表达式

  OGNL和EL表达式意义相同,在Strusts2和Mybatis中都有用到,在mybatis中OGNL用于从parameterType中取到值并进行处理.不管从哪个标签中进行取值都是取得java类型,所以根据java类型的不同取值写法也有所不同

  OGNL的一个强大功能就是不仅能从java对象中去取属性值,而且还可以调用java的方法,本例调用了equals和trim方法

<if test="description != null and!&quot;&quot;.equals(description.trim())">
    and DESCRIPTION like \'%\' #{description} \'%\'
</if>

四 应用log4j调试动态SQL

  Mybatis可以通过日志将最终执行的SQL语句打印出来,达到调试SQL的目的

  log4j的配置文件为properties文件,其中的值都为键值对的形式.程序可以通过key得到配置文件中对应的value.

    log4j.rootLogger:配置实用log4j输出日志的时候输出的级别和位置,级别由低到高分别为debug,info,warn,error其中≥配置中的优先级的设置都会打印出来

  Console指的是输出的位置为控制台

    log4j.appender.Console=org.apache.log4j.ConsoleAppender:日志打印到控制台,如果需要打印到文件中,此处的类就需要换掉

    log4j.appender.Console.layout:value表示按照自己的想法去输出

    log4j.appender.Console.layout.ConversionPattern:定义的输出日志格式%d打印时间,%t打印线程名称,%-5p表示打印的级别,%c指的是输出的时候所处的类的全名

    log4j.logger.org.apache:个性化,覆盖第一行的信息设置 

log4j.rootLogger=DEBUG,Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.org.apache=INFO

五 实现单条信息删除

配置文件:

  做删除的SQL语句比较简单,通过外部传递参数所以需要添加parameterType="int",数据库里都是自增列的整形,因此此处选择java的int类型.当参数为基本类型的时候参数可以以#{_parameter}类型书写---------

  <delete id="deleteOne" parameterType="int">
      delete from MESSAGE where ID = #{_parameter}
  </delete>

 

DAO层:

  参数选择int类型的ID

  /**
     * 单条删除
     */
    public void deleteOne(int id) {
        DBAccess dbAccess = new DBAccess();
        SqlSession sqlSession = null;
        try {
            sqlSession = dbAccess.getSqlSession();
            // 通过sqlSession执行SQL语句
            sqlSession.delete("Message.deleteOne", id);
            sqlSession.commit();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(sqlSession != null) {
                sqlSession.close();
            }
        }
    }

Service层:

  创建维护Service,没有建删除Service,在没有框架的情况下,Servlet也没有封装的情况下,页面只要有一个动作就对应了而一个Servlet,Service是很多的,因此把相似的功能放在一个Service中,因此创建一个维护Service,将新增 修改的功能都放置其中.

 1 /**
 2  * 维护相关的业务功能
 3  */
 4 public class MaintainService {
 5     /**
 6      * 单条删除
 7      */
 8     public void deleteOne(String id) {
 9         if(id != null && !"".equals(id.trim())) {
10             MessageDao messageDao = new MessageDao();
11             messageDao.deleteOne(Integer.valueOf(id));
12         }
13     }
14     
15     /**
16      * 批量删除
17      */
18     public void deleteBatch(String[] ids) {
19         MessageDao messageDao = new MessageDao();
20         List<Integer> idList = new ArrayList<Integer>();
21         for(String id : ids) {
22             idList.add(Integer.valueOf(id));
23         }
24         messageDao.deleteBatch(idList);
25     }
26 }

 

Servlet层:

  使用过滤器可以将Servlet中的重复代码进行处理,将判断等逻辑代码不应该写在Servlet中,而是将其写在Service中,Servlet负责接收页面的值及向页面传输值,如果需要根据业务逻辑进行数据处理应该调用相应的Service.DAO层完成与数据库的交互,完成相应的SQL语句

 1 /**
 2  * 单条删除控制层
 3  */
 4 @SuppressWarnings("serial")
 5 public class DeleteOneServlet extends HttpServlet{
 6 
 7     @Override
 8     protected void doGet(HttpServletRequest req, HttpServletResponse resp)
 9             throws ServletException, IOException {
10         // 设置编码
11         req.setCharacterEncoding("UTF-8");
12         // 接受页面的值
13         String id = req.getParameter("id");
14         MaintainService maintainService = new MaintainService();
15         maintainService.deleteOne(id);
16         // 向页面跳转
17         req.getRequestDispatcher("/List.action").forward(req, resp);
18     }
19     
20     @Override
21     protected void doPost(HttpServletRequest req, HttpServletResponse resp)
22             throws ServletException, IOException {
23         this.doGet(req, resp);
24     }
25 }

 

WEB.xml

  完成Servlet之后,应该在web.xml之中配置映射

1   <servlet>
2       <servlet-name>DeleteOneServlet</servlet-name>
3       <servlet-class>com.imooc.servlet.DeleteOneServlet</servlet-class>
4   </servlet>
5   <servlet-mapping>
6       <servlet-name>DeleteOneServlet</servlet-name>
7       <url-pattern>/DeleteOneServlet.action</url-pattern>
8   </servlet-mapping>

 

JSP改造:

  单条删除,找到foreach标签,删除操作需要向servlet提交并且传参,直接写地址使用问号传参.信息不想暴露的时候不能使用GET传参

<c:forEach items="${messageList}" var="message" varStatus="status">                     
    <tr  <c:if test="${status.index % 2 != 0}">style=\'background-color:#ECF6EE;\'</c:if>>
        <td><input type="checkbox"  name="id" value="${message.id}"/></td>              
        <td>${status.index + 1}</td>                                                    
        <td>${message.command}</td>                                                     
        <td>${message.description}</td>                                                 
        <td>                                                                            
            <a href="#">修改</a>&nbsp;&nbsp;&nbsp;                                        
            <a href="${basePath}DeleteOneServlet.action?id=${message.id}">删除</a>        
        </td>                                                                           
    </tr>                                                                               
</c:forEach>                                                                            

 六 实现信息批量删除

1 mybatis配置文件写法,separator=","用于在循环中添加逗号

1 <delete id="deleteBatch" parameterType="java.util.List">
2       delete from MESSAGE where ID in(
3           <foreach collection="list" item="item" separator=",">
4               #{item}
5           </foreach>
6       )
7   </delete>

 

2 dao层代码

 1 public void deleteBatch(List<Integer> ids) {
 2         DBAccess dbAccess = new DBAccess();
 3         SqlSession sqlSession = null;
 4         try {
 5             sqlSession = dbAccess.getSqlSession();
 6             // 通过sqlSession执行SQL语句
 7             sqlSession.delete("Message.deleteBatch", ids);
 8             sqlSession.commit();
 9         } catch (IOException e) {
10             // TODO Auto-generated catch block
11             e.printStackTrace();
12         } finally {
13             if(sqlSession != null) {
14                 sqlSession.close();
15             }
16         }
17     }

 

3 servlet层,servlet接收前台的多值使用String[] ids = req.getParameterValues("id");按照数组形式接收,依赖于前端多个名字相同的控件

/**
 * 批量删除控制层
 */
@SuppressWarnings("serial")
public class DeleteBatchServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // 设置编码
        req.setCharacterEncoding("UTF-8");
        // 接受页面的值
        String[] ids = req.getParameterValues("id");
        MaintainService maintainService = new MaintainService();
        maintainService.deleteBatch(ids);
        // 向页面跳转
        req.getRequestDispatcher("/List.action").forward(req, resp);
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

七 常用标签

 1 where标签:帮助SQL语句输出where关键字

  a 当没有检索条件的时候就不输出where关键字,无条件检索.

  b 将拼出来的检索条件中的第一条前面的and或者or去掉.

  <select id="queryMessageList" parameterType="com.imooc.bean.Message" resultMap="MessageResult">
    select ID,COMMAND,DESCRIPTION,CONTENT from MESSAGE
    <where>
        <if test="command != null and !&quot;&quot;.equals(command.trim())">
            and COMMAND=#{command}
        </if>
        <if test="description != null and !&quot;&quot;.equals(description.trim())">
            and DESCRIPTION like \'%\' #{description} \'%\'
        </if>
    </where>
 </select>

 

2 sql标签:相当于java中常量定义的概念

3 set标签:用于代替sql语句中的set关键字,用于判断条件之后去掉最后一条set语句中的逗号

4 trim标签:可以代替where和set标签,prefix为前缀,suffix为后缀需要输出的值. 

5 choose标签:类似于java中的if else

八 容易混淆的概念

 1 resultMap和resultType的区别与联系

  都是用于表示结果集和java对象之间的一种关系, 好让mybatis帮助我们处理结果集,将结果集放在java对象中,  当使用resultType的时候就不需要配置resultMap了,只需要通过名字即可,只要sql结果集中的列明和bean对象中的属性名相同的时候就表示它们之间有映射关系.结果集中的对应关系是大小写不敏感的.

  使用resultMap不需要保证结果集中的列名和java中的属性名相同, resultMap中的typeHandler属性可以保证数据库中的日期或者布尔表示以正确的形式封装到java对象中.

2 parameterMap和parameterType的区别与联系

   parameterType它是指向一个java类型的并且与ognl表达式和#{}紧密相连,parameterMap需要指向一个使用parameterMap标签配置的映射关系的id, parameterMap表明参数中的列与数据库中的列之间的对应关系.

3 总结

  以Map结尾的首先应该想到映射,因此首先想到的应该是映射关系,Type结尾的应该想到的是java类型

4 #{}与${}

  都是写在sql语句中,成为sql语句的一部分,#{}会被mybatis变成一个预编译的?,然后再通过preparedStatement为?赋值为前台传输的变量,${}会直接将变量值拼接到sql语句的位置,所以可以看出${}是没有预编译效果的,它和java中的变量去拼接一个字符串的效果是一样的.在sql中不需要进行预编译的地方则只能使用${}

九 常见问题解析

 

以上是关于Mybatis学习的主要内容,如果未能解决你的问题,请参考以下文章

markdown [mybatis参考]关于mybatis #mybatis的一些片段

Mybatis 学习笔记总结

Mybatis学习笔记:动态SQL

SSM-MyBatis-05:Mybatis中别名,sql片段和模糊查询加getMapper

mybatis动态sql片段与分页,排序,传参的使用

MyBatis动态SQL标签用法