目录
十二、ThreadLocal+OpenSessionInView(实现mybatis的封装(利用Filter))
对我而言,算是第一次接触框架,框架是是什么,我感觉像是我在以前学习中把一些复用率高的代码封装起来,方便自己的使用或者说别人的使用。对于刚开始接触的我来说,最大的用处就是减少重复劳力。重复造轮子
壹、什么是框架?
- 框架:软件的半成品.未解决问题制定的一套约束,在提供功能基础上进行扩充。
- 框架中一些不能被封装的代码(变量),需要使用框架者新建一个xml 文件,在文件中添加变量内容.
- 需要建立特定位置和特定名称的配置文件
- 需要使用XML解析技术和反射技术
- 常用概念:
- 类库:提供的类没有封装一定的逻辑,
- eg:类库就是名言警句,写作文时引入
- 框架:里面有一些特定的约束
- 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命名目录接口技术
- <transactionManager/> type 数据可取值
四、三种查询方式
- .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]}
五、注解
- 注解存在的意义:简化XML文件的开发
- 注解在servlet3.0规范之后大力推广
- 注解前面的@XXX,表示引用一个@interface
- @interface表示注解声明
- 注解也可以有属性,它本质就是一个接口(类)
- 每次使用注解都需要到导包
- 注解语法:@XXX(属性名=值)
- 值的分类
- 如果值是基本数据类型或字符串:属性名=值
- 如果值是数组类型:属性名={值,值}
- 如果只有一个值是可以省略大括号
- 如果值是类类型,属性名=@名称
- 如果注解只需要一个属性赋值,且这个属性是默认属性,可以省略属性名
六、路径
- 编写路径为了告诉编译器如何找到其他资源.
- 路径分类
-
相对路径: 从当前资源出发找到其他资源的过程
-
绝对路径: 从根目录(服务器根目录或项目根目录)出发找到其他资源的过程
-
标志: 只要以/开头的都是绝对路径
-
-
-
绝对路径
-
如果是请求转发 / 表示项目根目录(WebRoot)
-
其他重定向,<img/> <script/>,<style/>,location.href 等/都表示服务器根目录(tomcat/webapps 文件夹),所以我们需要填写路径的时候需要加上项目名称
-
-
如果客户端请求的控制器,控制器转发到JSP后,jsp中如果使用相对路径,需要按照控制器的路径去寻找其他资源
-
保鲜方法就使用绝对路径
-
注意:浏览器不是直接把有静态资源(scr、href......)东西,直接响应回来,而响应的是一个字符串,而浏览器是去解析这个字符串,每次解析到在去请求服务器,服务器在进行响应
七、Log4J日志(Log for Java)(<Setting>标签)
在mybatis里面同样也支持日志文件输出,日志文件的输出有利于我们调制程序,了解程序的运行过程和细节详细信息
- 由 apache 推出的开源免费日志处理的类库.
- 作用:
-
在项目中编写 System.out.println();输出到控制台,当项目发布到 tomcat 后,没有控制台(在命令行界面能看见.),不容易观察一些输出结果.
-
log4j 不仅能把内容输出到控制台,还能把内容输出到文件中.便于观察结果
-
-
Log4j的输出级别
-
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类型
-
在XXXMapper.xml中<select><delete>等标签的 parameterType 可以控制参数类型
-
sqlsession的selectOne() 和 selectList()的第二个参数和selectMap()的第三个参数都表示参数的数
-
<select id="selAll" resultType="com.tk.pojo.User" parameterType="int"> select * from m_user where id = #{0} </select>
-
在 Mapper.xml 中可以通过#{}获取参数
-
parameterType 控制参数类型
-
#{}获取参数内容
-
使用索引,从 0 开始 #{0}表示第一个参数
-
也可以使用#{param1}第一个参数
-
如果只有一个参数(基本数据类型或 String),mybatis对#{}里面内容没有要求只要写内容即可.
-
如果参数是对象 #{属性名}
-
如果参数是 map 写成#{key}
-
-
-
- #{ } 和 ${ }的区别
- #{} 获取参数的内容支持 索引获取,param1获取指定位置参数,并且 SQL 使用?占位符
- ${} 字符串拼接不使用?,默认找${内容}内容的 get/set 方法,如果写数字,就是一个数字
- 如果在 xml 文件中出现 “<”,“>”,双引号 等特殊字符时可以使用XML 文件转义标签(XML 自身的)
- mybatis 中实现 mysql 分页写法
- ? 不允许在关键字前后进行数学运算,需要在代码中计算完成后传递到 mapper.xml 中
- mapper中
-
<select id="selAll" resultType="com.tk.pojo.User" parameterType="map"> select * from m_user limit #{pageStart},#{pageSize} </select>
-
- 测试类中
-
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设置)
- 系统内置别名:一般是把大写变为小写,本身就小写的在前面加下划线
- 给某个类起别名
-
<typeAliases> <typeAlias type="com.tk.pojo.User" alias="ctpu"/> </typeAliases>
-
- 直接给某个包下所以类起别名,别名为类名,不区分大小写
-
<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实现新增(事务处理)
- 概念复习
- 功能:从应用程序角度出发,软件具有哪些功能.
- 业务:完成功能时的逻辑.对应 Service 中一个方法
- 事务:从数据库角度出发,完成业务时需要执行的 SQL 集合,统称为一个事务
- 事务回滚:如果在一个事务中某个 SQL 执行事务,希望回归到事务的原点,保证数据库数据的完整性.(比如在银行存钱)
- 在 mybatis 中默认是关闭了 JDBC 的自动提交功能
- 每一个 SqlSession 默认都是不自动提交事务.
- session.commit()提交事务.
- openSession(true);自动提交 等价于=setAutoCommit(true);
- mybatis 底层是对 JDBC 的封装.
- JDBC 中 executeUpdate()执行新增,删除,修改的 SQL.返回值 int,表示受影响的行数.
- mybatis 中<insert><delete><update>标签没有 resultType 属性,认为返回值都是 int
- openSession()时 Mybatis 会创建 SqlSession 时同时创建一个Transaction(事务对象),同时 autoCommit 都为 false
- 如果出现异常,应该 session.rollback()回滚事务.
- 增删改 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、缓存流程
- 步骤一: 先去缓存区中找是否存在 statement
- 步骤二:返回结果
- 步骤三:如果没有缓存 statement 对象,去数据库获取数据
- 步骤四:数据库返回查询结果
- 步骤五:把查询结果放到对应的缓存区中
5、SqlSessionFactory缓存
- 又叫:二级缓存
- 有效范围:同一个 factory 内哪个 SqlSession 都可以获取
- 什么时候使用二级缓存:
- 当数据频繁被使用,很少被修改
- 使用二级缓存步骤
- 在 mapper.xml 中添加
-
<cache readOnly="true"></cache>
-
- 如果不写 readOnly=”true”需要把实体类序列化
- 在 mapper.xml 中添加
- 当 SqlSession 对象 close()时或 commit()时会把 SqlSession 缓存的数据刷(flush)到 SqlSessionFactory 缓存区中
十四、Mybaits的多表查询
- Mybatis 实现多表查询方式
- 业务装配.对两个表编写单表查询语句,在业务(Service)把查询的两个结果进行关联.
- 使用AutoMapping特性,在实现两表联合查询时通过别名完成映射
- 使用 MyBatis 的resultMap 属性进行实现
- 多表查询时,类中包含另一个类的对象的分类
- 单个对象
- 集合对象
①、ResultMap属性
- resultMap 属性写在mapper.xml中,由程序员控制SQL查询结果与实体类的映射关系
- 默认 MyBatis 使用 AutoMapping 特性
- 使用<resultMap>标签时,<select>标签不写 resultType 属性,而是使用 resultMap 属性引用<resultMap>标签
- 使用 resultMap 实现单表映射关系
-
<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>
-
-
使用 resultMap 实现关联单个对象(N+1 方式)
-
N+1 查询方式,先查询出某个表的全部信息,根据这个表的信息查询另一个表的信息.
-
与业务装配的区别:
-
在 service 里面写的代码,由 mybatis 完成装配
-
-
实现步骤:
-
在 Student 实现类中包含了一个 Teacher 对象
-
public class Student { private int id; private String name; private int age; private int tid; private Teacher teacher;
-
-
在 TeacherMapper 中提供一个查询
-
<select id="selById" resultType="Teacher" parameterType="int"> select * from t_teacher where id=#{0} </select>
-
-
在 StudentMapper 中
-
<association> 装配一个对象时使用
-
property: 对象在类中的属性名
-
select:通过哪个查询查询出这个对象的信息
-
column: 把当前表的哪个列的值做为参数传递给另一个查询
-
大前提使用 N+1 方式.和时如果列名属性名相同可以不配置,使用 Automapping 特性.但是 mybatis 默认只会给列
专配一次-
<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>
-
-
把上面代码可以简化为
-
<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>
-
-
-
-
-
使用 resultMap 实现关联单个对象(联合查询方式)
-
只需要编写一个 SQL,在 StudentMapper 中添加下面效果
-
<association/>只要专配一个对象就用这个标签
-
此时时把<association/>小的<resultMap>看待
-
javaType 属性:<association/>专配完后返回一个什么类型的对象.取值是一个类(或类的别名)
-
<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>
-
-
-
N+1 方式和联合查询方式对比
-
N+1:需求不确定时.
-
联合查询:需求中确定查询时两个表一定都查询.
-
-
N+1 名称由来
-
举例:学生中有 3 条数据
-
需求:查询所有学生信息级授课老师信息
-
需要执行的 SQL 命令
-
查询全部学生信息:select*from 学生
-
执行 3 遍 select*from 老师 whereid=学生的外键
-
-
使用多条 SQl 命令查询两表数据时,如果希望把需要的数据都查询出来,需要执行 N+1 条 SQl 才能把所有数据库查询出来
- 缺点:效率低
- 优点:
- 如果有的时候不需要查询学生是同时查询老师.只需要执行一个 select*fromstudent
- 适用场景: 有的时候需要查询学生同时查询老师,有的时候只需要查询学生
- 如果解决 N+1 查询带来的效率低的问题
- 默认带的前提: 每次都是两个都查询
- 使用两表联合查询
-
②、使用<resultMap>查询关联集合对象(N+1)
- 在 Teacher 中添加 List<Student>
-
public class Teacher { private int id; private String name; private List<?> student;
-
- 在 StudentMapper.xml 中添加通过 tid 查询
-
<select id="selById" resultType="Student" parameterType="int"> select * from t_student where tid=#{0} </select>
-
- 在 TeacherMapper.xml 中添加查询全部
- <collection/> 当属性是集合类型时使用的标签.
-
<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>实现加载集合数据(联 合查询方式)
- 在 teacherMapper.xml 中添加
- mybatis 可以通过主键判断对象是否被加载过.
- 不需要担心创建重复 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 结合别名实现多表查询
- 只能使用多表联合查询方式
- 要求:查询出的列别和属性名相同.
- 实现方式
- 在 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