Mybatis是什么?
mybatis是一个封装jdbc的持久层框架,属于ORM框架。它让我们只关注sql本身,而不需要去关注 如:连接的创建、statement的创建等操作。
Mybatis框架原理
image.pngmybatis入门
目录结构
image.png准备数据库
create database MIng
USE MIng;
CREATE TABLE category_ (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(32) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
USE MIng;
INSERT INTO category_ VALUES (1,'ZZM');
INSERT INTO category_ VALUES (2,'WYQ');
//pojo
//用于映射数据库表category_
public class Category {
private int id;
private String name;
//set get..
SqlMapConfig.xml(mybatis配置文件)
<?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="com.pojo"/>
</typeAliases>
<!--配置环境-->
<environments default="development">
<!--环境变量-->
<environment id="development">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/MIng?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
<!--mapper-->
<mappers>
<mapper resource="mapper/Category.xml"/>
</mappers>
</configuration>
//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.pojo">
<!--因为配置了别名,所以resultType可以直接写类名-->
<select id="listCategory" resultType="Category"> //pojo
select * from category_
</select>
</mapper>
//测试
public class TestMybatis {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
//listCategory就是mapper.xml的sql语句的id
List<Category> cs = session.selectList("listCategory");
for (Category c : cs) {
System.out.println(c.getName());
}
}
}
mybais的增删改查(CRUD)
//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.pojo.Category">
<!--因为配置了别名,所以resultType可以直接写类名-->
<select id="listCategory" resultType="Category">
select * from category_
</select>
<!--增(C)-->
<insert id="addCategory" parameterType="Category">
INSERT INTO category_(name) VALUES (#{name})
</insert>
<!--删(D)-->
<delete id="deleteCategory" parameterType="Category">
DELETE FROM category_ WHERE id = #{id}
</delete>
<!--更(U)-->
<update id="updateCategory" parameterType="Category">
UPDATE category_ SET name=#{name} WHERE id=#{id}
</update>
<!--读(R)-->
<select id="getCategory" parameterType="int" resultType="Category">
SELECT * FROM category_ WHERE id=#{id}
</select>
</mapper>
//Test
public class TestMybatis {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
//增
Category add = new Category();
add.setName("新增");
session.insert("addCategory", add);
//删
Category delete = new Category();
delete.setId(7);
session.delete("deleteCategory", delete);
//更
Category update = session.selectOne("getCategory",11);
update.setName("把id=11对象更新成功");
session.update("updateCategory",update);
//查
Category category = session.selectOne("getCategory",3);
System.out.println(category.getName());
//listCategory就是mapper.xml的sql语句的
//查询所有数据
List<Category> cs = session.selectList("listCategory");
for (Category s : cs) {
System.out.println(s.getName());
}
}
}
Mybatis模糊查询
模糊查询
SQL模糊查询,使用like比较关键字,加上SQL里的通配符,请参考以下:
1、LIKE'Mc%' 将搜索以字母 Mc 开头的所有字符串(如 McBadden)。
2、LIKE'%inger' 将搜索以字母 inger 结尾的所有字符串(如 Ringer、Stringer)。
3、LIKE'%en%' 将搜索在任何位置包含字母 en 的所有字符串(如 Bennet、Green、McBadden)。
4、LIKE'_heryl' 将搜索以字母 heryl 结尾的所有六个字母的名称(如 Cheryl、Sheryl)。
5、LIKE'[CK]ars[eo]n' 将搜索下列字符串:Carsen、Karsen、Carson 和 Karson(如 Carson)。
6、LIKE'[M-Z]inger' 将搜索以字符串 inger 结尾、以从 M 到 Z 的任何单个字母开头的所有名称(如 Ringer)。
7、LIKE'M[^c]%' 将搜索以字母 M 开头,并且第二个字母不是 c 的所有名称(如MacFeather)。
SELECT 字段 FROM 表 WHERE 某字段 Like 条件
concat() 函数,是用来连接字符串。
精确查询: select * from user where name=”zhangsan”
模糊查询; select * from user where name like “%zhang%”
在实际的使用中,条件是作为参数传递进来的。 所以我们使用 concat() 函数连接字符串
select * from user where name like concat(“%”, #{name},”%”)
concat(str1,str2,str3,str4,……….); 连接字符串函数,会生成一个字符串
<!--模糊查询:单条件-->
<select id="CategoryByName" resultType="Category">
SELECT * FROM category_ WHERE name LIKE concat("%",#{name},"%");
</select>
//模糊查询
List<Category> byname = session.selectList("CategoryByName","cat");
for (Category s : byname) {
System.out.println(s.getName());
}
//输出数据库所有有cat的数据
//多条件模糊查询
/**
* (多条件模糊:因为是多个参数,而selectList方法又只接受一个参数对象,
* 所以需要把多个参数放在Map里,然后把这个Map对象作为参数传递进去
*/
Map<String, Object> params = new HashMap<>();
params.put("id", 3);
params.put("name", "cat");
List<Category> cs = session.selectList("CategoryByIdAndName", params);
for (Category s : cs) {
System.out.println(s.getName());
}
Mybatis高级映射
resultType:查询到的列名和resultType指定的pojo的属性名一致,才能映射成功。
reusltMap:可以通过resultMap 完成一些高级映射。
如果查询到的列名和映射的pojo的属性名不一致时,通过resultMap设置列名和属性名之间的对应关系(映射关系)。可以完成映射。
高级映射:
将关联查询的列映射到一个pojo属性中。(一对一)
将关联查询的列映射到一个List<pojo>中。(一对多)
订单商品数据模型
用户表 : user 记录了购买商品的用户信息
订单表:orders 记录了用户所创建的订单(购买商品的订单)
订单明细表:orderdetail 记录了订单的详细信息即购买商品的信息
商品表:items 记录了商品信息
表与表之间的业务关系:
在分析表与表之间的业务关系时需要建立 在某个业务意义基础上去分析。
先分析数据级别之间有关系的表之间的业务关系:
usre和orders:
user---->orders:一个用户可以创建多个订单,一对多
orders--->user:一个订单只由一个用户创建,一对一
orders和orderdetail:
orders--->orderdetail:一个订单可以包括 多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系
orderdetail--> orders:一个订单明细只能包括在一个订单中,一对一
一对多
//数据库表结构
use how2java;
create table product_(
id int NOT NULL AUTO_INCREMENT,
name varchar(30) DEFAULT NULL,
price float DEFAULT 0,
cid int ,
PRIMARY KEY (id)
)AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
//导入数据
use how2java;
delete from category_;
INSERT INTO category_ VALUES (1,'category1');
INSERT INTO category_ VALUES (2,'category2');
delete from product_;
INSERT INTO product_ VALUES (1,'product a', 88.88, 1);
INSERT INTO product_ VALUES (2,'product b', 88.88, 1);
INSERT INTO product_ VALUES (3,'product c', 88.88, 1);
INSERT INTO product_ VALUES (4,'product x', 88.88, 2);
INSERT INTO product_ VALUES (5,'product y', 88.88, 2);
INSERT INTO product_ VALUES (6,'product z', 88.88, 2);
分类和产品的一对多关系
//pojo.Product
private int id;
private String name;
private float price;
//get set...
//pojo.Category
private int id;
private String name;
List<Product> products;
//get set...
<!--一对多-->
什么是一对多?
一个用户对应多个订单,而一个订单只能对应一个用户
<!-- id:指定查询列中的唯一标识,订单信息的中的唯一标识,如果有多个列组成唯一标识,配置多个id
column:订单信息的唯一标识列
property:订单信息的唯一标识列所映射到Orders中哪个属性
-->
<resultMap id="categoryBean" type="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
<!-- property: 指的是集合属性的值, ofType:指的是集合中元素的类型 -->
<collection property="products" ofType="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
</collection>
</resultMap>
<!-- 关联查询分类和产品表 -->
<select id="listCategory2" resultMap="categoryBean">
select p.price 'price', c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
</select>
//Test
List<Category> cs = session.selectList("listCategory2");
for (Category c : cs) {
System.out.println(c);
List<Product> ps = c.getProducts();
for (Product p : ps) {
System.out.println("\t"+p);
输出:
Category [id=1, name=category1]
Product [id=1, name=product a, price=88.88]
Product [id=2, name=product b, price=88.88]
Product [id=3, name=product c, price=88.88]
Category [id=2, name=category2]
Product [id=4, name=product x, price=88.88]
Product [id=5, name=product y, price=88.88]
Product [id=6, name=product z, price=88.88]
多对一
什么是多对一?
多个订单表可以对应一个用户,一个用户是可以拥有多个订单的。
public class Product {
private int id;
private String name;
private float price;
private Category category;
//get和set..
//product.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.pojo">
<resultMap id="productBean" type="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
<!--多对一-->
<association property="category" javaType="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</association>
</resultMap>
<!--根据id查询product 关联orders查询-->
<select id="listProduct3" resultMap="productBean">
select p.price'price', c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
</select>
</mapper>
//mybatis配置文件
<mapper resource="mapper/Product.xml"/>
//test
List<Product> ps = session.selectList("listProduct3");
for (Product p : ps) {
System.out.println(p+" 对应的分类是 \t "+ p.getCategory());
}
多对多
什么是多对多呢?
一个订单可以有多种商品。
一种商品可以在多个订单里。
为了维系多对多的关系,必要有有中间表。
本例以订单Order和产品Product为例,使用订单项OrderItem表来作为中间表。
//数据库表结构
create table order_ (
id int(11) NOT NULL AUTO_INCREMENT,
code varchar(32) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
create table order_item_(
id int(11) NOT NULL AUTO_INCREMENT,
oid int ,
pid int ,
number int ,
PRIMARY KEY(id)
)AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
//导入数据
//两条订单
INSERT INTO order_ VALUES (1,'code000A');
INSERT INTO order_ VALUES (2,'code000B');
//插入六条订单项数据
INSERT INTO order_item_ VALUES (null, 1, 1, 100);
INSERT INTO order_item_ VALUES (null, 1, 2, 100);
INSERT INTO order_item_ VALUES (null, 1, 3, 100);
INSERT INTO order_item_ VALUES (null, 2, 2, 100);
INSERT INTO order_item_ VALUES (null, 2, 3, 100);
INSERT INTO order_item_ VALUES (null, 2, 4, 100);
//实体类
//Order
public class Order {
private int id;
private String code;
List<OrderItem> orderItems;
//get set...
//OrderItem
public class OrderItem {
private int id;
private int number;
private Order order;
private Product product;
//get set...
order.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.pojo">
<resultMap id="orderBean" type="Order">
<id column="oid" property="id"/>
<result column="code" property="code"/>
<collection property="orderItems" ofType="OrderItem">
<id column="oiid" property="id"/>
<result column="number" property="number"/>
<association property="product" javaType="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
</association>
</collection>
</resultMap>
<!--联合order_, order_item_, product_ 三张表进行查询-->
<select id="listOrder" resultMap="orderBean">
select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
from order_ o
left join order_item_ oi on o.id =oi.oid
left join product_ p on p.id = oi.pid
</select>
<select id="getOrder" resultMap="orderBean">
select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
from order_ o
left join order_item_ oi on o.id =oi.oid
left join product_ p on p.id = oi.pid
where o.id = #{id}
</select>
</mapper>
//Product.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.pojo">
<resultMap id="productBean" type="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
<!--多对一-->
<association property="category" javaType="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</association>
</resultMap>
<!--根据id查询product 关联orders查询-->
<select id="listProduct3" resultMap="productBean">
select p.price'price', c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
</select>
</mapper>
//mybatis.xml新增
<mapper resource="mapper/Order.xml"/>
//Test
List<Order> os = session.selectList("listOrder");
for (Order o : os) {
System.out.println(o.getCode());
List<OrderItem> ois = o.getOrderItems();
for (OrderItem oi : ois) {
System.out.format("\t%s\t%f\t%d%n", oi.getProduct().getName(), oi.getProduct().getPrice(), oi.getNumber());
//输出:
code000A
product a 88.879997 100
product b 88.879997 100
product c 88.879997 100
code000B
product b 88.879997 100
product c 88.879997 100
product x 88.879997 100
动态 SQL
MyBatis 的强大特性之一便是它的动态 SQL。
·if
·choose (when, otherwise)
·trim (where, set)
·foreach
if
if是mybatis动态SQL中的判断元素,这个有点类似于Java中的if语句,不同的是这里的if一般常常和test配合使用。我们来看一个简单的例子:
<select id="getUser" parameterType="String">
select * from user
<if test="address!=null" >
WHERE address LIKE concat('%',#{address},'%')
</if>
</select>
当用户传入的address不为null的时候,就加上一个where条件,否则就什么条件都不加入。
choose
choose有点类似于Java中的switch,常常配合when和otherwise一起来使用。我们来看一个简单的例子:
<select id="getUser" resultMap="u">
SELECT * FROM user2 WHERE 1=1
<choose>
<when test="id!=null">
AND id=#{id}
</when>
<when test="address!=null">
AND address=#{address}
</when>
<when test="username!=null">
AND user_name LIKE concat(#{username},'%')
</when>
<otherwise>
AND 10>id
</otherwise>
</choose>
</select>
在查询条件中,如果用户传来了id,那么我就查询该id的数据,如果用户传来了address,那么我就我们添加address的查询条件,如果用户传来了username,那么我就添加username的查询条件,最后如果用户任何一个查询条件都没有添加进来,那么默认查询条件就是查询id小于10的所有数据。
where 1=1是sql语句条件逻辑判断表达式,由于1=1成立,永为真,该表达式1=1将始终返回"真"。举个例子:
1)select * from t1 where 1=1;
-- 实际等效于select * from t1 where true;
-- 语句将返回t1中所有的记录行
2)select * from t1 where 1<>1;
-- 实际等效于 select * from t1 where false;
-- 语句将返回空记录集
where
在上面的案例中小伙伴们可能都发现了一个问题,就是我们在添加查询条件的时候,在查询条件之前都先添加了where 1=1,然后后面直接在这之后再追加and什么什么的,那么每次这样来写显然有点麻烦,有没有简单一点的方案呢?当然有,我们可以通过where元素,如下:
<select id="getUser3" resultMap="u">
SELECT * FROM user2
<where>
<choose>
<when test="id!=null">
AND id=#{id}
</when>
<when test="address!=null">
AND address=#{address}
</when>
<when test="username!=null">
AND user_name LIKE concat(#{username},'%')
</when>
<otherwise>
AND 10>id
</otherwise>
</choose>
</where>
</select>
这样,只有where元素中有条件成立,才会将where关键字组装到SQL中,这样就比前一种方式简单许多。
trim
trim有点元素替换的意思,还是上面的案例,我们可以将and替换为where,如下:
<select id="getUser4" resultMap="u">
SELECT * FROM user2
<trim prefix="where" prefixOverrides="and">
AND id=1
</trim>
</select>
这个最终执行的sql是SELECT * FROM user2 where id=1。
set
set是我们在更新表的时候使用的元素,通过set元素,我们可以逐字段的修改一条数据,如下:
<update id="update">
UPDATE user2
<set>
<if test="username!=null">
user_name=#{username},
</if>
<if test="password!=null">
password=#{password}
</if>
</set>
WHERE id=#{id}
</update>
在set元素中,如果遇到了逗号,系统会自动将之去除。
foreach
foreach元素用来遍历集合,比如我想查询多个城市的人,我的sql语句可能是这样SELECT * FROM user2
WHERE address IN('西安','北京'),我在查询的时候可能只是传入了一个list集合,该集合中有西安和北京两个查询条件,那我如何将这个集合组装成一个sql语句呢?很简单,如下:
<select id="getUserInCities" resultMap="u">
SELECT * FROM user2
WHERE address IN
<foreach collection="cities" index="city" open="(" separator="," close=")" item="city">
#{city}
</foreach>
</select>
collection表示传入的参数中集合的名称,index表示是当前元素在集合中的下标,open和close则表示如何将集合中的数据包装起来,separator表示分隔符,item则表示循环时的当前元素。这样一段配置最终组合成的sql就是SELECT * FROM user2 WHERE address IN('西安','北京')。
注解方式(CRUD)
注解方式其实就是把SQL语句从XML挪到了注解上。
//本例先创建CategoryMapper接口。
public interface CategoryMapper {
@Insert(" insert into category_ ( name ) values (#{name}) ")
public int add(Category category);
@Delete(" delete from category_ where id= #{id} ")
public void delete(int id);
@Select("select * from category_ where id= #{id} ")
public Category get(int id);
@Update("update category_ set name=#{name} where id=#{id} ")
public int update(Category category);
@Select(" select * from category_ ")
public List<Category> list();
//在这里可能有个疑问,实体类是怎么对应mapper接口的呢?
//举个例子
@Select(" select * from category_ ")
public List<Category> list();
这个方法会返回泛型是Category的集合,Mybatis就知道把数据查出来之后,放进Category对象了。
//还有一个疑问,为什么mapper是一个接口,为什么没有实现类呢?
答:这个是由mybatis采用动态代理技术临时创建的实现类。
}
//mapper.xml
<mapper class="com.mapper.CategoryMapper"/>
//Test
mapper接口
CategoryMapper mapper = session.getMapper(CategoryMapper.class);
private static void update(CategoryMapper mapper) {
Category c= mapper.get(8);
c.setName("修改了的Category名稱");
mapper.update(c);
listAll(mapper);
}
事务管理
事务:保证操作的一致性,要么操作同时成功,要么同时失败;
最经典的例子就是转账:A向B转账,如果转账成功,那么必然A的钱减少,B的钱增多;如果转账失败,那么必然是A和B的余额都没有发生变化;这个例子就用到了事务操作;
Mybatis管理事务是分为两种方式:
(1)使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交
(2)使用MANAGED的事务管理机制,这种机制mybatis自身不会去实现事务管理,而是让程序的容器(JBOSS,WebLogic)来实现对事务的管理
如果开启MyBatis事务管理,则需要手动进行sqlSession.commit()事务提交,否则事务会回滚到原状态;只有执行了commit()事务提交方法才会真正完成操作;
SqlMapConfig.xml(mybatis配置文件)
<!--事务管理器-->
<transactionManager type="JDBC"/>
Mybatis提供了一个事务接口Transaction,以及两个实现类jdbcTransaction和ManagedTransaction,当spring与Mybatis一起使用时,spring提供了一个实现类SpringManagedTransaction
Transaction接口:提供的抽象方法有获取数据库连接getConnection,提交事务commit,回滚事务rollback和关闭连接close
SpringManagedTransaction实现类:它其实也是通过使用JDBC来进行事务管理的,当spring的事务管理有效时,不需要操作commit/rollback/close,spring事务管理会自动帮我们完成
Category c1 = new Category();
c1.setName("短的名称");
mapper.add(c1);
Category c2 = new Category();
c2.setName("超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称");
mapper.add(c2);
listAll(mapper);
session.commit();
session.close();
private static void listAll(CategoryMapper mapper) {
List<Category> cs = mapper.list();
for (Category c : cs) {
System.out.println(c.getName());
}
插入第二个数据会因为长度超过32,而导致无法插入到数据库。
因为在同一个事务里,所以第一个数据,也无法插入成功。
在Mysql中,只有当表的类型是INNODB的时候,才支持事务,所以需要把表category_的类型设置为INNODB,否则无法观察到事务.
修改表的类型为INNODB的SQL:alter table category_ ENGINE = innodb;
延迟加载
什么是延迟加载?
resultMap可实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
需求:如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。(登录时候就只加载用户名,用户需要查看或者修改资料时候 再加载详情)
延迟加载:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
使用association实现延迟加载
1、需求:查询订单并且关联查询用户信息
2、mapper.xml
需要定义两个mapper的方法对应的statement。
在查询订单的statement中使用association去延迟加载(执行)下边的statement。
SqlMapConfig.xml(mybatis配置文件)
<configuration>
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 将积极加载改为消息加载即按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
....
1) 只查询订单信息 SELECT * FROM orders
<!-- 查询订单关联查询用户,用户信息需要延迟加载 -->
<select id="findOrdersUserLazyLoading" resultMap="OrderUserLazyLoadingResultMap">
SELECT * FROM orders
</select>
2) 关联查询用户信息
通过上边查询到的订单信息中user_id去关联查询用户信息
使用UserMapper.xml中的findUserById
<select id="findUserById" resultType="user" parameterType="int">
SELECT * FROM USER WHERE id=#{value}
</select>
3) 执行思路:
上边先去执行findOrdersuserLazyLoading,当需要的时候再去执行findUserById,通过resultMap的定义将延迟加载执行配置起来。
延迟加载的resultMap
<resultMap type="cn.itcast.mybatis.po.Orders" id="OrderUserLazyLoadingResultMap">
<!-- 对订单信息进行映射配置 -->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 对用户信息进行延迟加载 -->
<!--
select:指定延迟加载要执行的statement的id(是根据user_id查询用户信息是statement)
要使用UserMapper.xml中findUserById完成根据用户id(user_id)对用户信息的查询,如果findUserById不在本mapper中,
需要前边加namespace
column:订单信息中关联用户信息的列,是user_id
-->
<association property="user" javaType="cn.itcast.mybatis.po.User"
select="cn.itcast.mybatis.mapper.UserMapper.findUserById" column="user_id">
</association>
</resultMap>
使用association中是select指定延迟加载去执行的statement的id。
总结:使用延迟加载方法,先去查询简单的sql(最好单表,也可关联查询),再去按需加载关联查询的其他信息。
一级缓存
基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
Mybatis的一级缓存在session上,只要通过session查过的数据,都会放在session上,下一次再查询相同id的数据,都直接冲缓存中取出来,而不用到数据库里去取了。
//Test
SqlSession session1 = sqlSessionFactory.openSession();
Category c1 = session1.selectOne("getCategory", 1);
System.out.println(c1);
Category c2 = session1.selectOne("getCategory", 1);
System.out.println(c2);
二级缓存
Mybatis二级缓存是SessionFactory,如果两次查询基于同一个SessionFactory,那么就从二级缓存中取数据,而不用到数据库里去取了。
对上面的一级缓存进行
SqlMapConfig.xml(mybatis配置文件)
//开启二级缓存
<setting name="cacheEnabled" value="true"/>
在Category.xml中增加 <cache/>
//例如
<mapper namespace="com.pojo">
</cache>//启动对Category对象的二级缓存
<select id = "" parameterType="">
select ....
</select>
序列化Category
public class Category implements Serializable{
private int id;
private String name;
}
再次运行TestMybatis,在同一个SessionFactory下查询id=1的数据,只有第一次需要执行sql语句,以后都是从缓存中取出。
网友评论