什么是Mybatis?
Mybatis是一款优秀的持久框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。Mybatis可以使用简单的XML或注解来配置和映射原声类型、接口和POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录。
0.0为什么要有MyBatis
传统的jdbc存在以下问题:
- 数据库连接和创建、释放造成系统资源浪费从而影响性能,如果使用数据库连接池可以解决此问题。
- sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
- 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易 维护。
- 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果将数据库记录封装成POJO对象解析比较方便。
我们传统编写持久层代码的时候,需要 加载驱动,准备PrepareStatement语句,然将写好的sql语句投入其中,最后如果查询到数据,还需要自己一点一点的将列名和POJO的属性进行匹配,最后还需要关闭数据库连接,比较复杂,在进行开发时 能否将我们关注的重点转移到 sql语句的编写,将sql语句提取出来重点关注,而将其余的加载驱动,准备preparestatemt,进行属性与字段的映射,关闭连接等这些操作交于框架呢?
使用框架重点关注sql语句,解耦和。
0.1、Mybatis的概述
MyBatis是一个优秀的基于Java的持久层框架,它内部封装了jdbc,使得开发者只需要关注sql语句本身,而不需要花精力去处理 加载驱动、创建连接、创建statement等繁杂的过程。
Mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。
采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
0.2、持久层技术解决方案
JDBC技术:
Connection
PreparedStatement
ResultSetSpring的JdbcTemplate:
Spring中对jdbc的简单封装Apache的DBUtils:
它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装以上都不是框架,
JDBC是规范
Spring的JdbcTemplate和Apache的DBUtils都只是工具类
1、MyBatis的入门
1.1环境的配置(基于Maven)
1.1.1、导入相关jar包,配置pom.xml
<dependencies>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
1.1.2、进行MyBatis-config的配置
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 配置参数-->
<settings>
<!-- 开启MyBatis支持延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- 类型别名 -->
<!-- 类型别名是Java类型设置的一个短的名字,它只和XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余-->
<typeAliases>
<package name="com.itheima.Domain"/> <!-- 可以指定一个包名,MyBatis会在包下面搜索需要的Java Bean -->
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
<!--
环境配置(environments)
MyBatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库中,
现实情况下有多种理由需要这么做,例如,开发,测试和生产环境需要不同的配置;或者想在具有相同Schema的多个生产数据库中使用相同的SQLSession
不过要记住:尽管可以配置多个环境,单每个sqlSessionFactory实例智能选择一种环境
所以,如果想要连接两个数据库,就需要创建两个SqlSessionFactory实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例
-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/> <!-- 事物管理器,在MyBatis中有两种类型的事物管理器 也就是 type=[JDBC|MANAFED] -->
<!--
dataSource:数据源: 使用标准的JDBC数据源接口来配置JDBC链接对象的资源
许多MyBatis的应用程序会按
UNPOOLED-这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单
应用程序来说,是一个很好的选择。不同的数据库在性能方面的表现的也是不一样的,对于某些数据库来说,使用链接池并不重要
这个配置就很适合这种情形,
POOLED - 这种数据源的实现利用 “池” 的概念将JDBC连接对象组织起来,避免了创建新的链接实例连接时所必须的初始化和认证时间。
这是一种使得并发Web应用快速响应请求的流处理方式。
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/heima?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--
映射器(mappers):
每个dao独立配置文件
既然Mybatis的行为已经由上述的元素配置完了,我们现在就要定义SQL映射语句了。
但是首先我们需要告诉MyBatis到哪里去找这些语句
-->
<mappers>
<mapper resource="com/itheima/Dao/UserDao.xml"></mapper>
</mappers>
</configuration>
1.1.2、编写实体类User
1.1.3、编写持久化接口层UserDao和编写持久层接口的映射文件UserDao.xmlmm
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//myba is.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.Dao.UserDao">
<!--配置查询所有-->
<select id="findAll" resultType="com.itheima.Domain.User">
select * from user
</select>
</mapper>
1.1.4、配置日志文件
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
要求:创建位置必须和持久层接口在享用的包中,即
配置完成的文件目录:
环境搭建的注意事项:
- mybatis的映射配置文件位置必须和dao接口的包结构相同
- 映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
- 映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名
当我们遵循上述几点之后,在开发中就无须再写dao的实现类。
1.1.5、测试
//1、读取配置文件
InputStream inputStream = org.apache.ibatis.io.Resources.getResourceAsStream("mybatis-config.xml");
//2、创建SqlSessionFactory的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3、使用构建者创建工厂对象SqlSessionfactory
SqlSessionFactory sqlSessionFactory =builder.build(inputStream);
//4、使用SqlSessionFactory生产SqlSession对象
SqlSession session = sqlSessionFactory.openSession();
//5、使用Sqlsession对象创建dao接口的代理对象
UserDao userDao = session.getMapper(UserDao.class);
//6、使用代理对象执行查询所有方法
List<User> users = userDao.findAllUsers();
//7、提交查询
session.commit();
session.close();
MyBatis在使用代理dao的方式实现增删该查时做什么事情呢?
第一:创建代理对象
第二:在代理对象中调用selectList方法
使用接口代理实现数据库查询分析:
1.1.6小结
通过快速入门案例,我们发现使用mybatis是非常容易的一件事情,因为只需要编写Dao接口并且按照mybatis要求编写两个配置文件,就可以实现共那个。远比之前的jdbc方便的多。
但是,这里面包含了许多细节,比如为什么会有工厂对象(SqlSessionFactory),为什么有了工厂之后还要有构建者对象(SqlSessionFactoryBuilder),为什么UserDao.xml在创建的时候有位置和文件名的要求,为什么不需要实现接口等问题等等。
明确:我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。不管使用XML还是注解配置。但是MyBatis它还是支持写dao实现类的
2、MyBatis的流程分析
2.1使用代理的方式
2.2、使用实现类的方式
3、基于代理Dao实现CRUD操作(基于xml)
使用MyBatis可以实现Dao接口,也可以不实现接口使用代理类:具体的流程见下图:
3.1、使用要求
1、持久层接口和映射配置必须在相同的包下
2、持久层映射配置中mapper标签的namespace属性取值必须是持久层接口的全限定类名
3、SQL语句的配置标签<select>、<insert>、<delete>、<update>的id属性必须和持久层接口的方法名相同
3.2、原理分析
使用Dao接口代理分析实现:
3.3、增删改查
4.Mybatis参数深入
4.1、ParameterType
我们在上一解介绍了SQL语句引传参,使用标签的parameterType属性来设定。该属性的取值可以是基本类型,引用类型(例如String类型),还可以是实体类类型(POJO类)。同时也可以是使用实体类的包装类。
注意:基本类型和String我们可以直接写类型名称,也可以使用包名.类名的方式,例如:java.lang.String。实体类类型,目前我们只能使用全限定类名。究其原因,是MyBatis在加载的时候已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,而我们的实体类并没有注册别名,所以必须写全限定类名。
为了解决写别名的问题:可以在配置文件中写别名的方式<
4.2、MyBatis输出结果封装
4.2.1、resultType配置结果类型
4.2.2、resultMap配置结果类型
使用resultMap情况:
- 复杂查询的情况,当JavaBean中的属性和数据库中的字段不一致的情况
- 当进行多表查询,尤其是一对一或者一对多的情况
resultMap的元素:
5、MyBatis配置文件
5.1配置的内容和顺序
5.2配置的实例
<?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>
<!-- 配置参数-->
<settings>
<!-- 开启MyBatis支持延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- 类型别名 -->
<!-- 类型别名是Java类型设置的一个短的名字,它只和XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余-->
<typeAliases>
<package name="com.itheima.Domain"/> <!-- 可以指定一个包名,MyBatis会在包下面搜索需要的Java Bean -->
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
<!--
环境配置(environments)
MyBatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库中,
现实情况下有多种理由需要这么做,例如,开发,测试和生产环境需要不同的配置;或者想在具有相同Schema的多个生产数据库中使用相同的SQLSession
不过要记住:尽管可以配置多个环境,单每个sqlSessionFactory实例智能选择一种环境
所以,如果想要连接两个数据库,就需要创建两个SqlSessionFactory实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例
-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/> <!-- 事物管理器,在MyBatis中有两种类型的事物管理器 也就是 type=[JDBC|MANAFED] -->
<!--
dataSource:数据源: 使用标准的JDBC数据源接口来配置JDBC链接对象的资源
许多MyBatis的应用程序会按
UNPOOLED-这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单
应用程序来说,是一个很好的选择。不同的数据库在性能方面的表现的也是不一样的,对于某些数据库来说,使用链接池并不重要
这个配置就很适合这种情形,
POOLED - 这种数据源的实现利用 “池” 的概念将JDBC连接对象组织起来,避免了创建新的链接实例连接时所必须的初始化和认证时间。
这是一种使得并发Web应用快速响应请求的流处理方式。
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/heima?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--
映射器(mappers):
每个dao独立配置文件
既然Mybatis的行为已经由上述的元素配置完了,我们现在就要定义SQL映射语句了。
但是首先我们需要告诉MyBatis到哪里去找这些语句
-->
<mappers>
<mapper resource="com/itheima/Dao/UserDao.xml"></mapper>
</mappers>
</configuration>
5.3常用标签的解析
5.3.1、设置标签
5.3.2类型别名
5.3.3、插件
5.3.4、环境
5.3.5、映射器(mappers)
6、MyBatis连接池
6.1、Pooled
6.2、Unpooled
7、MyBatis事务和自动提交配置
7.1、什么是事务
7.2事务的四大特性
7.3、四种隔离级别
7.4、MyBatis中的事务
通过sqlSession对象的commit方法和rollback方法实现事务的提交和回滚。
默认情况下,事务打开,因此,在对数据库进行修改的时候,最后都需要进行事务的提交,否则对数据库修改失败
8、MyBatis中的动态语句
8.0、概念
8.1、< if>标签
我们根据实体类的不同取值,使用不同的SQL语句来进行查询,比如在id如果不能为空时则根据Id查询,如果username不同时为空时还要加入用户名作为条件,这种情况在多条件查询时经常会碰到。
8.2、< where>标签
为了简化上面where 1=1的条件拼装,我们可以采用标签来简化开发。
8.3、< foreach>标签
9、MyBatis中的多表查询以及ResultMap的使用
<resultMap 元素结果映射集,主要作用是定义映射规则,级联更新以及定义类型转换器等。
<resultMap type="" id="">
<id/> ---用于表示哪个列是主键
<result/> ---注入到字段或JavaBean属性的普通结果
<association/> --用于一对一的关联
<collection> --用于一对多的管理
</resultMap>
在默认情况下,MyBatis程序在运行时会自动的将查询 到的数据与需要返回的对象的属性进行匹配赋值(需要表中的列名与对象的属性名称完全一致)。然而实际开发中,数据表中的列和需要返回的对象的属性可能不完全一致,这种情况下MyBatis是不会自动完成赋值的,此时就可以使用.
9.1、表之间的多种关系
一对多
多对一
一对一
多对多
例如:一个用户可以下多个订单,多个订单可以属于同一个用户
一个人只能有一个身份证号
MyBatis的多表查询:
示例:用户和账户:
一个用户可以有多个账户
一个账户只能)
一对多查询
<resultMap id="productwithCategory" type="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
<association property="category" javaType="Category">
<id property="id" column="cid" />
<result property="name" column="cname"/>
</association>
</resultMap>
<select id="findProductWithProduct" resultMap="productwithCategory">
SELECT c.*,p.id pid,c.name cname,p.name pname,c.id cid
FROM category_ c LEFT JOIN product_ p
ON c.id = p.cid;
</select>
多对一查询
<resultMap id="productsAndCategory" type="Category">
<id property="id" column="cid"/>
<result property="name" column="cname" />
<collection property="products" ofType="Product">
<id property="id" column="pid"/>
<result property="name" column="pname"/>
<result property="price" column="price"/>
</collection>
</resultMap>
<select id="listProductsByCategory" resultMap="productsAndCategory">
select c.id,p.price,c.id cid,p.id pid,c.name cname,p.name pname
from category_ c left join product_ p
on c.id = p.cid;
</select>
10、MyBatis中的延迟加载
问题:在一对多种,当一个用户,有100个账户。在查询用户的时候,要不要把关联的账户查出来?在查询账户的时候要不要把关联的用户查出来?
在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询。
在查询账户时,账户所属的用户信息应该随着账户查询时一起查询出来
10.1、什么是延迟加载
在真正使用数据时才发起查询,不用的时候不查询,也叫按需加载。
10.2、什么是立即加载
不管用不用,只要一调用方法啊就马上发起查询
在对应的四种表关系中:一对多,多对一,一对一,多对多。
一对多,多对多:通常情况下懒加载
多对一,一对一:通常情况下立即加载
10.3例子:在Collection中实现延迟加载
- 在User实体类中加入List属性
- 编写用户和账户持久层接口的方法
- 编写用户持久层映射配置
- 编写账户持久层映射配置
- 测试只加载用户信息
11、MyBatis中的缓存
11.1、什么是缓存
缓存:存在于内存之中的临时数据。
11.2、为什么使用缓存
减少和数据的交互次数,提高交换效率。
11.3、什么样的数据能使用缓存,什么样的数据不能使用缓存
适用于缓存:经常查询的,并且不经常改变的,数据的正确与否对最终结果影响不大。
不适用缓存:经常改变的数据,数据的正确与否对最终结果影响很大的
11.4、MyBatis中的一级缓存和二级缓存
11.4.1、一级缓存
一级缓存:MyBatis中SqlSession对象的缓存,当我们执行查询之后,查询的结果会同时存入SqlSession为我们提供的一片区域之中。该区域的结构是一个Map,当我们再次查询同样的数据,mybatis会先去sqlSession中查询是否有,有的话直接拿来用。当SqlSession消失时,MyBatis的一级缓存也就消失了
11.4.2、二级缓存
二级缓存是mapper映射级别的缓存,多个sqlSession去操作同一个Mapper映射的Sql语句,多个SqlSession可以共用二级缓存。二级缓存是跨SqlSession的。
二级缓存:它指的是MyBatis中的SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让MyBatis框架支持二级缓存,(在SqlConfig.xml中配置)
第二部:在当前的映射文件中进行配置(在UserDao.xml中配置)
第三部:在当前的操作中支持二级缓存(在select标签中支持配置)
二级缓存:存放的内容是数据,而不是对象
12、MyBatis中的注解开发
12.1、MyBatis的常用注解说明
@Insert: 实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集
@One:实现一对一封装
@Many:实现一对多结果集封装
@SelectProvide:实现动态Sql语句
@CacheNamesPace
12.2、单标CRUD操作(代理Dao)
package com.itheima.Dao;
import com.itheima.Domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* @Author Zhou jian
* @Date 2019 ${month} 2019/12/2 0002 12:17
用户的持久层接口
在MyBatis中,针对CRUD一共有四个注解
@SELECT @INSERT @UPDATE @DELETE
*/
public interface UserDao {
/**
* 查询所有用户并获取对应账户信息 一对多
* @return
*/
@Select("select * from user")
List<User> findAll();
/**
* 保存用户
* @param user
*/
@Insert("insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})")
void saveUser(User user);
/**
* 更新用户
* @param user
* @return
*/
@Update("update user set username=#{username},birthdat=#{birthday},sex=#{sex},address=#{address} where id=#{id}")
void updatUser(User user);
/**
* 删除用户
* @param id
*/
@Delete("delete from user where id=#{id}")
void delete(Integer id);
/**
* 根据id查询用户
* @param id
* @return
*/
@Select("select * from user where id=#{id}")
User findById(Integer id);
/**
* 查询记录条数
* @return
*/
@Select("select count(*) from user")
int findTotal();
/**
* 根据传入的参数条件查询
* @param user 查询条件 有可能有用户名 有可能有性别
* @return
*/
List<User> findByCondition(User user);
12.3、多表查询操作
实现复杂关系映射之前我们可以在映射文件中通过配置来实现,在使用注解开发时我们需要借助@Result直接,@Results注解,@One注解,@Many注解