Mybaits学习笔记

文章目录

一、什么是Mybatis?

** Mybatis原来叫ibatis,时apache的一个开源项目,后来迁移到google code,才交Mybatis.它是一个持久层框架。封装了jdbc对数据库的操作,如不需要手动注解驱动,手动设置参数,建立connection,创建statment,结果集检索等。
Mybatis通过xml或注解把要执行的sql语句配置起来,通过java对象与sql语句映射形成最终的执行语句,最后由mybatis执行sql语句并将结果映射为java对象返回。**

二、jdbc的缺点和mybatis相应的解决办法

  1. 频繁的创建连接,释放连接等浪费系统资源,影响系统性能。数据库连接池可以解决。
    ==解法:==在SqlMapConfig.xml文件中配置数据库连接池
  2. Sql语句存在硬编码,实际应用中sql语句变化较大,需要改动java代码。
    解法: 将sql语句配置在XXXmapper.xml文件中,与java代码分离。
  3. 使用preparedStatement的where条件存在硬编码,因为条件在应用中可能需要变动,需要改sql语句,还要改java代码,很不方便。
    解法 Mybatis自动将java对象映射到sql语句,通过parameterType定义参数类型
  4. 返回结果集存在硬编码,如果查询一列数据,sql变化,列名变化,还是需要改java代码,如果把结果集映射成pojo对象,就比较方便了。
    解法: Mybatis自动将sql与句的执行结果映射到java对象,通过resultType定义输出类型。

三、Mybatis架构

在这里插入图片描述

  1. sqlMapConfig.xml是Mybatis的全局配置文件,如果配置数据库连接池(但不一定在这里配),还有就是加载mapper.xml映射文件,其中mapper.xml文件里放了可以映射的sql语句,这些语句有相应的id.
  2. 通过在java语句中使用sqlMapConfig.xml或在applicationContext.xml文件里配置sqlSessionFactory,用于生产sqlSession.
  3. sqlSession对数据库进行增删查改等操作。如sqlSession.insert(mapper语句id,对象).
  4. Executor是mybatis底层自定义的执行器接口,对数据库进行操作,有两种实现,以上基本执行器,另一种是缓存执行器。
  5. MappedStatment,中文意思是映射语句,Executor通过MappedStatement用传入的java对象把sql语句补充完整,mapper.xml里的语句id也是MappedStatement的id,实际上,一条mapper.xml里的sql语句对应一个MappedStatement,MappedStatement是把语句补充完整用于Executor执行。
  6. 同时MappedStatement对Executor执行sql语句后返回的结果进行定义,包括pojo,HashMap,原生类对象等,映射到Mapper.xml里resultType里规定的java对象。相当于jdbc里结果集里resultSet.next遍历获取对象。Mybatis对这一步进行了封装。

四、mybatis入门程序

1.创建一个java程序,然后导入mybatis的jar包,还有mybatis里lib下的jar包,再导入jdbc驱动jar包。
在这里插入图片描述
2.加入配置文件,sqlMapC,onfig.xml还有log4j.properties
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>
	<!-- 和spring整合后 environments配置将废除 -->
	<environments default="development">
		<environment id="development">
			<!-- 使用jdbc事务管理 -->
			<transactionManager type="JDBC" />
			<!-- 数据库连接池 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver" />
				<property name="url"
					value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&serverTimezone=UTC" />
				<property name="username" value="root" />
				<property name="password" value="bxswls" />
			</dataSource>
		</environment>
	</environments>
</configuration>

Log4j.xml如下

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

3.在src下创建pojo包,然后在pojo包下创建User.java类,同时创建数据对应的数据库表

public class User {
	private int id;
	private String username;// 用户姓名
	private String sex;// 性别
	private Date birthday;// 生日
	private String address;// 地址
添加get和set方法,重写toString()
...
}
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` date DEFAULT NULL COMMENT '生日',
  `sex` char(1) DEFAULT NULL COMMENT '性别',
  `address` varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '王五', null, '2', null);
INSERT INTO `user` VALUES ('10', '张三', '2014-07-10', '1', '北京市');
INSERT INTO `user` VALUES ('16', '张小明', null, '1', '河南郑州');
INSERT INTO `user` VALUES ('22', '陈小明', null, '1', '河南郑州');
INSERT INTO `user` VALUES ('24', '张三丰', null, '1', '河南郑州');
INSERT INTO `user` VALUES ('25', '陈小明', null, '1', '河南郑州');
INSERT INTO `user` VALUES ('26', '王五', null, null, null);

4.在src下建立mapper包,在mapper包里建立User.xml文件,并添加select语句

<?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:命名空间,用于隔离sql,还有一个很重要的作用,后面会讲 -->
<mapper namespace="test">
	<!-- id:statement的id 或者叫做sql的id-->
	<!-- parameterType:声明输入参数的类型 -->
	<!-- resultType:声明输出结果的类型,应该填写pojo的全路径 -->
	<!-- #{}:输入参数的占位符,相当于jdbc的? -->
	<select id="queryUserById" parameterType="int" resultType="pojo.User">
		select * from user where id=#{id}
	</select>
</mapper>

在sqlMapConfig.xml添加mapper

<?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>
	<!-- 和spring整合后 environments配置将废除 -->
	<environments default="development">
		<environment id="development">
			<!-- 使用jdbc事务管理 -->
			<transactionManager type="JDBC" />
			<!-- 数据库连接池 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver" />
				<property name="url"
					value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&serverTimezone=UTC" />
				<property name="username" value="root" />
				<property name="password" value="bxswls" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="mapper/User.xml"/>
	</mappers>
</configuration>

5.在src下建立单元测试包unitTest,在测试包加建立测试类
MyTest.class

public class MyTest {
	private SqlSessionFactory sqlSessionFactory=null;
	@Before
	public void init()throws Exception{
		// 1. 创建SqlSessionFactoryBuilder对象
		SqlSessionFactoryBuilder sessionFactoryBuilder=new SqlSessionFactoryBuilder();
		// 2. 加载sqlMapConfig.xml配置文件
		String resource = "sqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 3. 创建SqlSessionFactory对象
		 this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	@Test
	public void testQueryUserById()throws Exception{
		SqlSession session=sqlSessionFactory.openSession();
		User user=session.selectOne("queryUserById",1);
		System.out.println(user);
	}
}

6.根据用户名进行模糊查询,在User.xml文件里添加查询语句,然后写单元测试

<select id="queryUserByUsername" parameterType="string" resultType="pojo.User">
		select * from user where username like #{username} 
	</select>
@Test
	public void testQueryUserByUsername()throws Exception{
		SqlSession sqlSession=sqlSessionFactory.openSession();
		List<User> userList=sqlSession.selectList("queryUserByUsername","%王%");
		for (User user:userList) {
			System.out.println(user);
		}
	}

模糊查询还有第二种方式

<!-- 如果传入的参数是简单数据类型,${}里面必须写value,这里stirng不是简单数据类型,写啥都可以 -->
	<select id="queryUserByUsername2" parameterType="string" resultType="pojo.User">
		select * from user where username like "%${value}%"
	</select>
public void testQueryUserByUsername2()throws Exception{
		SqlSession sqlSession=sqlSessionFactory.openSession();
		List<User> userList=sqlSession.selectList("queryUserByUsername2","王");
		for (User user:userList) {
			System.out.println(user);
		}
	}

**#{}和KaTeX parse error: Expected 'EOF', got '#' at position 12: {}的使用** (1)#̲{}表示占位符,直接#{val…{}表示字符串拼接,上面例子里,用的是 like “% u s e r n a m e 则 能 查 出 姓 张 和 姓 王 的 人 , 导 致 信 息 被 爬 取 , 泄 露 。 因 为 字 符 串 拼 接 不 会 进 行 j d b c 类 型 的 转 换 。 还 有 要 注 意 的 是 简 单 数 据 类 型 , {username}% ”加了双引号,这也是能防止注入的,但如果没加双引号,也没加%%,那么,使用使用字符串拼接sqlSession.selectList("queryUserByUsername2","'%王%' or username like '%张%'"); 则能查出姓张和姓王的人,导致信息被爬取,泄露。因为字符串拼接不会进行jdbc类型的转换。还有要注意的是简单数据类型, usernamejdbc{value},括号内只能是value。

7.实现添加用户,添加用户,占位符要用#,insert后要记得commit

<select id="addUser" parameterType="pojo.User">
		INSERT INTO user
		(username,birthday,sex,address) VALUES
		(#{username},#{birthday},#{sex},#{address})
</select>
@Test
	 public void testQueryUserByUsername2()throws Exception{
		User user = new User();
		user.setUsername("张则太");
		user.setSex("1");
		user.setBirthday(new Date());
		user.setAddress("蜀国");
		SqlSession sqlSession=sqlSessionFactory.openSession();
		sqlSession.insert("addUser", user);
		sqlSession.commit();
		sqlSession.close();
	}

8.主键自增,有两种方法
(1)非uuid
查询id的sql
SELECT LAST_INSERT_ID()

通过修改User.xml映射文件,可以将mysql自增主键返回:
如下添加selectKey 标签

<!-- 保存用户 -->
<insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">
	<!-- selectKey 标签实现主键返回 -->
	<!-- keyColumn:主键对应的表中的哪一列 -->
	<!-- keyProperty:主键对应的pojo中的哪一个属性 -->
	<!-- order:设置在执行insert语句前执行查询id的sql,孩纸在执行insert语句之后执行查询id的sql -->
	<!-- resultType:设置返回的id的类型 -->
	<selectKey keyColumn="id" keyProperty="id" order="AFTER"
		resultType="int">
		SELECT LAST_INSERT_ID()
	</selectKey>
	INSERT INTO `user`
	(username,birthday,sex,address) VALUES
	(#{username},#{birthday},#{sex},#{address})
</insert>

LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。
效果如下图所示:
在这里插入图片描述
返回的id为48,能够正确的返回id了。

(2)uuid实现主键自增
需要增加通过select uuid()得到uuid值

<!-- 保存用户 -->
<insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">
	<!-- selectKey 标签实现主键返回 -->
	<!-- keyColumn:主键对应的表中的哪一列 -->
	<!-- keyProperty:主键对应的pojo中的哪一个属性 -->
	<!-- order:设置在执行insert语句前执行查询id的sql,孩纸在执行insert语句之后执行查询id的sql -->
	<!-- resultType:设置返回的id的类型 -->
	<selectKey keyColumn="id" keyProperty="id" order="BEFORE"
		resultType="string">
		SELECT LAST_INSERT_ID()
	</selectKey>
	INSERT INTO `user`
	(username,birthday,sex,address) VALUES
	(#{username},#{birthday},#{sex},#{address})
</insert>

注意这里使用的order是“BEFORE”

五、Dao开发

1.原始Dao开发

(1)原始Dao开发,需要Dao接口,和相应的实现实现类。在src下建立Dao包,再建立UserDao接口,然后写实现类UserDaoImp.class
UserDao

public interface UserDao {
	
	User queryUserById(int id);
	List<User> queryUserByUsername(String username);
	void savaUser(User user);
}

UserDaoImpl

public class UserDaoImpl implements UserDao {
	private SqlSessionFactory sqlSessionFactory;
	public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
		super();
		this.sqlSessionFactory = sqlSessionFactory;
	}
	@Override
	public User queryUserById(int id) {
		//有了工厂,就要创建SqlSession
		SqlSession sqlSession=sqlSessionFactory.openSession();
		//执行逻辑查询,把原先单元测试的东西放到了这里
		User user=sqlSession.selectOne("test.queryUserById", id);
		//释放资源
		sqlSession.close();
		return user;
	}
	@Override
	public List<User> queryUserByUsername(String username) {
		//有了工厂,就要创建SqlSession
		SqlSession sqlSession=sqlSessionFactory.openSession();
		List<User> userlist=sqlSession.selectList("test.queryUserByUsername",username);
		sqlSession.close();
		return userlist;
	}
	@Override
	public void savaUser(User user) {
		//有了工厂,就要创建SqlSession
		SqlSession sqlSession=sqlSessionFactory.openSession();
		sqlSession.insert("test.addUser", user);
		sqlSession.commit();
		sqlSession.close();
	}
}

(2)在uniTest包下创建UserDaoTest类,同样需要SqlSessionFactoryBuilder,然后加载配置文件。
需要增加通过select uuid()得到uuid值

<!-- 保存用户 -->
<insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">
	<!-- selectKey 标签实现主键返回 -->
	<!-- keyColumn:主键对应的表中的哪一列 -->
	<!-- keyProperty:主键对应的pojo中的哪一个属性 -->
	<!-- order:设置在执行insert语句前执行查询id的sql,孩纸在执行insert语句之后执行查询id的sql -->
	<!-- resultType:设置返回的id的类型 -->
	<selectKey keyColumn="id" keyProperty="id" order="BEFORE"
		resultType="string">
		SELECT LAST_INSERT_ID()
	</selectKey>
	INSERT INTO `user`
	(username,birthday,sex,address) VALUES
	(#{username},#{birthday},#{sex},#{address})
</insert>

UserDaoTest

public class UserDaoTest {
	private SqlSessionFactory sqlSessionFactory;
	@Before
	public void init()throws Exception{
		SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
		// 2. 加载sqlMapConfig.xml配置文件
		String resource = "sqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 3. 创建SqlSessionFactory对象
		 this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	@Test
	public void testQueryUserById() {
		//创建Dao
		UserDao userDao=new UserDaoImpl(sqlSessionFactory);
		User user=userDao.queryUserById(10);
		System.out.println(user);
	}
	
}

对比之前的Mytest.java文件

public class MyTest {
	private SqlSessionFactory sqlSessionFactory=null;
	@Before
	public void init()throws Exception{
		// 1. 创建SqlSessionFactoryBuilder对象
		SqlSessionFactoryBuilder sessionFactoryBuilder=new SqlSessionFactoryBuilder();
		// 2. 加载sqlMapConfig.xml配置文件
		String resource = "sqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 3. 创建SqlSessionFactory对象
		 this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
	}
@Test 
	  public void testQueryUserById()throws Exception{ 
		  SqlSession sqlSession=sqlSessionFactory.openSession(); 
		  User user=sqlSession.selectOne("queryUserById",1); 
           sqlSession.close()
		  System.out.println(user); 
	  }
}

仔细观察,原始Dao开发只是在入门程序的基础上,把MyTest.java里的打开会话sqlSessionFactory.openSession(),sqlSession.selectOne(),还有关闭回话sqlSession.close()都放到了Dao接口实现类DaoImpl里了。使用的时候,只要传入sqlSessionFactory,new 一个DaoImpl实现类即可,
然后用该类或Dao接口调用的查询,添加,修改等方法即可。

2.Mapper动态代理开发

(1)原始Dao开发存在的问题

(1)实现类方法体代码重复,需要openSession,close等,调用sqlSession.selectOne等操作方法,非常不方便。
(2)调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不利于开发维护。

(2)动态代理解决办法

代理要求如下:
(1)Mapper.xml文件的namespace.xml要与mapper接口的类路径相同。
(2)Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。
(3)Mapper接口输入类型Mapper.xml中定义的每个statement的parameterType的类型相同。
(4)Mapper接口方法的返回类型和Mapper.xml中定义的resultType相同。

把原来入门程序的代码copy一份,改名叫mybatis-dao。去掉Dao接口实现类。按照上面要就,该UserMapper.xml文件里的namespace,根据mapper.xml文件里的sql语句名,paramterType,resultType,id写接口。
在这里插入图片描述
接口方法要合mapper文件相对应
在这里插入图片描述
如何使用,如下,先写单元测试,然后sqlSession.getMapper(接口.class),来获取实现类对象,其实,实现类,我们并没有写,动态代理帮我们写了而已。

public class DongTaiDaoDaiLiTest {
	private SqlSessionFactory sqlSessionFactory;
	@Before
	public void init()throws Exception{
		SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
		// 2. 加载sqlMapConfig.xml配置文件
		String resource = "sqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 3. 创建SqlSessionFactory对象
		 this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	@Test
	public void testQueryUserById() {
		//创建会话
		SqlSession sqlSession=sqlSessionFactory.openSession();
		UserDao userDao=sqlSession.getMapper(UserDao.class);
		User user=userDao.queryUserById(10);
		System.out.println(user);
		sqlSession.close();
		
	}
	@Test
	public void testAddUser() {
		User user = new User();
		user.setUsername("张则太");
		user.setSex("1");
		user.setBirthday(new Date());
		user.setAddress("蜀国");
		//创建会话
		SqlSession sqlSession=sqlSessionFactory.openSession();
		UserDao userDao=sqlSession.getMapper(UserDao.class);
		//sqlSession.commit();动态代理帮我们写了
		userDao.addUser(user);
		sqlSession.close();
	}
}

==总结:==到这,动态代理帮我们做了哪些事,现在看来,就帮我们把sqlSession处理数据的一两行代码给解决了,我们不用写sqlSession.insert(),sqlSession.commit()等,并没有什么卵用。连入门程序都不如,还平白多了个接口,气死你。动态代理的关键点是namespace,把接口的类路径放上去即可。

五、sqlMapConfig.xml配置文件

1.配置文件里可以写那些节点。

properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
evironment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)

2.Properties的使用。

现在在mybatis-dao里改动sqlMapConfig.xml文件,把数据库jdbc的属性独立出来,然后用properties属性来导入相应的值,如下

<?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属性加载外部配置文件 -->
	<properties resource="db.properties">
		<!-- 在properties内部用property定义属性,
		其实下面这些属性已经在db.properties里存在了,这里再写,表示可以在这里重新给他们赋值 -->
		<property name="jdbc.username" value="root"/>
		<property name="jdbc.password" value="wlsbxs"/>
	</properties>

	<!-- 和spring整合后 environments配置将废除 -->
	<environments default="development">
		<environment id="development">
			<!-- 使用jdbc事务管理 -->
			<transactionManager type="JDBC" />
			<!-- 数据库连接池 -->
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url"
					value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<!-- 加载映射文件 -->
	<mappers>
		<mapper resource="mapper/User.xml"/>
	</mappers>
</configuration>

3.typeAlias类型别名的使用。

在使用pojo对象中,我们长会写包名+类名,如果包名比较长,写起来费劲,如在mapper.xml文件的的resultType=”pojo.User”,本例虽然很短,没有什么,但在一些大项目中长包名是不可避免的,如何解决这个问题呢,可以使用别名。

<typeAliases>
		<!-- 可以单个定义别名 -->
		<!-- <typeAlias alias="user" type="pojo.User" /> -->
		
		<!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
		<package name="pojo" />
</typeAliases>

4.Mapper映射器的使用

入门程序里,我们就使用了映射器。<mapper resource=” mapper/User.xml”/>这是我们现在所使用的。
还有几总其它的用法,如下

(1)使用接口类路径

如:
==注意:==此种方法要求mapper接口和mapper映射映射文件名称相同,而且放在同一目录下。
复制一份mybatis-dao项目,命名为mybatis-dao-mapper-classAndPackege,然后把dao包下的UserDao.java改名为UserMapper.java,移动到mapper包下,然后把mapper包下原来的User.xml改名为UserMapper.xml.
在这里插入图片描述

(2)注册指定包下的所有mapper接口,实际应用中使用的是这种方法

如:
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
在这里插入图片描述

六、Mapper.xml各个参数详解

1.parameterType(输入类型)

前面已经使用过了,可以用来传要输入的对象,但只有一张User表,传入的参数为User对象,但如果有两张表(加一张订单包orders),该怎么做呢?那就得新建一个类,既包含用户User,又包含orders。复制一份mybatis-dao-mapper-classAndPackage包,命名为mybaits-dao-mapper。

pojo下新建一个包装查询类QueryUserWrap,并在UserMapper.xml与UserMapper接口下里添加查询语句与对应的抽象方法。
在这里插入图片描述
在这里插入图片描述
==注意:==包装类型的使用方法,包装类型里有User对象,我们要的是username,所以要user.username
在这里插入图片描述

测试代码如下
在这里插入图片描述
结果如下
在这里插入图片描述

2.resultType(输出类型)

可以是简单数据类型,也可以是pojo对象,如果查询得到的是多个pojo对象,则自动生成一个pojo对象列表

3.resultMap

resultType可以指定将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名移植才可以映射成功。
(1)新建orders表,创建orders的pojo对象。
Order对象:

public class Order {
	// 订单id
	private int id;
	// 用户id
	private Integer userId;
	// 订单号
	private String number;
	// 订单创建时间
	private Date createtime;
	// 备注
	private String note;
get/set。。。
}

在这里插入图片描述
(2)mapper包下新建OrdersMapper.xml映射文件,OrdersMapper.java接口文件。

例子:如果sql语句查询得到的字段名与pojo属性名不一致,如查询所有的订单,出现如下情况:

<!-- 查询所有的订单数据 -->
	<select id="queryOrderAll" resultType="order">
		SELECT id, user_id,
		number,
		createtime, note FROM `order`
	</select>

因为user_id与pojo对象里的userId不一样,所以,映射失败

==该怎么办?==这时候需要用到resultMap,将字段名和pojo属性名做一个映射。如下步骤
(3)在ordersMapper.xml文件下建立查询语句,并添加查询方法,然后用resultMap做映射,最后写单元测试。
OrdersMapper.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:命名空间,用于隔离sql,还有一个很重要的作用,
Mapper动态代理开发的时候使用,需要指定Mapper的类路径 -->
<mapper namespace="mapper.OrdersMapper">

	<!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
	<!-- id:设置ResultMap的id -->
	<resultMap type="orders" id="ordersResultMap">
		<!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
		<!-- property:主键在pojo中的属性名 -->
		<!-- column:主键在数据库中的列名 -->
		<id property="id" column="id"/>
		<!-- 定义普通类型 -->
		<result property="userId" column="user_id"/>
		<result property="number" column="number" />
		<result property="createtime" column="createtime" />
		<result property="note" column="note" />
		
	</resultMap>
	<!-- 查询所有的订单数据 -->
	<select id="queryOrdersAll" resultMap="ordersResultMap">
		select id,user_id,number,createtime,note from orders
	</select>
</mapper>

OdersMapper.java接口

public interface OrdersMapper {
	public List<Orders>queryOrdersAll();
}

单元测试ResultMapTest.java文件
public class resultMapTest {
	private SqlSessionFactory sqlSessionFactory;
	@Before
	public void init()throws Exception{
		SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
		// 2. 加载sqlMapConfig.xml配置文件
		String resource = "sqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 3. 创建SqlSessionFactory对象
		 this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	@Test
	public void queryOrdersAll() {
		SqlSession sqlSession=sqlSessionFactory.openSession();
		OrdersMapper ordersMapper=sqlSession.getMapper(OrdersMapper.class);
		List<Orders> list= ordersMapper.queryOrdersAll();
		for(Orders o:list) {
			System.out.println(o);
		}
	}
}

结果如下
在这里插入图片描述

4.动态sql

(1)if标签

没有if标签出现的情况
UserMapper.xml配置sql,如下:

<!-- 根据条件查询用户 -->
<select id="queryUserByWhere" parameterType="user" resultType="user">
	SELECT id, username, birthday, sex, address FROM `user`
	WHERE sex = #{sex} AND username LIKE
	'%${username}%'
</select>

编写Mapper接口,如下图:
在这里插入图片描述
测试方法如下

public class ifTest {
	private SqlSessionFactory sqlSessionFactory;
	@Before
	public void init()throws Exception{
		SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
		// 2. 加载sqlMapConfig.xml配置文件
		String resource = "sqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 3. 创建SqlSessionFactory对象
		 this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	@Test
	public void testQueryUserByWhere() {
		SqlSession sqlSession=sqlSessionFactory.openSession();
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		User user = new User();
		user.setSex("1");
		user.setUsername("张");
		List<User> list = userMapper.queryUserByWhere(user);
		for(User u:list) {
			System.out.println(u);
		}
		sqlSession.close();
	}
}

结果如下
在这里插入图片描述
如果注释掉user.setSex(“1”)结果如下:
在这里插入图片描述
啥都查不到
该如何解决呢,用if标签,如下
修改UserMapper.xml

<!-- 根据条件查询用户 -->
<select id="queryUserByWhere" parameterType="user" resultType="user">
	SELECT id, username, birthday, sex, address FROM `user`
	WHERE 1=1
	<if test="sex != null and sex != ''">
		AND sex = #{sex}
	</if>
	<if test="username != null and username != ''">
		AND username LIKE
		'%${username}%'
	</if>
</select>

结果如下
在这里插入图片描述

(2)where标签

上面的sql语句还有where 1=1这样的语句,很麻烦,可以使用where标签进行改造
改造UserMapper.xml,如下

<!-- 根据条件查询用户 -->
<select id="queryUserByWhere" parameterType="user" resultType="user">
	SELECT id, username, birthday, sex, address FROM `user`
<!-- where标签可以自动添加where,同时处理sql语句中第一个and关键字 -->
	<where>
		<if test="sex != null">
			AND sex = #{sex}
		</if>
		<if test="username != null and username != ''">
			AND username LIKE
			'%${username}%'
		</if>
	</where>
</select>

同时注释掉user.setSex和user.setUsername,结果如下
在这里插入图片描述

5.sql片段

Sql中可以将sql语句中重复使用的片段提交出来,使用时用include引用即可。

<!-- 根据条件查询用户 -->
<select id="queryUserByWhere" parameterType="user" resultType="user">
	<!-- SELECT id, username, birthday, sex, address FROM `user` -->
	<!-- 使用include标签加载sql片段;refid是sql片段id -->
	SELECT <include refid="userFields" /> FROM `user`
	<!-- where标签可以自动添加where关键字,同时处理sql语句中第一个and关键字 -->
	<where>
		<if test="sex != null">
			AND sex = #{sex}
		</if>
		<if test="username != null and username != ''">
			AND username LIKE
			'%${username}%'
		</if>
	</where>
</select>

<!-- 声明sql片段 -->
<sql id="userFields">
	id, username, birthday, sex, address
</sql>

如果要使用别的Mapper.xml配置的sql片段,可以在refid前面加上对应的Mapper.xml的namespace
例如下图
在这里插入图片描述

5.foreach标签

向sql语句传递数组或list,mybatis使用foreach解析
如,根据多个id查询用户信息
Select*from user where id in (1,10,24)
(1)在原来程序里的pojo里的QueryUserWrapper.java查询用户包装类里添加一个private Listids属性;
在这里插入图片描述
(2)在UserMapper.xml添加sql语句如下

<select id="queryUserByIds" parameterType="int" resultType="pojo.User">
		select * from user
		<where>
			<!-- foreach标签,进行遍历 -->
			<!-- collection:遍历的集合,这里是QueryUserWrapper的ids属性 -->
			<!-- item:遍历的项目,可以随便写,,但是和后面的#{}里面要一致 -->
			<!-- open:在前面添加的sql片段 -->
			<!-- close:在结尾处添加的sql片段 -->
			<!-- separator:指定遍历的元素之间使用的分隔符 -->
			<foreach collection="ids" item="item" open="id in (" close=")"  separator=",">
				#{item}
			</foreach>
		</where>
	</select>

并在接口UserMapper.java里添加相应的方法
在这里插入图片描述
然后写单测试,并得出结果,大致如下

结果
在这里插入图片描述
还有queryUserByIds(Integer[]array),item里要填array
queryUserByIds(Listlist);item里要填list
如果为String数组的话,可以用java.util.HashMap
Map<String,Object>p=new HashMap<String,Object>;
Str={“aaa”,”bbb”,”ccc”,”ddd”}
p.put(“a”,str);

七、关联查询

1.商品订单数据模型

在这里插入图片描述

2.一对一查询

需求:查询所有订单信息,关联查询下单用户信息。也就是调订单信息对应一个客户
Sql语句

SELECT
	o.id,
	o.user_id userId,
	o.number,
	o.createtime,
	o.note,
	u.username,
	u.address
FROM
	`order` o
LEFT JOIN `user` u ON o.user_id = u.id

(1)方法一:使用resultType

使用resultType,改造订单pojo类,此pojo类中包括了订单信息和用户信息,这样返回对象的时候,mybatis自动把用户信息也注入进来了。
在UserMapper.xml文件里添加如下sql语句

<select id="queryUserOrders" resultType="ordersUser">
		select
		o.id,
		o.user_id
		userId,注意,这里必须写userId,这样映射才会正确
		o.number,
		o.createtime,
		o.note,
		u.username,
		u.address
		from
		orders o
		left join user u on o.user_id=u.id
	</select>

结果:
在这里插入图片描述
小结:定义专门的pojo作为输出类型,其中定义了sql查询结果集的所有的字段,此方法较为简单,企业中使用普遍。

(2)方法二:使用resultMap

在pojo下的Orders类里加入对应User对象,并重写toString方法,添加get和set方法。
然后在OrderMapper.xml文件里添加sql语句

<resultMap type="orders" id="ordersUserResultMap">
		<id property="id" column="id"/>
		<result property="userId" column="user_id"/>
		<result property="number" column="number" />
		<result property="createtime" column="createtime" />
		<result property="note" column="note" />

		<!-- association :配置一对一属性 -->
		<!-- property:order里面的User属性名 -->
		<!-- javaType:属性类型 -->
		<!-- 上面已经配了Orders的属性映射,还需要配Orders里user对象的映射 -->
		<association property="user" javaType="user">
			<id property="id" column="user_id"/>
			<result property="username" column="username"/>
			<result property="address" column="address"/>	
		</association>
	</resultMap>
	
	<select id="queryOrdersUserResultMap" resultMap="ordersUserResultMap">
		select
		o.id,
		o.user_id,
		o.number,
		o.createtime,
		o.note,
		u.username,
		u.address
		from 
		orders o
		left join user u on o.user_id=u.id
	</select>

结果和第一种方法一样。
对比:
第一种方法是新建一个类,用于映射,该类会继承两张表中其中的一张表对应的pojo,然后添加另一张表对应的pojo中的一些属性。
第二种方法是在已有对象表对应的pojo对象上添加另一张表对应的pojo作为属性,然后用resultMap和association进行映射。

3.一对多查询

需求:查询所有用户信息及关联的订单信息。
用户信息与订单信息是一对多的关系
Sql语句

SELECT
	u.id,
	u.username,
	u.birthday,
	u.sex,
	u.address,
	o.id oid,
	o.number,
	o.createtime,
	o.note
FROM
	`user` u
LEFT JOIN `order` o ON u.id = o.user_id

(1)在UserMapper.xml里写sql语句,并根据语句id写相应的接口方法。

<!-- 一对多关联,查询订单同时查询该用户下的订单 -->
	<select id="queryUserOrdersOneToMany" resultType="OrdersUser">
		select
		u.id userId,
		u.username,
		u.birthday,
		u.sex,
		u.address,
		o.id,
		o.number,
		o.createtime 
		from
		user u
		left join orders o on u.id=o.user_id
	</select>

(2)重写OrdersUser的toString方法

@Override
	public String toString() {
		return "Orders [id=" + super.getId() + ", userId=" + super.getUserId() + ", number=" + super.getNumber() + ", createtime=" + super.getCreatetime()
				+ ", note=" + super.getNote() + ", username=" + username +", address=" + address + "]";
	}

(3)写单元测试和运行结果
在这里插入图片描述

八、Mybatis与Spring进行整合

1.整合需要的jar包

(1)spring的jar包
(2)Mybatis的jar包
(3)Spring+Mybatis整合jar包
(4)数据库连接池的jar包
(5)数据库驱动包
在这里插入图片描述

2.创建一个java工程,加入配置文件

  1. 创建完成后导入需要的jar包
  2. 创建一个config包,mybatis配置SqlMapConfig.xml文件和applicationContext.xml文件放入config
    (1)applicationContext.xml文件的配置
    先配数据连接池,再配mybatis配置文件,并在配置sqlSessionFactory里放入数据源如下
    把Mybaits-dao-mapper文件里的src下的pojo,mapper等文件粘过来
    (2)再在sqlMapConfig.xml文件里进行相应的配置,像原先的数据库连接,数据源等都放到了applicatinContext.xml里了,所以,只要配别名和mapper映射器
<?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>
	<!-- 设置别名 -->
	<typeAliases>
		<package name="pojo"/>
	</typeAliases>
<mappers>
    <package name=”mapper”
</mappers>
</configuration>

3.进行Dao开发,原始Dao开发和动态代理开发

(1)原始Dao开发,把原来原始Dao开发的dao包粘过来,再把user.xml,UserDaoImpl粘到dao包下,并进行相应的修改。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
	 <!-- 加载配置文件 -->
     <context:property-placeholder location="classpath:db.properties" />
	 <!-- 数据库连接池 -->
	 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		  <property name="driverClassName" value="${jdbc.driver}" />
		  <property name="url" value="${jdbc.url}" />
		  <property name="username" value="${jdbc.username}"></property>
		  <property name="password" value="${jdbc.password}"></property>
		  <property name="maxActive" value="10"></property>
		  <property name="maxIdle" value="5"></property>
	 </bean>

	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 配置mybatis核心配置文件 -->
		<property name="configLocation" value="classpath:sqlMapConfig.xml"/>
		<!-- 配置数据源 -->
		<property name="dataSource" ref="dataSource"/>
	</bean>
	<bean id="userDao" class="dao.UserDaoImpl">
		<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	</bean>
	
</beans>

UserDaoImpl.java
编写DAO实现类,实现类必须集成SqlSessionDaoSupport
SqlSessionDaoSupport提供getSqlSession()方法来获取SqlSession

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
	@Override
	public User queryUserById(int id) {
		// 获取SqlSession
		SqlSession sqlSession = super.getSqlSession();

		// 使用SqlSession执行操作
		User user = sqlSession.selectOne("queryUserById", id);

		// 不要关闭sqlSession

		return user;
	}

	@Override
	public List<User> queryUserByUsername(String username) {
		// 获取SqlSession
		SqlSession sqlSession = super.getSqlSession();

		// 使用SqlSession执行操作
		List<User> list = sqlSession.selectList("queryUserByUsername", username);

		// 不要关闭sqlSession

		return list;
	}

	@Override
	public void saveUser(User user) {
		// 获取SqlSession
		SqlSession sqlSession = super.getSqlSession();

		// 使用SqlSession执行操作
		sqlSession.insert("saveUser", user);

		// 不用提交,事务由spring进行管理
		// 不要关闭sqlSession
	}
}

配置dao
在这里插入图片描述
测试
创建测试方法,可以直接创建测试Junit用例。
如下图所示进行创建。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
编写测试方法如下:

public class UserDaoTest {
	private ApplicationContext context;

	@Before
	public void setUp() throws Exception {
		this.context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
	}

	@Test
	public void testQueryUserById() {
		// 获取userDao
		UserDao userDao = this.context.getBean(UserDao.class);

		User user = userDao.queryUserById(1);
		System.out.println(user);
	}

	@Test
	public void testQueryUserByUsername() {
		// 获取userDao
		UserDao userDao = this.context.getBean(UserDao.class);

		List<User> list = userDao.queryUserByUsername("张");
		for (User user : list) {
			System.out.println(user);
		}
	}

	@Test
	public void testSaveUser() {
		// 获取userDao
		UserDao userDao = this.context.getBean(UserDao.class);

		User user = new User();
		user.setUsername("曹操");
		user.setSex("1");
		user.setBirthday(new Date());
		user.setAddress("三国");
		userDao.saveUser(user);
		System.out.println(user);
	}
}

(2)动态代理开发

方法1:现在有了spring,可以把接口,sqlSessionFactory配置到bean里

<!-- Mapper代理的方式开发方式一,配置Mapper代理对象 -->
	<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<!-- 配置Mapper接口 -->
		<property name="mapperInterface" value="mapper.UserMapper"/>
		<!-- 配置sqlSessionFactory -->
		<property name="sqlSessionFactory" ref="sqlSessionFactory"/>	
	</bean>

然后写测试单元

public class DynamicMapperTest {
	private ApplicationContext context;

	@Before
	public void setUp() throws Exception {
		this.context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
	}

	@Test
	public void testQueryUserById() {
		UserMapper userMapper=context.getBean(UserMapper.class);
		User user = userMapper.queryUserById(10);
		System.out.println(user);
	}

}

方法2:上面方法只能配置一个UserMapper接口,效率不高,如何配置多个接口呢,可以用包扫描方式

<!-- Mapper代理的方式开发方式二,扫描包方式配置代理 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 配置Mapper接口 -->
		<property name="basePackage" value="mapper"/>
	</bean>

九、逆向工程

使用官方网站的Mapper自动生成工具mybatis-generator-core-1.3.2来生成po类和Mapper映射文件
【使用插件】
  mybatis-generator-core :进入https://mvnrepository.com/搜索 MyBatis ,找到 MyBatis Generator Core

1.导入逆向工程

使用课前资料已有逆向工程,如下图:
在这里插入图片描述

2.复制逆向工程到工作空间中

复制的效果如下图:
在这里插入图片描述

3.导入逆向工程到eclipse中

如下图方式进行导入:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.修改配置文件

在generatorConfig.xml中配置Mapper生成的详细信息,如下图:
在这里插入图片描述
注意修改以下几点:
1.修改要生成的数据库表
2.pojo文件所在包路径
3.Mapper所在的包路径

配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
	<context id="testTables" targetRuntime="MyBatis3">
		<commentGenerator>
			<!-- 是否去除自动生成的注释 true:是 : false:否 -->
			<property name="suppressAllComments" value="true" />
		</commentGenerator>
		<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
		<jdbcConnection driverClass="com.mysql.jdbc.Driver"
			connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" password="root">
		</jdbcConnection>
		<!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
			userId="yycg" password="yycg"> </jdbcConnection> -->

		<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 
			和 NUMERIC 类型解析为java.math.BigDecimal -->
		<javaTypeResolver>
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>

		<!-- targetProject:生成PO类的位置 -->
		<javaModelGenerator targetPackage="cn.itcast.ssm.po"
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
			<!-- 从数据库返回的值被清理前后的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>
		<!-- targetProject:mapper映射文件生成的位置 -->
		<sqlMapGenerator targetPackage="cn.itcast.ssm.mapper"
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>
		<!-- targetPackage:mapper接口生成的位置 -->
		<javaClientGenerator type="XMLMAPPER"
			targetPackage="cn.itcast.ssm.mapper" targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</javaClientGenerator>
		<!-- 指定数据库表 -->
		<table schema="" tableName="user"></table>
		<table schema="" tableName="order"></table>
	</context>
</generatorConfiguration>

5.生成逆向工程代码

找到下图所示的java文件,执行工程main主函数,
在这里插入图片描述
在这里插入图片描述
刷新工程,发现代码生成,如下图:
在这里插入图片描述

6.测试逆向工程代码

(1) 复制生成的代码到mybatis-spring工程,如下图

在这里插入图片描述

(2)修改spring配置文件

在applicationContext.xml修改

<!-- Mapper代理的方式开发,扫描包方式配置代理 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<!-- 配置Mapper接口,如果需要加载多个包,直接写进来,中间用,分隔 -->
	<!-- <property name="basePackage" value="cn.itcast.mybatis.mapper" /> -->
	<property name="basePackage" value="cn.itcast.ssm.mapper" />
</bean>

(3) 编写测试方法:

public class UserMapperTest {
	private ApplicationContext context;

	@Before
	public void setUp() throws Exception {
		this.context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
	}

	@Test
	public void testInsert() {
		// 获取Mapper
		UserMapper userMapper = this.context.getBean(UserMapper.class);

		User user = new User();
		user.setUsername("曹操");
		user.setSex("1");
		user.setBirthday(new Date());
		user.setAddress("三国");

		userMapper.insert(user);
	}

	@Test
	public void testSelectByExample() {
		// 获取Mapper
		UserMapper userMapper = this.context.getBean(UserMapper.class);

		// 创建User对象扩展类,用户设置查询条件
		UserExample example = new UserExample();
		example.createCriteria().andUsernameLike("%张%");

		// 查询数据
		List<User> list = userMapper.selectByExample(example);

		System.out.println(list.size());
	}

	@Test
	public void testSelectByPrimaryKey() {
		// 获取Mapper
		UserMapper userMapper = this.context.getBean(UserMapper.class);

		User user = userMapper.selectByPrimaryKey(1);
		System.out.println(user);
	}
}

注意:
1.逆向工程生成的代码只能做单表查询
2.不能在生成的代码上进行扩展,因为如果数据库变更,需要重新使用逆向工程生成代码,原来编写的代码就被覆盖了。
3.一张表会生成4个文件

十、缓存

1.一级缓存

在这里插入图片描述
MyBatis是默认开启一级缓存,一级缓存是指session缓存,它的作用域是一个sqlSession对应着一个缓存空间,互不影响。

当你再同一个sqlSession执行sql时,第一次会查询数据库,写入到缓存中,第二次会先去缓存中获取,没有命中就再次查询数据库。

当执行增删改的操作时,MyBatis会把SqlSession对应的缓存清空。

Spring项目中一个Mapper文件对应着一个SqlSession。

2.二级缓存

二级缓存是需要手动开启,在配置文件里加

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

在这里插入图片描述
二级缓存是在一级缓存的基础上开启多个SqlSession对应缓存空间共享

这样我就可以在第一个SqlSession执行一条查询语句时会去数据库中读取并存入对应的缓存空间,第二个SqlSession执行同一条查询语句时会先去所有的SqlSession对应的缓存空间中获取数据。

值得一提的是 当一个SqlSession执行增删改的时候,他只会清空自己对应的缓存空间,假如第一个缓存中存储的name叫"小明"
第二个SqlSession执行修改操作改为name"小红",然后执行查询语句会命中第一个缓存中的"小明" 这就会造成脏读。

解决办法
如果是两个mapper命名空间的话,可以使用 来把一个命名空间指向另外一个命名空间,从而消除上述的影响,再次执行,就可以查询到正确的数据。

https://www.cnblogs.com/cxuanBlog/p/11333021.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值