Mybatis 基础知识

1.JDBC缺点及解决方案
在这里插入图片描述
2.MyBatis介绍
在这里插入图片描述
2.2.官方文档
在这里插入图片描述
2.3.特点
Mybatis:
1)支持自定义SQL、存储过程、及高级映射
2)实现自动对SQL的参数设置
3)实现自动对结果集进行解析和封装
4)通过XML或者注解进行配置和映射,大大减少代码量
5)数据源的连接信息通过配置文件进行配置
可以发现,MyBatis是对JDBC进行了简单的封装,帮助用户进行SQL参数的自动设置,以及结果集与Java对象的自动映射。
与Hibernate相比,配置更加简单、灵活、执行效率高。但是正因为此,所以没有实现完全自动化,需要手写SQL,这是优点也是缺点。
对性能要求较高的电商类项目,一般会使用MyBatis,而对与业务逻辑复杂,不太在乎执行效率的传统行业,一般会使用Hibernate.

2.4.Mybaits整体架构
在这里插入图片描述
1、配置文件
全局配置文件:mybatis-config.xmlhibernate.cfg.xml,作用:配置数据源,引入映射文件
映射文件:XxMapper.xmlxx.hbm.xml,作用:配置sql语句、参数、结果集封装类型等
2、SqlSessionFactory
相当于Hibernate的SessionFactory,作用:获取SqlSession
通过newSqlSessionFactoryBuilder().build(inputStream)来构建,inputStream:读取配置文件的IO流
3、SqlSession
相当于Hibernate的Session,作用:执行CRUD操作
4、Executor
执行器,SqlSession通过调用它来完成具体的CRUD
5、Mapped Statement
在映射文件里面配置,包含3部分内容:具体的sql,sql执行所需的参数类型,sql执行结果的封装类型

参数类型和结果集封装类型包括3种:HashMap,基本数据类型,pojo

3. 快速入门
3.1.目录结构
在这里插入图片描述
3.2.引入依赖(pom.xml)

<dependencies>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.2</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.2</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.43</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.6</version>
		</dependency>
	</dependencies>

3.3.全局配置文件(mybatis-config.xml)
在这里插入图片描述
Mybatis-config.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>
	<!-- 环境:说明可以配置多个,default:指定生效的环境 -->
	<environments default="development">
		<!-- id:环境的唯一标识 -->
		<environment id="development">
			<!-- 事务管理器,type:类型 -->
			<transactionManager type="JDBC" />
			<!-- 数据源:type-池类型的数据源 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-49" />
				<property name="username" value="root" />
				<property name="password" value="root" />
			</dataSource>
		</environment>
	</environments>
	<!-- 映射文件 -->
	<mappers>
		<mapper resource="UserMapper.xml" />
	</mappers>
</configuration>

3.4.映射文件(UserMapper.xml)
在这里插入图片描述
UserMapper.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="UserMapper">

	<!-- 查询的statement,id:在同一个命名空间下的唯一标识,resultType:sql语句的结果集封装类型 -->
	<select id="queryUserById" resultType="User">
		select * from tb_user where id=#{id}
	</select>
	
</mapper>

3.5.编写mybatis程序(MybatisTest.java)
在这里插入图片描述

public static void main(String[] args) throws IOException {
		
		SqlSession sqlSession = null;
		try {
			// 指定mybatis的全局配置文件
			String resource = "mybatis-config.xml";
			// 读取mybatis-config.xml配置文件
			InputStream inputStream = Resources.getResourceAsStream(resource);
			// 构建sqlSessionFactory
			SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			// 获取sqlSession回话
			sqlSession = sqlSessionFactory.openSession();
			// 执行查询操作,获取结果集。参数:1-命名空间(namespace)+“.”+statementId,2-sql的占位符参数
			User user = sqlSession.selectOne("UserMapper.queryUserById", 1l);
			System.out.println(user);
		} finally {
			// 关闭连接
			if (sqlSession != null) {
				sqlSession.close();
			}
		}
	}

4.引入log日志
打印日志2个步骤:
1、在pom.xml中,引入slf4j的依赖
2、在src/main/resources目录下添加log4j.properties文件

4.1.引入日志依赖包(pom.xml)
在这里插入图片描述
4.2.添加log4j.properties
在这里插入图片描述

log4j.rootLogger=DEBUG,A1
log4j.logger.org.apache=DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

4.3.日志输出
控制台截图
在这里插入图片描述
4.4.MyBatis使用步骤总结
1)配置mybatis-config.xml 全局的配置文件 (1、数据源,2、外部的mapper)
2)创建SqlSessionFactory
3)通过SqlSessionFactory创建SqlSession对象
4)通过SqlSession操作数据库CRUD
5)调用session.commit()提交事务
6)调用session.close()关闭会话

5.完整的CRUD操作

5.1.创建UserDao接口

public interface UserDao {

	/**
	 * 根据id获取用户信息
	 * @param id
	 * @return
	 */
	public User queryUserById(Long id);
	
	/**
	 * 查询所有用户
	 * @return
	 */
	public List<User> queryUserAll();
	
	/**
	 * 新增用户
	 * @param user
	 */
	public void insertUser(User user);
	
	/**
	 * 更新用户信息
	 * @param user
	 */
	public void updateUser(User user);
	
	/**
	 * 根据id删除用户信息
	 * @param id
	 */
	public void deleteUserById(Long id);
}

5.2.创建UserDaoImpl

public class UserDaoImpl implements UserDao {
	
	private SqlSession sqlSession;
	
	public UserDaoImpl(SqlSession sqlSession){
		this.sqlSession = sqlSession;
	}

	@Override
	public User queryUserById(Long id) {
		return this.sqlSession.selectOne("UserDaoMapper.queryUserById", id);
	}

	@Override
	public List<User> queryUserAll() {
		return this.sqlSession.selectList("UserDaoMapper.queryUserAll");
	}

	@Override
	public void insertUser(User user) {
		this.sqlSession.insert("UserDaoMapper.insertUser", user);
		this.sqlSession.commit();
	}

	@Override
	public void updateUser(User user) {
		this.sqlSession.update("UserDaoMapper.updateUser", user);
		this.sqlSession.commit();
	}

	@Override
	public void deleteUserById(Long id) {
		this.sqlSession.delete("UserDaoMapper.deleteUserById", id);
		this.sqlSession.commit();
	}

}

5.3.编写UserDaoMapper.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="UserDaoMapper">

	<select id="queryUserById" resultType="cn.itcast.mybatis.pojo.User">
		select * from tb_user where id = #{id}
	</select>
	
	<select id="queryUserAll" resultType="cn.itcast.mybatis.pojo.User">
		select * from tb_user
	</select>
	
	<insert id="insertUser" parameterType="cn.itcast.mybatis.pojo.User">
		INSERT INTO tb_user (
			user_name,
			password,
			name,
			age,
			sex,
			birthday,
			created,
			updated
		)
		VALUES
			(
				#{userName},
				#{password},
				#{name},
				#{age},
				#{sex},
				#{birthday},
				NOW(),
				NOW()
			);
	</insert>
	
	<update id="updateUser" parameterType="cn.itcast.mybatis.pojo.User">
		UPDATE tb_user
		SET
		 user_name = #{userName},
		 password = #{password},
		 name = #{name},
		 age = #{age},
		 sex = #{sex},
		 birthday = #{birthday},
		 updated = NOW()
		WHERE
			(id = #{id});
	</update>
	
	<delete id="deleteUserById" parameterType="java.lang.Long">
		delete from tb_user where id=#{id}
	</delete>
	
</mapper>

5.4.引入UserDaoMapper.xml
在mybatis-config.xml中引入UserDaoMapper.xml映射文件:
在这里插入图片描述
5.5.测试UserDao
1、引入Junit依赖:参照父工程,在mybatis工程中的pom.xml中引入junit的依赖
在这里插入图片描述
2、创建junit测试用例:右键UserDao—>New—>Junit Test Case
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3、编写测试用例:

public class UserDaoTest {
	
	private UserDao userDao;

	@Before
	public void setUp() throws Exception {
		
		String resource = "mybatis-config.xml";
		// 读取配置文件
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 构建sqlSessionFactory
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		// 获取sqlSession
		SqlSession sqlSession = sqlSessionFactory.openSession();
		// 初始化userDao
		this.userDao = new UserDaoImpl(sqlSession);
	}

	@Test
	public void testQueryUserById() {
		User user = this.userDao.queryUserById(1l);
		System.out.println(user);
	}

	@Test
	public void testQueryUserAll() {
		List<User> users = this.userDao.queryUserAll();
		for (User user : users) {
			System.out.println(user);
		}
	}

	@Test
	public void testInsertUser() {
		User user = new User();
		user.setAge(18);
		user.setName("柳岩");
		user.setPassword("123456");
		user.setUserName("yanyan");
		user.setSex(3);
		user.setBirthday(new Date());
		this.userDao.insertUser(user);
	}

	@Test
	public void testUpdateUser() {
		// 查询
		User user = this.userDao.queryUserById(7l);
		// 更新
		user.setAge(28);
		user.setPassword("111111");
		this.userDao.updateUser(user);
	}

	@Test
	public void testDeleteUserById() {
		this.userDao.deleteUserById(7l);
	}

}

5.6.总结
5.6.总结
1、 编写UserDao接口
2、 编写UserDao的实现类UserDaoImpl及映射文件UserDaoMapper.xml
3、 修改全局配置文件,引入UserDaoMapper.xml
4、 编写UserDao的Junit Test Case测试用例

5.7.解决UserName为null
查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name POJO中的属性名是userName
两端不一致,造成mybatis无法填充对应的字段信息。修改方法:在sql语句中使用别名

解决方案1:在sql语句中使用别名

修改映射文件(UserDaoMapper.xml)中的sql语句
在这里插入图片描述
控制台Log日志:
在这里插入图片描述

6.动态代理Mapper实现类

6.1.思考CRUD中的问题
1、接口->实现类->mapper.xml。
2、实现类中,使用mybatis的方式非常类似。
3、sql statement 硬编码到java代码中。
思考:能否只写接口,不书写实现类,只编写Mapper.xml即可
因为在dao(mapper)的实现类中对sqlsession的使用方式很类似。mybatis提供了接口的动态代理.

Mapper接口的动态代理实现,需要满足以下条件:
1.映射文件中的命名空间与Mapper接口的全路径一致
2.映射文件中的statementId与Mapper接口的方法名保持一致
3.映射文件中的statement的ResultType必须和mapper接口方法的返回类型一致(即使不采用动态代理,也要一致)
4.映射文件中的statement的parameterType必须和mapper接口方法的参数类型一致(不一定,该参数可省略)

6.2.使用动态代理改造CRUD
在mybatis中,持久层的XxxDao通常习惯上命名为XxxMapper(例如:以前命名为UserDao的接口,现在命名为UserMapper)。
当然,仍有少部分项目会保留Dao的命名,以后见到Mapper或者Dao的命名都不要奇怪。
采用动态代理之后,只剩下UserMapper接口、UserMapper.xml映射文件以及UserMapper接口的测试文件**,即可以少写一个接口的实现类.

6.2.1.创建UserMapper接口
在这里插入图片描述

public interface UserMapper {
	/**
	 * 根据id获取用户信息
	 * @param id
	 * @return
	 */
	public User queryUserById(Long id);
	
	/**
	 * 查询所有用户
	 * @return
	 */
	public List<User> queryUserAll();
	
	/**
	 * 新增用户
	 * @param user
	 */
	public void insertUser(User user);
	
	/**
	 * 更新用户信息
	 * @param user
	 */
	public void updateUser(User user);
	
	/**
	 * 根据id删除用户信息
	 * @param id
	 */
	public void deleteUserById(Long id);
}

6.2.2.UserMapper映射文件
名称空间必须改成UserMapper接口的全路径StatementId必须和接口方法名一致结果集的封装类型已经和方法的返回类型一致

<?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="cn.itcast.mybatis.mapper.UserMapper">

	<select id="queryUserById" resultType="cn.itcast.mybatis.pojo.User">
		select * from tb_user where id = #{id}
	</select>
	
	<select id="queryUserAll" resultType="cn.itcast.mybatis.pojo.User">
		select * from tb_user
	</select>
	
	<insert id="insertUser" parameterType="cn.itcast.mybatis.pojo.User">
		INSERT INTO tb_user (
			user_name,
			password,
			name,
			age,
			sex,
			birthday,
			created,
			updated
		)
		VALUES
			(
				#{userName},
				#{password},
				#{name},
				#{age},
				#{sex},
				#{birthday},
				NOW(),
				NOW()
			);
	</insert>
	
	<update id="updateUser" parameterType="cn.itcast.mybatis.pojo.User">
		UPDATE tb_user
		SET
		 user_name = #{userName},
		 password = #{password},
		 name = #{name},
		 age = #{age},
		 sex = #{sex},
		 birthday = #{birthday},
		 updated = NOW()
		WHERE
			(id = #{id});
	</update>
	
	<delete id="deleteUserById" parameterType="java.lang.Long">
		delete from tb_user where id=#{id}
	</delete>
	
</mapper>

6.2.3.UserMapperTest测试

public class UserMapperTest {
	
	private UserMapper userMapper;

	@Before
	public void setUp() throws Exception {
		// 读取mybatis的全局配置文件
		InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
		// 构建sqlSessionFactory
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		// 获取sqlSession会话
		SqlSession sqlSession = sqlSessionFactory.openSession();

		// 初始化userDao
		this.userMapper = sqlSession.getMapper(UserMapper.class);
	}

	@Test
	public void testQueryUserAll() {
		List<User> userList = this.userMapper.queryUserAll();
		for (User user : userList) {
			System.out.println(user);
		}
	}

}

1、如果名称空间不和mapper接口的全路径保持一致
在这里插入图片描述
在这里插入图片描述

7.mybatis-config.xml配置
mybatis-config.xml讲究严格的顺序,具体顺序遵循文档的顺序
在这里插入图片描述
在这里插入图片描述

7.1.properties属性读取外部资源
添加jdbc.properties资源文件:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis-44
username=root
password=root

在Mybatis-config.xml中引入jdbc.properties资源文件:
引入外部资源文件,resource:相对路径,url:绝对路径

<?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>
	<!-- 引入外部资源文件,resource:相对路径,url:绝对路径 -->
	<properties resource="jdbc.properties" />
	<!-- 环境:说明可以配置多个,default:指定生效的环境 -->
	<environments default="development">
		<!-- id:环境的唯一标识 -->
		<environment id="development">
			<!-- 事务管理器,type:类型 -->
			<transactionManager type="JDBC" />
			<!-- 数据源:type-池类型的数据源 -->
			<dataSource type="POOLED">
				<property name="driver" value="${driver}" />
				<property name="url" value="${url}" />
				<property name="username" value="${username}" />
				<property name="password" value="${password}" />
			</dataSource>
		</environment>
	</environments>
	<!-- 映射文件 -->
	<mappers>
		<mapper resource="UserMapper.xml" />
		<mapper resource="UserDaoMapper.xml" />
	</mappers>
</configuration>

通过properties引入外部资源文件之后,就可以通过${xxx}的方式使用资源文件里的参数了。

7.2.settings设置

settings参数有很多,重点学习这4个参数。
在这里插入图片描述

开启驼峰匹配:
完成经典的数据库命名到java属性的映射。
经典数据库命名:如果多个单词之间,通常使用下划线进行连接。
java中命名:第二个单词首字母大写。
驼峰匹配:相当于去掉数据中的名字的下划线,和java进行匹配。

问题:查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name,POJO中的属性名字是userName,两端不一致,造成mybatis无法填充对应的字段信息。修改方法:在sql语句中使用别名。
解决方案1:在sql语句中使用别名
解决方案2:参考驼峰匹配 — mybatis-config.xml

<settings>
	<!-- 开启驼峰匹配:经典的数据库列名(多个单词下划线连接)映射到经典的java属性名(多个单词驼峰连接) -->
	<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

7.3.typeAliases
问题:之前咱们在映射文件中用到java类型时,都是使用类的全路径,书写起来非常麻烦。
解决方案:类型别名是为 Java 类型命名的一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。(官方文档)
7.3.1.方式一:typeAlias 缺点:每个pojo类都要去配置。

<typeAliases>
		<!-- 类型别名:type-pojo类的全路径,alias-别名名称(可随便写,推荐和类名一致) -->
		<typeAlias type="cn.itcast.mybatis.pojo.User" alias="user" />
	</typeAliases>

7.3.2.方式二:package

扫描指定包下的所有类,扫描之后的别名就是类名,大小写不敏感(不区分大小写),建议使用的时候和类名一致。

<typeAliases>
	<!-- 类型别名:type-pojo类的全路径,alias-别名名称(可随便写,推荐和类名一致) -->
	<!-- <typeAlias type="cn.itcast.mybatis.pojo.User" alias="user" /> -->
	<!-- 开启别名包扫描,name:包路径,扫描的别名就是类名,并且大小写不敏感 -->
	<package name="cn.itcast.mybatis.pojo"/>
</typeAliases>

在映射文件中使用类型别名:
在这里插入图片描述
已经为普通的 Java 类型内建了许多相应的类型别名。它们都是大小写不敏感的,需要注意的是由于重载原始类型的名称所做的特殊处理。

在这里插入图片描述
在这里插入图片描述

7.4.typeHandlers(类型处理器)
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。
在这里插入图片描述
注:截图默认的类型处理器 不全,需要请查询相关资料。

7.5.plugins(插件又称拦截器)
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。
默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.6.environments(环境)
MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;
尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。
虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离

7.6.1.方法一:default
添加一个test(测试)环境,并在default参数中指向test环境。
在这里插入图片描述
7.6.2.方法二:build方法
通过build方法的重载方法
在这里插入图片描述使用:
在这里插入图片描述

7.7.Mappers
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。
Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。
7.7.1.方式一:resource
在mybatis-config.xml引入项目目录下的映射文件:
在这里插入图片描述
7.7.2.方式二:file(不采用)
引入硬盘目录下的映射文件:
在这里插入图片描述
缺点:1、硬盘的位置可能随着项目的部署或迁移,路径发生变化
2、每新增一个映射文件,就要在全局配置文件中引入。

7.7.3.方式三:class
在mybatis-config.xml配置mapper接口的全路径:
在这里插入图片描述
这种配置方式,在全局配置文件中配置了mapper接口的全路径,并没有配置mapper接口的映射文件的位置。。如果要让mybatis找到对应的映射文件,则必须满足一定的条件或规则:
1、映射文件和mapper接口在同一个目录下
2、文件名必须一致
3、映射文件的namespace必须和mapper接口的全路径保持一致
目录结构:
在这里插入图片描述
缺点:
1、java文件和xml映射文件耦合
2、每新增一个映射文件,就要在全局配置文件中引入

7.7.4.方式四:package
在mybatis-config.xml中,开启包扫描:
在这里插入图片描述
原理:扫描目标包目录下的mapper接口,并按照class的方式找到接口对应的映射文件。
缺点:
1、如果包的路径有很多
2、mapper.xml和mapper.java没有分离。

8.Mapper XML 文件(映射文件)
在这里插入图片描述
8.1.CRUD标签

8.1.1.select
在这里插入图片描述
8.1.2.insert
在这里插入图片描述
8.1.3.update
在这里插入图片描述
8.1.4.delete
在这里插入图片描述

8.2.parameterType传入参数
CRUD标签都有一个属性parameterType,statement通过它指定接收的参数类型。
接收参数的方式有两种:
1、#{}预编译
2、${}非预编译(直接的sql拼接,不能防止sql注入)
参数类型有三种:
1、基本数据类型
2、HashMap(使用方式和pojo类似)
3、Pojo自定义包装类型

**8.2.1. 的 用 法 ∗ ∗ 场 景 : 数 据 库 有 两 个 一 模 一 样 的 表 。 历 史 表 , 当 前 表 查 询 表 中 的 信 息 , 有 时 候 从 历 史 表 中 去 查 询 数 据 , 有 时 候 需 要 去 新 的 表 去 查 询 数 据 。 希 望 使 用 1 个 方 法 来 完 成 操 作 。 在 U s e r M a p p e r 接 口 中 , 添 加 根 据 表 名 查 询 用 户 信 息 的 方 法 : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20190507164803591. ) 在 U s e r M a p p e r 映 射 文 件 中 , 添 加 方 法 对 应 的 s t a t e m e n t : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20190507164814841. ) 输 出 ( 报 错 ) : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20190507164827749. ? x − o s s − p r o c e s s = i m a g e / w a t e r m a r k , t y p e Z m F u Z 3 p o Z W 5 n a G V p d G k , s h a d o w 1 0 , t e x t a H R 0 c H M 6 L y 9 i b G 9 n L m N z Z G 4 u b m V 0 L 3 R h b m d p d 2 F u Z w = = , s i z e 1 6 , c o l o r F F F F F F , t 7 0 ) 如 果 你 要 动 态 传 入 的 字 段 名 是 表 名 , 并 且 s q l 执 行 是 预 编 译 的 , 这 显 然 是 不 行 的 , 所 以 你 必 须 改 成 非 预 编 译 的 , 也 就 是 这 样 : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20190507164858617. ) 注 意 : 1. 使 用 {}的用法** 场景:数据库有两个一模一样的表。历史表,当前表 查询表中的信息,有时候从历史表中去查询数据,有时候需要去新的表去查询数据。希望使用1个方法来完成操作。 在UserMapper接口中,添加根据表名查询用户信息的方法: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190507164803591.) 在UserMapper映射文件中,添加方法对应的statement: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190507164814841.) 输出(报错): ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190507164827749.?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Rhbmdpd2FuZw==,size_16,color_FFFFFF,t_70) 如果你要动态传入的字段名是表名,并且sql执行是预编译的,这显然是不行的,所以你必须改成非预编译的,也就是这样: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190507164858617.) 注意: 1.使用 使1UserMapper![](https://imgblog.csdnimg.cn/20190507164803591.)UserMapperstatement![](https://imgblog.csdnimg.cn/20190507164814841.)![](https://imgblog.csdnimg.cn/20190507164827749.?xossprocess=image/watermark,typeZmFuZ3poZW5naGVpdGk,shadow10,textaHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Rhbmdpd2FuZw==,size16,colorFFFFFF,t70)sql![](https://imgblog.csdnimg.cn/20190507164858617.)1.使{} 去接收参数信息,在一个参数时,默认情况下必须使用${value}获取参数值,
2.#{} 只是表示占位,与参数的名字无关,如果只有一个参数,可以使用任意参数名接收参数值,会自动对应。

但是这并不是一种稳妥的解决方案,推荐使用@Param注解指定参数名,所以以上接口及映射文件可以改成如下:
在这里插入图片描述
在这里插入图片描述
一个参数时,在使用#{}传参时,可以通过任意参数名接收参数;而${},默认必须通过value来接收参数。可以通过@Param注解指定参数名

8.2.2.多个参数
当mapper接口要传递多个参数时,有两种传递参数的方法:
1、默认规则获取参数{0,1,param1,param2}
2、使用@Param注解指定参数名

案例:实现一个简单的用户登录,根据username和password验证用户信息
在这里插入图片描述
在UserMapper.xml配置中,添加登陆方法对应的Statement配置:
在这里插入图片描述
在UserMapperTest测试用例中,添加测试方法:
在这里插入图片描述
运行报错:
在这里插入图片描述
注意报错信息:没有找到参数userName,可用的参数是:[1,0,param1,param2],所以可有以下解决方案:
解决方案一:
在映射文件里,通过#{0},#{1}获取参数
在这里插入图片描述
解决方案二:
在映射文件里,通过param1,param2获取参数
在这里插入图片描述
最终解决方案:
在接口方法中的参数前,添加@Param注解指定参数名
在这里插入图片描述
在这里插入图片描述
通常在方法的参数列表上加上一个注解@Param(“xxxx”) 表示参数的名字,然后通过${“xxxx”}或#{“xxxx”}获取参数。
注意:
单个参数时,#{}与参数名无关的。
多个参数时,#{} ${}与参数名(@Param)有关。

什么时候需要加@Param注解?什么时候不加?
单个参数不加,多个参数加;终极解决方案:都加。

8.2.3.HashMap

parameterType有三种类型的输入参数:

1、基本数据类型
2、hashMap
3、pojo包装类

hashMap这种类型的参数怎么传递参数呢?
它的使用方式和pojo有点类似,简单类型通过#{key}或者 k e y , 复 杂 类 型 通 过 {key},复杂类型通过 key{key.属性名}或者#{key.属性名}

UserMapper接口方法:
在这里插入图片描述
UserMapper配置文件:
在这里插入图片描述
测试用例:
在这里插入图片描述

8.2.4.面试题(#、$区别)
在这里插入图片描述
练习:根据用户名进行模糊查询
UserMapper接口:
在这里插入图片描述
UserMapper映射文件:
在这里插入图片描述
8.3.resultMap
在这里插入图片描述
8.3.1.解决列名和属性名不一致
查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name,而POJO中的属性名字是userName
两端不一致,造成mybatis无法填充对应的字段信息。修改方法:在sql语句中使用别名。
解决方案1:在sql语句中使用别名
解决方案2:参考驼峰匹配 — mybatis-config.xml 的时候
解决方案3:resultMap自定义映射。
在UserMapper.xml中配置resultMap
在这里插入图片描述
在UserMapper.xml中使用resultMap:
在这里插入图片描述
8.3.2.resultMap的自动映射
在上面讲到的resultMap中,主键需要通过id子标签配置,表字段和属性名不一致的普通字段需要通过result子标签配置。
那么,字段名称匹配的字段要不要配置那?
这个取决于resultMap中的autoMapping属性的值:
为true时:resultMap中的没有配置的字段会自动对应。
为false时:只针对resultMap中已经配置的字段作映射。
并且resultMap会自动映射单表查询的结果集。

8.4.sql片段
在java代码中,为了提高代码的重用性,针对一些出现频率较高的代码,抽离出来一个共同的方法或者类
么针对一些重复出现的sql片段,mybatis有没有一个比较好的解决方案呢?
Mybatis当然想到了这一点,它就是sql标签。

8.4.1.用法一
sql标签可以定义一个sql片段,在需要使用该sql片段的地方,通过include标签来使用,如改造根据表名查询用户信息:
在这里插入图片描述
测试:
在这里插入图片描述
在这里插入图片描述
测试结果:
在这里插入图片描述

8.4.2.用法二
很多时候同一个sql片段,可能在很多映射文件里都有使用,这就需要添加一个映射文件,用来统一定义sql片段。
如下,在resource目录下新增CommonSQL.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="CommonSQL">

	<sql id="commonSql">
		id,user_name,
			password,
			name,
			age,
			sex,
			birthday,
			created,
			updated
	</sql>
</mapper>

定义好sql片段的映射文件之后,接下来就该使用它了,首先应该把该映射文件引入到mybatis的全局配置文件中(mybatis-config.xml):
在这里插入图片描述
最后在需要使用该sql片段的地方通过include标签的refId属性引用该sql片段:

在UserMapper.xml的映射文件中,进一步改造根据用户名查询用户信息
在这里插入图片描述

8.5.本章代码汇总

在这里插入图片描述
8.5.1.UserMapper.java

public interface UserMapper {
	
	/**
	 * 根据用户名模糊查询用户信息
	 * @param userName
	 * @return
	 */
	public List<User> queryUsersLikeUserName(@Param("userName")String userName);
	
	/**
	 * 登陆(Map的方式)
	 * @param map
	 * @return
	 */
	public User loginByMap(Map<String,Object> map);
	
	/**
	 * 根据用户名和密码登陆
	 * @param userName
	 * @param password
	 * @return
	 */
	public User login(@Param("userName")String userName, @Param("password")String password);
	
	/**
	 * 根据表名查询用户信息
	 * @param tableName
	 * @return
	 */
	public List<User> queryUsersByTableName(@Param("tableName")String tableName);
	
	/**
	 * 根据id查询用户信息
	 * @param id
	 * @return
	 */
	public User queryUserById(Long id);
	
	/**
	 * 查询所有用户信息
	 * @return
	 */
	public List<User> queryUserAll();
	
	/**
	 * 新增用户信息
	 * @param user
	 */
	public void insertUser(User user);
	
	/**
	 * 更新用户信息
	 * @param user
	 */
	public void updateUser(User user);
	
	/**
	 * 根据id删除用户信息
	 * @param id
	 */
	public void deleteUserById(Long id);
	
}

8.5.2.UserMapper.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="cn.itcast.mybatis.mapper.UserMapper">

	<!-- <sql id="commonSql">
		id,user_name,
			password,
			name,
			age,
			sex,
			birthday,
			created,
			updated
	</sql> -->

	<select id="queryUsersLikeUserName" resultType="User">
		select <include refid="CommonSQL.commonSql"></include> from tb_user where user_name like '%' #{userName} '%'
	</select>

	<select id="loginByMap"  resultType="User">
		select * from tb_user where user_name=#{userName} and password=#{password}
	</select>

	<select id="login" resultType="User">
		select * from tb_user where user_name=#{userName} and password=#{password}
	</select>

	<select id="queryUsersByTableName" resultType="User">
		select * from ${tableName}
	</select>
	
	<!-- resultMap:自定义映射关系
		属性:type-结果集的封装类型,id-唯一标识,autoMapping-开启自动匹配,如果开启了驼峰匹配,就以驼峰匹配的形式进行匹配
		id:指定主键映射的,不要省。提高性能
		result:其他的非主键普通字段
			子标签的属性:Column-表中的字段名,property-对应的java属性名
	 -->
	<resultMap type="User" id="userMap" autoMapping="true">
		<id column="id" property="id"/>
		<!-- <result column="user_name" property="userName"/> -->
	</resultMap>

	<!-- statement:查询的statement
		id:在该映射文件下的唯一标识。在使用动态代理之后,必须和接口的方法名一致。必须属性
		resultType:结果集的映射类型。在使用动态里之后,必须和接口方法的返回值类型一致。必须属性
		parameterType:参数类型。可省略
	 -->
	<select id="queryUserById" resultMap="userMap">
		select * from tb_user where id = #{id}
	</select>
	
	<select id="queryUserAll" resultType="User">
		select * from tb_user
	</select>
	
	<!-- 插入的statement
		id:在该映射文件下的唯一标识。在使用动态代理之后,必须和接口的方法名一致。必须属性
		parameterType:参数类型。可省略
		useGeneratedKeys:开启主键回写,回写到参数的pojo对象里
		keyColumn:主键列名
		keyProperty:主键对应的属性名
	 -->
	<insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id" >
		INSERT INTO tb_user (
			user_name,
			password,
			name,
			age,
			sex,
			birthday,
			created,
			updated
		)
		VALUES
			(
				#{userName},
				#{password},
				#{name},
				#{age},
				#{sex},
				#{birthday},
				now(),
				now()
			);
	</insert>
	
	<!-- 更新的statement
		id:在该映射文件下的唯一标识。在使用动态代理之后,必须和接口的方法名一致。必须属性
		parameterType:参数类型。可省略
	 -->
	<update id="updateUser" >
		UPDATE tb_user
		SET
		 user_name = #{userName},
		 password = #{password},
		 name = #{name},
		 age = #{age},
		 sex = #{sex},
		 birthday = #{birthday},
		 updated = now()
		WHERE
			(id = #{id});
	</update>
	
	<!-- 删除的statement
		id:在该映射文件下的唯一标识。在使用动态代理之后,必须和接口的方法名一致。必须属性
		parameterType:参数类型。可省略
	 -->
	<delete id="deleteUserById">
		delete from tb_user where id=#{id}
	</delete>
	
</mapper>

8.5.3.CommonSQL.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="CommonSQL">

	<sql id="commonSql">
		id,user_name,
			password,
			name,
			age,
			sex,
			birthday,
			created,
			updated
	</sql>
</mapper>

8.5.4.Mybatis-config.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>
	<!-- 引入外部资源文件,resource:相对路径,url:绝对路径 -->
	<properties resource="jdbc.properties" />
	<settings>
		<!-- 行为参数,name:参数名,value:参数值,默认为false,true:开启驼峰匹配,即从经典的数据库列名到经典的java属性名 -->
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
	<typeAliases>
		<!-- 类型别名:type:类型的全路径,alias:别名名称,推荐和class类名一致 -->
		<!-- <typeAlias type="cn.itcast.mybatis.pojo.User" alias="User"/> -->
		<!-- 别名扫描,name:包的路径 -->
		<package name="cn.itcast.mybatis.pojo"/>
	</typeAliases>
	<!-- 环境:说明可以配置多个,default:指定生效的环境 -->
	<environments default="test">
		<!-- id:环境的唯一标识 -->
		<environment id="development">
			<!-- 事务管理器,type:类型 -->
			<transactionManager type="JDBC" />
			<!-- 数据源:type-池类型的数据源 -->
			<dataSource type="POOLED">
				<property name="driver" value="${driver}" />
				<property name="url" value="${url}" />
				<property name="username" value="${username}" />
				<property name="password" value="${password}" />
			</dataSource>
		</environment>
		<environment id="test">
			<!-- 事务管理器,type:类型 -->
			<transactionManager type="JDBC" />
			<!-- 数据源:type-池类型的数据源 -->
			<dataSource type="POOLED">
				<property name="driver" value="${driver}" />
				<property name="url" value="${url}" />
				<property name="username" value="${username}" />
				<property name="password" value="${password}" />
			</dataSource>
		</environment>
	</environments>
	<!-- 映射文件 -->
	<mappers>
		<mapper resource="CommonSQL.xml"/>
		<mapper resource="UserMapper.xml" />
		<mapper resource="UserDaoMapper.xml" />
		<!-- 
			1.映射文件和mapper接口在同一个目录下
			2.命名一致
			3.命名空间必须是接口的全路径
		 -->
		<!-- <mapper class="cn.itcast.mybatis.mapper.UserMapper" /> -->
		<!-- <package name="cn.itcast.mybatis.mapper"/> -->
	</mappers>
</configuration>

8.5.5.UserMapperTest

public class UserMapperTest {
	
	private UserMapper userMapper;

	@Before
	public void setUp() throws Exception {
		
		String resource = "mybatis-config.xml";
		// 读取配置文件
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 构建sqlSessionFactory
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, "development");
		
		// 获取sqlSession
		SqlSession sqlSession = sqlSessionFactory.openSession(true);
		
		this.userMapper = sqlSession.getMapper(UserMapper.class);
	}
	
	@Test
	public void testQueryUsersLikeUserName(){
		List<User> users = this.userMapper.queryUsersLikeUserName("zhang");
		for (User user : users) {
			System.out.println(user);
		}
	}
	
	@Test
	public void testMap(){
		Map<String,Object> map = new HashMap<>();
		map.put("userName", "zhangsan");
		map.put("password", "123456");
		System.out.println(this.userMapper.loginByMap(map));
	}
	
	@Test
	public void testLogin(){
		System.out.println(this.userMapper.login("zhangsan", "123456"));
	}
	
	@Test
	public void testQueryUsersByTableName(){
		List<User> users = this.userMapper.queryUsersByTableName("tb_user");
		for (User user : users) {
			System.out.println(user);
		}
	}

	@Test
	public void testQueryUserById() {
		System.out.println(this.userMapper.queryUserById(1l));
	}

	@Test
	public void testQueryUserAll() {
	}

	@Test
	public void testInsertUser() {
		User user = new User();
		user.setAge(18);
		user.setName("柳岩");
		user.setPassword("123456");
		user.setUserName("yanyan1");
		user.setSex(3);
		user.setBirthday(new Date());
		this.userMapper.insertUser(user);
		System.out.println(user.getId());
	}

	@Test
	public void testUpdateUser() {
	}

	@Test
	public void testDeleteUserById() {
	}

}

9.动态sql
在这里插入图片描述

9.1.if
判断语句
案例:查询男性用户,如果输入了用户名,按用户名模糊查询
9.1.1.定义接口
在UserMapper接口中定义方法:

/**
	 * 查询男性用户,如果输入了用户名,按用户名模糊查询
	 * @param userName
	 * @return
	 */
	public List<User> queryUserListLikeUserName(@Param("userName")String userName);

9.1.2.编写mapper.xml
在UserMapper映射文件中,定义接口方法对应的Statement

<select id="queryUserListLikeUserName" resultType="User">
		select * from tb_user where sex=1
		<!-- if:判断
			test:OGNL表达式
		 -->
		<if test="userName!=null and userName.trim()!=''">
			 and user_name like '%' #{userName} '%'
		</if>
	</select>

9.1.3.测试
在UserMapperTest测试类中,添加测试用例

  @Test
     public void testQueryUserListLikeUserName(){
	List<User> users = this.userMapper.queryUserListLikeUserName("zhang");
	for (User user : users) {
		System.out.println(user);
	}
}

9.2.choose when otherwise
查询男性用户,如果输入了用户名则按照用户名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找用户名为“zhangsan”的用户。

9.2.1.定义接口
在UserMapper接口中,定义接口方法:

/**
	 * 查询男性用户,如果输入了用户名则按照用户名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找用户名为“zhangsan”的用户。
	 * @param userName
	 * @param age
	 * @return
	 */
	public List<User> queryUserListLikeUserNameOrAge(@Param("userName")String userName, @Param("age")Integer age);

9.2.2.编写mapper.xml
在UserMapper.xml中,定义接口方法对应的Statement

<select id="queryUserListLikeUserNameOrAge" resultType="User">
		select * from tb_user where sex=1 
		<!-- choose:条件选择
			when:test-判断条件,一旦有一个when成立,后续的when都不再执行
			otherwise:所有的when都不成立时,才会执行
		 -->
		<choose>
			<when test="userName!=null and userName.trim()!=''">and user_name like '%' #{userName} '%'</when>
			<when test="age != null">and age = #{age}</when>
			<otherwise>and user_name = 'zhangsan' </otherwise>
		</choose>
	</select>

9.2.3.测试
在UserMapperTest测试类中,添加测试用例

@Test
public void testQueryUserListLikeUserNameOrAge(){
	List<User> users = this.userMapper.queryUserListLikeUserNameOrAge(null, null);
	for (User user : users) {
		System.out.println(user);
	}
}

9.3.where
案例:查询所有用户,如果输入了用户名按照用户名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立。
9.3.1.定义接口
在UserMapper接口中,定义接口方法:

/**
	 * 查询所有用户,如果输入了用户名按照用户名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立。
	 * @param userName
	 * @param age
	 * @return
	 */
	public List<User> queryUserListLikeUserNameAndAge(@Param("userName")String userName, @Param("age")Integer age);

9.3.2.编写mapper.xml

在UserMapper.xml中,定义接口方法对应的Statement

<select id="queryUserListLikeUserNameAndAge" resultType="User">
		select * from tb_user
		<!-- 
			自动添加where关键字
			有一定的纠错功能:去掉sql语句块之前多余的一个and|or
			通常结合if或者choose使用
		 -->
		<where>
			<if test="userName!=null and userName.trim()!=''">user_name like '%' #{userName} '%'</if>
			<if test="age!=null">and age = #{age}</if>
		</where>
	</select>

9.3.3.测试

在UserMapperTest测试类中,添加测试用例

@Test
	public void testQueryUserListLikeUserNameAndAge(){
		List<User> users = this.userMapper.queryUserListLikeUserNameAndAge(null, 30);
		for (User user : users) {
			System.out.println(user);
		}
	}

9.4.set

案例:修改用户信息,如果参数user中的某个属性为null,则不修改。
9.4.1.定义接口
在UserMapper接口中,定义接口方法:

/**
	 * 修改用户信息,如果参数user中的某个属性为null,则不修改。
	 * @param user
	 */
	public void updateUserSelective(User user);

9.4.2.编写mapper.xml

在UserMapper.xml中,定义接口方法对应的Statement

<update id="updateUserSelective" >
		UPDATE tb_user
		<!-- 
			set自动添加set关键字
			也有一定的纠错功能:自动去掉sql语句块之后多余的一个逗号
		 -->
		<set>
			<if test="userName!=null and userName.trim()!=''">user_name = #{userName},</if>
			<if test="password!=null and password.trim()!=''">password = #{password},</if>
			<if test="name!=null and name.trim()!=''">name = #{name},</if>
			<if test="age!=null">age = #{age},</if>
			<if test="sex!=null">sex = #{sex}</if>
		</set>
		WHERE
			(id = #{id});
	</update>

9.4.3.测试

在UserMapperTest测试类中,添加测试用例

	@Test
	public void testUpdateUserSelective(){
		User user = new User();
		user.setAge(18);
		user.setName("柳岩");
		user.setPassword("123456");
		user.setUserName("yanyan2");
//		user.setSex(3);
		user.setBirthday(new Date());
		user.setId(12l);
		this.userMapper.updateUserSelective(user);
	}

9.5.foreach

案例:按照多个id查询用户信息
在UserMapper接口中,定义接口方法:

/**
	 * 根据多个id查询用户信息
	 * @param ids
	 * @return
	 */
	public List<User> queryUserListByIds(@Param("ids")Long[] ids);

9.5.2.配置

在UserMapper.xml中,定义接口方法对应的Statement

<select id="queryUserListByIds" resultType="User">
		select * from tb_user where id in 
		<!-- 
			foreach:遍历集合
			collection:接收的集合参数
			item:遍历的集合中的一个元素
			separator:分隔符
			open:以什么开始
			close:以什么结束
		 -->
		<foreach collection="ids" item="id" separator="," open="(" close=")">
			#{id}
		</foreach>
	</select>

9.5.3.测试

在UserMapperTest测试类中,添加测试用例

@Test
	public void testQueryUserListByIds(){
		List<User> users = this.userMapper.queryUserListByIds(new Long[]{1l,2l,3l,4l});
		for (User user : users) {
			System.out.println(user);
		}
	}

10.缓存

执行相同的sql语句和参数,mybatis不进行执行sql,而是从缓存中命中返回。
10.1.一级缓存
在mybatis中,一级缓存默认是开启的,并且一直无法关闭,作用域:在同一个sqlSession下。
在UserMapperTest中添加测试一级缓存的方法:

@Test
public void testCache(){
	User user1 = this.userMapper.queryUserById(1l);
	System.out.println(user1);
	System.out.println("=================第二次查询======================");
	User user2 = this.userMapper.queryUserById(1l);
	System.out.println(user2);
}

由于一级缓存的存在,此时在log日志中,应该只会在第一次查询是执行sql语句,第二次查询时直接从缓存中命中,即不再执行sql语句。
在这里插入图片描述

使用:sqlSession.clearCache();可以强制清除缓存
在测试方法中清空一级缓存:

@Test
public void testCache(){
	User user1 = this.userMapper.queryUserById(1l);
	System.out.println(user1);
	this.sqlSession.clearCache();
	System.out.println("=================第二次查询======================");
	User user2 = this.userMapper.queryUserById(1l);
	System.out.println(user2);
}

在执行第二次查询之前清空缓存,再去执行查询。这时无法从缓存中命中,便会去执行sql从数据库中查询。
在这里插入图片描述
执行update、insert、delete的时候,会清空缓存

在测试方法中添加更新的操作:

	@Test
	public void testCache(){
		User user1 = this.userMapper.queryUserById(1l);
		System.out.println(user1);
//		this.sqlSession.clearCache();
		System.out.println("================更新======================");
		User user = new User();
		user.setAge(18);
		user.setName("柳岩");
		user.setPassword("123456");
		user.setUserName("yanyan2");
//		user.setSex(3);
		user.setBirthday(new Date());
		user.setId(12l);
		this.userMapper.updateUser (user);
		System.out.println("=================第二次查询======================");
		User user2 = this.userMapper.queryUserById(1l);
		System.out.println(user2);
	}

由于insert、update、delete会清空缓存,所以第二次查询时,依然会输出sql语句,即从数据库中查询。
在这里插入图片描述

10.2.二级缓存

mybatis 的二级缓存的作用域:

1、同一个mapper的namespace,同一个namespace中查询sql可以从缓存中命中。
2、跨sqlSession,不同的SqlSession可以从二级缓存中命中

怎么开启二级缓存:
1、在映射文件中,添加标签
2、在全局配置文件中,设置cacheEnabled参数,默认已开启。
注意:
由于缓存数据是在sqlSession调用close方法时,放入二级缓存的,所以第一个sqlSession必须先关闭。
二级缓存的对象必须序列化,例如:User对象必须实现Serializable接口。

开启二级缓存,在映射文件(UserMapper.xml)中添加:
在这里插入图片描述

在UserMapperTest中添加二级缓存的测试方法:
在这里插入图片描述

给User对象实现序列化接口后,重新运行测试用例,日志:
在这里插入图片描述
执行update、insert、delete的时候,会清空缓存

@Test
	public void testCache2(){
		User user1 = this.userMapper.queryUserById(1l);
		System.out.println(user1);
		
		// 注意:关闭sqlSession
		sqlSession.close();
		System.out.println("=======================================");
		// 重新打开一个sqlSession会话
		SqlSession sqlSession2 = this.sqlSessionFactory.openSession();
		// 通过sqlSession2重新实例化UserMapper
		this.userMapper = sqlSession2.getMapper(UserMapper.class); 		
		User user = new User();
		user.setAge(18);
		user.setName("柳岩");
		user.setPassword("123456");
		user.setUserName("yanyan2");
		user.setBirthday(new Date());
		user.setId(12l);
		this.userMapper.updateUserSelective(user);
		System.out.println("=======================================");
		User user2 = this.userMapper.queryUserById(1l);
		System.out.println(user2);
	}

关闭二级缓存:
不开启,或者在全局的mybatis-config.xml 中去关闭二级缓存
在这里插入图片描述

在mybatis-config.xml配置中:

<settings>
	<!-- 行为参数,name:参数名,value:参数值,默认为false,true:开启驼峰匹配,即从经典的数据库列名到经典的java属性名 -->
	<setting name="mapUnderscoreToCamelCase" value="true"/>
	<!-- 关闭二级缓存,默认是开启,false:关闭 -->
	<setting name="cacheEnabled" value="false"/>
</settings>

在这里插入图片描述

11.高级查询(OrderMapper.xml)

11.1.表关系说明
在这里插入图片描述

需求说明:
在这里插入图片描述
目录结构:
在这里插入图片描述
11.2.一对一查询
需求:查询出订单信息,并查询出下单人信息
在这里插入图片描述
11.2.1.扩展Order类的实现方式(了解)
思路:
创建新的pojo对象OrderUser继承Order对象,这样OrderUser就从Order中继承了订单信息。
再把User对象中的属性copy到OrderUser对象中,这样OrderUser又有了User中的用户信息。
把sql语句的结果集封装成OrderUser对象,那么结果中就既包含了订单信息又包含了下单人信息。
新建OrderUser实体类继承Order,在把User中的属性copy到OrderUser中,并添加get、set方法:
在这里插入图片描述
在OrderMapper接口中,添加查询方法:
在这里插入图片描述
在OrderMapper.xml中,配置对应的Statement,并把结果集封装成OrderUser对象:
在这里插入图片描述
在OrderMapperTest中添加测试方法:
在这里插入图片描述

11.2.2.添加User属性的实现方式
思路:
从订单的角度来看,订单和用户的关系是一对一的关系,并且通过tb_order表中的id进行关联。
用面向对象的思想实现,就是在Order对象中添加user属性。
在Order对象中添加User属性,并添加user的get、set方法:
在这里插入图片描述
在OrderMapper接口中,添加接口方法:

/**
	 * 根据订单号查询订单信息,并且查询出下单人信息
	 * @param number
	 * @return
	 */
	public Order queryOrderWithUser(@Param("number")String number);

使用resultType不能完成user对象的自动映射,需要手动完成结果集映射,即使用resultMap标签自定义映射。
在OrderMapper.xml中配置,结果集的映射,这时必须使用resultMap:

<resultMap type="Order" id="orderUserMap" autoMapping="true">
		<id column="id" property="id"/>
		<!-- 
			association:一对一的映射
			property:java的属性名
			javaType:属性名对应的java类型
			autoMapping:开启自动映射
			子标签:参照resultMap
		 -->
		<association property="user" javaType="User" autoMapping="true">
			<id column="user_id" property="id"/>
		</association>
	</resultMap>
	
	<!-- resultType不能完成user信息的映射,必须使用resultMap,resultMap的值对应resultMap标签的id,resultMap和resultType必须二选一 -->
	<select id="queryOrderWithUser" resultMap="orderUserMap">
		select * from tb_order a 
			LEFT JOIN tb_user b on a.user_id=b.id
		where a.order_number = #{number}
	</select>

在OrderMapperTest添加测试:

@Test
public void testQueryOrderWithUser() {
	System.out.println(this.orderMapper.queryOrderWithUser("20140921001"));
}

11.3.一对多查询

一对多查询:查询订单,查询出下单人信息并且查询出订单详情。
思路:
订单 : 订单详情 = 1 : n(体现在pojo对象中,就是在Order对象中添加OrderDetail对象的集合)
SQL:在这里插入图片描述
在Order类添加List属性,并添加get、set方法:
在这里插入图片描述

OrderMapper接口:

/**
	 * 查询订单,查询出下单人信息并且查询出订单详情。
	 * @param number
	 * @return
	 */
	public Order queryOrderWithUserDetail(@Param("number")String number);

OrderMapper映射:

<resultMap type="Order" id="orderUserDetailMap" autoMapping="true">
		<id column="id" property="id"/>
		<association property="user" javaType="User" autoMapping="true">
			<id column="user_id" property="id"/>
		</association>
		<!-- 
			collection:一对多的查询
			property:属性名
			javaType:集合类型
			ofType:集合中的元素类型
			autoMapping:开启自动映射
			子标签:参照resultMap
		 -->
		<collection property="detailList" javaType="list" ofType="Orderdetail" autoMapping="true">
			<id column="detail_id" property="id"/>
		</collection>
	</resultMap>
	
	<select id="queryOrderWithUserDetail" resultMap="orderUserDetailMap">
		select *,c.id as detail_id from tb_order a
			LEFT JOIN tb_user b on a.user_id=b.id
			LEFT JOIN tb_orderdetail c on a.id=c.order_id
		where a.order_number=#{number}
	</select>

OrderMapperTest测试:

@Test
	public void testQueryOrderWithUserDetail(){
		System.out.println(this.orderMapper.queryOrderWithUserDetail("20140921001"));
	}

11.4.多对多查询
多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。
思路:
订单:订单详情 = 1 : n(体现在pojo对象中就是在Order对象中添加OrderDetail对象的集合)
订单详情:商品 = 1 : 1(体现在pojo对象中就是在OrderDetail对象中添加Item对象)
Sql:
在这里插入图片描述
在Orderdetail添加Item属性,并添加get、set方法:
在这里插入图片描述
OrderMapper接口:

   /**
    	 * 查询订单,查询出下单人信息并且查询出订单详情中的商品数据。
    	 * @param number
    	 * @return
    	 */
    	public Order queryOrderWithUserDetailItem(@Param("number")String number);

OrderMapper配置(通过在collection标签中嵌套使用association标签):

<resultMap type="Order" id="orderUserDetailItemMap" autoMapping="true">
		<id column="id" property="id"/>
		<association property="user" javaType="User" autoMapping="true">
			<id column="user_id" property="id"/>
		</association>
		<collection property="detailList" javaType="list" ofType="Orderdetail" autoMapping="true">
			<id column="detail_id" property="id"></id>
			<association property="item" javaType="Item" autoMapping="true">
				<id column="item_id" property="id"/>
			</association>
		</collection>
	</resultMap>
	
	<select id="queryOrderWithUserDetailItem" resultMap="orderUserDetailItemMap">
		select *,c.id as detail_id from tb_order a
			LEFT JOIN tb_user b on a.user_id=b.id
			LEFT JOIN tb_orderdetail c on a.id=c.order_id
			LEFT JOIN tb_item d on c.item_id=d.id
		where a.order_number=#{number}
	</select>

OrderMapperTest测试:

@Test
public void testQueryOrderWithUserDetailItem(){
	System.out.println(this.orderMapper.queryOrderWithUserDetailItem("20140921001"));
}

11.5.resultMap的继承
在这里插入图片描述

11.6.高级查询的整理
resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射
resultMap:

  • type 结果集对应的数据类型
  • id 唯一标识,被引用的时候,进行指定
  • autoMapping 开启自动映射
  • extends 继承

子标签:
association:一对一的映射

  • property 定义对象的属性名

  • javaType 属性的类型

  • autoMapping 开启自动映射

    collection:一对多的映射

  • property 定义对象的属性名

  • javaType 集合的类型

  • ofType 集合中的元素类型

  • autoMapping 开启自动映射

12.延迟加载
在这里插入图片描述

分析:
采用之前的配置方式(参考高级查询),肯定是不能做到延迟加载的,因为咱是通过一个查询sql直接查询出所有的数据。为了测试延迟加载的效果,必须改造高级查询的配置,使Order的查询和User或者OrderDetail的查询分开。只有当我们访问Order对象的User或者OrderDetail属性时,才去执行User或者OrderDetail的查询。

12.1.改造一对一查询
在OrderMapper接口中,编写接口方法:

/**
 * 测试延迟加载
 * @param number
 * @return
 */
public Order queryOrderLazy(@Param("number")String number);

OrderMapper配置:
在这里插入图片描述
处理组合键时,需要传递多个参数,可以使用column=”{prop1=col1, prop2=col2, prop3=col3…}”,设置多个列名传入到嵌套查询语句,mybatis会把prop1,prop2,prop3设置到目标嵌套的查询语句中的参数对象中。
子查询中,必须通过prop1,prop2,prop3获取对应的参数值,你也可以使用这种方式指定参数名例如:
在这里插入图片描述
测试:
在这里插入图片描述
日志信息:
在这里插入图片描述
分析:红色部分为查询Order信息的日志,绿色为查询Order中的User信息的日志。已经成功拆分出两个子查询,并且查询出了带有User信息的Order信息,说明改造OK。
接下来开启延迟加载

12.2.开启延迟加载
在mybatis-config.xml中配置行为参数:
在这里插入图片描述
修改测试用例,注释掉打印Order信息的这行代码:
在这里插入图片描述
执行,报错:
在这里插入图片描述
说明延迟加载需要cglib的支持。
在pom.xml中,添加cglib的依赖:

<dependency>
		<groupId>cglib</groupId>
		<artifactId>cglib</artifactId>
		<version>3.1</version>
	</dependency>

执行:
在这里插入图片描述

13.如果sql语句中出现’<’的解决方案
1、使用xml中的字符实体
在这里插入图片描述

2、使用<![CDATA[ < ]]>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值