Java高级框架——Mybatis学习

北京尚学堂——基于此教育机构的视频所学习

目录

壹、什么是框架?

贰、Mybatis

一、mybites简介

二、搭建环境

三、环境搭建的详解

四、三种查询方式

五、注解

六、路径

七、Log4J日志(Log  for  Java)(标签)

八、parameterType类型

完整实现一个数据翻页

九、别名 Typealiases(mybatis设置)

十、Mybatis实现新增(事务处理)

实现一个银行转账(加+数据分页,分页具体步骤已在上面实现)

 

十一、getMapper接口绑定以及多参数传递

十二、动态SQL

十二、ThreadLocal+OpenSessionInView(实现mybatis的封装(利用Filter))

十三、缓存

十四、Mybaits的多表查询

十五、Mybatis的注解

十六、Mybatis的运行原理

班级查询功能

飞机起降系统查询


 

对我而言,算是第一次接触框架,框架是是什么,我感觉像是我在以前学习中把一些复用率高的代码封装起来,方便自己的使用或者说别人的使用。对于刚开始接触的我来说,最大的用处就是减少重复劳力。重复造轮子

壹、什么是框架?

  1. 框架:软件的半成品.未解决问题制定的一套约束,在提供功能基础上进行扩充。
  2.  框架中一些不能被封装的代码(变量),需要使用框架者新建一个xml 文件,在文件中添加变量内容.
    1. 需要建立特定位置和特定名称的配置文件
    2. 需要使用XML解析技术和反射技术
  3. 常用概念:
    1. 类库:提供的类没有封装一定的逻辑,
      1. eg:类库就是名言警句,写作文时引入
    2. 框架:里面有一些特定的约束
      1. eg:框架是选择题

       eg:我们可以写俩个程序理解下框架

我们先创建俩个项目,MyProject作为正常的项目,Framework作为框架,那我们要如何使用框架里面写的东西呢?

 

贰、Mybatis

一、mybites简介

  • Mybatis 开源免费框架.原名叫iBatis,2010在googlecode,2013年迁移到 github
  • 作用:数据库访问层框架
    • 底层对JDBC封装
  • 优点:不需要编写实现类,只需要写需要的sql命令语句;

二、搭建环境

1. 导入 jar

2. 在 src 下新建全局配置文件(编写 JDBC 四个变量)

  • 没有名字路径要求
  • 在全局配置文件中引入DTD或schema
  • 在全局配置文件中引入 DTD 或 schema

    •  如果导入 dtd 后没有提示

      Window-->preference-->Myeclipse->Files and Editors-->XML-->XMlcatalog-->add 按钮

 全局配置文件内容

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
  <!-- 全局配置文件内容 -->
<configuration>
	<!-- default引用的environment的id ,当前所使用的环境 -->
	<environments default="default">
		<!-- 声明可以使用的环境、可以配置多个环境 -->
		<environment id="default">
			<!-- 事务: 使用原生JDBC  -->
			<transactionManager type="JDBC"></transactionManager>
			<!-- 利用数据库连接池技术 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/rejava"/>
				<property name="username" value="root"/>
				<property name="password" value="1327148745"/>
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="com/tk/mapper/StudentMapper.xml"/>
	</mappers>
</configuration>



3. 新建以 mapper 结尾的包,在包下新建:实体类名+Mapper.xml

  • 文件作用:编写需要执行的sql命令
  • 可把xml理解为实现类

XML内容

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  <!-- namespace理解为实现类的全路径(包名+类名) -->
<mapper namespace="com.tk">
	<!-- id 方法名 
		 paramaterType  参数类型
		 resultType		返回值类型
		 
		 如果返回值的List类型,在resultType中写的List的泛型,因为mybatis
		 对jdbc封装,一行一行读取数据
	-->
	<select id="selAll"  resultType="com.tk.pojo.Student">
		<!-- 利用反射机制自动赋值(auto mapping 自动映射,通过同名),
			但要求属性值要相对一样(数据库中名字和实体类中属性名)
		 -->
		select * from Student
	</select>
</mapper>

4.测试结果(只有在单独使用 mybatis 时使用,最后 ssm 整合时下面代码不需要编写.)

package com.tk.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.tk.pojo.Student;

public class Test {
	public static void main(String args[]) throws IOException {
		
		InputStream is = Resources.getResourceAsStream("mybatis.xml");
		//使用工厂设计模式
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
		//生产SqlSession
		SqlSession session = factory.openSession();
		
		List<Student> lists = session.selectList("com.tk.selAll");
		for (Student stu : lists) {
			System.out.println(stu);
		}
		
		session.close();
	}
}

测试结果

Student [id=1, name=田坤, sex=男, age=18]
Student [id=2, name=宋旭东, sex=女, age=18]
Student [id=3, name=宁迁迁, sex=男, age=18]
Student [id=4, name=吴飞龙, sex=男, age=18]

假如,我们的实体类和数据库中的名字不一样,那么通过反射机制还可以赋值成功吗?我们尝试把实体类重的属性值name 改为 noname;

下面是我们的输出结果

Student [id=1, name=null, sex=男, age=18]
Student [id=2, name=null, sex=女, age=18]
Student [id=3, name=null, sex=男, age=18]
Student [id=4, name=null, sex=男, age=18]

因为数据库名字和实体类的属性名字不一样,所以无法给name赋值,而从中我们也可得知,我们查询出来结果我数据的键名是对应于数据库的,额,回归正题,那我们如何可以获取到数据呢,我们应该给取别名来操控

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tk">
	<select id="selAll"  resultType="com.tk.pojo.Student">
              <!--将name取别名为 noname-->
		select id,name noname,sex,age from Student
	</select>
</mapper>

下面是我们的改变mapper后的输出结果:同正常一样

Student [id=1, name=田坤, sex=男, age=18]
Student [id=2, name=宋旭东, sex=女, age=18]
Student [id=3, name=宁迁迁, sex=男, age=18]
Student [id=4, name=吴飞龙, sex=男, age=18]

所以,我们在构建实体类的时候要和数据库中的名字保持一致

三、环境搭建的详解

  • 全局配置文件中的内容
    • <transactionManager/> type 数据可取值
      • JDBC:事务管理使用JDBC原生事务管理方式
      • Managed:把数据管理交给其他容器,原生JDBC事务--->setAutoMapping(false);
    • <dataSourse/> type的属性值
      • POOLED:使用的是数据库连接池
      • UNPOOLED:不使用数据库连接池,和直接使用JDBC一样
      • JNDI:java命名目录接口技术

四、三种查询方式

  • .selectList()  返回值为 List<resultTyp属性控制>
    • 适用于查询结果都需要遍历的需求
  • selectOne()  返回值  Object
    • 适用于返回结果只是一个变量或者一行数据
  • selectMap()  返回值为  Map  <key , resultType控制>
    • 适用于需要在查询结果中通过某列的值获取到这行数据的需求(eg:电话簿)
package com.tk.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.tk.pojo.Student;

public class Test {
	public static void main(String args[]) throws IOException {
		
		InputStream is = Resources.getResourceAsStream("mybatis.xml");
		//使用工厂设计模式
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
		//生产SqlSession
		SqlSession session = factory.openSession();
		//查询一:session.selectList
		System.out.println("查询一:selectList");
		List<Student> lists = session.selectList("com.tk.selAll");
		for (Student stu : lists) {
			System.out.println(stu);
		}
		
		//查询二:session.selectOne
		System.out.println("查询二:selectOne");
		Student stu  = session.selectOne("com.tk.selone");
		System.out.println(stu);
		//查询三:session.selectMap
		System.out.println("查询三:selectMap");
		Map<String, Student> stumap = session.selectMap("com.tk.selmap", "name");
		System.out.println(stumap);
		
		session.close();
	}
}

查询结果

查询一:selectList
Student [id=1, name=田坤, sex=男, age=18]
Student [id=2, name=宋旭东, sex=女, age=18]
Student [id=3, name=宁迁迁, sex=男, age=18]
Student [id=4, name=吴飞龙, sex=男, age=18]
查询二:selectOne
Student [id=2, name=宋旭东, sex=女, age=18]
查询三:selectMap
{田坤=Student [id=1, name=田坤, sex=男, age=18], 吴飞龙=Student [id=4, name=吴飞龙, sex=男, age=18], 宁迁迁=Student [id=3, name=宁迁迁, sex=男, age=18], 宋旭东=Student [id=2, name=宋旭东, sex=女, age=18]}

 

五、注解

  1. 注解存在的意义:简化XML文件的开发
  2. 注解在servlet3.0规范之后大力推广
  3. 注解前面的@XXX,表示引用一个@interface
    1. @interface表示注解声明
  4. 注解也可以有属性,它本质就是一个接口(类)
    1. 每次使用注解都需要到导包
  5. 注解语法:@XXX(属性名=值)
  6. 值的分类
    1. 如果值是基本数据类型或字符串:属性名=值
    2. 如果值是数组类型:属性名={值,值}
      1. 如果只有一个值是可以省略大括号
    3. 如果值是类类型,属性名=@名称
  7. 如果注解只需要一个属性赋值,且这个属性是默认属性,可以省略属性名

六、路径

  1. 编写路径为了告诉编译器如何找到其他资源.
  2. 路径分类
    1.  相对路径: 从当前资源出发找到其他资源的过程

    2. 绝对路径: 从根目录(服务器根目录或项目根目录)出发找到其他资源的过程

      1.  标志: 只要以/开头的都是绝对路径

  3. 绝对路径

    1. 如果是请求转发 / 表示项目根目录(WebRoot)

    2. 其他重定向,<img/> <script/>,<style/>,location.href 等/都表示服务器根目录(tomcat/webapps 文件夹),所以我们需要填写路径的时候需要加上项目名称

  4. 如果客户端请求的控制器,控制器转发到JSP后,jsp中如果使用相对路径,需要按照控制器的路径去寻找其他资源

    1. 保鲜方法就使用绝对路径

注意:浏览器不是直接把有静态资源(scr、href......)东西,直接响应回来,而响应的是一个字符串,而浏览器是去解析这个字符串,每次解析到在去请求服务器,服务器在进行响应

七、Log4J日志(Log  for  Java)(<Setting>标签)

在mybatis里面同样也支持日志文件输出,日志文件的输出有利于我们调制程序,了解程序的运行过程和细节详细信息

关于Log4j的详细用法

  1. 由 apache 推出的开源免费日志处理的类库.
  2. 作用:
    1. 在项目中编写 System.out.println();输出到控制台,当项目发布到 tomcat 后,没有控制台(在命令行界面能看见.),不容易观察一些输出结果.

    • log4j 不仅能把内容输出到控制台,还能把内容输出到文件中.便于观察结果 

  3. Log4j的输出级别

    1. fatal(致命错误) > error (错误) > warn (警告) > info(普通信息)>debug(调试信息)

那么?如何在Mybatis中使用呢?————》<Settings>标签

  • 在Mybatis全局配置文件中,通过<Settings>标签来控制mybatis全局开关
  • 在mybatis.xml文件中开启log4f
    • src必须用  log4j.properties
    • 必须有log4j.jar
    • <settings>
         <setting name="logImpl" value="LOG4J"/>
      </settings>
      
      
  • Log4j中可以输出指定内容的日志(控制某个局部内容日志级别)

    • 命名级别(包级别):<mapper>namespace 属性中除了最后一个类名

      • 例如 namespace=”com.bjsxt.mapper.PeopleMapper” 其中包级别为com.bjsxt.mapper,需要在 log4j.propeties 中

        • 先在总体级别调成 Error 不输出无用信息

        • 在设置某个指定位置级别为 DEBUG

        • 
          log4j.rootLogger = error,stdout,D
          
          #only output dao data
          log4j.logger.com.tk.mapper = debug
          
          
          log4j.appender.stdout = org.apache.log4j.ConsoleAppender
          log4j.appender.stdout.Target = System.out
          log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
          log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
          
          log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
          log4j.appender.D.File = log/log.txt
          log4j.appender.D.Append = true
          log4j.appender.D.Threshold = DEBUG 
          log4j.appender.D.layout = org.apache.log4j.PatternLayout
          log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
          
          
    • 类级别:

      • 3.2.1namespace 属性值 ,namespace 类名

    • 方法级别

      • 使用 namespace 属性值+标签 id 属性

八、parameterType类型

  1. 在XXXMapper.xml中<select><delete>等标签的 parameterType 可以控制参数类型

  2. sqlsession的selectOne() selectList()的第二个参数和selectMap()的第三个参数都表示参数的数

    1. <select id="selAll" resultType="com.tk.pojo.User" parameterType="int">
        	select * from m_user where id = #{0}
      </select>
    2. 在 Mapper.xml 中可以通过#{}获取参数

      1. parameterType 控制参数类型

      2. #{}获取参数内容

        1. 使用索引,从 0 开始 #{0}表示第一个参数

        2. 也可以使用#{param1}第一个参数

        3.  如果只有一个参数(基本数据类型或 String),mybatis对#{}里面内容没有要求只要写内容即可.

        4. 如果参数是对象   #{属性名}

        5. 如果参数是 map 写成#{key}

  3. #{ } 和 ${ }的区别
    1. #{}   获取参数的内容支持 索引获取,param1获取指定位置参数,并且 SQL 使用?占位符
    2. ${}  字符串拼接不使用?,默认找${内容}内容的 get/set 方法,如果写数字,就是一个数字
  4. 如果在 xml 文件中出现 “<”,“>”,双引号 等特殊字符时可以使用XML 文件转义标签(XML 自身的)
  5. mybatis 中实现 mysql 分页写法
    1. ? 不允许在关键字前后进行数学运算,需要在代码中计算完成后传递到 mapper.xml 中
    2. mapper中
      1. <select id="selAll" resultType="com.tk.pojo.User" parameterType="map">
          select * from m_user limit #{pageStart},#{pageSize}
        </select>
    3. 测试类中
      1. InputStream is = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        		
        //每页显示几个
        int pageSize = 3;
        //第几页
        int pageNumber = 1;
        		
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("pageSize", 3);
        map.put("pageStart", pageSize*(pageNumber-1));
        		
        SqlSession session = factory.openSession();
        		
        List<User> listu = session.selectList("selAll",map);
        
        System.out.println(listu);
        session.close();

完整实现一个数据翻页

1。mapper层(Dao层)

        <!-- 设置查询分页 -->
  	<select id="selinfo" resultType="User" parameterType="map">
  		select * from m_user limit #{pageStart},#{pageSize}
  	</select>

        <!-- 查询总的数据量 -->
  	<select id="selAllCount" resultType="int">
  		select count(*) from m_user
  	</select>

2.我们把分页信息封装称为一个类

package com.tk.pojo;

import java.util.List;

public class Page {
	//每页显示个数
	private int pageSize;
	//当前第几页
	private int pageNumber;
	//总页数
	private int totol;
	//在泛型里面填写? 表名什么类型都可以
	//当前显示的数据集合
	private List<?> info;
	public int getPageSize() {
		return pageSize;
	}
	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}
	public int getPageNumber() {
		return pageNumber;
	}
	public void setPageNumber(int pageNumber) {
		this.pageNumber = pageNumber;
	}
	public int getTotol() {
		return totol;
	}
	public void setTotol(int totol) {
		this.totol = totol;
	}
	public List<?> getInfo() {
		return info;
	}
	public void setInfo(List<?> info) {
		this.info = info;
	}
	@Override
	public String toString() {
		return "Page [pageSize=" + pageSize + ", pageNumber=" + pageNumber
				+ ", totol=" + totol + ", info=" + info + "]";
	}
	public Page(int pageSize, int pageNumber, int totol, List<?> info) {
		super();
		this.pageSize = pageSize;
		this.pageNumber = pageNumber;
		this.totol = totol;
		this.info = info;
	}
	public Page() {
		
	}

}

3。业务层(Service + ServiceImpl)

public Page getInfos(int pageSize, int pageNumber) throws IOException {
		InputStream is =  Resources.getResourceAsStream("mybatis.xml");
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
		SqlSession session = factory.openSession();
		
		Page page = new Page();
		page.setPageNumber(pageNumber);
		page.setPageSize(pageSize);
		int count = session.selectOne("selAllCount");
		
		page.setTotol(count/pageSize==0?count/pageSize:count/pageSize+1);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("pageSize",pageSize);
		map.put("pageStart", pageSize*(pageNumber-1));

		List<User> listu = session.selectList("selinfo",map);
		page.setInfo(listu);
		session.close();
		return page;
	}

4.控制器(servlet)

rotected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		 int number = 1;
		 int size = 2;
		 String pageStart = req.getParameter("pageNumber");
		 String pageSize = req.getParameter("pageSize");
		 if(pageStart!=null && !pageStart.equals("")){
			 number = Integer.parseInt(pageStart);
		 }
		 if(pageSize!=null && !pageSize.equals("")){
			 size = Integer.parseInt(pageSize);
		 }
		 
		 //获取业务层
		 UserService us = new UserServiceImpl();
		 Page page = us.getInfos(size, number);
		 req.setAttribute("page", page);
		 req.getRequestDispatcher("/page.jsp").forward(req, resp);
		 
	}

5.视图层

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'page.jsp' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body>
    	<table border="2px">
    		<tr>
    			<td>学号</td>
    			<td>姓名</td>
    			<td>年龄</td>
    		</tr>
    		<c:forEach items="${page.info}" var="user">
    			<tr>
    				<td>${user.id }</td>
    				<td>${user.name }</td>
    				<td>${user.age }</td>
    			</tr>
    		
    		</c:forEach>
    		
    	</table>	
    	<a href="page?pageNumber=${page.pageNumber-1 }&pageSize=${page.pageSize}"  <c:if test="${page.pageNumber<=1 }"> onclick="javascript:return false;" </c:if> >上一页</a>
    	<a href="page?pageNumber=${page.pageNumber+1 }&pageSize=${page.pageSize}"  <c:if test="${page.pageNumber>=page.totol }"> onclick="javascript:return false;" </c:if> >下一页</a>
    	
  </body>
</html>

九、别名 Typealiases(mybatis设置)

  1. 系统内置别名:一般是把大写变为小写,本身就小写的在前面加下划线
  2. 给某个类起别名
    1.   	<typeAliases>
        		<typeAlias type="com.tk.pojo.User" alias="ctpu"/>
        	</typeAliases>
  3. 直接给某个包下所以类起别名,别名为类名,不区分大小写
    1. <typeAliases>
         <package name="com.tk.pojo"/>
      </typeAliases>
系统内置别名
别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator
   

十、Mybatis实现新增(事务处理)

  1. 概念复习
    1. 功能:从应用程序角度出发,软件具有哪些功能.
    2. 业务:完成功能时的逻辑.对应 Service 中一个方法
    3. 事务:从数据库角度出发,完成业务时需要执行的 SQL 集合,统称为一个事务
      1. 事务回滚:如果在一个事务中某个 SQL 执行事务,希望回归到事务的原点,保证数据库数据的完整性.(比如在银行存钱)
  2. 在 mybatis 中默认是关闭了 JDBC 的自动提交功能
    1. 每一个 SqlSession 默认都是不自动提交事务.
    2. session.commit()提交事务.
    3. openSession(true);自动提交  等价于=setAutoCommit(true);
  3. mybatis 底层是对 JDBC 的封装.
    1. JDBC 中 executeUpdate()执行新增,删除,修改的 SQL.返回值 int,表示受影响的行数.
    2. mybatis 中<insert><delete><update>标签没有 resultType 属性,认为返回值都是 int
  4. openSession()时 Mybatis 会创建 SqlSession 时同时创建一个Transaction(事务对象),同时 autoCommit 都为 false
    1. 如果出现异常,应该 session.rollback()回滚事务.
  5. 增删改   insert 、delete、update

实现一个银行转账(加+数据分页,分页具体步骤已在上面实现)

1.mapper(Dap)

<mapper namespace="com.tk.mapper.AccountMapper">
	<!-- 通过用户账号和密码返回对应对象 -->
	<select id="selAccount" resultType="Account" parameterType="Account">
		select * from m_account where id = #{id} and password=#{password}
	</select>
	<!-- 通过用户账号和用户名返回对应对象 -->
	<select id="selAccount2" resultType="Account" parameterType="Account">
		select * from m_account where id = #{id} and name=#{name}
	</select>
	<!-- 根据用户修改账户的余额 -->
	<update id="selAccountBalance"  parameterType="Account">
		update m_account set balance=balance +#{balance} where id=#{id}
	</update>
	
</mapper>

2.实体类(Account -账户类  Log-日志类)

package com.tk.pojo;

public class Account {
	//用户账户
	private int id;
	//用户名
	private String name;
	//用户密码
	private String password;
	//账户余额
	private double balance;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public double getBalance() {
		return balance;
	}
	public void setBalance(double balance) {
		this.balance = balance;
	}
	@Override
	public String toString() {
		return "Account [id=" + id + ", name=" + name + ", password="
				+ password + ", balance=" + balance + "]";
	}
	public Account(int id, String name, String password, double balance) {
		super();
		this.id = id;
		this.name = name;
		this.password = password;
		this.balance = balance;
	}
	public Account() {
		super();
	}
	
	

}
package com.tk.pojo;

import java.sql.Timestamp;

/**
 * 用来记录日志信息的实体类
 * @data :2019-4-9上午9:30:22
 * @author :田坤
 */
public class Log {
	private int id;
	private int accOut;
	private int accIn;
	private double money;
	private Timestamp time;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getAccOut() {
		return accOut;
	}
	public void setAccOut(int accOut) {
		this.accOut = accOut;
	}
	public int getAccIn() {
		return accIn;
	}
	public void setAccIn(int accIn) {
		this.accIn = accIn;
	}
	public double getMoney() {
		return money;
	}
	public void setMoney(double money) {
		this.money = money;
	}
	public Timestamp getTime() {
		return time;
	}
	public void setTime(Timestamp time) {
		this.time = time;
	}
	@Override
	public String toString() {
		return "Log [id=" + id + ", accOut=" + accOut + ", accIn=" + accIn
				+ ", money=" + money + ", time=" + time + "]";
	}
	public Log(int id, int accOut, int accIn, double money, Timestamp time) {
		super();
		this.id = id;
		this.accOut = accOut;
		this.accIn = accIn;
		this.money = money;
		this.time = time;
	}
	public Log() {
		super();
	}


}

3.业务层(service +serviceImpl)

package com.tk.service;

import java.io.IOException;

import com.tk.pojo.Account;
import com.tk.pojo.Page;

public interface AccountService {
	
	int ACCOUNTOUT_INFO_ERROR = 1;
	int ACCOUNTOUT_BALANCE_NOTENOUGH =2;
	int ACCOUNTIN_INFO_ERROR = 3;
	int ACCOUNT_ERROR = 4;
	int OK = 5;
	
	/**
	 * 完成俩个用户之间的转账
	 * @param accOut   转账用户
	 * @param accIn	   收款用户
	 * @return
	 */
	int tranfer(Account accOut,Account accIn) throws IOException;
	
	/**
	 * 查询转账信息,并实现分页
	 * @param pageNumber 当前第几页
	 * @param pageSize	   每页的信息数
	 * @return
	 */
	Page pageInfo(int pageNumber,int pageSize) throws IOException;
}
package com.tk.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Timestamp;
import java.util.Map;
import java.util.*;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.tk.pojo.Account;
import com.tk.pojo.Log;
import com.tk.pojo.Page;
import com.tk.service.AccountService;

public class AccountServiceImpl implements AccountService{

	private SqlSession getSqlSession(String config) throws IOException{
		InputStream is = Resources.getResourceAsStream(config);
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
		return factory.openSession();
	}
	
	@Override
	public int tranfer(Account accOut, Account accIn) throws IOException {
		SqlSession  session= getSqlSession("mybatis.xml");
		Account accountOut = session.selectOne("com.tk.mapper.AccountMapper.selAccount",accOut);
		//首先判断转账用户账号和密码是否正确
		if(accountOut!=null){
			//判断转账用户的余额是否足够
			if(accountOut.getBalance() >= accOut.getBalance()){
				//判断收款账号的信息
				Account accountIn = session.selectOne("com.tk.mapper.AccountMapper.selAccount2",accIn);
				if(accountIn != null){
					accountIn.setBalance(accOut.getBalance());
					int index = session.update("com.tk.mapper.AccountMapper.selAccountBalance",accountIn);
					accOut.setBalance(-accOut.getBalance());
					index += session.update("com.tk.mapper.AccountMapper.selAccountBalance",accOut);
					//如何俩次操作都成功
					if(index==2){
						
						//记录转账信息,把信息转入到数据库中
						Log log = new Log();
						log.setTime(new Timestamp(System.currentTimeMillis()));
						log.setAccOut(accOut.getId());
						log.setAccIn(accIn.getId());
						log.setMoney(-accOut.getBalance());
						
						int i = session.insert("com.tk.mapper.LogMapper.insInfo", log);
						if(i>0){
							session.commit();
							session.close();
							return OK;
						}else{
							session.rollback();
							return 0;
						}
	
					}else{
						return ACCOUNT_ERROR;
					}
				}else{
					return ACCOUNTIN_INFO_ERROR;
				}

				
			}else{
				
				return ACCOUNTOUT_BALANCE_NOTENOUGH;
			}
		}else{
			
			return ACCOUNTOUT_INFO_ERROR;
		}
		
	}

	@Override
	public Page pageInfo(int pageNumber, int pageSize) throws IOException {
		SqlSession session = getSqlSession("mybatis.xml");
		Page page = new Page();
		page.setPageSize(pageSize);
		page.setPageNumber(pageNumber);
		//查询出信息总数
		int allInfo = session.selectOne("com.tk.mapper.LogMapper.selAll");
		page.setTotal(allInfo/pageSize == 0? allInfo/pageSize : allInfo/pageSize+1);
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("pageStart", (pageNumber-1)*pageSize);
		map.put("pageSize", pageSize);
		List<Log> listl = session.selectList("com.tk.mapper.LogMapper.selPageInfo", map);
		page.setList(listl);
		System.out.println(listl);
		return page; 
	}

}

4.Controller层

package com.tk.servlet;


import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.tk.pojo.Account;
import com.tk.service.AccountService;
import com.tk.service.impl.AccountServiceImpl;

public class transfer extends HttpServlet {
	 @Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		//转账账户
		Account accOut = new Account();
		//收款账户
		Account accIn = new Account();
		if(!req.getParameter("accOutId").equals("")){
			accOut.setId(Integer.parseInt(req.getParameter("accOutId")));
		}
		accOut.setPassword(req.getParameter("accOutPassword"));
		if(!req.getParameter("accOutBalance").equals("")){
			accOut.setBalance(Double.valueOf(req.getParameter("accOutBalance")));
		}
		if(!req.getParameter("accInId").equals("")){
			accIn.setId(Integer.parseInt(req.getParameter("accInId")));
		}
		accIn.setName(req.getParameter("accInName"));
		
		System.out.println(accOut);
		System.out.println(accIn);
		
		AccountService as = new AccountServiceImpl();
		int flag = as.tranfer(accOut, accIn);
		if(flag == 5){
			resp.sendRedirect("/Bank/pageFen");
		}else{
			HttpSession hs = req.getSession();
			hs.setAttribute("code", flag);
			resp.sendRedirect("/Bank/error.jsp");
		}
	}
}

5.视图层

    <form action="transfer">
    	转账账户:<input type="text" name = "accOutId"><br>
    	密码:<input type="password" name = "accOutPassword"><br>
    	转账金额:<input type="text" name = "accOutBalance"><br>
    	收帐账户:<input type="text" name = "accInId"><br>
    	收账用户名:<input type="text" name = "accInName"><br>
    	<input type="submit" value="转账">
    </form>

 

十一、getMapper接口绑定以及多参数传递

1、作用:实现创建一个接口后把mapper.xml由mybatis生成接口的实现类,通过调用接口对象就可以获取 mapper.xml 中编写的 sql.

2、后面 mybatis 和 spring 整合时使用的是这个方案.。、

3、实现步骤

  • 创建一个借口
    • 接口包名和接口名与 mapper.xml 中<mapper>namespace相同

    • 接口中方法名和 mapper.xml 标签的 id 属性相
  • 在 mybatis.xml 中使用<package>进行扫描接口和 mapper.xml

4、代码实现步骤

  • 在 mybatis.xml 中<mappers>下使用<package>
    • 	<mappers>
      		<package name="com.tk.mapper"/>
      	</mappers>
  • 在 com.bjsxt.mapper 下新建接口

    • public interface LogMapper {
      
      	/**
      	 * 查询所有的转账日志信息
      	 * @return
      	 */
      	List<Log> selAll();
      	/**
      	 * 查询俩个用户之前的转账信息
      	 * @param accOut
      	 * @param accIn
      	 * @return
      	 */
      	List<Log> selAccount(int accOut,int accIn);
      	
      }
  • 在 com.bjsxt.mapper 新建一个 LogMapper.xml

    • namespace 必须和接口全限定路径(包名+类名)一致

    • id 值必须和接口中方法名相同

    •  如果接口中方法为多个参数,可以省略 parameterType

    •     <mapper namespace="com.tk.mapper.LogMapper">
        	<select id="selAll" resultType="Log">
        		select * from m_log
        	</select> 
        	<!-- 多参数可以不用写参数类型 -->
        	<select id="selAccount" resultType="Log">
        		select * from m_log where accOut=#{0} and accIn=#{1}
        	</select>
         </mapper>

5、参数实现方法:

  • 在接口中声明方法
  • 在mapper.xml中添加 #{ 0 / param1 }

6、使用注解方式

  • 在接口中声明方法
    •         /**
      	 * mybatis 把参数转换为 map 了,其中@Param("key") 参数内 容就是 map 的 value 
      	 * @param accOut
      	 * @param accIn
      	 * @return
      	 */
      	List<Log> selAccount(@Param("accOut")int accOut,@Param("accIn") int accIn);
  • 在 mapper.xml 中添加 #{ } 里面写@Param(“内容”)参数中内容

    •   	<select id="selAccount" resultType="Log">
        		select * from m_log where accOut=#{accOut} and accIn=#{accIn}
        	</select>

十二、动态SQL

1、根据不同的条件需要执行不同的 SQL 命令.称为动态 SQL

2、MyBatis 中动态 SQL 在 mapper.xml 中添加逻辑判断等.

3、if

<select id="selInfoByOutIn" resultType="Log">
  	select * from m_log where 1=1
  	<if test="accOut!=null and accOut!=''">
  	     and accOut=#{accOut}
  	</if>
  	<if test="accIn!=null and accIn!=''">
             and accIn=#{accIn}
  	</if>
 </select>

4、where

  • 当编写 where 标签时,如果内容中第一个是 and 去掉第一个and
  • 如果<where>中有内容会生成 where 关键字,如果没有内容不生成 where 关键
    •  比直接使用<if>少写 where1=1
<select id="selInfoByOutIn" resultType="Log">
  	select * from m_log 
  	<where>
  	    <if test="accOut!=null and accOut!=''">
  		and accOut=#{accOut}
  		</if>
  		<if test="accIn!=null and accIn!=''">
  		       	and accIn=#{accIn}
  		</if>
  	</where>
</select>

5、 choose、when、otherwise

  • 只有有一个成立,其他都不执行。
  • 如果 accIn 和 accOut 都不是 null 或不是””生成的sql 中只有 where accin=?
  <select id="selInfoByOutIn" resultType="Log">
  	select * from m_log 
  	<where>
  	    <choose>
	  	<when test="accOut!=null and accOut!=''">
	  		and accOut=#{accOut}
	  	</when>
	  	<when test="accIn!=null and accIn!=''">
	  		and accIn=#{accIn}
	  	</when>
	  	<otherwise>
	  					
	  	</otherwise>
  	    </choose>
      </where>
 </select>

6、 set

  • 用于修改set中的set从句
  • 作用:去掉最后一个逗号、
  • 如果<set>里面有内容生成 set 关键字,没有就不生成
    • id=#{id} 目的防止<set>中没有内容,mybatis 不生成 set 关键字,如果修改中没有 set 从句 SQL 语法错误.
  <update id="upd">
  	update m_log
  	<set>
  		id=#{id}
  	</set>
  	<if test="accOut!=null and accOut!=''">
  	     ,accOut=#{accOut}
  	</if>
  	<if test="accIn!=null and accIn!=''">
  		,accIn=#{accIn}
  	</if>
  	where id=#{id}
  </update>

7、Tirm

  • prefix 在前面添加内容
  • prefixOverrides 去掉前面内容
  • suffix 在后面添加内容
  • suffixOverrieds 去掉后面内容
  • 执行顺序去掉内容后添加内容
      <update id="upd">
      	update m_log
      	<trim prefix="set" suffixOverrides=",">
      		a=a,
      	</trim>
      	where id =100
      </update>

8、bind

1、作用:给参数重新赋值

2、应用场景:

  • 模糊查询
  • 在原内容前后添加东西
  <select id="selInfoByOutIn1" resultType="Log">
  	<bind name="money" value="'%'+money+'%'"/>
  	select * from m_log where money like #{money}
  </select>

9、foreach

  • 循环参数内容,还具备在内容的前后添加内容,还具备添加分隔符功能
  • 适用场景:in 查询中.批量新增中(mybatis 中 foreach 效率比较低)
  • 使用:
    • collectino=”” 要遍历的集合
    • item 迭代变量,#{迭代变量名}获取内容
    • open 循环后左侧添加的内容
    • close 循环后右侧添加的内容
    • separator 每次循环时,元素之间的分隔符
  <select id="selInfoByOutIn1" resultType="Log" parameterType="list">
  	select * from m_log where id in 
  	<foreach collection="list" item="aaa" open="(" close=")" separator=",">
  	    #{aaa}
  	</foreach>
  </select>

10、sql  include

1.某些 SQL 片段如果希望复用,可以使用<sql>定义这个片段

  <sql id="mysql">
  	id,accOut,accIn
  </sql>

2.在<select>或<delete>或<update>或<insert>中使用<include>引用

   <select id="selInfoByOutIn2" resultType="Log" parameterType="list">
  	select
  	<include refid="mysql"/>
  	from m_log
  </select>

十二、ThreadLocal+OpenSessionInView(实现mybatis的封装(利用Filter))

1.ThreadLocal

 线程容器,给线程绑定一个 Object 内容,后只要线程不变,可以随时取出.

 改变线程,无法取出内容.

            封装mybatis

package com.tk.test;

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * Mybatis工具类
 * @data :2019-4-9下午7:06:52
 * @author :田坤
 */
public class MybatisUtil {
    //factory实例化过程是一个比较消耗性能的过程
    //保证有且仅有一个factory
    private static SqlSessionFactory factory = null;
    private static ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();
	//当执行类的时候加载
	static{
		try {
			InputStream is = Resources.getResourceAsStream("mybatis.xml");
			factory = new SqlSessionFactoryBuilder().build(is);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取sqlsession
	 * @return
	 */
	public static SqlSession getSession(){
		SqlSession session = tl.get();
		if(session==null){
			tl.set(factory.openSession());
		}
		return tl.get();
	}
	
	public static void cloesSqlSession(){
		SqlSession session = tl.get();
		if(session!=null){
			session.close();
		}
		tl.set(null);
	}
	
}

            写过滤器(OpenSessionInView)

package com.tk.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;

import org.apache.ibatis.session.SqlSession;

import com.tk.test.MybatisUtil;
/**
 * openSessionInView最早是由spring框架提出的,整合hibenate就是用的OpenSessionInView
 * @data :2019-4-9下午7:13:52
 * @author :田坤
 */
//@WebFilter("/*")
public class OpenSessionInView implements javax.servlet.Filter{

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void doFilter(ServletRequest arg0, ServletResponse arg1,
			FilterChain chain) throws IOException, ServletException {
		SqlSession session = MybatisUtil.getSession();
		try {
			System.out.println("我被执行了");
			chain.doFilter(arg0, arg1);
			session.commit();
		} catch (Exception e) {
			session.rollback();
			e.printStackTrace();
		}finally{
			MybatisUtil.cloesSqlSession();
		}
		
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		System.out.println("过滤器生成了");
		
	}

}

在servlet类中检测

package com.tk.servlet;


import java.io.IOException;
import java.sql.Timestamp;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.ibatis.session.SqlSession;

import com.tk.mapper.LogMapper;
import com.tk.pojo.Log;
import com.tk.test.MybatisUtil;

public class Test extends HttpServlet {
	@Override
	protected void service(HttpServletRequest arg0, HttpServletResponse resp)
			throws ServletException, IOException {
		resp.setContentType("text/html;charset=utf-8");
		SqlSession session = MybatisUtil.getSession();
		LogMapper mapper = session.getMapper(LogMapper.class);
		int i = mapper.ins(new Log(0, "11", "12", 180, new Timestamp(System.currentTimeMillis())));
		resp.getWriter().write(i);
	}
}

十三、缓存

1、应用程序和数据库交互的过程是一个相对比较耗时的过程

2、缓存存在的意义:让应用程序减少对数据库的访问,提升程序运行效率

3、MyBatis 中默认 SqlSession 缓存开启

  •  同一个 SqlSession 对象调用同一个<select>时,只有第一次访问数据库,第一次之后把查询结果缓存到 SqlSession 缓存区(内存)

  • 缓存的是 statement 对象.(简单记忆必须是用一个<select>)

    • 在 myabtis 时一个<select>对应一个 statement 对象

  • 有效范围必须是同一个 SqlSession 对象

4、缓存流程

  1. 步骤一: 先去缓存区中找是否存在 statement
  2. 步骤二:返回结果
  3. 步骤三:如果没有缓存 statement 对象,去数据库获取数据
  4. 步骤四:数据库返回查询结果
  5. 步骤五:把查询结果放到对应的缓存区中

5、SqlSessionFactory缓存

  • 又叫:二级缓存
  • 有效范围:同一个 factory 内哪个 SqlSession 都可以获取
  • 什么时候使用二级缓存:
    • 当数据频繁被使用,很少被修改
  • 使用二级缓存步骤
    • 在 mapper.xml 中添加
      • <cache readOnly="true"></cache>
    • 如果不写 readOnly=”true”需要把实体类序列化
  • 当 SqlSession 对象 close()时或 commit()时会把 SqlSession 缓存的数据刷(flush)到 SqlSessionFactory 缓存区中

十四、Mybaits的多表查询

  1. Mybatis 实现多表查询方式
    1. 业务装配.对两个表编写单表查询语句,在业务(Service)把查询的两个结果进行关联.
    2. 使用AutoMapping特性,在实现两表联合查询时通过别名完成映射
    3. 使用 MyBatis 的resultMap 属性进行实现
  2. 多表查询时,类中包含另一个类的对象的分类
    1. 单个对象
    2. 集合对象

①、ResultMap属性

  1. resultMap 属性写在mapper.xml中,由程序员控制SQL查询结果与实体类的映射关系
    1. 默认 MyBatis 使用 AutoMapping 特性
  2. 使用<resultMap>标签时,<select>标签不写 resultType 属性,而是使用 resultMap 属性引用<resultMap>标签
  3. 使用 resultMap 实现单表映射关系
    1. <resultMap type="Teacher" id="my1">
      	<!-- 主键使用id配置映射关系 -->
      	<id property="id" column="id"/>
      	<!-- 其他列使用result标签配置映射关系 -->
      	<result property="name" column="name"/>
      </resultMap>
      <select id="selAll" resultMap="my1">
      	select * from t_teacher
      </select>
      
  4. 使用 resultMap 实现关联单个对象(N+1 方式)

    1. N+1 查询方式,先查询出某个表的全部信息,根据这个表的信息查询另一个表的信息.

    2. 与业务装配的区别:

      1. 在 service 里面写的代码,由 mybatis 完成装配

    3. 实现步骤:

      1. 在 Student 实现类中包含了一个 Teacher 对象

        1. public class Student {
          
          	private int id;
          	private String name;
          	private int age;
          	private int tid;
          	private Teacher teacher;
      2. 在 TeacherMapper 中提供一个查询

        1. <select id="selById" resultType="Teacher" parameterType="int">
          	select * from t_teacher where id=#{0}
          </select>
      3.  在 StudentMapper 中

        1. <association> 装配一个对象时使用

        2. property: 对象在类中的属性名

        3. select:通过哪个查询查询出这个对象的信息

        4. column: 把当前表的哪个列的值做为参数传递给另一个查询

        5. 大前提使用 N+1 方式.和时如果列名属性名相同可以不配置,使用 Automapping 特性.但是 mybatis 默认只会给列
          专配一次

          1. <resultMap type="Student" id="my1">
            	<id property="id" column="id"/>
            	<result property="name" column="name"/>
            	<result property="age" column="age"/>
            	<result property="tid" column="tid"/>
            	<association property="teacher" select="tk.selById" column="tid"></association>
            </resultMap>
            <select id="selAll" resultMap="my1">
            	select * from t_student
            </select>
        6. 把上面代码可以简化为

          1. <resultMap type="Student" id="my1">
            	<result property="tid" column="tid"/>
            	<association property="teacher" select="tk.selById" column="tid"></association>
            </resultMap>
            <select id="selAll" resultMap="my1">
            	select * from t_student
            </select>
  5. 使用 resultMap 实现关联单个对象(联合查询方式)

    1. 只需要编写一个 SQL,在 StudentMapper 中添加下面效果

      1. <association/>只要专配一个对象就用这个标签

      2. 此时时把<association/>小的<resultMap>看待

      3. javaType 属性:<association/>专配完后返回一个什么类型的对象.取值是一个类(或类的别名)

      4. <resultMap type="Student" id="stuMap1"> 
        	<id column="sid" property="id"/> 
        	<result column="sname" property="name"/> 
        	<result column="age" property="age"/> 
        	<result column="tid" property="tid"/> 
        	<association property="teacher" javaType="Teacher">
        			<id column="tid" property="id"/> 
        		<result column="tname" property="name"/> 
        	 </association>
         </resultMap> 
        <select id="selAll1" resultMap="stuMap1"> 
        		select s.id sid,s.name sname,age age,t.id tid,t.name tname FROM t_student s left outer join t_teacher t on s.tid=t.id 
        </select>
  6.  N+1 方式和联合查询方式对比

    1. N+1:需求不确定时.

    2. 联合查询:需求中确定查询时两个表一定都查询.

  7. N+1 名称由来

    1. 举例:学生中有 3 条数据

    2. 需求:查询所有学生信息级授课老师信息

    3. 需要执行的 SQL 命令

      1. 查询全部学生信息:select*from 学生

      2. 执行 3 遍 select*from 老师 whereid=学生的外键

    4. 使用多条 SQl 命令查询两表数据时,如果希望把需要的数据都查询出来,需要执行 N+1 条 SQl 才能把所有数据库查询出来

    5. 缺点:效率低
    6. 优点:
      1. 如果有的时候不需要查询学生是同时查询老师.只需要执行一个 select*fromstudent
    7. 适用场景: 有的时候需要查询学生同时查询老师,有的时候只需要查询学生
    8. 如果解决 N+1 查询带来的效率低的问题
      1. 默认带的前提: 每次都是两个都查询
      2. 使用两表联合查询

②、使用<resultMap>查询关联集合对象(N+1)

  1. 在 Teacher 中添加 List<Student>
    1. public class Teacher {
      	
      	private int id;
      	private String name;
      	private List<?> student;
  2. 在 StudentMapper.xml 中添加通过 tid 查询
    1. <select id="selById" resultType="Student" parameterType="int">
      	select * from t_student where tid=#{0}
      </select>
  3. 在 TeacherMapper.xml 中添加查询全部
    1. <collection/> 当属性是集合类型时使用的标签.
    2. <resultMap type="Teacher" id="my2">
      	<id property="id" column="id"/>
      	<result property="name" column="name"/>
      	<collection property="student" select="com.tk.mapper.StudentMapper.selById" column="id"></collection>
      </resultMap>
      <select id="selAll2" resultMap="my2">
      	select * from t_teacher 
      </select>

③.使用<resultMap>实现加载集合数据(联 合查询方式)

  1. 在 teacherMapper.xml 中添加
    1. mybatis 可以通过主键判断对象是否被加载过.
    2. 不需要担心创建重复 Teacher
<resultMap type="Teacher" id="my3">
	<id property="id" column="tid"/>
	<result property="name" column="tname"/>
	<collection property="student" ofType="Student">
		<id property="id" column="sid"/>
		<result property="name" column="sname"/>
		<result property="age" column="age"/>
		<result property="tid" column="tid"/>
	</collection>
</resultMap>
<select id="selAll3" resultMap="my3">
	select t.id teid,t.name tname,s.id sid,s.name sname,age,tid from t_teacher t LEFT JOIN t_student s ON  t.id=s.tid 
</select>

④使用 AutoMapping 结合别名实现多表查询

  1. 只能使用多表联合查询方式
  2. 要求:查询出的列别和属性名相同.
  3. 实现方式
    1. 在 SQL 是关键字符,两侧添加反单引号
	<select id="selAll4" resultType="teacher">
		select t.id id,t.name name,s.id `t_student.id` ,s.name `t_student.name`,age,tid  
		from t_teacher t LEFT JOIN t_student s ON  t.id=s.tid 
	</select>

十五、Mybatis的注解

1、注解:为了简化配置文件.
2、Mybatis 的注解简化 mapper.xml 文件.  如果涉及动态 SQL 依然使用 mapper.xml

3、mapper.xml和注解可以共存

4、使用注解时 mybatis.xml 中<mappers>使用

  • <package>
  • <mapper class="接口">

5、实现查询

@select("select * from t_student")
List<Student> selAll();

6、实现新增

	@Insert("insert into t_student values(default,#{name},#{age},#{tid})")
	int insertStudent(Student stu);

7、实现修改

	@Update("update t_student set name=#{name} where id = #{id}")
	int updateStudent(Student stu);

8、实现删除

	@Delete("delete from t_student where id =#{0}")
	int deleteStudent(int i);

9、使用注解实现<resultMap>功能

以N+1为例子

@Results() 相当于<resultMap>

@Result() 相当于<id/>或<result/>     

@Result(id=true) 相当与<id/>

@Many() 相当于<collection/>

@One() 相当于<association/>

学生接口

	@Select("select * from t_student where tid=#{0}")
	List<Student> selAll(int i);

教师接口

	@Results(value={
		@Result(id=true,property="id",column="id"),
		@Result(property="name",column="name"),
		@Result(property="list",column="id",many=@Many(select="com.tk.mapper.StudentMapper.selAll"))
			
	})
	@Select("select * from t_teacher")
	List<Teacher> selAll();

十六、Mybatis的运行原理

1、运行过程中设计到的类

  • ResourcesMyBatis 中 IO 流的工具类      加载配置文件
  • SqlSessionFactoryBuilder()构建器
    • 作用:创建 SqlSessionFactory 接口的实现类
  • XMLConfigBuilder MyBatis 全局配置文件内容构建器类
    • 作用:负责读取流内容并转换为 JAVA 代码.
  • Configuration 封装了全局配置文件所有配置信息
    • 全局配置文件内容存放在 Configuration 中
  • DefaultSqlSessionFactory 是SqlSessionFactory接口的实现类
  • transaction事务类
    • 每一个 SqlSession 会带有一个 Transaction 对象
  • TransactionFactory 事务工厂
    • 负责生产 Transaction
  • Executor MyBatis 执行器
    • 作用:负责执行 SQL 命令
    • 相当于 JDBC 中 statement 对象(或 PreparedStatement或 CallableStatement)
    • 默认的执行器 SimpleExcutor
    • 批量操作 BatchExcutor
    • 通过 openSession(参数控制)
  • DefaultSqlSession 是 SqlSession 接口的实现类
  • ExceptionFactory   MyBatis 中异常工厂

2、流程图

3、文字解释

在 MyBatis 运行开始时需要先通过 Resources 加载全局配置文件.下面需要实例化 SqlSessionFactoryBuilder 构建器.帮SqlSessionFactory 接口实现类 DefaultSqlSessionFactory.在实例化 DefaultSqlSessionFactory 之前需要先创建 XmlConfigBuilder解析全局配置文件流,并把解析结果存放在 Configuration 中.之后把Configuratin 传递给 DefaultSqlSessionFactory.到此 SqlSessionFactory 工厂创建成功.

由 SqlSessionFactory 工厂创建 SqlSession.每次创建 SqlSession 时,都需要由 TransactionFactory 创建 Transaction对象,同时还需要创建SqlSession 的执行器 Excutor,最后实例化DefaultSqlSession,传递给 SqlSession 接口.根据项目需求使用 SqlSession 接口中的 API 完成具体的事务操作.

如果事务执行失败,需要进行 rollback 回滚事务.
           如果事务执行成功提交给数据库.关闭 SqlSession
 

 

 

班级查询功能

飞机起降系统查询

猜你喜欢

转载自blog.csdn.net/qq_41965731/article/details/89066487
今日推荐