数据库:mysql5.7
jdk:15
引入依赖
<!--引入依赖-->
<dependencies>
<!--mybatis坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--mysql驱动坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<!--单元测试坐标-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
User实体
@Data
public class User {
private Integer id;
private String username;
}
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1/zdy_mybatis
jdbc.username=root
jdbc.password=root
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>
<!--加载外部的properties文件-->
<properties resource="jdbc.properties"></properties>
<!--给实体类的全限定类名给别名-->
<typeAliases>
<!--给单独的实体起别名-->
<!-- <typeAlias type="com.yun.pojo.User" alias="user"></typeAlias>-->
<!--批量起别名:该包下所有的类的本身的类名:别名还不区分大小写-->
<package name="com.yun.pojo"/>
</typeAliases>
<!--environments:运行环境-->
<environments default="development">
<environment id="development">
<!--当前事务交由JDBC进行管理-->
<transactionManager type="JDBC"></transactionManager>
<!--当前使用mybatis提供的连接池-->
<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="UserMapper.xml"></mapper>
</mappers>
</configuration>
案例一-查询用户
UserMapper.xml
<mapper namespace="user">
<!--select-->
<select id="findAll" resultType="com.yun.pojo.User">
select * from user
</select>
</mapper>
@Test
public void test1() throws IOException {
//1.Resources工具类,配置文件的加载,把配置文件加载成字节输入流
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
//2.解析了配置文件,并创建了sqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.生产sqlSession
// 默认开启一个事务,但是该事务不会自动提交
//在进行增删改操作时,要手动提交事务
SqlSession sqlSession = sqlSessionFactory.openSession();
//4.sqlSession调用方法:查询所有selectList 查询单个:selectOne 添加:insert 修改:update 删除:delete
List<User> users = sqlSession.selectList("user.findAll");
users.forEach(item ->{
System.out.println(item);
});
sqlSession.close();
}
输出结果
User(id=1, username=Tom)
User(id=2, username=Jerry)
案例二-添加用户
UserMapper.xml
<!--parameterType:参数类型-->
<insert id="saveUser" parameterType="com.yun.pojo.User">
insert into user Values (#{id},#{username})
</insert>
@Test
public void test2() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setId(3);
user.setUsername("jack");
sqlSession.insert("user.saveUser",user);
sqlSession.commit();
sqlSession.close();
}
数据库结果:
案例三-编辑用户
UserMapper.xml
<update id="updateUser" parameterType="com.yun.pojo.User">
update user set username = #{username} where id = #{id}
</update>
@Test
public void test3() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setId(3);
user.setUsername("rose");
sqlSession.update("user.updateUser",user);
sqlSession.commit();
sqlSession.close();
}
数据库结果:
案例四-删除用户
UserMapper.xml
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id =#{id}
</delete>
@Test
public void test4() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setId(3);
sqlSession.delete("user.deleteUser",3);
sqlSession.commit();
sqlSession.close();
}
数据库结果:
传统开发方式
public interface IUserDao {
//查询所有用户
List<User> findAll() throws IOException;
}
UserDaoImpl
public class UserDaoImpl implements IUserDao {
@Override
public List<User> findAll() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> users = sqlSession.selectList("user.findAll");
sqlSession.close();
return users;
}
}
@Test
public void test5() throws IOException {
UserDaoImpl dao = new UserDaoImpl();
List<User> users = dao.findAll();
System.out.println(users);
}
打印结果:
[User(id=1, username=Tom), User(id=2, username=Jerry)]
代理开发方式(使用JDK动态代理产生代理对象,由代理对象执行并且操作)
Mapper接口开发需要遵行以下规范:
1. mapper.xml文件中的namespace与mapper接口的全限定名相同;
2. mapper接口方法名和mapper.xml中定义的每个statement的id相同
3. mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
4. mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
根据上述的规范修改UserMapper.xml
<mapper namespace="com.yun.dao.IUserDao">
<!--select-->
<select id="findAll" resultType="com.yun.pojo.User">
select * from user
</select>
</mapper>
public interface IUserDao {
//查询所有用户
List<User> findAll() throws IOException;
}
@Test
public void test6() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
IUserDao mapper = sqlSession.getMapper(IUserDao.class);
List<User> all = mapper.findAll();
all.forEach(item ->{
System.out.println(all);
});
}
输出结果:
User(id=1, username=Tom)
User(id=2, username=Jerry)
动态sql语句 if标签
public interface IUserDao {
//多条件组合查询:演示if
public List<User> findByCondition(User user);
}
<!--抽取sql片段-->
<sql id="selectUser">
select * from user
</sql>
<!--多条件组合查询:演示if-->
<select id="findByCondition" parameterType="user" resultType="user">
<include refid="selectUser"></include>
<where>
<if test="id !=null">
and id = #{id}
</if>
<if test="username !=null">
and username = #{username}
</if>
</where>
</select>
@Test
public void test7() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
IUserDao mapper = sqlSession.getMapper(IUserDao.class);
User user1 = new User();
user1.setId(1);
user1.setUsername("Tom");
List<User> all = mapper.findByCondition(user1);
for (User user : all) {
System.out.println(user);
}
}
输出结果:
User(id=1, username=Tom)
动态sql语句 foreach标签
public interface IUserDao {
//多值查询:演示foreach
public List<User> findByIds(int[] ids);
}
<!--多值查询:演示foreach-->
<select id="findByIds" parameterType="list" resultType="user">
<include refid="selectUser"></include>
<where>
<foreach collection="array" open="id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
@Test
public void test8() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
IUserDao mapper = sqlSession.getMapper(IUserDao.class);
int[] arr = {1,2};
List<User> all = mapper.findByIds(arr);
for (User user : all) {
System.out.println(user);
}
}
输出结果:
User(id=1, username=Tom)
User(id=2, username=Jerry)
Mybatis复杂映射
一对一
User实体
@Data
public class User {
private Integer id;
private String username;
//该用户所具有的订单信息
private List<Order> orders;
//该用户所具有的角色信息
private List<Role> roles;
}
Order实体
@Data
public class Order {
private Integer id;
private String orderTime;
private BigDecimal total;
//表明该订单属于哪个用户
private User user;
}
Role实体
@Data
public class Role {
private Integer id;
private String roleName;
}
public interface IOrderMapper {
/**
* 查询订单的同时还查询该订单所属的用户
* @return
*/
public List<Order> findOrderAndUser();
}
<resultMap id="orderMap" type="com.yun.pojo.Order">
<result property="id" column="id"></result>
<result property="orderTime" column="order_time"></result>
<result property="total" column="total"></result>
<association property="user" javaType="com.yun.pojo.User">
<result property="id" column="uid"></result>
<result property="username" column="username"></result>
</association>
</resultMap>
<!--resultMap:手动来配置实体属性与表字段的映射关系-->
<select id="findOrderAndUser" resultMap="orderMap">
select * from orders o,user u where o.uid = u.id
</select>
@Test
public void test1() throws IOException {
//1.Resources工具类,配置文件的加载,把配置文件加载成字节输入流
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
//2.解析了配置文件,并创建了sqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.生产sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
IOrderMapper mapper = sqlSession.getMapper(IOrderMapper.class);
List<Order> orderAndUser = mapper.findOrderAndUser();
orderAndUser.forEach(order -> {
System.out.println(order);
});
}
运行结果:
Order(id=1, orderTime=2022-05-01, total=1000.00, user=User(id=1, username=Tom, orders=null, roles=null))
Order(id=2, orderTime=2022-05-10, total=2000.00, user=User(id=2, username=Jerry, orders=null, roles=null))
Order(id=3, orderTime=2022-05-20, total=3000.00, user=User(id=2, username=Jerry, orders=null, roles=null))
一对多
public interface IUserMapper {
/**
* 查询所有用户信息,同时查询出每个用户关联的订单信息
* @return
*/
public List<User> findAll();
}
<resultMap id="userMap" type="com.yun.pojo.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<collection property="orders" ofType="com.yun.pojo.Order">
<id property="id" column="oid"></id>
<result property="orderTime" column="order_time"></result>
<result property="total" column="total"></result>
</collection>
</resultMap>
<!--resultMap:手动来配置实体属性与表字段的映射关系-->
<select id="findAll" resultMap="userMap">
select u.*,o.id oid,o.order_time,o.total,o.uid from user u left join orders o on u.id = o.uid
</select>
@Test
public void test2() throws IOException {
//1.Resources工具类,配置文件的加载,把配置文件加载成字节输入流
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
//2.解析了配置文件,并创建了sqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.生产sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
List<User> users = mapper.findAll();
users.forEach(user -> {
System.out.println(user.getUsername());
System.out.println(user.getOrders());
});
}
运行结果:
Tom
[Order(id=1, orderTime=2022-05-01, total=1000.00, user=null)]
Jerry
[Order(id=2, orderTime=2022-05-10, total=2000.00, user=null), Order(id=3, orderTime=2022-05-20, total=3000.00, user=null)]
多对多
public interface IUserMapper {
/**
* 查询所有用户信息,同事查询出每个用户关联的角色信息
* @return
*/
public List<User> findUserAndRole();
}
<resultMap id="userAndRoleMap" type="com.yun.pojo.User">
<result property="id" column="userId"></result>
<result property="username" column="username"></result>
<collection property="roles" ofType="com.yun.pojo.Role">
<result property="id" column="roleId"></result>
<result property="roleName" column="rolename"></result>
</collection>
</resultMap>
<select id="findUserAndRole" resultMap="userAndRoleMap">
SELECT * FROM USER u
LEFT JOIN sys_user_role sur ON u.id = sur.user_id
LEFT JOIN sys_role sr ON sur.role_id = sr.id
</select>
@Test
public void test3() throws IOException {
//1.Resources工具类,配置文件的加载,把配置文件加载成字节输入流
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
//2.解析了配置文件,并创建了sqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.生产sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
List<User> users = mapper.findUserAndRole();
users.forEach(user -> {
System.out.println(user.getUsername());
System.out.println(user.getRoles());
});
}
运行结果:
Tom
[Role(id=null, roleName=董事长), Role(id=null, roleName=经理)]
Jerry
[Role(id=null, roleName=董事长), Role(id=null, roleName=经理)]
Mybatis注解开发
Mybasits常用注解:
@Insert: 实现新增
@Update: 实现更新
@Delete: 实现删除
@Select: 实现查询
@Result: 实现结果集封装;他代替的是标签<resultMap>,该注解中可以使用单个@Result注解,也可以使用@Result集合,
使用格式:@Results({@Result(),@Result()}) 或 @Results(@Result())
@Results: 可以与@Result一起使用,封装多个结果集
@One: 实现一对一结果集封装
@Many: 实现一对多结果集封装
测试案例
public interface IUserMapper {
//添加用户
@Insert("insert into user values(#{id},#{username})")
public void addUser(User user);
//更新用户
@Update("update user set username = #{} where id = #{id}")
public void updateUser(User user);
//查询用户
@Select("select * from user")
public List<User> getAllUser();
//删除用户
@Delete("delete from user where id=#{id}")
public void delUser(Integer id);
}
private IUserMapper mapper;
@Before
public void before() throws IOException {
//1.Resources工具类,配置文件的加载,把配置文件加载成字节输入流
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
//2.解析了配置文件,并创建了sqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.生产sqlSession true:事务自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
mapper = sqlSession.getMapper(IUserMapper.class);
}
@Test
public void addUser(){
User user = new User();
user.setId(3);
user.setUsername("jack");
mapper.addUser(user);
}
@Test
public void updateUser(){
User user = new User();
user.setId(3);
user.setUsername("rose");
mapper.updateUser(user);
}
@Test
public void getAllUser(){
List<User> userList = mapper.getAllUser();
userList.forEach(item -> {
System.out.println(item);
});
}
@Test
public void delUser(){
mapper.delUser(3);
}
运行结果:
Mybatis注解实现复杂映射开发
一对一
public interface IOrderMapper {
/**
* 查询订单的同时还查询该订单所属的用户
* @return
*/
@Results({
@Result(property = "id",column = "id"),
@Result(property = "orderTime",column = "order_time"),
@Result(property = "total",column = "total"),
@Result(property = "user",column = "uid",javaType = User.class,
one = @One(select = "com.yun.mapper.IUserMapper.getUserById")),
})
@Select("select * from orders")
public List<Order> findOrderAndUser();
}
public interface IUserMapper {
@Select("select * from user where id = #{id}")
public User getUserById(Integer id);
}
@Test
public void oneToOne(){
List<Order> orderAndUser = orderMapper.findOrderAndUser();
orderAndUser.forEach(item -> {
System.out.println(item);
});
}
运行结果:
Order(id=1, orderTime=2022-05-01, total=1000.00, user=User(id=1, username=Tom, orders=null, roles=null))
Order(id=2, orderTime=2022-05-10, total=2000.00, user=User(id=2, username=Jerry, orders=null, roles=null))
Order(id=3, orderTime=2022-05-20, total=3000.00, user=User(id=2, username=Jerry, orders=null, roles=null))
一对多
public interface IUserMapper {
/**
* 查询所有用户信息,同时查询出每个用户关联的订单信息
* @return
*/
@Select("select * from user")
@Results({
@Result(property = "id",column = "id"),
@Result(property = "username",column = "username"),
@Result(property = "orders",column = "id",javaType = List.class,
many = @Many(select = "com.yun.mapper.IOrderMapper.getOrderByUid"))
})
public List<User> findAll();
}
public interface IOrderMapper {
@Select("select * from orders where uid = #{uid}")
public List<Order> getOrderByUid(Integer uid);
}
@Test
public void oneToMore(){
List<User> users = mapper.findAll();
users.forEach(item -> {
System.out.println(item);
});
}
运行结果:
User(id=1, username=Tom, orders=[Order(id=1, orderTime=null, total=1000.00, user=null)], roles=null)
User(id=2, username=Jerry, orders=[Order(id=2, orderTime=null, total=2000.00, user=null), Order(id=3, orderTime=null, total=3000.00, user=null)], roles=null)
多对多
public interface IUserMapper {
/**
* 查询所有用户信息,同事查询出每个用户关联的角色信息
* @return
*/
@Select("select * from user")
@Results({
@Result(property = "id",column = "id"),
@Result(property = "username",column = "username"),
@Result(property = "roles",column = "id",javaType = List.class,
many = @Many(select ="com.yun.mapper.IRoleMapper.getAll"))
})
public List<User> findUserAndRole();
}
public interface IRoleMapper {
@Select("select * from sys_role sr,sys_user_role sur where sr.id = sur.role_id and sur.user_id = #{uid}")
public List<Role> getAll(Integer uid);
}
@Test
public void moreToMore(){
List<User> users = mapper.findUserAndRole();
users.forEach(item -> {
System.out.println(item);
});
}
运行结果:
User(id=1, username=Tom, orders=null, roles=[Role(id=1, roleName=董事长), Role(id=2, roleName=经理)])
User(id=2, username=Jerry, orders=null, roles=[Role(id=1, roleName=董事长), Role(id=2, roleName=经理)])
Mybatis缓存
基础概念
缓存就是内存中的数据,常常来自对数据库查询结果的保存,使用缓存,我们可以避免频繁的与数据库进行交互,进而提高响应速度.
一级缓存是SqlSession,在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据.不同的sqlSession之间互不影响.
二级缓存是mapper级别的缓存,多个sqlSession去操作同一个mapper的sql语句,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的.
一级缓存
demo
public class CacheTest {
private IUserMapper mapper;
private SqlSession sqlSession;
@Before
public void before() throws IOException {
//1.Resources工具类,配置文件的加载,把配置文件加载成字节输入流
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
//2.解析了配置文件,并创建了sqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.生产sqlSession true:事务自动提交
sqlSession = sqlSessionFactory.openSession(true);
mapper = sqlSession.getMapper(IUserMapper.class);
}
@Test
public void test1() {
//第一次查询id为1的用户
User user1 = mapper.getUserById(1);
//第二次查询id为1的用户
User user2 = mapper.getUserById(1);
System.out.println(user1 == user2);
}
}
返回结果为 true ;测试一级缓存是默认开启的
结论:第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户,如果没有,则从数据库查询用户信息,得到用户信息并且将用户信息存储到一级缓存中,第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户,如果缓存中有,直接从缓存中获取用户信息;
现在我们变换一下上面的demo
public class CacheTest {
private IUserMapper mapper;
private SqlSession sqlSession;
@Before
public void before() throws IOException {
//1.Resources工具类,配置文件的加载,把配置文件加载成字节输入流
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
//2.解析了配置文件,并创建了sqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.生产sqlSession true:事务自动提交
sqlSession = sqlSessionFactory.openSession();
mapper = sqlSession.getMapper(IUserMapper.class);
}
@Test
public void test1() {
//第一次查询id为1的用户
User user1 = mapper.getUserById(1);
User user = new User();
user.setId(1);
user.setUsername("Lucy");
mapper.updateUser(user);
sqlSession.commit();
//第二次查询id为1的用户
User user2 = mapper.getUserById(1);
System.out.println(user1 == user2);
}
}
返回结果为 false
结论:做增删改操作,并进行了事物的提交,就会刷新以及缓存;或者还可以通过 sqlSession.clearCache() 清楚缓存;这样做的目的就是为了让缓存中存储的是最新的信息,避免脏读;
二级缓存
二级缓存的原理和一级缓存原理一样,第一次查询会将数据放入缓存中,然后第二次查询则会直接从缓存中获取,但是一级缓存是基于sqlSession的,而二级缓存是基于mapper文件的namespace,也就是说,多个sqlSession可以共享一个mapper中的二级缓存,并且如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper执行sql查询到的数据也将存在相同的二级缓存区域中.
如何使用二级缓存
首先在全局配置文件sqlMapconfig.xml文件加入如下代码
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
注意,该配置需要放在properties标签下,具体顺序,可百度了解
其次,在xxxMapper.xml文件中开启缓存(如果当前操作时基于注解开发的话,使用注解@CacheNamespace)
<cache></cache>
demo2
public class CacheTest {
private IUserMapper mapper;
private SqlSession sqlSession;
private SqlSessionFactory sqlSessionFactory;
@Before
public void before() throws IOException {
//1.Resources工具类,配置文件的加载,把配置文件加载成字节输入流
InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
//2.解析了配置文件,并创建了sqlSessionFactory工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.生产sqlSession
sqlSession = sqlSessionFactory.openSession();
mapper = sqlSession.getMapper(IUserMapper.class);
}
@Test
public void test2() {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class);
IUserMapper mapper2 = sqlSession2.getMapper(IUserMapper.class);
IUserMapper mapper3 = sqlSession3.getMapper(IUserMapper.class);
User user1 = mapper1.getUserById(1);
//清空一级缓存
sqlSession1.close();
User user2 = mapper2.getUserById(1);
System.out.println(user1 == user2);
}
}
运行结果为 false
结论:通过debug断点显示,实际上,第二次查询则会直接从缓存中获取用户信息了, 不过二级缓存缓存的不是对象,而是缓存的对象中的数据,所以查询结果为false;
注意,二级缓存底层还是HashMap结构,所以 po类需要实现序列化接口 ;因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中,如果我们要在取这个缓存的话,就需要反序列化了,所以mybatis中的pojo都去实现Serializable接口;
变换一下demo2
@Test
public void test2() {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class);
IUserMapper mapper2 = sqlSession2.getMapper(IUserMapper.class);
IUserMapper mapper3 = sqlSession3.getMapper(IUserMapper.class);
User user1 = mapper1.getUserById(1);
//清空一级缓存
sqlSession1.close();
User user = new User();
user.setId(1);
user.setUsername("Tom");
mapper3.updateUser(user);
sqlSession3.commit();
User user2 = mapper2.getUserById(1);
System.out.println(user1 == user2);
}
返回结果为 false
结论:做增删改操作,并进行了事物的提交,就会刷新以及缓存;这样做的目的就是为了让缓存中存储的是最新的信息,避免脏读;
此外mybatis中还可以配置useCache和flushCache等配置项;
useCache
是用来设置是否禁用二级缓存的,在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会会发出sql去查询,默认情况是true,即该sql使用二级缓存,例如
<select id="findAll" useCache = "false" resultMap="userMap">
select u.*,o.id oid,o.order_time,o.total,o.uid from user u left join orders o on u.id = o.uid
</select>
使用sql注解方式可以使用@Options(useCache = false)的方式
flushCache
在mapper的同一个namespace中,如果有其他的insert,update,delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读,设置statememt配置中的
flushCache = "true"属性,默认情况下为true,即刷新缓存,如果改成false则不会刷新,使用缓存时如果手动修改数据库表中的查询数据会出现脏读 例如
<select id="findAll" flushCache = "true" useCache = "false" resultMap="userMap">
select u.*,o.id oid,o.order_time,o.total,o.uid from user u left join orders o on u.id = o.uid
</select>
至此,mybatis基础概念及应用回顾完成!