- Mybatis是什么?
- xml是什么?
- Junit单元测试
- 作用:让普通的方法,可以不用主函数的时候,直接自己执行。
- ORM框架Mybatis的基本介绍(包含配置文件)
- mybatis基础
- 使用别名和mapper映射
- 自定义的POJO类(高级查询使用)
- mybatis中输入映射和输出映射(重点)
- 输入映射
- 输出映射
- resultType:
- 1.输出类型为输出的类型为POJO包装类(model失血模型),用的是resultType
- 2.输出的类型为简单数据类型,resultType,要求:查询的结果集必须是一行一列的数据
- resultMap
- 自定义映射关系:当java中失血模型对象中的字段和数据库中的字段名称不一致的时候,可以使用resultMap方式进行自定义映射关系
- resultType和resultMap区别
- resultType:会增加一个扩展的模型类,但是,这样可以简化xml配置文件中的繁琐步骤。操作和单表查询操作一致,一对一的映射关系一般推荐使用resultType。
- resultMap:需要有关联映射,需要自定义resultMap的配置信息,增加配置的难度,但是,可以用在一对多的关系映射中(较为复杂的关系映射)。
- 自定义映射关系:一对一,一对多,多对多(分解成一对多,一对多,一对一的关系)
- 延迟加载(按需加载)
- 缓存
Mybatis是什么?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
xml是什么?
XML是一种可以描述数据之间层级关系的配置文件。可扩展的标记语言。可以根据程序员的需求自己进行添加标签对数据进行标记。
xml应用场景:作为框架配置文件
xml文件语法:
语法包含:声明、标签、属性、注释、CDATA区、指令(引入其他的文件,比如css)、特殊字符(转义字符)
xml文件的声明:
<?xml version="1.0" encoding="UTF-8" ?>
java解析xml文件
SAX解析:需要使用数据的时候,一层一层的去遍历找正确的值,读取效率很低,但是节约内存
DOM解析:会一次性将xml文件中的所有数据,读取到内存中,生成一个Document文档树对象,读取效率高,但是会消耗内存
<!--DOM解析-->
//步骤1:根据工厂类获取工厂对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//步骤2:根据工厂类对象获取解析器
DocumentBuilder builder = factory.newDocumentBuilder();
//步骤3:根据解析器,获取文档树对象
Document document = builder.parse(new File("src/xml文件名"));
Junit单元测试
作用:让普通的方法,可以不用主函数的时候,直接自己执行。
当前测试的方法,在方法的上面加@Test注解,这个方法不能加static修饰,也不能有形式参数
想要在测试方法之前执行的方法,在方法的上面加@Before注解,这个方法不能加static修饰,也不能有形式参数
想要在测试方法执行之后,执行的方法,在方法的上面加@After注解,这个方法不能加static修饰,也不能有形式参数
ORM框架Mybatis的基本介绍(包含配置文件)
原生JDBC与Mybatis的对比
public class Demo{
private static String driver = "com.mysql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/db_shop";
private static String username = "root";
private static String password = "";
static{
//加载驱动
Class.forName(driver);
}
public static void main(String[] args){
try{
Connection conn = ManagerDriver.getConnection(url,username,password);
String sql = "select * from t_user where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,user.getInt());
ResultSet rs = ps.executeQuery();
while(rs.next()){
}
rs.close();
ps.close();
conn.close();
}catch(Exception e){
}
}
}
- 原生jdbc频繁创建链接和释放资源,会增加数据库的链接压力
- 原生jdbc写在java代码中,属于硬编码,不利于sql代码维护
jdbc预编译对象中的?也属于硬编码
mybatis使用连接池,用连接池来管理数据库的链接和释放资源
- mybatis将sql语句提取到xml配置文件中,修改sql语句只需修改xml配置文件即可。
- mybatis实现关系对象映射,存在输入对象映射和输出对象映射。
mybatis基础
概念:
用来解决java对象和数据库之间数据交互的框架,apache公司提供的轻量级的ORM框架。目的:将程序员的所有精力全部放在编写sql语句的思路上,至于执行sql语句,填充占位符和查询的数据与对象进行关联,全部由mybatis框架完成。
基本mybatis配置
SqlMapConfig.xml
log4j.properties
db.properties
最简单的mybatisDAO(使用mapper代理)
思路:
步骤1:创建xml文件,里面配置增、删、改、查的sql语句
步骤2:将创建的xml文件在SqlMapConfig.xml文件中进行配置加载
步骤3:创建一个接口,接口当中包含方法,只有定义。
要求
1)mapper标签里面的namespace:必须写成对应的Mapper接口的全路径名
2)sql语句里面的id:必须和接口中对应方法的名称一致。
3)sql语句里面的parameterType:必须要和接口中方法的形式参数数据类型一致
4)sql语句里面的resultType:必须和接口中方法的返回值数据类型一致
步骤4:创建实现类,实现定义的接口,按照规定进行重写方法。声明工厂类对象,该对象由该类的构造方法进行赋值。(使用MVC三层架构实现)
步骤5:编写测试类,在测试类中,创建工厂对象,传递给dao的实现类。
简单的实例代码
步骤1:创建xml文件,里面配置增、删、改、查的sql语句
<?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接口的全路径名 -->
<mapper namespace="com.woniuxy.mall.mapper.UserMapper">
<insert id="insert" parameterType="oldUser">
INSERT INTO t_user(user_id ,user_name,user_psword ,user_phon )
VALUES(#{user_id},#{user_name},#{user_psword},#{user_phon});
</insert>
<!-- 根据电话号码查询用户,只会返回一条数据 -->
<select id="queryUserByTel" parameterType="java.lang.String"
resultType="oldUser">
select * from t_user where user_phon=#{tel}
</select>
</mapper>
步骤2:将创建的xml文件在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="db.properties" />
<typeAliases>
<!-- name:包名 自动扫描,定义别名,默认类名 -->
<package name="com.woniuxy.mall.model" />
</typeAliases>
<!-- 使用mybatis需要的数据源和事务配置,后续如果整合spring之后,将不再需要 -->
<environments default="development">
<!-- 配置数据源和事务 -->
<environment id="development">
<!-- 配置事务管理,将事务管理交给mybatis管理 -->
<transactionManager type="JDBC" />
<!-- 配置数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<!-- 加载**.xml配置文件mapper映射 -->
<mappers>
<mapper resource="mapper/UserMapper.xml" />
</mappers>
</configuration>
步骤3:创建一个接口,接口当中包含5个方法,两个查询、增、删、改的方法。只有定义。
package com.woniuxy.mall.mapper;
import com.woniuxy.mall.model.oldUser;
/*
* mapper接口,增删改查与mapper.mal文件对应
*/
public interface UserMapper {
/*
* 注册,根据名字和密码,电话
*/
public int insert(oldUser user)throws Exception;
/*
* 查询单个查询,登录,根据电话、密码
*/
public oldUser queryUserByTel(String tel) throws Exception;
}
步骤4.1:创建Mapper实现类,实现定义的接口,按照规定进行重写方法。声明工厂类对象,该对象由该类的构造方法进行赋值。
package com.woniuxy.mall.dao.imple;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.woniuxy.mall.mapper.UserMapper;
import com.woniuxy.mall.model.oldUser;
import com.woniuxy.mall.util.User;
public class UserDaoImpl implements UserMapper {
/*
* 声明会话工厂
*/
private static SqlSessionFactory factory;
/*
* 获取流
*/
static {
String path = "SqlMapConfig.xml";
try {
InputStream config= Resources.getResourceAsStream(path);
factory = new SqlSessionFactoryBuilder().build(config);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public int insert(oldUser user) throws Exception {
// TODO Auto-generated method stub
/*
* 开启会话
* 获取mapper反射
*/
SqlSession session = factory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int row =mapper.insert(user);
session.commit();
session.close();
return row;
}
@Override
public oldUser queryUserByTel(String tel) throws Exception {
//开启会话
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
oldUser user = mapper.queryUserByTel(tel);
//关闭会话:
sqlSession.close();
return user;
}
}
步骤4.2:编写Service层接口
package com.woniuxy.mall.service;
import com.woniuxy.mall.model.oldUser;
public interface UserService {
/*
* 注册,根据名字和密码,电话
*/
public String insert(oldUser user)throws Exception;
public String login(String tel,String password)throws Exception;
}
步骤4.3:编写Service实现类,关联mapper,进行逻辑判断
package com.woniuxy.mall.service.impl;
import com.woniuxy.mall.dao.imple.UserDaoImpl;
import com.woniuxy.mall.mapper.UserMapper;
import com.woniuxy.mall.model.oldUser;
import com.woniuxy.mall.service.UserService;
import com.woniuxy.mall.util.User;
public class UserServiceImple implements UserService{
@Override
public String insert(oldUser user) throws Exception {
// 新增用户
String pass = user.getUser_psword();
//校验密码是否合法
boolean flag = pass.matches("[A-Z][A-Za-z0-9]{5,9}");
if(flag) {
UserMapper dao = new UserDaoImpl();
int row = dao.insert(user);
if(row>0) {
return "成功";
}else {
return "失败";
}
}else {
return "密码格式有误";
}
}
@Override
public String login(String tel, String password) throws Exception {
UserMapper dao = new UserDaoImpl();
oldUser user = dao.queryUserByTel(tel);
if(user != null) {
//获取数据库中的密码:
String userPass = user.getUser_psword();
//比较
if(userPass.equals(password)) {
return "success";
}else {
return "fail";
}
}else {
return "fail";
}
}
}
步骤5:编写测试类,在测试类中,创建工厂对象,传递给dao的实现类。
package com.woniuxy.mall.controller;
import java.util.Scanner;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import com.woniuxy.mall.model.oldUser;
import com.woniuxy.mall.service.UserService;
import com.woniuxy.mall.service.impl.UserServiceImple;
public class UserController {
// @Test
// public void insert() throws Exception {
// // TODO Auto-generated constructor stub
// System.out.println("注册:");
// Scanner input = new Scanner(System.in);
// System.out.println("请输入账号:");
// String userName = input.next();
// System.out.println("请输入密码:");
// String userpass = input.next();
// System.out.print("请输入电话:");
// String userphon = input.next();
//
// String userId = UUID.randomUUID().toString();
// oldUser user = new oldUser();
// user.setUser_id(userId);
// user.setUser_name(userName);
// user.setUser_psword(userpass);
// user.setUser_phon(userphon);
//
// /*
// * 调用service
// */
// UserService userService = new UserServiceImple();
// String flag = userService.insert(user);
//
// if (flag.equals("成功")) {
// System.out.println("注册成功");
// } else {
// System.out.println("注册失败");
// }
//
// }
@Test
public void login() throws Exception {
System.out.println("登录:");
Scanner in = new Scanner(System.in);
System.out.print("请输入电话号码:");
String tel = in.next();
System.out.print("请输入密码:");
String password = in.next();
//创建Service对象
UserService userService = new UserServiceImple();
String flag = userService.login(tel, password);
if(flag.equals("success")) {
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
}
使用别名和mapper映射
别名
为什么设置别名
在开发中,一般mapper.xml配置文件中的parameterType和resultType往往对应的名称需要写上model的全路径,而且还是多次被重复使用,可以选择给该路径取一个别名。用别名来替换比较复杂的全路径名
好处:将复杂的全路径可以一次性设置好,后续在任何xxxmapper.xml文件中要使用的时候,直接可以使用简单的别名即可。
别名的设置(在SqlMapConfig.xml配置文件中)
<!-- 设置一个别名,使用包扫描 -->
<typeAliases>
<!-- name:包名 自动扫描,定义别名,默认类名 -->
<package name="com.woniuxy.mall.model" />
</typeAliases>
Mapper映射关系配置(三种方式)
mapper.xml配置文件的方式
<mappers>
<mapper resource="mapper/CommodityMapper.xml"/>
</mappers>
以配置Mapper接口的方式进行配置
<!-- 加载mapper.xml文件 -->
<mappers>
<!--
class:对应接口的全路径名
要求:
对应的mapper.java接口文件和mapper.xml配置文件必须放在同一个包下面。
mapper.java接口文件和mapper.xml配置文件的名字必须要一致。
-->
<mapper class="com.woniuxy.mall.mapper.CommodityMapper"/>
</mappers>
以扫描包的形式进行配置
<!-- 加载mapper.xml文件 -->
<mappers>
<!--
name:mapper接口的包名
规则同上。
-->
<package name="com.woniuxy.mall.mapper"/>
</mappers>
自定义的POJO类(高级查询使用)
POJO类
自定义POJO类,一般指的是高级查询,在想要的数据和传入的条件不再一张表中,通过表所创建的失血模型model已经不能够满足传入参数的需求了,这个时候,就需要使用自定义的POJO类
(个人理解实质就是:在主要查询表的失血模型中添加其他表的失血模型。例如查询购物车表中的商品,在购物车表添加商品作为成员属性)
失血模型配置
public class shoppingCart {
private String shoppingCart_id;
private String user_id;
private String commodity_id;
private String number;
private String total;
//添加商品表
private Commodity commodity;
mapper.xml配置文件(使用resultMap)
<resultMap type="shoppingCart" id="shoppingCartAndCommodity">
<!--
id:在关联里面,能够主要唯一识别一个实体的声明
column:能够唯一识别一个实体的数据库字段名
property:和model里面相对应的属性名
-->
<id column="shoppingCart_id" property="shoppingCart_id" />
<!--
result:关联普通字段和属性的标签
column:数据库字段名
property:对应的model属性名
-->
<result column="user_id" property="user_id" />
<result column="commodity_id" property="commodity_id" />
<result column="number" property="number" />
<result column="total" property="total" />
<!--
collection:关联映射一对多的关系。属性是集合类型声明的
property:关联在Order表中的属性名称
-->
<collection property="commodityList" ofType="commodity">
<id column="commodity_id" property="commodity_id" />
</collection>
</resultMap>
<!-- 查询自己的购物车 -->
<select id="selectshoppingCart" resultMap="shoppingCartAndCommodity">
SELECT shoppingCart_id,user_id ,number,total,c.commodity_name FROM
t_shoppingcart s JOIN t_commodity c ON s.commodity_id = c.commodity_id WHERE user_id =
#{user_id};
</select>
Mapper.java
/*
* 查询自己的购物车。
*/
public List<shoppingCart> selectshoppingCart(String user_phon) throws Exception;
mybatis中输入映射和输出映射(重点)
输入映射
目前在mybatis用到的都是parameterType类型,其中可以是简单数据类型,也可以是包装类型(POJO)
parameterType:java.lang.Integer\java.lang.String\POJO模型类。
输出映射
resultType:
1.输出类型为输出的类型为POJO包装类(model失血模型),用的是resultType
2.输出的类型为简单数据类型,resultType,要求:查询的结果集必须是一行一列的数据
resultMap
自定义映射关系:当java中失血模型对象中的字段和数据库中的字段名称不一致的时候,可以使用resultMap方式进行自定义映射关系
resultType和resultMap区别
resultType:会增加一个扩展的模型类,但是,这样可以简化xml配置文件中的繁琐步骤。操作和单表查询操作一致,一对一的映射关系一般推荐使用resultType。
resultMap:需要有关联映射,需要自定义resultMap的配置信息,增加配置的难度,但是,可以用在一对多的关系映射中(较为复杂的关系映射)。
<mapper namespace="com.woniuxy.mall.mapper.CommodityMapper">
<!--商家添加商品 -->
<resultMap type="Commodity"
id="businessInformationAndCommodity">
<!--
id:在关联里面,能够主要唯一识别一个实体的声明
column:能够唯一识别一个实体的数据库字段名
property:和model里面相对应的属性名
-->
<id column="commodity_id" property="commodity_id" />
<!--
result:关联普通字段和属性的标签
column:数据库字段名
property:对应的model属性名
-->
<result column="commodity_name" property="commodity_name" />
<result column="commodity_price" property="commodity_price" />
<result column="commodity_number" property="commodity_number" />
<result column="classtwo_id" property="classtwo_id" />
<result column="businessInformation_id"
property="businessInformation_id" />
<result column="commodity_status" property="commodity_status" />
<result column="commodity_add_time"
property="commodity_add_time" />
<!--
association:用于关联一对一关系映射的标签
property:要映射的模型类中的属性名
javaType:属性名对应的java失血模型全路径,可以使用别名
-->
<association property="businessInformation" javaType="BusinessInformation">
<id column="businessInformation_id" property="businessInformation_id" />
<result column="businessInformation_phone" property="businessInformation_phone" />
</association>
</resultMap>
<insert id="insertCommodity" parameterType="Commodity">
INSERT INTO
t_commodity(commodity_name,
commodity_price,commodity_number,classtwo_id,businessInformation_id,commodity_status,commodity_add_time)
VALUES(#{0},#{1},#{2},#{3},
(SELECT businessInformation_id FROM t_businessinformation WHERE
businessInformation_phone=#{4}
),#{5},#{6});
</insert>
</mapper>
自定义映射关系:一对一,一对多,多对多(分解成一对多,一对多,一对一的关系)
<将商品添加到购物车 -->
<resultMap type="shoppingCart" id="shoppingCartAndCommodity">
<!--
id:在关联里面,能够主要唯一识别一个实体的声明
column:能够唯一识别一个实体的数据库字段名
property:和model里面相对应的属性名
-->
<id column="shoppingCart_id" property="shoppingCart_id" />
<!--
result:关联普通字段和属性的标签
column:数据库字段名
property:对应的model属性名
-->
<result column="user_id" property="user_id" />
<result column="commodity_id" property="commodity_id" />
<result column="number" property="number" />
<result column="total" property="total" />
<!-- 关联用户 -->
<!--
association:用于关联一对一关系映射的标签
property:要映射的模型类中的属性名
javaType:属性名对应的java失血模型全路径,可以使用别名
-->
<association property="user" javaType="User">
<!--
id:user对应的能够唯一识别的标签
column:对应数据库字段名
property:对应的模型类属性名
-->
<id column="user_id" property="user_id" />
<result column="user_phon" property="user_phon" />
</association>
<!-- 关联商品 -->
<!--
collection:关联映射一对多的关系。属性是集合类型声明的
property:关联在Order表中的属性名称
-->
<collection property="commodityList" ofType="commodity">
<id column="commodity_id" property="commodity_id" />
</collection>
</resultMap>
</resultMap>
<!-- 查询自己的购物车 -->
<!-- 查询自己的购物车 -->
<select id="selectshoppingCart" resultMap="shoppingCartAndCommodity">
SELECT shoppingCart_id ,(SELECT u.user_id FROM t_user u WHERE
u.user_phon=#{user_phon}),number,total,c.commodity_name FROM
t_shoppingcart s JOIN t_user u ON s.user_id = u.user_id JOIN
t_commodity c ON s.commodity_id = c.commodity_id WHERE u.user_phon =
#{user_phon};
</select>
延迟加载(按需加载)
什么是延迟加载
将一条复杂的查询语句,分为多个部分可以分别进行查询,一般以主表开始进行单表的查询数据,什么时候需要进一步的数据需求的时候,再向数据库发送请求。
目的:是为了提高查询效率,因为单表的查询比多表的查询效率要高的多,减轻数据库服务的压力
举例用户查询购物车
1.只需用户信息是,向数据库发出单表查询
2.进一步要求用户购物车信息,再进行延迟加载去查询用户关联的购物车信息,更具用户id查询
mapper.java接口
//查询用户查询购物车,并且使用延迟加载
public List<User> queryOrderAndUserLazy() throws Exception;
mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.woniuxy.mapper.LazyMapper">
<!--按需求查询查询购物车-->
<select id="queryUserById" resultType="shoppingCart">
SELECT * FROM t_shoppingcart WHERE user_id = #{user_id};;
</select>
<!-- 自定义resultMap -->
<resultMap type="User" id="UserAndshoppingCartLazyLoading">
<id column="user_id" property="user_id"/>
<result column="user_name" property="user_name"/>
<result column="user_phon" property="user_phon"/>
<result column="user_email" property="user_email"/>
<!-- 配置user对应的映射对象,要使用association标签,可以使用延迟加载-->
<association property="shoppingCart" javaType="shoppingCart" select="queryUserById" column="user_id">
</association>
</resultMap>
<!--1.先查询用户-->
<select id="queryUserAndshoppingLazy" resultMap="UserAndshoppingCartLazyLoading">
SELECT * FROM t_user;
</select>
</mapper>
在sqlMapConfig.xml配置文件中开启延迟记载
<!-- 设置延迟加载 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
缓存
缓存存在有意义
在mybatis中提供了两种缓存机制,当第一次执行方法要查询数据库中的值,先查询缓存空间是否存在,不存在则去数据库中查询,将查询到的值更新到缓存空间中。
注意
当在代码中如果使用了commit提交事务的操作,那么会将缓存空间里面清空,避免数据的脏读。
一级缓存
一级缓存的作用范围是一次会话,只要会话没有关闭,那么一级缓存就生效,一次会话(一次SqlSession)共享一个一级缓存空间。
第一次查询数据:第一次查询数据的时候,先去缓存空间,缓存空间中还没有值,所以需要去数据库中查找,将查找到的值放置到缓存空间中,供第二次方位直接拿数据。
如果中间任何位置提交了事务(commit),那么mybatis将清空缓存空间。避免脏读。
第二次查询相同的数据:会先检查缓存空间中是否有值,如果有值将不再请求数据库查询,而是直接拿到缓存空间中的数据。
注意:一级缓存根本不用程序去操心,mybatis默认开启一级缓存。
二级缓存(多个会话共用一个二级缓存)
在一个Mapper层面的多个会话,都执行同样的查询语句和结果的时候,第一次需要从数据库中读取,将读取的数据存放的二级缓存区域中,当第二次去查询相同的语句的时候,那么直接拿到缓存空间的值,不再次向数据库发送请求。一旦发生了commit事务的提交,则会清空二级缓存的值。
注意:mybatis不会默认开启二级缓存,需要程序员手动开启二级缓存
测试
步骤1:在SqlMapConfig.xml配置文件中设置二级缓存。
<setting name="cacheEnabled" value="true"/>
步骤2:在对应的mapper中也要开启二缓存。
<!-- 开启二级缓存 -->
<cache/>
步骤3:要对mapper操作的对应的失血模型model对象实现序列化。
public class User implements Serializable{
}
测试代码
@Test
public void test2() throws Exception {
//开启多个会话
SqlSession ss1 = factory.openSession();
SqlSession ss2 = factory.openSession();
SqlSession ss3 = factory.openSession();
//使用不同的会话产生Mapepr对象
CacheMapper cm1 = ss1.getMapper(CacheMapper.class);
User u1 = cm1.queryUserById(1);
//关闭第一个会话
ss1.close();
CacheMapper cm2 = ss2.getMapper(CacheMapper.class);
User u2 = cm2.queryUserById(1);
//关闭第二个会话
ss2.close();
CacheMapper cm3 = ss3.getMapper(CacheMapper.class);
System.out.println("u1:"+u1);
System.out.println("u2:"+u2);
//关闭第三个会话
ss3.close();
}