从来就没有救世主  也不靠神仙皇帝  要创造人类的幸福  全靠我们自己  

mybatis小结

1. mybatis

  一个ORM--对象关系映射 框架,内部封装了JDBC,使得开发时只需要关注SQL语句本身,不需要关注加载驱动、获取连接、创建Statement等对象的过程

  可通过xml或注解来配置和映射关系,将实体类和数据库记录对应起来,避免了繁杂的JDBC代码和设置参数、获取结果集并封装等过程

 

2. 编写sql时 #{} 和 ${}

  #{} 是编译预处理,而${}是字符串替换

  mybatis会将 #{}替换为 ? ,并通过PreparedStatement的方法来设置值,可避免sql注入

 

3.  实体类的属性名字和数据库表中字段名字不一致

(1)查询的sql语句中字段定义别名,别名与实体类属性名一致

 

(2)映射器查询标签里面 返回值类型为 resultMap

  在resutMap里面建立实体类属性和表字段的对应关系

<select id="findUser" parameterType="string" resultMap="userInfoResMap">
    select * from users where id = #{id}
</select>

<resultMap id="userInfoResMap" type="xx.xx.xxx.User">
    <id column="user_id" property="userId" />
    <result column="create_time" property="createTime"/>
    <result column="note" property="note"/>
</resultMap>

  column为表字段名字,而property为实体类属性名字

 

4. 模糊查询

(1)java代码中将参数加上 % ,即硬编码

String str = "xt";
String name = "%"+str+"%";
xxx.findByName(value);

<select id="findByName">
    select * from users where name like ${name}
</select>

 

(2)在sql语句中写

  不能在sql语句中直接拼接通配符,即不能:

<select id="xxx">
    select * from users where name like '%'${name}'%'
</select>

 

  正确写法:①用 concat( ) 函数拼接  ②用 #{}代替${}

<select id="xxx">
    select * from users where name like concat('%',#{name},'%')
</select>

  如果 name 里面有sql语句,concat也会将name里面的内容视作字符串与% 合成一个字符串,不会解析为sql执行

 

<select id="xxx">
    select * from users where name like '%' #{} '%'
</select>

  #{}之后会用PreparedStatement来执行sql,也可防止注入

 

5. 映射文件与dao接口

  接口的全限定名是映射文件的 namespace 属性值

  接口的方法名是映射文件中 Mapper下的标签的id值

 

  Mybatis中,每个select、insert、update、delete标签,会被解析为一个MapperStatement对象,并通过 namespace和标签id唯一定位

 

  接口里面的方法不能重载,因为 只能通过 namespace+方法名 唯一确定一个标签

 

  实现:只有dao接口,而没有实现类,mybatis通过JDK动态代理为Mapper接口生成代理对象proxy

 

6. 分页

  6.1 物理分页与逻辑分页

(1)物理分页

  查询 从offset条起,后面的共rows条记录(offset从0开始)

SELECT * FROM table1 LIMIT offset,rows  

 

(2)逻辑分页

  RowBounds分页:一次性查出很多记录,然后再这些数据中进行分页查询

 

  RowBounds查询数据:

    jdbc驱动中有 Fetch Size 参数,表示每次最多从数据库中查询多少条数据,如果超过了这个数,实际上jdbc自动去再执行了查询。

    Mybatis实际上通过多次查询查询出所有数据,然后我们在RowBounds上进行分页查询

    如果数据很多,使用RowBounds查询所有数据会有内存溢出风险。所以RowBounds适合数据量不大的查询。

 

(3)使用RowBounds

  原来的接口:

public List<User> findUserByName(String name);

  对应的sql--mapper:

<mapper namespace="com.xt.dao.UserDao">
    <select id="findUserByName" resultType="com.xt.domain.User">
        select * from user where userName like concat('%',#{name},'%')
    </select>
</mapper>

  使用:

UserDao dao = sqlSession.getMapper(UserDao.class);
List<User> list = dao.findUserByName("name1");

 

  使用RowBounds的接口:

public List<User> findUserByName(String name,RowBounds rowbounds);

  sql--mapper不变

 

  使用:

UserDao dao = sqlSession.getMapper(UserDao.class);
List<User> list = dao.findUserByName("name1",new RowBounds(0,5)); //查询 0,1,2,3,4

 

  6.2 分页插件

 

 

7. sql结果如何封装为目标对象返回

  sql使用字段别名或通过resultMap建立实体类属性与表字段的关系,在查询标签里可通过 resultType,在resultMap里面可通过type属性指定实体类的位置。mybatis通过反射创建对象,然后根据属性与字段的关系对对象的属性逐一赋值

 

8. 批量插入

(1)方式一

InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession(ExecutorType.BATCH);
UserDao userDao = session.getMapper(UserDao.class);
List<String> names = new ArrayList<String>();
names.add("name1");
names.add("name2");
names.add("name3");
for(String name:names) {
//  int x = userDao.insertNames(name);
    int x = userDao.insertNames2(name);
    System.out.println(x);
}

  映射器:

<insert id="insertNames2">
        insert into testBatch2(name) values(#{name})
</insert>

  框架来实现批量插入:ExecutorType.BATCH

  效果对比:通过mybatis自带的日志输出

    不带BATCH的:                                                                                                                        带BATCH的:

              

    非批量插入,产生了三条sql,且每次都能获取insert的返回值为1                                             批量插入,只有一条sql,但不能直接获取insert的返回值

 

(2)方式2

  方式一是对此session下所有操作都改为批量插入,方式二通过改变映射器中sql写法来实现批量插入(foreach标签)

InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();                            //这里没有ExecutorType.BATCH
UserDao userDao = session.getMapper(UserDao.class);
List<String> names2 = new ArrayList<>();
names2.add("name1");
names2.add("name2");
names2.add("name3");
System.out.println(userDao.insertNames3(names2));

  映射器写法:

<insert id="insertNames3">
    insert into testBatch2(name) values
    <foreach collection="list" item="name" separator=",">
        (#{name})
    </foreach>
</insert>

  效果:

  

 

 

 

9.  主键的生成和返回(mysql、oracle)

  9.1 插入数据时的主键id如何产生

  oracle和mysql

    ①mysql可以设置主键为 auto_increment,插入数据时插入null或者不给此字段赋值即可

    ②oracle通过序列值作为主键;mysql也可以使用 uuid 来作为主键id

    对比:自增主键可能仅适用于部分数据库,但id是递增的,便于排序、查找

         uuid 更通用,但不利于排序、查找,且空间消耗大一些

  9.2 如何返回插入的主键id值

(1)准备两张表:表testPriKey使用 uuid、表testPriKey2使用自增主键

    

 

(2)准备实体类

package com.xt.domain;

public class Name {
    private String id;
    private String name;

    public Name(String id,String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

(3)使用uuid

InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

SqlSession session = factory.openSession();
UserDao userDao = session.getMapper(UserDao.class);
//通过代理对象调用方法
List<Name> names = new ArrayList<Name>();
names.add(new Name("","name1"));
names.add(new Name("","name2"));
names.add(new Name("","name3"));
for(Name name:names) {
    int x = userDao.insertNames(name);
}
//查看返回的主键id
for(Name name:names) {
    System.out.println(name.getId());
}

session.commit();
//session.close();
is.close();

  映射器:

<insert id="insertNames" parameterType="com.xt.domain.Name">
    <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
        select replace(uuid(),'-','')
    </selectKey>
    insert into testPriKey(id,name) values(#{id},#{name})
</insert>

  获取id:在insert执行之前(before),查询uuid,这个uuid会写入到 keyProperty上(即Name对象的id属性上),则接下来的insert就能通过 #{id} 获取唯一主键了

  返回id:此时List里面,每个Name对象的id字段就有值了

  效果:

  

 

 

(4)使用自增主键

InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

SqlSession session = factory.openSession();
UserDao userDao = session.getMapper(UserDao.class);
//通过代理对象调用方法
List<Name> names = new ArrayList<Name>();
names.add(new Name("","name1"));
names.add(new Name("","name2"));
names.add(new Name("","name3"));
for(Name name:names) {
    int x = userDao.insertNamess(name);
    System.out.println(x);
}
/*查看返回的主键id*/
for(Name name:names) {
    System.out.println(name.getId());
}

session.commit();
//session.close();
is.close();

  映射器:

<insert id="insertNamess" parameterType="com.xt.domain.Name">
    insert into testPriKey2(name) values(#{name})
    <selectKey keyProperty="id" order="AFTER" resultType="string">  <!--实体属性id是String类型的-->
        select LAST_INSERT_ID()
    </selectKey>
</insert>

  返回id:在执行每条insert之后(AFTER),通过 select LAST_INSERT_ID() 获取此id值

  效果:

    

 

 

   

 

 

10.  在mapper中传递多个参数

  准备表:

    

 

 

(1)方式一

  表有n个字段需要插值,则方法参数就有n个;并通过@Param注解表明形参和表字段名的关系

//接口声明
int addPerson(@Param("name")String personName, @Param("age") int personAge);

//测试方法
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
PersonDao personDao = session.getMapper(PersonDao.class);
//通过代理对象调用方法
personDao.addPerson("name1",10);
session.commit();
//session.close();
is.close();

  映射器:

<insert id="addPerson">
    insert into testTable1(name,age) values(#{name},#{age})
</insert>

 

(2)方式二

  参数很多时,全部放到Map里面

//接口
int addPerson2(Map<String,String> map);

InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
PersonDao personDao = session.getMapper(PersonDao.class);
//通过代理对象调用方法
Map<String,String> map = new HashMap<>();
map.put("name","name2");
map.put("age","20");
personDao.addPerson2(map);
session.commit();
//session.close();
is.close();

  映射器:指定参数为map

<insert id="addPerson2" parameterType="map">
    insert into testTable1(name,age) values(#{name},#{age})
</insert>

 

(3)方式三

  通过实体类

  实体类:

package com.xt.domain;

public class Person {
    private String personName;
    private int personAge;

    public Person(String personName, int personAge) {
        this.personName = personName;
        this.personAge = personAge;
    }

    public String getPersonName() {
        return personName;
    }

    public void setPersonName(String personName) {
        this.personName = personName;
    }

    public int getPersonAge() {
        return personAge;
    }

    public void setPersonAge(int personAge) {
        this.personAge = personAge;
    }
}

  

//接口
int addPerson3(Person person);

InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
PersonDao personDao = session.getMapper(PersonDao.class);
//通过代理对象调用方法
Person person = new Person("name3",30);
personDao.addPerson3(person);
session.commit();
//session.close();
is.close();

  映射器:参数类型为该实体类

<insert id="addPerson3" parameterType="com.xt.domain.Person">
    insert into testTable1(name,age) values(#{personName},#{personAge})
</insert>

 

 

11.  动态sql、原理

  原理:根据表达式的值完成逻辑判断,然后动态拼接sql

  11.1 if

  例:模糊查询,传入的username可能传了空,则需要判断入参值然后决定是否拼sql

<select id="findUser" parameterType="string" resultTypeMap="userResultMap">
    select user_no,user_name,note from t_user where 1 = 1
    <if test="username != null and username != ''">
        and user_name like concat('%',#{username},'%')
    </if>
</select>

 

  11.2  choose、when、otherwise

<select id="findUser" parameterType="User" resultTypeMap="userResultMap">
    select user_no,user_name,note from t_user where 1 = 1
    <choose>
        <when test="userNo != null and userNo != ''">
            AND user_no = #{userNo}
        </when>
        <when test="userName != null and userName != ''">
            AND user_name like concat('%',#{userName},'%')
        </when>
        <otherwise>
            AND note is not null
        </otherwise>
    </choose>
</select>

 

  11.3 trim、where、set

(1)where:where里面的元素的条件成立时,生成 where 关键字组成sql(这样就不用写 where 1 = 1了)

  而且,如果里面语句开头是 AND、OR,也会去掉(比如这里前面的sex为空,而username不为空,则去掉and),然后组成正确的SQL

<select id="findUser" parameterType="string" resultTypeMap="userResultMap">
    select user_no,user_name,note from t_user 
    <where>
        <if test="sex != null and sex != ''">
            sex like concat('%',#{sex},'%')
        </if>
        <if test="username != null and username != ''">
            and user_name like concat('%',#{username},'%')
        </if>
    </where>
</select>

 

(2)trim

  trim可以自定义元素的功能

  ①自定义上面的where元素:

<select id="findUser" parameterType="string" resultTypeMap="userResultMap">
    select user_no,user_name,note from t_user 
    <trim prefix="WHERE" prefixOverride="AND | OR">
        <if test="sex != null and sex != ''">
            sex like concat('%',#{sex},'%')
        </if>
        <if test="username != null and username != ''">
            and user_name like concat('%',#{username},'%')
        </if>
    </trim>
</select>

  trim里面的子元素不是空的的时候,插入 prefix(即 WHERE);如果这里第一个条件是空的而第二个不是空,则去掉 prefixOverride(即这里的and)形成正确的sql

 

  ②自定义下面的set元素

<update id="xx">
    update table1
    <trim prefix="SET" suffixOverrides=",">
        <if test="username != null and username != ''">username=#{username},</if>
        <if test="sex != null and sex != ''">sex=#{sex},</if>
        <if test="addr != null and addr != ''">address=#{addr},</if>
    </trim>
    where id = #{id}
</update>

 

(3)set

  上面的where、trim可用来动态拼接条件,而set可用在update更新语句中动态包含要更新的列

<update id="xx">
    update table1
    <set>
        <if test="username != null and username != ''">username=#{username},</if>
        <if test="sex != null and sex != ''">sex=#{sex},</if>
        <if test="addr != null and addr != ''">address=#{addr},</if>
    </set>
    where id = #{id}
</update>

 

 

 

  11.4 foreach

  如果要动态生成 如 select * from xx where field1 in (xxx,xxx,xxx) 这样的语句,调用者可传入数组、List、Set集合对象等

  例:

<select id="findByAddrList" resultType="com.xt.domain.User">
    select * from user where address in
    <foreach item="addr" index="i" collection="list" open="(" separator="," close=")">
        #{addr}
    </foreach>
</select>

  collection:传进来的参数名称

  item:循环中当前元素,即值

  index:集合下标或键

  open、close:

  separator:分隔符

 

  另一个问题:如果输入的list或set、数组里面没有数据,则会导致  select * from t where address in ()  这样的错误SQL

  修改如下:

<select id="findByAddrList" resultType="com.xt.domain.User">
    select * from user
    <where>
        <if test="list.size()>0">
            address in
        </if>
        <foreach item="addr" index="i" collection="list" open="(" separator="," close=")">
            #{addr}
        </foreach>
    </where>
</select>

 

   11.5  bind

  在OGNL表达式外创建一个变量,并将其绑定到当前的上下文

  作用就是简化写法。而且bind是将param视作字符串拼接,能防止注入。

<select id="xx" resultType="xxx">
    <bind name="pattern" value="'%' + param + '%'"/>
    select * from table1 where name like #{pattern}  --在上面定义的pattern在这里就用到了
</select>

 

 

12.  xml映射文件中的标签

(1)sql标签

   <select>、<insert>、<update>、<delete>

  <selectKey>、<resultMap>、

(2)<sql>、<include>

<sql id="role_columns">
    id,name,note
</sql>

<select id="xx" parameterType="xx" resultMap="xx">
    select <include refid="role_columns"> from table1
</select>

 

 

 

13. 一对一、一对多、多对多关联查询

   13.1 一对一

  一对一:从实体类上说,就是类A有成员是B类对象,一个A类对象只有一个B类对象成员;

      从表的关系上看,就是A表有一个字段是外键,关联到B表的主键,且通过A表记录的这个字段,只能从B表查出一条记录出来

(1)准备两张表

create table cards(cid int(5) primary key,cnum varchar(10));
create table students(sid int(5) primary key,sname varchar(10),scid int(5),
                      constraint scid_fk foreign key(scid) references cards(cid));

insert into cards(10,'202010');        
insert into cards(20,'202020');    
insert into cards(30,'202030');        

insert into students values(1,'name1',10);
insert into students values(2,'name2',20);
insert into students values(3,'name3',30);

      

 

(2)准备实体类

  学生类:省略getter、setter

package com.xt.domain;

public class Student {
    private int id;
    private String name;
    private Card card;
    //getter、setter
}

  身份证类:

package com.xt.domain;

public class Card {
    private int id;
    private String num;
}

(3)映射文件

  CardDao.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.xt.dao.CardDao">

    <resultMap type="com.xt.domain.Card" id="cardMap">
        <id property="id" column="cid"/>
        <result property="num" column="cnum"/>
    </resultMap>
</mapper>

  StudentDao.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.xt.dao.StudentDao">
    <resultMap type="com.xt.domain.Student" id="studentMap">
        <id property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="card" resultMap="com.xt.dao.CardDao.cardMap"/>
    </resultMap>

    <select id="findStudentBySid" resultMap="studentMap" parameterType="string">
        select t1.sid,t1.sname,t2.cid,t2.cnum from students t1,cards t2 where t1.scid = t2.cid and t1.sid=#{sid}
    </select>

</mapper>

 

(4)接口、测试使用

  StudentDao.java      CardDao.java暂无内容

Student findStudentBySid(String sid);

  测试:

StudentDao studentDao = session.getMapper(StudentDao.class);
//通过代理对象调用方法
Student student = studentDao.findStudentBySid("2");
System.out.println(student.getId()+" "+student.getName()+" "+student.getCard().getNum());
session.commit();

  

 

 

  一对一的测试案例与下面的延迟加载案例非常类似,区别在于,延迟加载将这里的关联查询sql分成了两个sql,并使得实体类A的成员属性 B类对象可延迟加载

  一对一案例说到底就是 <association>标签的使用

 

  13.2 一对多

  一对多:实体类A有一个属性成员,是B类对象的List或其它集合

      表C有一个主键,表D有一个字段关联到表C的主键,且D中有很多记录的该字段值相同(则从表C的角度看过去,就是C的一条记录对应D的很多条记录了)

   例子:班级信息表table1,有如下信息:班级编号、班级名字(根据一条班级编号就可以查询出很多条信息)

      学生信息表,有如下信息:学号、姓名、班级编号

(1)准备表

CREATE TABLE grades(gid INT(5) PRIMARY KEY,gname VARCHAR(10));
CREATE TABLE studentss(sid INT(5) PRIMARY KEY,sname VARCHAR(10),sgid INT(5),
CONSTRAINT sgid_fk FOREIGN KEY(sgid) REFERENCES grades(gid));

INSERT INTO grades VALUES(11,'高二');
INSERT INTO grades VALUES(10,'高一');

INSERT INTO studentss VALUES(1,'name1',11);
INSERT INTO studentss VALUES(2,'name2',11);
INSERT INTO studentss VALUES(3,'name3',11);
INSERT INTO studentss VALUES(4,'name1',10);

SELECT * FROM grades;
SELECT * FROM studentss;

 

(2)实体类 

   班级类:

package com.xt.domain;

import java.util.ArrayList;
import java.util.List;

public class Grade {
    private int id;
    private String name;
    private List<Studentt> studenttList = new ArrayList<Studentt>();
}

  学生类:

package com.xt.domain;

public class Studentt {
    private int id;
    private String name;
    private Grade grade;
}

(3)映射文件

  GradeDao.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.xt.dao.GradeDao">

    <resultMap type="com.xt.domain.Grade" id="gradeMap">
        <id property="id" column="gid"/>
        <result property="name" column="gname"/>
        <collection property="studenttList" resultMap="com.xt.dao.StudenttDao.studenttMap">
            <id property="id" column="sid"/>
            <result property="name" column="sname"/>
        </collection>
    </resultMap>

    <select id="findGradeByGName" parameterType="string" resultMap="gradeMap">
        select t1.gid,t1.gname,t2.sid,t2.sname from grades t1,studentss t2 where t2.sgid=t1.gid and t1.gname=#{gname}
    </select>

</mapper>

 

  StudenttDao.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.xt.dao.StudenttDao">

    <resultMap type="com.xt.domain.Studentt" id="studenttMap">
        <id property="id" column="sid"/>
        <result property="name" column="sname"/>
    </resultMap>
</mapper>

 

(4)接口、测试

  GradeDao.java

Grade findGradeByGName(String gname);

 

  测试:

SqlSession session = factory.openSession();
GradeDao gradeDao = session.getMapper(GradeDao.class);
//通过代理对象调用方法
Grade grade = gradeDao.findGradeByGName("高二");
System.out.println(grade.getId()+" "+grade.getName()+":");
List<Studentt> studenttList = grade.getStudenttList();
for(Studentt studentt:studenttList) {
    System.out.println(studentt.getId()+" "+studentt.getName());
}
session.commit();

  效果:根据班级名查到一个班级,而班级类对象里面有所有学生的信息

    

 

 

  13.3 多对多

  暂

 

 

  13小结:  

  联合查询:联合多个表查询结果,在resultMap里面配置 association或collection 配置一对一或一对多的类存储数据

  嵌套查询:先查一个表,然后根据这个表里的外键去查其父表(即分成几个sql来完成),查另外的表要通过select标签完成(例如下面的延迟加载就是嵌套查询的案例)

 

 

14.  延迟加载

延迟加载与按需加载

  例:一张订单表,表里面有用户id字段,当执行查询订单时,可能需要根据用户id查询用户信息,也可能不需要。因此可对查询用户信息实行延迟加载。当没有获取用户信息的操作出现时,即不执行用户信息查询。

    这里即将两个sql语句分开执行

 

  14.1 设置

  默认设置:

<settings>
    <setting name="lazyLoadingEnabled" value="false"/>
    <setting name="aggressiveLazyLoading" value="true"/>
</settings>

 

  lazyLoadingEnable:是否启用延迟加载

  aggressiveLazyLoading:是否按需加载属性。为true时,当启用延迟加载时,只要加载对象,就加载对象的所有属性

    真正需要的是按需加载:延迟加载开启(lazyLoadingEnabled为true),且按需加载开启(aggressiveLazyLoading为false)

               若延迟加载关闭,则按需加载无论开启还是关闭,一开始所有sql都会执行

 

 14.2  测试

  14.2.1 准备两张表

//测试mybatis延迟加载
create table orders(id int primary key,
                    user_id int,
                    createtime timestamp default current_timestamp,
                    note varchar(20)
                    );

create table users(id int primary key auto_increment,
                   name varchar(20),
                   age int,
                   sex varchar(10),
                   money double
                   );
                  

  表orders有 user_id 字段,通过此字段可以去查询uses表获取user信息

 

  14.2.2 实体类

  Order.java(表orders有4个字段,但实体类有第五个属性,这第五个属性即是展现延迟加载的地方)

public class Order {
    private int id;
    private int userId;
    private Date createTime;
    private String note;
    private User userInfo;
    
    getter、setter
    toString()
}

 

  User.java

public class User {
    private int id;
    private String name;
    private int age;
    private String sex;
    Double money;
    
    getter、setter
}

 

  延迟加载说明:当我们查询orders里面的信息,映射到Order实体类上时,如果不需要根据Order的 userInfo 字段去获取User信息,则不执行查询User的操作;当需要时才去执行

 

  14.2.3 OrderDao、UserDao以及他们的映射文件

  OrderDao.java

public interface OrderDao {
    /*查询订单信息*/
    List<Order> findAllOrders();
}

  OrderDao.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.xt.dao.OrderDao">
    <!--配置查询所有-->
    <select id="findAllOrders" resultMap="orderInfoResMap">
        select * from orders
    </select>

    <resultMap type="com.xt.domain.Order" id="orderInfoResMap">
        <id column="id" property="id"/>
        <result column="user_id" property="userId"/>
        <result column="createtime" property="createTime"/>
        <result column="note" property="note"/>
        <association property="userInfo" select="com.xt.dao.UserDao.findUserById" column="user_id"></association>  <!--按需加载的地方-->
    </resultMap>

</mapper>

 

  UserDao.java

public interface UserDao {
    /*根据订单id查询*/
    User findUserById(int id);
}

  UserDao.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.xt.dao.UserDao">
    <select id="findUserById" parameterType="int" resultType="com.xt.domain.User">
        select * from users where id = #{id}
    </select>
</mapper>

 

  14.2.4 测试

(1)开启延迟加载,开启按需加载

<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>

  

InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
OrderDao orderDao = session.getMapper(OrderDao.class);
//通过代理对象调用方法
List<Order> allOrders = orderDao.findAllOrders();
Order order = allOrders.get(0);
String note = order.getNote();

//String name = order.getUserInfo().getName();

session.close();
is.close();

  测试结果:因为没有调用 getUserInfo(),则就不需要根据order里面的user_id 去查询用户信息了,mybatis日志输出发现只执行了查询orders表的sql

       如果执行 getUserInfo,则会执行根据 user_id 查询 users 表的sql

 

(2)开启延迟加载,关闭按需加载

  关闭了按需加载,则只要去获取Order的某个属性,所有的属性都会加载(包括本来要延迟加载的 userInfo 字段信息)

<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>

  测试代码同上,但当执行到 getNote() 时,第二个sql(根据 user_id 去查第二张表)也会执行

 

15. 缓存

(1)系统缓存

  默认开启一级缓存,在参数和SQL完全一样的情况下,使用同一个SqlSession对象调用同一个Mapper的方法,一般只会执行一次SQL。但在一个SqlSession提交commit后再开一个SqlSession,如果也有缓存的话,它和之前的缓存是相互隔离的,需要重新执行SQL(即使参数、SQL还是一样)

  二级缓存可使缓存在SqlSessionFactory层面上能提供给各个SqlSession对象共享

 

  开启二级缓存要求返回的POJO能序列化(实现了Serializable接口),然后在映射xml文件里加上 <cache/>

  ①全局配置的settings里面加

<settings>
    <setting name="cacheEnabled" value="true" />
</settings>

  ②映射xml里面

<cache/>

  也可以设置缓存的属性

 

  如果某些语句需要每次调用都执行sql(即个别语句不用缓存),可在其标签内加 useCache   flushCache属性

  如:useCache默认值为true

<select id="xx" useCache="false" resultType="xxxxx" parameterType="xxx">
    sql语句
</select>

 

 

(2)自定义缓存

  比如用Redis作为缓存

  需要实现 org.apache.ibatis.cache.Cache 接口,实现类在 cache标签里通过type属性指定

 

16.  自定义插件

 

  

 

posted @ 2020-06-30 13:45  T,X  阅读(244)  评论(0编辑  收藏  举报