Java笔记-----(6)MyBatis必会知识点


MyBatis是一个优秀的持久层框架,基本上已经取代了Hibernate成为了当前市场上的主流持久层框架。在温习之前,先展示一下使用MyBatis+Druid连接MySQL数据库的Demo,便于加深印象。

(1) Demo展示

(1.1)在 pom.xml 文件中添加 Mybatis3.4.5 的坐标

<dependencies>
	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.4.5</version>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.10</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.6</version>
		<scope>runtime</scope>
	</dependency>
	<dependency>
		<groupId>log4j</groupId>
		<artifactId>log4j</artifactId>
		<version>1.2.12</version>
	</dependency>
</dependencies>

(1.2)编写 User 实体类

public class User implements Serializable {
	private Integer id;
	private String username;
	private Date birthday;
	private String sex;
	private String address;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	@Override
	public String toString() {
		return "User [id=" + id +
				", username=" + username +
				", birthday=" + birthday +
				", sex=" + sex +
				", address=" + address + "]";
	}
}

(1.3)编写持久层接口 IUserDao

也可以写成 UserDao 或者 UserMapper

public interface IUserDao {
	/**
	* 查询所有用户
	* @return
	*/
	List<User> findAll();
}

(1.4)编写持久层接口的映射文件 IUserDao.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">
<mapper namespace="com.itheima.dao.IUserDao">
	<!-- 配置查询所有操作 -->
	<select id="findAll" resultType="com.itheima.domain.User">
		select * from user
	</select>
</mapper>

(1.5)编写 SqlMapConfig.xml 配置文件

<?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>
	<!-- 配置 mybatis 的环境 -->
	<environments default="mysql">
		<!-- 配置 mysql 的环境 -->
		<environment id="mysql">
			<!-- 配置事务的类型 -->
			<transactionManager type="JDBC"></transactionManager>
			<!-- 配置连接数据库的信息:用的是数据源(连接池) -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/ee50"/>
				<property name="username" value="root"/>
				<property name="password" value="1234"/>
			</dataSource>
		</environment>
	</environments>
	
	<!-- 告知 mybatis 映射配置的位置 -->
	<mappers>
		<mapper resource="com/itheima/dao/IUserDao.xml"/>
	</mappers>
</configuration>

(1.6)编写测试类

public class MybatisTest {
	public static void main(String[] args)throws Exception {
	
		//1.读取配置文件
		InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
		//2.创建 SqlSessionFactory 的构建者对象
		SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
		//3.使用构建者创建工厂对象 SqlSessionFactory
		SqlSessionFactory factory = builder.build(in);
		//4.使用 SqlSessionFactory 生产 SqlSession 对象
		SqlSession session = factory.openSession();
		//5.使用 SqlSession 创建 dao 接口的代理对象
		IUserDao userDao = session.getMapper(IUserDao.class);
		//6.使用代理对象执行查询所有方法
		List<User> users = userDao.findAll();
		for(User user : users) {
			System.out.println(user);
		}
		//7.释放资源
		session.close();
		in.close();
	}
}

(2)MyBatis中的设计模式

(2.1)工厂模式 SqlSessionFactory

创建型模式,封装创建逻辑,使用一个共同的接口来指向新创建的对象,使其创建过程延迟到子类进行
优点:
1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事
实例请点击链接 工厂模式|菜鸟教程

(2.2)代理模式 MapperProxyFactory

代理模式分为静态代理和动态代理,动态代理的特点:
字节码随用随创建,随用随加载。在不修改源码的基础上对方法增强
它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
装饰者模式就是静态代理的一种体现。

在讲Spring AOP时讲过基于接口的动态代理基于子类的动态代理
优点:
1、职责清晰。
2、高扩展性。
3、智能化。
缺点:
1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
实例请点击链接 代理模式|菜鸟教程

(2.3)构建者模式 SqlSessionFactoryBuilder

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序
优点:
1、建造者独立,易扩展。
2、便于控制细节风险。
缺点:
1、产品必须有共同点,范围有限制。
2、如内部变化复杂,会有很多的建造类。
查看更多请点击:建造者模式|菜鸟教程

(2.4)单例模式 SqlSession

主要解决一个全局使用的类频繁地创建与销毁 (sqlSession)
为了减少每次都创建一个会话带来的资源消耗,一般情况下都会使用单例模式来创建SqlSession。创建方式如下:

SqlSession sqlSession = SqlSessionFactory.openSession();

优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
2、避免对资源的多重占用(比如写文件操作)。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

单例模式比较有代表性,这里放一下链接
单例模式|菜鸟教程

以下内容转载自菜鸟教程
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

(3)MyBatis,JDBC,Hibernate

MyBatis是一个半ORM(对象关系映射)框架,内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。通过直接编写原生态SQL,可以严格控制SQL语句的执行性能,灵活度高(支持动态SQL语句)。

MyBatis 使用XML或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了JDBC代码手动设置参数以及获取结果集的繁琐步骤。

MyBatis通过xml文件或注解的方式将要执行的各种 statement 配置起来,并通过Java对象和 statement中SQL的动态参数进行映射生成最终执行的SQL语句,最后由MyBatis框架执行SQL语句,并将结果映射为Java对象并返回。

MyBatis和Hibernate的区别:

  • MyBatis的优点是代码开发量少、简单易上手、SQL语句和代码分离(便于修改)、数据库可移植;但是,其缺点是SQL语句需要自己写,并且参数只能有一个
  • Hibernate的优点是进行了对象关系数据库映射、完全面向对象、提供缓存机制和HQL编程;但是,其缺点是不能灵活使用原生SQL、无法对SQL优化、全表映射效率低下并且存在N+1的问题。

JDBC、MyBatis和Hibernate的主要区别:

  • JDBC更为灵活,更加有效率,系统运行速度快。但是代码繁琐复杂,如果使用了存储过程就不方便数据库移植了。
  • Hibernate和MyBatis是关系数据库框架,开发速度快,更加面向对象,可以移植更换数据库,但影响系统性能。

(4)MyBatis的核心组件

MyBatis的核心组件包括SqlSessionFactoryBuilder,SqlSessionFactory,SqlSession和Mapper。

  • SqlSessionFactoryBuilder
    是一个构建器,通过XML配置文件或者Java编码获得资源来构建SqlSessionFactory,通过Builder可以构建多个SessionFactory。其生命周期一般只存在于方法的局部,用完即可回收。一般通过如下语句建立:
SqlSessionFactory factory = SqlSessionFactoryBuilder.build(inputStream);
  • SqlSessionFactory
    的作用就是创建SqlSession,也就是创建一个会话。每次程序需要访问数据库,就需要使用到SqlSession。所以SqlSessionFactory应该在MyBatis应用的整个生命周期中。为了减少每次都创建一个会话带来的资源消耗,一般情况下都会使用单例模式来创建SqlSession。创建方式如下:
SqlSession sqlSession = SqlSessionFactory.openSession();

创建SqlSessionFactory的过程:
首先是通过读取xml配置,然后使用SqlSessionFactorybuilder来build一个SqlSessionFactory
具体创建过程的步骤如下:

  • 通过XMLConfigBuilder解析配置的XML文件,读出配置参数,封装到Configuration类
  • 使用Configuration对象去创建SqlSessionFactory,SqlSessionFactory在MyBatis中是一个接口而不是一个类,所以提供了一个其默认实现类DefaultSqlSessionFactory

创建SqlSessionFactory的本质是一种Builder模式。Configuration的主要作用如下:

  • 读入配置文件
  • 初始化基础配置。包括全局配置,别名,类型处理器,环境数据库标识以及Mapper映射器等
  • 提供单例
  • 执行一些重要的对象方法和初始化配置信息

  • SqlSession
    就是一个会话,相当于JDBC中的Connection对象,既可以发送SQL去执行并返回结果,也可以获取Mapper接口。SqlSession是一个线程不安全的对象,其生命周期应该是请求数据库处理事务的过程中。每次创建的SqlSession对象必须及时关闭,否则会使得数据库连接池的活动资源减少,影响系统性能。
    通过 session.commit() 提交事务
  • Mapper
    也叫做映射器,由Java接口和XML文件(或者是注解)共同组成,给出了对应的SQL和映射规则,主要负责发送SQL去执行,并且返回结果。通过下边的语句来获取Mapper,接下来就可以调用Mapper中的各个方法去获取数据库结果了。
    可以查看Demo测试类中的第五步。
XXMapper xxMapper = sqlSession.getMapper(XXMapper.class);

(5)MyBatis的动态SQL(与hibernate区别)

MyBatis动态SQL可以在XML映射文件内,以标签的形式编写动态SQL。动态SQL的执行原理是,根据表达式的值完成逻辑判断并动态拼接SQL语句

对动态SQL的支持是MyBatis的一大优点。MyBatis提供了9种动态SQL标签:

  • if:单条件分支的判断语句
  • choose, when, otherwise:多条件的分支判断语句
  • foreach:列举条件,遍历集合,实现循环语句
  • trim,where,set:是一些辅助元素,可以对拼接的SQL进行处理
  • bind:进行模糊匹配查询的时候使用,提高数据库的可移植性

这里给一下if标签的示例。详细用法查看Mybatis第三天讲义。

<select id="findByUser" resultType="user" parameterType="user">
	select * from user where 1=1
	<if test="username != null and username != '' ">
		and username like #{username}
	</if>
	<if test="address != null">
		and address like #{address}
	</if>
</select>
注意:<if>标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。
另外要注意 where 1=1 的作用~

细节拿出来说一下:
sql 语句中使用#{} 字符 :
它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。具体的数据是由#{}里面的内容决定的。

  • 参数是基本数据类型,可以随意写。
<!-- 根据 id 查询 -->
<select id="findById" resultType="com.itheima.domain.User" parameterType="int">
	select * from user where id = #{uid}
</select>
  • 参数是一个 User 对象,此处要写 User 对象中的属性名称。
<!-- 保存用户-->
<insert id="saveUser" parameterType="com.itheima.domain.User">
	<!--  配置保存时获取插入的 id -->
	<selectKey keyColumn="id" keyProperty="id" resultType="int">
		select last_insert_id();
	</selectKey>
	insert into user(username,birthday,sex,address)
		values(#{username},#{birthday},#{sex},#{address})
</insert>

它用的是 ognl 表达式。
ognl 表达式:它是 apache 提供的一种表达式语言,全称是:Object Graphic Navigation Language 对象图导航语言。
按照 #{对象.对象} 的语法格式来获取数据。

#{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用 getUsername() 方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user 而直接写 username。

获取保存时插入的id:用selectKey标签
新增用户后,同时还要返回当前新增用户的 id 值,因为 id 是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长 auto_increment 的值返回。

(6)MyBatis的Mapper中的常见标签

  • select | insert | updae | delete
  • resultMap | parameterMap | sql | include | selectKey
  • trim | where | set | foreach | if | choose | when | otherwise | bind

用多了就熟了,可查看第二天讲义

(7)MyBatis中 # 和 $ 的区别是什么?(高频考点)

首先来看模糊查询的两种方式:

方式一:

<!-- 根据名称模糊查询 -->
<select id="findByName" resultType="com.itheima.domain.User" parameterType="String">
	select * from user where username like #{username}
</select>

查询语句,注意参数:
List<User> users = userDao.findByName("%王%");

控制台输出执行SQL:
select * from user where username like ?

方式二:

<!-- 根据名称模糊查询 -->
<select id="findByName" parameterType="string" resultType="com.itheima.domain.User">
	select * from user where username like '%${value}%'
</select>
我们在上面将原来的#{}占位符,改成了${value}。注意如果用模糊查询的这种写法,那么${value}的写法就是固定的,不能写成其它名字。

查询语句,注意参数:
List<User> users = userDao.findByName("王");

控制台输出执行SQL:
select * from user where username like '%王%'

#{}${} 的区别:

  • #{} 表示一个占位符号
    通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换#{}可以有效防止 sql 注入#{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类型值,#{}括号中可以是 value 或其它名称。

  • ${} 表示拼接 sql 串
    通过${}可以将 parameterType 传入的内容拼接在 sql中且不进行 jdbc 类型转换${}可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${}括号中只能是 value。

这两种方式的实现效果是一样的,但执行的语句是不一样的。


MyBatis中我们能用#号就尽量不要使用$符号,它们的区别主要体现在下边几点:

  • #符号将传入的数据都当做一个字符串,会对自动传入的数据加一个双引号
  • $符号将传入的数据直接显示在生成的SQL语句中
  • #符号存在预编译的过程,对问号赋值,防止SQL注入。
  • $符号是直译的方式,一般用在order by ${列名}语句中。

(8)MyBatis的Dao接口的工作原理

查看Demo中的 1.4 和 1.5

Dao接口即Mapper接口

  • 接口的全限名,就是映射文件中的namespace的值。
  • 接口的方法名,就是映射文件中Mapper的Statement的id值。
  • 接口方法内的参数,就是传递给SQL的参数。

Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MapperStatement。在MyBatis中,每一个<select>、<insert>、<update>、<delete>标签,都会被解析为一个MapperStatement对象

例如:
com.mapper.UserMapper.getUserInfo,
可以唯一找到namespace为com.mapper.UserMapper下面 id 为 getUserInfo的 MapperStatement。

Mapper接口的工作原理是JDK动态代理,MyBatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,转而执行MapperStatement所代表的SQL,然后将SQL执行结果返回。

(8.1)Dao接口中的方法可以重载吗(掌握)

重载:

  • 重载是指一个类里面(包括父类的方法)存在方法名相同,但是参数不一样的方法,参数不一样可以是不同的参数个数、类型或顺序
  • 如果仅仅是修饰符、返回值、throw的异常不同,那么这是2个相同的方法

Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名保存和寻找策略

(8.2)不同的映射文件xml中的id值可以重复吗?

MyBatis使用全限名+方法名来寻找对应的MapperStatement,那么看起来我们是不可以定义相同的id值的。但是注意需要分情况讨论该问题。

  • 不同的xml映射文件,如果配置了namespace,那么id可以重复
  • 如果没有配置namespace,那么id不能重复

(9)MyBatis的缓存机制有了解吗?

MyBatis的缓存机制分为一级和二级缓存,分别介绍如下:
在这里插入图片描述


(9.1)一级缓存(同一个SqlSession)

基于 HashMap 的本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有缓存就将清空,默认打开一级缓存

useCache 开启一级缓存	
<mapper namespace="com.itheima.dao.IUserDao">
	<!-- 根据 id 查询 -->
	<select id="findById" resultType="UsEr" parameterType="int" useCache="true">
		select * from user where id = #{uid}
	</select>
</mapper>

一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读

在这里插入图片描述


(9.2)二级缓存(同一个SqlSessionFactory)

二级缓存与一级缓存其机制相同,默认也是采用 HashMap 的本地存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源。

第一步:在 SqlMapConfig.xml 文件开启二级缓存

<settings>
<!-- 开启二级缓存的支持 -->
	<setting name="cacheEnabled" value="true"/>
</settings>

因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。

第二步:配置相关的 Mapper 映射文件

<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。
<?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.itheima.dao.IUserDao">
	<!-- 开启二级缓存的支持 -->
	<cache></cache>
</mapper>

第三步:配置 statement 上面的 useCache 属性

<!-- 根据 id 查询 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
	select * from user where id = #{uid}
</select>
在 UserDao.xml 映射文件中的 <select> 标签中设置 useCache=true”,代表当前这个 statement 要使用二级缓存。
如果不使用二级缓存可以设置为 false。

注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存二级缓存是跨 SqlSession 的

在这里插入图片描述

(10)MyBatis的接口绑定是什么?有哪些实现方式?

接口绑定就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以。
接口绑定有两种实现方式:

  • 通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含SQL语句来绑定。
  • 通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名

接口绑定方式的选择:
当SQL语句比较简单时候,用注解绑定;当SQL语句比较复杂时候,用xml绑定;实际的开发中,我们一般都使用xml绑定方式

(11)Mybatis延迟加载策略(待更新)

(12)Mybatis连接池与事务深入(待更新)

(13)Mybatis注解开发(待更新)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值