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 !"".equals(command.trim())"> and COMMAND=#{command} </if> <if test="description != null and !"".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!"".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> <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 !"".equals(command.trim())"> and COMMAND=#{command} </if> <if test="description != null and !"".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中不需要进行预编译的地方则只能使用${}
九 常见问题解析