环境搭建
- 导入各种
jar
包 - 在
src
下新建全局配置文件(编写JDBC
四个变量)- 没有名称和地址要求
- 在全局配置文件中引入
DTD
或schema
- 若导入
dtd
之后没有提示
window-->preference-->XML-->XML catalog-->add //添加dtd
- 若导入
- 全局配置文件内容
<?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/ssm"/> <property name="username" value="root"/> <property name="password" value="smallming"/> </dataSource> </environment> </environments> </configuration>
- 新建以
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"> <!-- namesapce:理解成实现类的全路径(包名+类名) --> <mapper namespace="a.b" > <!-- id:方法名 parameterType:定义参数类型 resultType:返回值类型. 如果方法返回值是 list,在 resultType 中写 List 的泛型, 因为 mybatis对 jdbc 封装,一行一行读取数据 --> <select id="selAll" resultType="com.bjsxt.pojo.Flower"> select * from flower </select> </mapper>
- 文件作用:编写需要执行的
- 测试结果(只有在单独使用
mybatis
时使用,跟ssm
整合时,下面代码不用写)
InputStream is = Resources.getResourceAsStream("myabtis.xml");
//使用工厂设计模式
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//生产 SqlSession
SqlSession session=factory.openSession();
List<Flower> list = session.selectList("a.b.selAll");
for (Flower flower : list) {
System.out.println(flower.toString());
}
session.close();
环境搭建详解
- 全局配置文件中内容
<transactionManager/>
的type
属性可取值JDBC
,事务管理使用JDBC
原生事务管理方式MANAGED
把事务管理转交给其他容器.原生JDBC
事务setAutoMapping(false)
;
<dataSouce/>
的type
属性POOLED
使用数据库连接池UNPOOLED
不实用数据库连接池,和直接使用JDBC
一样JNDI
:java 命名目录接口技术
数据库连接池
- 在内存中开辟一块空间,存放多个数据库连接对象.
JDBC Tomcat Pool
,直接由tomcat
产生数据库连接池.- 状态
active
状态: 当前连接对象被应用程序使用中Idle
空闲状态: 等待应用程序使用
- 使用数据库连接池的目的
- 在高频率访问数据库时,使用数据库连接池可以降低服务器系
统压力,提升程序运行效率.- 小型项目不适用数据库连接池.
- 在高频率访问数据库时,使用数据库连接池可以降低服务器系
- 实现
JDBC tomcat Pool
的步骤.- 在
web
项目的META-INF
中存放context.xml
,在context.xml
编写数据库连接池相关属性
<?xml version="1.0" encoding="UTF-8"?> <Context> <Resource driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/ssm" username="root" password="smallming" maxActive="50" maxIdle="20" name="test" auth="Container" maxWait="10000" type="javax.sql.DataSource" /> </Context>
- 把项目发布到
tomcat
中,数据库连接池就产生了
- 在
- 可以在
java
中使用jndi
获取数据库连接池中对象Context
: 上下文接口,context.xml
文件对象类型- 代码:
Context cxt = new InitialContext(); DataSource ds = (DataSource) cxt.lookup("java:comp/env/test"); Connection conn = ds.getConnection();
- 当关闭连接对象时,把对象归还给数据库连接池,把状态改成
Idle
三种查询方式
selectList()
返回值为List<resultType 属性控制>
- 适用于查询结果都需要遍历的需求
List<Flower> list = session.selectList("a.b.selAll"); for (Flower flower : list) { System.out.println(flower.toString()); }
selectOne()
返回值为Object
- 适用于返回结果只是变量或一行数据时
int count = session.selectOne("a.b.selById"); System.out.println(count);
- selectMap() 返回值 Map
- 适用于需要在查询结果中通过某列的值取到这行数据的需求.
Map<key,resultType 控制>
Map<Object, Object> map = session.selectMap("a.b.c","name123"); System.out.println(map);
注解
- 注解存在的意义:简化xml文件的开发
- 注解在
servlet3.0
规范之后大力推广的 - 注解前面的
@XXX
表示引用一个@interface
@interface
表示注解的声明
- 注解可以有属性,因为注解就是一个接口(类)
- 每次使用注解都要导包
- 注解的语法:
@XXX(属性名 = 值)
- 值的分类
- 如果值是基本的数据类型或字符串:属性名=值
- 如果值是数组类型:属性名={值,值}
- 如果只有一个值可以省略大括号
- 如果值是类类型,属性名=@名称
- 如果注解只需要给一个属性赋值,且这个属性是默认属性,可以省略属性名
路径
- 编写路径为了告诉编译器如何找到其他资源.
- 路径分类
- 相对路径:从当前资源出发找到其他资源的过程
- 绝对路径: 从根目录(服务器根目录或项目根目录)出发找到其他资源的过程
- 标志: 只要以
/
开头的都是绝对路径
- 标志: 只要以
- 绝对路径:
- 如果是请求转发
/
表示项目根目录(WebContent
) - 其他重定向,
<img/>
<script/>
,<style/>
,location.href
等/
都表示
服务器根目录(tomcat/webapps
文件夹)
- 如果是请求转发
- 如果客户端请求的控制器,控制器转发到JSP后,jsp中如果使用相对
路径,需要按照控制器的路径去找其他资源.- 保险办法:使用绝对路径,可以防止上面的问题
Log4J
- 由
apache
推出的开源免费日志处理的类库 - 为什么需要日志:
- 在项目中编写
System.out.println();
输出到控制台,当项目发布到tomcat
以后,没有控制台(在命令行界面能看见),不容易观察一些输出结果 - log4j作用,不仅能把内容输出到控制台,还能把内容输出到文件中,便于观察结果
- 在项目中编写
- 使用步骤
- 导入
log4j-xxx.jar
- 在
src
下新建log4j.properties
(路径和名称都不允许改变)ConversionPattern
: 写表达式log4j.appender.appender.LOGFTLE.File
:文件位置及名称(日志文件拓展名.log)
log4j.rootCategory=DEBUG, CONSOLE ,LOGFILE //控制输出级别为info log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%C %d{YYYY-MM-dd hh:mm:ss} %m %n log4j.appender.LOGFILE=org.apache.log4j.FileAppender log4j.appender.LOGFILE.File=E:/my.log log4j.appender.LOGFILE.Append=true log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.LOGFILE.layout.ConversionPattern=%C %m %L %n
- 导入
log4j
输出级别fatal
(致命错误) >error
(错误) >warn
(警告) >info
(普通信息) >debug
(调试信息)- 在
log4j.properties
的第一行控制输出级别
log4j
输出目的地- 在一行控制输出目的地
log4j.rootCategory=DEBUG, CONSOLE ,LOGFILE //console对应文件中的log4j.appender.CONSOLE.* //logfile对应文件中的log4j.appender.LOGFILE.*
pattern
中常用几个表达式%C
包名+类名%d{YYYY-MM-dd HH:mm:ss}
时间%L
行号%m
信息%n
换行
标签
- 在
mybatis
全局配置文件中通过<settings>
标签控制mybatis
全局开关 - 在
mybatis.xml
中开启log4j
- 必须保证有
log4j.jar
- 在
src
下有log4j.properties
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
- 必须保证有
log4j
中可以输出指定内容的日志(控制某个局部内容的日志级别)- 命名级别(包级别):
<mapper>namespace
中除了最后一个是类名。例如namespace=”com.bjsxt.mapper.PeopleMapper
,其中包级别为com.bjsxt.mapper
,需要在log4j.propeties
中- 先在总体级别调成
Error
不输出无用信息 - 在设置某个指定位置级别为
DEBUG
log4j.rootCategory=ERROR, CONSOLE ,LOGFILE log4j.logger.com.bjsxt.mapper.mapper = DEBUG
- 先在总体级别调成
- 类级别
namespace
属性值 ,namespace
类名
- 方法级别
- 使用
namespace
属性值+标签id
属性值
- 使用
- 命名级别(包级别):
parameterType 属性
- 在
XXXMapper.xml
中<select><delect>
等标签的parameterType
可以控制参数类型 SqlSession
的selectList()
和selectOne()
的第二个参数和selectMap()
的第三个参数都表示方法的参数- 示例
People p = session.selectOne("a.b.selById",1); System.out.println(p
- 在
Mapper.xml
中可以通过#{}
获取参数parameterType
控制参数类型#{}
获取参数内容- 使用索引,从
0
开始#{0}
表示第一个参数 - 也可以使用
#{param1}
第一个参数 - 如果只有一个参数(基本数据类型或
String
),mybatis
对#{}
里面内容没有要求只要写内容即可<select id="selById" resultType="com.bjsxt.pojo.People" parameterType="int"> select * from people where id=#{0} </select>
- 如果参数是对象
#{属性名}
- 如果参数是
map
写成#{key}
- 使用索引,从
#{}
和${}
的区别#{}
获取参数的内容支持索引获取,param1
获取指定位置参数, 并且SQL
使用?占位符
${}
字符串拼接不使用?
,默认找${内容}
内容的get/set
方法,如果写数字,就是一个数字
- 如果在
xml
文件中出现“<” , “>”
,双引号 等特殊字符时可以使用
XML
文件转义标签(XML 自身的
)**例如:<![CDATA[ 内容 ]]>
mybatis
中实现 mysql 分页写法?
不允许在关键字前后进行数学运算,需要在代码中计算完成
后传递到mapper.xml
中- 在
java
代码中计算//显示几个 int pageSize = 2; //第几页 int pageNumber= 2; //如果希望传递多个参数,可以使用对象或map mapMap<String,Object> map = new HashMap<>(); map.put("pageSize", pageSize); //每一页开始的编号 map.put("pageStart", pageSize*(pageNumber-1)); //第二个参数传递了map给xml List<People> p =session.selectList("a.b.page",map);
- 在
mapper.xml
中的代码<select id="page" resultType="com.bjsxt.pojo.People" parameterType="map"> select * from people limit #{pageStart},#{pageSize} </select>
- 在
typeAliases 别名
- 系统内置别名: 把类型全小写
- 给某个类起别名
- alias=“自定义”
//给com.bjsxt.pojo.People起别名为peo <typeAliases> <typeAlias type="com.bjsxt.pojo.People"alias="peo"/> </typeAliase
mapper.xml
中peo
引用People
类<select id="page" resultType="peo" parameterType="map"> select * from people limit #{pageStart},#{pageSize} </select>
- alias=“自定义”
- 直接给某个包下所有类起别名,别名即为类名,不区分大小写
mybatis.xml
中配置<typeAliases> <package name="com.bjsxt.pojo" /> </typeAliases>
mapper.xml
中通过类名引用<select id="page" resultType="People" parameterType="map"> select * from people limit #{pageStart},#{pageSize} </select>
Mybatis实现新增
- 概念
- 功能:从应用程序角度出发,软件具有哪些功能.
- 业务:完成功能时的逻辑.对应
Servic
e 中一个方法 - 事务:从数据库角度出发,完成业务时需要执行的
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()
回滚事务
- 如果出现异常,应该
- 实现新增的步骤
- 在
mapper.xml
中提供<insert>
标签,标签没有返回值<insert id="ins" parameterType="People"> insert into people values(default,#{name},#{age}) </insert>
- 通过
session.insert()
调用新增方法//传给xml文件一个p对象 int index1 = session.insert("a.b.ins", p); if(index1>0){ System.out.println("成功"); }else{ System.out.println("失败"); }
- 在
MyBatis实现修改
- 在
mapper.xml
中提供<update>
标签<update id="upd" parameterType="People"> update people set name = #{name} where id = #{id} </update>
- 测试代码
People peo = new People(); peo.setId(3); peo.setName("王五"); int index = session.update("a.b.upd", peo); if(index>0){ System.out.println("成功"); }else{ System.out.println("失败"); } //MyBatis默认不自动提交 session.commit();
MyBatis实现删除
- 在
mapper.xml
提供<delete>
<delete id="del" parameterType="int"> delete from people where id = #{0} </delete>
- 测试代码
int del = session.delete("a.b.del",3); if(del>0){ System.out.println("成功"); }else{ System.out.println("失败"); } session.commit();
MyBatis 接口绑定方案及多参数传递
- 作用:实现创建一个接口后把
mapper.xml
由mybatis
生成接口的实现类,通过调用接口对象就可以获取mapper.xml
中编写的sql
- 后面
mybatis
和spring
整合时使用的是这个方案 - 实现步骤:
- 创建一个接口
- 接口包名和接口名与
mapper.xml
中<mapper>namespace
相同 - 接口中方法名和
mapper.xml
标签的id
属性相同
- 接口包名和接口名与
- 在
mybatis.xml
中使用<package>
进行扫描接口和mapper.xml
- 创建一个接口
- 代码实现步骤:
- 在
mybatis.xml
中<mappers>
下使用<package>
<mappers> <package name="com.xxx.mapper"/> </mappers>
- 在
com.xxx.mapper
下新建接口public interface LogMapper { List<Log> selAll(); }
- 在
com.xxx.mapper
新建一个LogMapper.xml
namespace
必须和接口全限定路径(包名+类名)一致id
值必须和接口中方法名相同- 如果接口中方法为多个参数,可以省略
parameterType
<mapper namespace="com.xxx.mapper.LogMapper"> <select id="selAll" resultType="log"> select * from log </select> </mapper>
- 在
- 多参数的实现方法
- 在接口中声明方法
List<Log> selByAccInAccout(String accin,String accout);
- 在 mapper.xml 中添加
#{}
中使用0,1,2
或param1,param2
<!-- 当多参数时,不需要写 parameterType --> <select id="selByAccInAccout" resultType="log" > select * from log where accin=#{0} and accout=#{1} </select>
- 可以使用注解的方式
- 在接口中声明方法
/** * mybatis 把参数转换为 map 了,其中@Param("key") 参数内 容就是 map 的 value * @param accin123 * @param accout3454235 * @return */ List<Log> selByAccInAccout(@Param("accin") String accin123,@Param("accout") String accout3454235);
- 在
mapper.xml
中添加#{}
里面写@Param(“内容”)
参数中内容
<!-- 当多参数时,不需要写 parameterType --> <select id="selByAccInAccout" resultType="log" > select * from log where accin=#{accin} and accout=#{accout} </select>
动态SQL
- 根据不同的条件需要执行不同的
SQL 命令
.称为动态SQL
MyBatis
中动态SQL
在mapper.xml
中添加逻辑判断等.<If>
使用<select id="selByAccinAccout" resultType="log"> select * from log where 1=1 <!-- OGNL 表达式,直接写 key 或对象的属性.不需要添加任何特字符号 --> <if test="accin!=null and accin!=''"> and accin=#{accin} </if> <if test="accout!=null and accout!=''"> and accout=#{accout} </if> </select>
<where>
- 当编写
where
标签时,如果内容中第一个是and
去掉第一个
and
- 如果
<where>
中有内容会生成where
关键字,如果没有内容不
生成where
关键字 - 使用示例
- 比直接使用
<if>
少写where 1=1
<select id="selByAccinAccout" resultType="log"> select * from log <where> <if test="accin!=null and accin!=''"> and accin=#{accin} </if> <if test="accout!=null and accout!=''"> and accout=#{accout} </if> </where> </select>
- 比直接使用
- 当编写
<choose> <when> <otherwise>
- 只有有一个成立,其他都不执行.
- 代码示例
- 如果
accin
和accout
都不是null
或不是空生成的sql
中只有where accin=?
(choose标签导致)
<select id="selByAccinAccout" resultType="log"> select * from log <where> <choose> <when test="accin!=null and accin!=''"> and accin=#{accin} </when> <when test="accout!=null and accout!=''"> and accout=#{accout} </when> </choose> </where> </select>
- 如果
<set>
用在修改SQL
中set
从句- 作用:去掉最后一个逗号
- 作用:如果
<set>
里面有内容生成set
关键字,没有就不生成 - 示例
id=#{id}
目的防止<set>
中没有内容,mybatis
不生成set
关键字,如果修改中没有set
从句SQL
语法错误.
<update id="upd" parameterType="log" > update log <set> id=#{id}, <if test="accIn!=null and accIn!=''"> accin=#{accIn}, </if> <if test="accOut!=null and accOut!=''"> accout=#{accOut}, </if> </set> where id=#{id} </update>
Trim
标签prefix
在前面添加内容prefixOverrides
去掉前面内容suffix
在后面添加内容suffixOverrieds
去掉后面内容- 执行顺序:去掉内容后添加内容
- 代码示例(去掉最后一个逗号)
<update id="upd" parameterType="log"> update log <trim prefix="set" suffixOverrides=","> a=a, </trim> where id=100 </update>
<bind>
- 作用:给参数重新赋值
- 使用场景:
- 模糊查询
- 在原内容前或后添加内容
- 示例
<select id="selByLog" parameterType="log" resultType="log"> <bind name="accin" value="'%'+accin+'%'"/> #{money} </select>
<foreach>
标签- 循环参数内容,还具备在内容的前后添加内容,还具备添加分
隔符功能. - 适用场景:
in
查询中.批量新增中(mybatis
中foreach
效率比较低)- 如果希望批量新增,
SQL
命令
insert into log VALUES (default,1,2,3),(default,2,3,4),(default,3,4,5)
openSession()
必须指定- 底层
JDBC
的PreparedStatement.addBatch();
factory.openSession(ExecutorType.BATCH);
- 底层
- 如果希望批量新增,
- 示例
collection=”xx”
:要遍历的集合item
: 迭代变量,#{迭代变量名}
获取内容
3.open
: 循环后左侧添加的内容close
: 循环后右侧添加的内容separator
: 每次循环时,元素之间的分隔符
<select id="selIn" parameterType="list" resultType="log"> select * from log where id in <foreach collection="list" item="abc" open="(" close=")" separator=","> #{abc} </foreach> </select>
- 循环参数内容,还具备在内容的前后添加内容,还具备添加分
<sql>
和<include>
- 某些
SQL
片段如果希望复用,可以使用<sql>
定义这个片段
<sql id="mysql"> id,accin,accout,money </sql>
- 某些
- 在
<select>
或<delete>
或<update>
或<insert>
中使用<include>
引用
<select id="abc">
select <include refid="mysql"></include>
from log
</select>
ThreadLocal
- 线程容器,给线程绑定一个
Object
内容,后只要线程不变,可以随时
取出.- 改变线程,无法取出内容.
- 语法示例
final ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("测试");
new Thread(){
public void run() {
String result = threadLocal.get();
System.out.println("结果:"+result);
};
}.start();
缓存
- 应用程序和数据库交互的过程是一个相对比较耗时的过程
- 缓存存在的意义:让应用程序减少对数据库的访问,提升程序运行
效率 MyBatis
中默认SqlSession
缓存开启- 同一个
SqlSession
对象调用同一个<select
>时,只有第一次访问
数据库,第一次之后把查询结果缓存到SqlSession
缓存区(内存)中 - 缓存的是
statement
对象.(简单记忆必须是用一个<select>
)- 在
myabtis
时一个<select>
对应一个statement
对象
- 在
- 有效范围必须是同一个
SqlSession
对象
- 同一个
- 缓存流程
- 步骤一: 先去缓存区中找是否存在
statement
- 步骤二:返回结果
- 步骤三:如果没有缓存
statement
对象,去数据库获取数据 - 步骤四:数据库返回查询结果
- 步骤五:把查询结果放到对应的缓存区中
- 步骤一: 先去缓存区中找是否存在
SqlSessionFactory
缓存- 又叫:二级缓存
- 有效范围:同一个
factory
内哪个SqlSession
都可以获取 - 什么时候使用二级缓存:
- 当数据频繁被使用,很少被修改
- 使用二级缓存步骤
- 在
mapper.xml
中添加
<cache readOnly="true"></cache>
- 如果不写
readOnly=”true”
需要把实体类序列化
- 在
- 当
SqlSession
对象close()
时或commit()
时会把SqlSession
缓存的数据刷(flush)到SqlSessionFactory
缓存区中
Mybatis实现多表查询
- Mybatis 实现多表查询方式
- 业务装配.对两个表编写单表查询语句,在业务(
Service
)把查询
的两个结果进行关联. - 使用
Auto Mapping
特性,在实现两表联合查询时通过别名完成
映射. - 使用
MyBatis
的<resultMap>
标签进行实现.
- 业务装配.对两个表编写单表查询语句,在业务(
- 多表查询时,类中包含另一个类的对象的分类
- 单个对象
- 集合对象.
resultMap标签
-
<resultMap>
标签写在mapper.xml
中,由程序员控制SQL
查询结果与
实体类的映射关系.- 默认
MyBatis
使用Auto Mapping
特性.
- 默认
-
使用
<resultMap>
标签时,<select>
标签不写resultType
属性,而是使
用resultMap
属性引用<resultMap>
标签. -
使用
resultMap
实现单表映射关系- 数据库设计
id name 1 老师1 2 老师2 - 实体类设计
public class Teacher{ private int id1; private String name1; //get和set方法 ... }
mapper.xml
代码
<resultMap type="teacher" id="mymap"> <!-- 主键使用 id 标签配置映射关系 --> <id column="id" property="id1" /> <!-- 其他列使用 result 标签配置映射关系 --> <result column="name" property="name1"/> </resultMap> <select id="selAll" resultMap="mymap"> select * from 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; //get 和 set 方法 ... }
- 在
TeacherMapper
中提供一个查询
<select id="selById" resultType="teacher" parameterType="int"> select * from teacher where id=#{0} </select>
- 在
StudentMapper
中<association>
装配一个对象时使用property
: 对象在类中的属性名select
:通过哪个查询查询出这个对象的信息column
: 把当前表的哪个列的值做为参数传递给另
一个查询- 大前提使用
N+1
方式.时如果列名和属性名相同可
以不配置,使用Auto mapping
特性.但是mybatis
默认只会给列
专配一次
<resultMap type="student" id="stuMap"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="age" column="age"/> <result property="tid" column="tid"/> <!-- 如果关联一个对象 --> <association property="teacher" select="com.bjsxt.mapper.TeacherMapper.selById" column="tid"></association> </resultMap> <select id="selAll" resultMap="stuMap"> select * from student </select>
- 把上面代码简化成
<resultMap type="student" id="stuMap"> <result column="tid" property="tid"/> <!-- 如果关联一个对象 --> <association property="teacher" select="com.bjsxt.mapper.TeacherMapper.selById" column="tid"></association> </resultMap> <select id="selAll" resultMap="stuMap"> select * from 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 student s left outer join teacher t on s.tid=t.id </select>
- 只需要编写一个
-
N+1
方式和联合查询方式对比N+1
:需求不确定时.- 联合查询:需求中确定查询时两个表一定都查询.
-
N+1
名称由来- 举例:学生中有 3 条数据
- 需求:查询所有学生信息级授课老师信息
- 需要执行的
SQL
命令- 查询全部学生信息:
select * from 学生
- 执行 3 遍
select * from 老师 where id=学生的外键
- 查询全部学生信息:
- 使用多条
SQl
命令查询两表数据时,如果希望把需要的数据都
查询出来,需要执行N+1
条SQl
才能把所有数据库查询出来. - 缺点:
- 效率低
- 优点:
- 如果有的时候不需要查询学生是同时查询老师.只需要
执行一个select * from student
;
- 如果有的时候不需要查询学生是同时查询老师.只需要
- 适用场景: 有的时候需要查询学生同时查询老师,有的时候只
需要查询学生. - 如何解决
N+1
查询带来的效率低的问题- 默认带的前提: 每次都是两个都查询.
- 使用两表联合查询.
使用查询关联集合对象(N+1)
- 在
Teacher
中添加List<Student>
public class Teacher {
private int id;
private String name;
private List<Student> list;
//get和set方法
...
- 在
StudentMapper.xml
中添加通过tid
查询
<select id="selByTid" parameterType="int" resultType="student">
select * from student where tid=#{0}
</select>
- 在
TeacherMapper.xml
中添加查询全部<collection/>
当属性是集合类型时使用的标签.
<resultMap type="teacher" id="mymap"> <id column="id" property="id"/> <result column="name" property="name"/> <collection property="list" select="com.bjsxt.mapper.StudentMapper.selByTid" column="id"></collection> </resultMap> <select id="selAll" resultMap="mymap"> select * from teacher </select>
使用实现加载集合数据(联合查询方式)
- 在
teacherMapper.xml
中添加mybatis
可以通过主键判断对象是否被加载过.- 不需要担心创建重复
Teacher
<resultMap type="teacher" id="mymap1"> <id column="tid" property="id"/> <result column="tname" property="name"/> <collection property="list" ofType="student" > <id column="sid" property="id"/> <result column="sname" property="name"/> <result column="age" property="age"/> <result column="tid" property="tid"/> </collection> </resultMap> <select id="selAll1" resultMap="mymap1"> select t.id tid,t.name tname,s.id sid,s.name sname,age,tid from teacher t LEFT JOIN student s on t.id=s.tid; </select>
使用 Auto Mapping 结合别名实现多表查询.
- 只能使用多表联合查询方式.
- 要求:查询出的列别和属性名相同.
- 实现方式
- 在
SQL
是关键字符,两侧添加反单引号
<select id="selAll" resultType="student"> select t.id `teacher.id`,t.name `teacher.name`,s.id id,s.name name,age,tid from student s LEFT JOIN teacher t on t.id=s.tid </select>
- 在
Mybatis注解
- 注解:为了简化配置文件.
2.Mybatis
的注解简化mapper.xml
文件.- 如果涉及动态
SQL
依然使用mapper.xml
- 如果涉及动态
mapper.xml
和注解可以共存.- 使用注解时
mybatis.xml
中<mappers>
使用<package/>
<mapper class=””/>
- 实现查询
@Select("select * from teacher")
List<Teacher> selAll();
- 实现新增
@Insert("insert into teacher
values(default,#{name})")
int insTeacher(Teacher teacher);
- 实现修改
@Update("update teacher set name=#{name} where
id=#{id}")
int updTeacher(Teacher teacher);
- 实现删除
@Delete("delete from teacher where id=#{0}")
int delById(int id);
- 使用注解实现
<resultMap>
功能(一般不用注解实现)- 以
N+1
举例 - 在
StudentMapper
接口添加查询
@Select("select * from student where tid=#{0}") List<Student> selByTid(int tid);
- 在
TeacherMapper
接口添加@Results()
相当于<resultMap>
@Result()
相当于<id/>
或<result/>
@Result(id=true)
相当与<id/>
@Many()
相当于<collection/>
@One()
相当于<association/>
@Results(value={ @Result(id=true,property="id",column="id"), @Result(property="name",column="name"), @Result(property="list",column="id",many=@Many(select="com.bjsxt.mapper.StudentMapper.selByTid"))}) @Select("select * from teacher") List<Teacher> selTeacher();
- 以
Mybatis运行原理
- 运行过程中涉及到的类
Resources
:MyBatis
中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
中异常工厂
- 流程图
graph TD
A[Resources加载全局配置文件];
B[实例化SqlSessionFactoryBuilder构建器];
C[由XMLConfigBuilder解析配置文件流];
D[把配置信息放在Configuration中];
E[实例化SqlSessionFactory实现类DefaultSqlSessionFactory];
F[由TransactionFactory创建一个Transaction事物对象];
G[创建执行器Excutor];
H[创建SqlSession接口实现类DefaultSqlSession];
I[实现CURD];
J{执行是否成功};
K[事务提交];
L[关闭];
A-->B
B-->C
C-->D
D-->E
E-->F
F-->G
G-->H
H-->I
I-->F
I-->J
J--成功-->K
J--失败,事物回滚-->F
K-->L
- 文字解释
在MyBatis
运行开始时需要先通过Resources
加载全局配置文件.下面
需要实例化SqlSessionFactoryBuilder
构建器.帮助SqlSessionFactory
接口实现类DefaultSqlSessionFactory
. 在实例化DefaultSqlSessionFactory
之前需要先创建XmlConfigBuilder
解析全局配置文件流,并把解析结果存放在Configuration
中.之后把
Configuratin
传递给DefaultSqlSessionFactory
.到此SqlSessionFactory
工
厂创建成功. 由SqlSessionFactory
工厂创建SqlSession
. 每次创建SqlSession
时,都需要由TransactionFactory
创建Transaction
对象,同时还需要创建SqlSession
的执行器Excutor
,最后实例化
DefaultSqlSession
,传递给SqlSession
接口. 根据项目需求使用SqlSession
接口中的API
完成具体的事务操作. 如果事务执行失败,需要进行rollback 回滚事务
. 如果事务执行成功提交给数据库.关闭SqlSession