SSM_Mybatis(下)

目录

八、增删改查中的细节

 8.1 插入数据的时候获取自增的id-befor方法

8.1.1 案例准备

 8.2 插入数据的时候获取自增的id-after方法

 8.3 输入映射

8.3.1 parameterType

​8.3.2 通过@Param注解(常用!!!)

8.3.3 通过map来传递多个参数

​8.3.3 通过pojo类传递多个参数

8.4 #{} 和 ${}的区别--面试中喜欢出的考题

 8.4.1 #{}

 8.4.2 ${}

​8.5 输出映射

 8.5.1 输出简单类型

 8.5.2 输出pojo类型

 8.5.3 输出Map类型

 8.5.4 resultMap

8.6 数据库表中列与实体类属性不一致的处理方式

8.6.1 使用列别名

8.6.2 使用resultMap

 九、Mybatis的全局配置文件

9.1 配置的内容

9.2 属性(properties)

9.3 设置 settings

9.4 类型别名 typeAliases

9.4.1 Mybatis中已经支持的别名

​9.4.2  自定义别名

9.5 映射器 Mappers

9.5.1 使用相对于类路径的资源引用

9.5.2 使用映射器接口实现类的完全限定类名

9.5.3 将包内的映射器接口实现全部注册为映射器--推荐

9.6 dataSource标签

9.7 事务

9.7.1 默认是需要手动提交事务的 

9.7.2 自动提交事务

十、Mybatis中的关系映射

        10.1对一关系的映射 

10.1.1 添加实体类

10.1.2 添加对应的mapper接口

10.1.3 对一映射方式1:通过关联对象打点调用属性的方式

10.1.4 对一映射方式2:直接引用关联对象的Mapper映射

10.1.5 对一映射方式3:直接引用关联对象的单独查询的方法

10.2 对多关系的映射

10.2.1 方式1:连接查询+引用关联对象的结果映射

​十一、动态SQL

11.1 where标签在select中的使用

11.2 set标签在update中的使用

11.2.1 更新的原有写法

​11.2.2 使用set标签构建动态的SQL语句

11.3 forEach标签

11.3.1 批量添加

​11.3.2 批量删除

​十二、分页插件

12.1 jar依赖

12.2 在Mybatis全局配置文件中添加插件配置 

12.3 使用插件

十三、Mybatis缓存

13.1 缓存作用

13.2 一级缓存:自动开启,SqlSession级别的缓存 

13.2.1 一级缓存分析 

13.2.2 清空缓存的方式

13.3 二级缓存:Mapper级别的缓存

​13.3.1 使用二级缓存步骤 

13.3.2 二级缓存的禁用 

13.3.3 缓存的属性配置 

十四、反向生成插件 

14.1 插件的配置

14.2 反向生成插件的使用


八、增删改查中的细节

8.1 插入数据的时候获取自增的id-befor方法

8.1.1 案例准备

(1)添加一张新表

CREATE TABLE `gamerecord` (
`recordId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`homeTeamId` int DEFAULT NULL COMMENT '主队ID',
`gameDate` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '比赛日期',
`score` int DEFAULT NULL COMMENT '得分',
`visitingTeamId` int DEFAULT NULL COMMENT '客队ID',
PRIMARY KEY (`recordId`),
KEY `homeTeamId` (`homeTeamId`),
KEY `visitingTeamId` (`visitingTeamId`),
CONSTRAINT `gamerecord_ibfk_1` FOREIGN KEY (`homeTeamId`) REFERENCES `team` (`teamId`),
CONSTRAINT `gamerecord_ibfk_2` FOREIGN KEY (`visitingTeamId`) REFERENCES `team` (`teamId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

 (2)实体类

package com.lxy.pojo;

import java.util.Date;

public class GameRecord {
    private String recordId;
    private Integer homeTeamId;
    private Date gameDate;
    private Integer score;
    private Integer visitingTeamId;

    public String getRecordId() {
        return recordId;
    }

    public void setRecordId(String recordId) {
        this.recordId = recordId;
    }

    public Integer getHomeTeamId() {
        return homeTeamId;
    }

    public void setHomeTeamId(Integer homeTeamId) {
        this.homeTeamId = homeTeamId;
    }

    public Date getGameDate() {
        return gameDate;
    }

    public void setGameDate(Date gameDate) {
        this.gameDate = gameDate;
    }

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }

    public Integer getVisitingTeamId() {
        return visitingTeamId;
    }

    public void setVisitingTeamId(Integer visitingTeamId) {
        this.visitingTeamId = visitingTeamId;
    }
}

(3)创建实体类的mapper接口

package com.lxy.mapper;

import com.lxy.pojo.GameRecord;

public interface GameRecordMapper {
    int add(GameRecord record);
}

(4)修改配置文件 

 添加GameRecordMapper.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.lxy.mapper.GameRecordMapper">

    <insert id="add" parameterType="com.lxy.pojo.GameRecord">
        <selectKey keyProperty="recordId" order="BEFORE" resultType="java.lang.String">
            select uuid()
        </selectKey>
        INSERT INTO `mybatis`.`gamerecord` (`recordId`, `homeTeamId`, `gameDate`, `score`, `visitingTeamId`)
        VALUES (#{recordId}, #{homeTeamId},default, #{score}, #{visitingTeamId})
    </insert>
</mapper>

 (5)测试类

package com.lxy.test;

import com.lxy.mapper.GameRecordMapper;
import com.lxy.pojo.GameRecord;
import com.lxy.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class GameRecordMapperTest {
    private SqlSession sqlSession = MybatisUtil.getSqlSession();

    @Test
    public void testAdd(){
        GameRecordMapper mapper = sqlSession.getMapper(GameRecordMapper.class);
        GameRecord record = new GameRecord();
        record.setHomeTeamId(1014);
        record.setVisitingTeamId(1019);
        record.setScore(99);
        int add = mapper.add(record);
        sqlSession.commit();
        System.out.println("add结果"+add);
        System.out.println(record.getRecordId());
    }
}

使用before方法能为insert方法添加一个recordId,可以在创建对象的时候不传参数

 8.2 插入数据的时候获取自增的id-after方法

 (1)在TeamMapper接口中添加addAfter方法

package com.lxy.mapper;


import com.lxy.pojo.Team;

import java.util.List;

public interface TeamMapper {
    List<Team> queryAll();
    Team queryById(Integer teamId);
    int add(Team team);
    int update(Team team);
    int del(Integer teamId);
    int addAfter(Team team);
}

(2)在TeamMapper.xml中添加addAfter方法

<?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 namespace="com.lxy.mapper.TeamMapper">
    
    <insert id="addAfter" parameterType="com.lxy.pojo.Team">
        <selectKey keyProperty="teamId" order="AFTER" resultType="java.lang.Integer">
            select LAST_INSERT_ID()
        </selectKey>
        INSERT INTO `team` (`teamName`, `location`, `createTime`)
        VALUES (#{teamName}, #{location}, #{createTime})
    </insert>
</mapper>

(3)编写测试类 

    @Test
    public void testAddAfter(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        Team team = new Team();
        team.setTeamName("lxy");
        team.setLocation("shanghai");
        int i = mapper.addAfter(team);
        sqlSession.commit();
        System.out.println(i);
        System.out.println("新增id:"+team.getTeamId());
    }

 虽然没添加id,但是使用after方法可以直接获取自动添加后的id

8.3 输入映射

8.3.1 parameterType

parameterType:接口中方法参数的类型,类型必须是完全限定名或别名(稍后讲别名)。该属性非必须,因为Mybatis框架能自行判断具体传入语句的参数,默认值为未设置(unset)。

当sql语句中需要多个参数时,就不能使用parameterType方法了。

 (1)首先在TeamMapper接口中添加方法声明

List<Team> queryByRange(int min, int max);

(2)在TeamMapper.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="名称必须与映射的接口的名字一致,是完全限定名"-->
<mapper namespace="com.lxy.mapper.TeamMapper">

    <!--多个参数:标签中不需要parameterType属性
        方式1:通过下标索引的方式
        select * from team where teamId >=#{arg0} and teamId <=#{arg1}; 也可以,注意下表索引
        细节1:
            mybatis3.3版本之前:可以直接写#{0} #{1}
            从mybatis3.4开始:#{arg0} #{arg1}... 或者是 #{param1} #{param2}...
        细节2:
            sql语句中不能使用小于号,使用转义符号替换;大于号没有限制,也可以使用转义符号替换>
            &gt;    greater than
            &lt;    less than
    -->
    <select id="queryByRange" resultType="com.lxy.pojo.Team">
        select * from team where teamId &gt;=#{param1} and teamId &lt;=#{param2};
    </select>
</mapper>

(3)编写测试方法

package com.lxy.test;

import com.lxy.mapper.TeamMapper;
import com.lxy.pojo.Team;
import com.lxy.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class TeamMapperTest {
    private SqlSession sqlSession = MybatisUtil.getSqlSession();

    @Test
    public void testTeamMapper() {
        TeamMapper teamMapper = sqlSession.getMapper(TeamMapper.class);
        List<Team> teams = teamMapper.queryAll();
        for (Team t : teams) {
            System.out.println(t);
        }
    }
    @Test
    public void testAddAfter(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        Team team = new Team();
        team.setTeamName("lxy");
        team.setLocation("shanghai");
        int i = mapper.addAfter(team);
        sqlSession.commit();
        System.out.println(i);
        System.out.println("新增id:"+team.getTeamId());
    }

    @Test
    public void teamTeamMapper(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        List<Team> teams = mapper.queryByRange(1009, 1019);
        teams.forEach(team -> System.out.println(team));
    }
}

测试结果:

8.3.2 通过@Param注解(常用!!!)

在方法的形参前面加入@Param("自定义参数名称"),mapper文件中使用#{自定义参数名称}的方式传参。

(1)TeamMapper接口添加如下内容:

List<Team> queryByRange2(@Param("min") Integer min, @Param("max") Integer max);

(2)TeamMapper.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="名称必须与映射的接口的名字一致,是完全限定名"-->
<mapper namespace="com.lxy.mapper.TeamMapper">
    <!--方式2:通过注解的方式:
    #{}中的名称必须与接口的方法中的参数注解@Param()保持一致
    select * from team where teamId >=#{param1} and teamId &lt;= #{param2}; 不推荐,但是语法            也是正确的,但是不能使用arg0,arg1......
    -->
    <select id="queryByRange2" resultType="com.lxy.pojo.Team">
        select * from team where teamId >=#{min} and teamId &lt;=#{max};
    </select>
</mapper>

 (3)添加测试方法

package com.lxy.test;

import com.lxy.mapper.TeamMapper;
import com.lxy.pojo.Team;
import com.lxy.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class TeamMapperTest {
    private SqlSession sqlSession = MybatisUtil.getSqlSession();

    @Test
    public void teamTeamMapper2(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        List<Team> teams = mapper.queryByRange2(1009, 1019);
        teams.forEach(team -> System.out.println(team));
    }
}

测试结果:

8.3.3 通过map来传递多个参数

Map 集合可以存储多个值,使用Map向 mapper 文件一次传入多个参数。Map 集合使用 String的 key,Object 类型的值存储参数。 mapper 文件使用 # { key } 引用参数值

(1)TeamMapper接口添加如下内容:

List<Team> queryByRange3(Map<String,Object> map);

(2)TeamMapper.xml配置文件中添加如下:

<!--方式3:通过map来传递多个参数:映射文件中的参数占位符必须和map中的String类型的字段名称一样-->
<select id="queryByRange3" resultType="com.kkb.pojo.Team">
    select * from team where teamId >=#{min} and teamId &lt;= #{max};
</select>

(3)添加测试方法

package com.lxy.test;

import com.lxy.mapper.TeamMapper;
import com.lxy.pojo.Team;
import com.lxy.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TeamMapperTest {
    private SqlSession sqlSession = MybatisUtil.getSqlSession();

    @Test
    public void teamTeamMapper3(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        Map<String,Object> map = new HashMap<>();
        map.put("min",1009);
        map.put("max",1019);
        List<Team> teams = mapper.queryByRange3(map);
        teams.forEach(team -> System.out.println(team));
    }
}

测试结果:

8.3.3 通过pojo类传递多个参数

 与map传递多个参数类似,要求映射文件中的参数占位符必须和pojo类中的属性完全一致。

(1)实体类:

package com.lxy.pojo;

public class QueryVO {
    private String name;
    private Integer min;
    private Integer max;
    private String location;

    public String getName() {
        return name;
    }

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

    public Integer getMin() {
        return min;
    }

    public void setMin(Integer min) {
        this.min = min;
    }

    public Integer getMax() {
        return max;
    }

    public void setMax(Integer max) {
        this.max = max;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }
}

 (2)TeamMapper接口添加如下内容:

List<Team> queryByCondition(QueryVO vo);

 (3)TeamMapper.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="名称必须与映射的接口的名字一致,是完全限定名"-->
<mapper namespace="com.lxy.mapper.TeamMapper">

    <!--方式4:通过pojo类传递多个参数:映射文件中的参数占位符必须和pojo类中的字段完全一致-->
    <select id="queryByCondition" resultType="com.lxy.pojo.Team">
            select * from team
            where teamId>=#{min} and teamId&lt;=#{max}
            and teamName like #{name} and location=#{location}
    </select>

</mapper>

(4)测试类添加方法:

    @Test
    public void testQueryByCondition(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        QueryVO vo = new QueryVO();
        vo.setLocation("北京");
        vo.setName("%球队%");
        vo.setMin(1001);
        vo.setMax(1020);
        List<Team> teams = mapper.queryByCondition(vo);
        teams.forEach(team -> System.out.println(team));
    }

 测试结果:

 8.4 #{} 和 ${}的区别--面试中喜欢出的考题

 8.4.1 #{}

#{}:表示一个占位符,通知Mybatis 使用实际的参数值代替。并使用 PrepareStatement 对象执行 sql 语句, #{…}代替sql 语句的“?”。这个是Mybatis 中的首选做法,安全迅速。

<select id="queryById" parameterType="int" resultType="com.kkb.pojo.Team">
    select * from team where teamId=#{id}
</select>
<!--Mybatis执行的时候是:
    String sql="select * from team where teamId=?";
    PreparedStatement ps = conn.prepareStatement(sql);
    ps.setInt(1,1001);
    where teamId=? 实际就是 where teamId=#{id}
    ps.setInt(1,1001) 中的1001会替换#{id}
-->

 8.4.2 ${}

${}:表示字符串原样替换,通知Mybatis 使用$包含的“字符串”替换所在位置。使用 Statement或者PreparedStatement 把 sql 语句和${}的内容连接起来。一般用在替换表名,
列名,不同列排序等操作。

例如:根据球队名称,球队位置查询球队列表

 示例:使用不同列作为查询条件

(1)TeamMapper接口添加如下内容:

List<Team> queryByFiled(@Param("column") String column,@Param("columnValue") String columnValue);

(2)TeamMapper.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="名称必须与映射的接口的名字一致,是完全限定名"-->
<mapper namespace="com.lxy.mapper.TeamMapper">

    <select id="queryByFiled" resultType="com.lxy.pojo.Team">
        select * from team where ${column}=#{columnValue}
    </select>

</mapper>

(3)添加测试方法

package com.lxy.test;

import com.lxy.mapper.TeamMapper;
import com.lxy.pojo.QueryVO;
import com.lxy.pojo.Team;
import com.lxy.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TeamMapperTest {
    private SqlSession sqlSession = MybatisUtil.getSqlSession();

    @Test
    public void testQueryByFiled(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        System.out.println("根据球队名称查询:");
        List<Team> teams = mapper.queryByFiled("teamName","LALALA");
        teams.forEach(team -> System.out.println(team));
        System.out.println("根据球队位置查询:");
        List<Team> teams2 = mapper.queryByFiled("location","北京");
        teams2.forEach(team -> System.out.println(team));

    }
}

测试结果:

 8.5 输出映射

 resultType: 执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名或别名。如果返回的是集合,设置的是集合元素的类型,而不是集合本身。resultType 和 resultMap,
不能同时使用。

 8.5.1 输出简单类型

案例:

(1)TeamMapper接口添加如下内容:

int getCount();

(2)TeamMapper.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="名称必须与映射的接口的名字一致,是完全限定名"-->
<mapper namespace="com.lxy.mapper.TeamMapper">

    <!-- 只有返回的结果是单行的时候,返回值类型才可以指定为基本类型
    如果是单行多列,也取不到后面的列的值;
    如果返回多行会报异常:TooManyResultsException-->
    <select id="getCount" resultType="java.lang.Integer">
        select count(teamId) from team
    </select>

</mapper>

(3)测试类添加方法:

    @Test
    public void testCount(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        int count = mapper.getCount();
        System.out.println("总共的行数:"+count);
    }

 8.5.2 输出pojo类型

案例:参考之前的查询所有球队信息

List<Team> queryAll();
<!--接口方法返回是集合类型,但是映射文件中的resultType需要指定集合中的类型,不是集合本身。-->
<select id="queryAll" resultType="com.lxy.pojo.Team">
    select * from team;
</select>

 8.5.3 输出Map类型

 当我们只需要查询表中几列数据的时候可以将sql的查询结果作为Map的key和value。一般使用的是Map<Object,Object>.


Map 作为接口返回值,sql 语句的查询结果最多只能有一条记录。大于一条记录会抛出TooManyResultsException异常。


如果有多行,使用List<Map<Object,Object>>.

案例:根据id查询球队名称和位置。

(1)TeamMapper接口添加如下内容:

Map<Object,Object> queryTwoColumn(int teamId);
List<Map<Object,Object>> queryTwoColumnList();

(2)TeamMapper.xml配置文件中添加如下:

<select id="queryTwoColumn" resultType="java.util.HashMap">
    select teamName,location from team where teamId=#{id}
</select>
<select id="queryTwoColumnList" resultType="java.util.HashMap">
    select teamName,location from team
</select>

(3)添加测试类

@Test
public void test08(){
    Map<String, Object> map = teamMapper.queryTwoColumn();
    System.out.println(map);
}

@Test
public void test09(){
    List<Map<String, Object>> list = teamMapper.queryTwoColumnList();
    for (Map<String, Object> map : list) {
        System.out.println(map);
    }
}

 测试结果:

8.5.4 resultMap

resultType要求数据库中的列名和实体类中的保持一致

resultMap 可以自定义 sql 的结果和 java 对象属性的映射关系。更灵活的把列值赋值给指定属性。常用在列名和 java 对象属性名不一样的情况。

(1)在TeamMapper接口中添加方法 

List<Team> queryAll2();

(2)添加select节点,并定义 resultMap,指定列名和属性的对应关系

<!--resultMap 和resultType不能同时出现
    resultMap:是引用的自己创建resultMap的id-->
    <select id="queryAll2" resultMap="baseResultMap">
        select * from team;
    </select>
    <!--创建resultMap:相当于自己编写表中的列与实体类中的属性的映射
        id:resultMap的名称,要求唯一
        type:期待要映射为java的类型
    -->
    <resultMap id="baseResultMap" type="com.lxy.pojo.Team">
        <!--一般主键列用id,其余列用result
            column:表示数据库表中的列名,不区分大小写
            property:表示实体类中的对应的属性名,区分大小写
            javaType:实体类中的对应的属性的类型,可以省略,mybatis会自己推断
            jdbcType="数据库中的类型column的类型" 一般省略
        -->
        <id column="teamId" property="teamId" javaType="java.lang.Integer" ></id>
        <result column="teamName" property="teamName" javaType="java.lang.String"></result>
        <result column="location" property="location" javaType="java.lang.String"></result>
        <result column="createTime" property="createTime" javaType="java.util.Date"></result>
    </resultMap>

(3)添加测试方法

    @Test
    public void test10(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        List<Team> teams = mapper.queryAll2();
        teams.forEach(team-> System.out.println(team));
    }

测试结果:

8.6 数据库表中列与实体类属性不一致的处理方式

案例准备  创建表:

use mybatis;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
    `user_id` int NOT NULL AUTO_INCREMENT COMMENT '用户id',
    `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT         NULL COMMENT '用户姓名',
    `user_age` int NULL DEFAULT NULL COMMENT '用户年龄',
    PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO `users` VALUES (1, '贾宝玉', 14);
INSERT INTO `users` VALUES (2, '林黛玉', 13);
INSERT INTO `users` VALUES (3, '薛宝钗', 15);
SET FOREIGN_KEY_CHECKS = 1;

8.6.1 使用列别名

(1)创建实体类

package com.kkb.pojo;

public class Users {
    private Integer userId;
    private String userName;
    private Integer userAge;

    @Override
    public String toString() {
        return "Users{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userAge=" + userAge +
                '}';
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getUserAge() {
        return userAge;
    }

    public void setUserAge(Integer userAge) {
        this.userAge = userAge;
    }
}

(2)创建UsersMapper接口

public interface UsersMapper {
    Users queryByID(int userId);
}

(3)映射文件UsersMapper.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.lxy.mapper.UsersMapper">
    <!--方式1:resultType中的实体类的属性作为查询语句中的别名,让别名和属性保持一致-->
    <select id="queryByID" resultType="com.lxy.pojo.Users">
        select user_id as userId,user_name as userName,user_age as userAge from users where user_id=#{id};
    </select>
</mapper>

(4)编写测试类

package com.lxy.test;

import com.lxy.mapper.UsersMapper;
import com.lxy.pojo.Users;
import com.lxy.util.MybatisUtil;
import org.junit.Test;

public class TestUsersMapper {
    private UsersMapper usersMapper = MybatisUtil.getSqlSession().getMapper(UsersMapper.class);

    @Test
    public void test01(){
        Users users = usersMapper.queryByID(1);
        System.out.println(users);
    }
}

测试结果:

8.6.2 使用resultMap

(1)接口UsersMapper.java添加方法

Users queryByID2(int userId);

(2)映射文件UsersMapper.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.lxy.mapper.UsersMapper">
    <!--方式1:resultType中的实体类的属性作为查询语句中的别名,让别名和属性保持一致-->
    <select id="queryByID" resultType="com.lxy.pojo.Users">
        select user_id as userId,user_name as userName,user_age as userAge from users where user_id=#{id};
    </select>

    <!--方式2:通过resultMap自行映射-->
    <select id="queryByID2" resultMap="baseMap">
        select * from users where user_id=#{id};
    </select>
    <resultMap id="baseMap" type="com.lxy.pojo.Users">
        <id column="user_id" property="userId"/>
        <result column="user_name" property="userName"/>
        <result column="user_age" property="userAge"/>
    </resultMap>

</mapper>

(3)测试类

package com.lxy.test;

import com.lxy.mapper.UsersMapper;
import com.lxy.pojo.Users;
import com.lxy.util.MybatisUtil;
import org.junit.Test;

public class TestUsersMapper {
    private UsersMapper usersMapper = MybatisUtil.getSqlSession().getMapper(UsersMapper.class);

    @Test
    public void test01(){
        Users users = usersMapper.queryByID(1);
        System.out.println(users);
    }


    @Test
    public void test02(){
        Users user = usersMapper.queryByID2(1);
        System.out.println(user);
    }
}

测试结果: 

 九、Mybatis的全局配置文件

案例中使用的 mybatis.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">

9.1 配置的内容

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

configuration(配置)
        properties--属性:加载外部的配置文件,例如加载数据库的连接信息
        Settings--全局配置参数:例如日志配置
        typeAliases--类型别名
        typeHandlers----类型处理器
        objectFactory-----对象工厂
        Plugins------插件:例如分页插件
        Environments----环境集合属性对象
                environment(环境变量)
                        transactionManager(事务管理器)
                        dataSource(数据源)
Mappers---映射器:注册映射文件用

9.2 属性(properties)

属性可以在外部进行配置,并可以进行动态替换。我们既可以在 properties 元素的子元素中设置(例如DataSource节点中的properties节点),也可以在 Java 属性文件中配置这些属性。


数据源中有连接数据库的四个参数数据,我们一般都是放在专门的属性文件中,mybatis的全局配置文件直接从属性文件中读取数据即可。

(1)在 resources 目录创建 jdbc.properties 文件,文件名称可以自定义。

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
jdbc.username=root
jdbc.a=???

(2)mybatis的全局配置文件引入属性文件

<properties resource="jdbc.properties"/>

(3)使用属性文件中的值

<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>

9.3 设置 settings

MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为.例如我们配置的日志就是应用之一。其余内容参考mybatis – MyBatis 3 | 配置

<!--配置日志-->
<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

9.4 类型别名 typeAliases

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

9.4.1 Mybatis中已经支持的别名

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

 9.4.2  自定义别名

<!--自定义类型别名-->
<typeAliases>
    <!--对单个的实体类定义别名-->
    <typeAlias type="com.lxy.pojo.Team" alias="Team"/>
    <!--推荐写法:批量定义别名:扫描指定包下的所有类,同时别名定义为类名,别名的首字母大小写都可以-->
    <package name="com.lxy.pojo"/>
</typeAliases>

9.5 映射器 Mappers

配置有多种方式:

9.5.1 使用相对于类路径的资源引用

语法:<mapper resource=""/>

使用相对于类路径的资源,从 classpath 路径查找文件
例如:<mapper resource="com/lxy/mapper/TeamMapper.xml" />

9.5.2 使用映射器接口实现类的完全限定类名

语法:<mapper class=""/>

使用的mapper接口的完全限定名
要求:接口和映射文件同包同名
例如<mapper class="com.lxy.mapper.GameRecordMapper"/>

9.5.3 将包内的映射器接口实现全部注册为映射器--推荐

语法:<package name=""/>

指定包下的所有Mapper接口
如:<package name="com.lxy.mapper"/>
注意:此种方法要求 Mapper接口名称和 mapper 映射文件名称相同,且在同一个目录中。

9.6 dataSource标签

Mybatis 中访问数据库支持连接池技术,而且是采用的自己的连接池技术。在 Mybatis 的 mybatis.xml配置文件中,通过来实现 Mybatis 中连接池的配置。MyBatis 在初始化时,根据的 type 属性来创建相应类型的的数据源 DataSource。

Mybatis 的数据源分为三类:

UNPOOLED: 不使用连接池的数据源
POOLED:使用连接池的数据源
JNDI:使用JNDI实现的数据源
前两个数据源都实现javax.sql.DataSource接口

 

9.7 事务

9.7.1 默认是需要手动提交事务的 

Mybatis 框架是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC 的 Connection对象的 commit(), rollback() .Connection 对象的 setAutoCommit()方法来设置事务提交方式的。自动提交和手工提交、

<transactionManager type="JDBC"/>

该标签用于指定 MyBatis所使用的事务管理器。MyBatis 支持两种事务管理器类型:JDBC 与 MANAGED。

(1)JDBC:使用JDBC的事务管理机制,通过Connection对象的 commit()方法提交,通过rollback()方法 回滚。默认情况下,mybatis将自动提交功能关闭了,改为了手动提交,观察日志可以看出,所以我们在程序中都需要自己提交事务或者回滚事务。

(2)MANAGED:由容器来管理事务的整个生命周期(如Spring容器)。

9.7.2 自动提交事务

SqlSessionFactory的openSession方法由重载,可以设置自动提交的方式。
如果sqlSession = SqlSessionFactory.openSession(true);参数设置为true,再次执行增删改的时候就不需要执行session.commit()方法,事务会自动提交。 

十、Mybatis中的关系映射

表结构如图: 

10.1对一关系的映射 

10.1.1 添加实体类

package com.lxy.pojo;

public class Player {
    private Integer playerId;
    private String playerName;
    private Integer playerNum;
    private Integer teamId;
    //多对一的体现:多方持有一方的对象 要有get方法
    private Team team1;//关联对象--多个球员可以属于同一个球队;
    private Team team2;//关联对象--多个球员可以属于同一个球队;
    private Team team3;//关联对象--多个球员可以属于同一个球队;

    public Integer getPlayerId() {
        return playerId;
    }

    public void setPlayerId(Integer playerId) {
        this.playerId = playerId;
    }

    public String getPlayerName() {
        return playerName;
    }

    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }

    public Integer getPlayerNum() {
        return playerNum;
    }

    public void setPlayerNum(Integer playerNum) {
        this.playerNum = playerNum;
    }

    public Integer getTeamId() {
        return teamId;
    }

    public void setTeamId(Integer teamId) {
        this.teamId = teamId;
    }

    public Team getTeam1() {
        return team1;
    }

    public void setTeam1(Team team1) {
        this.team1 = team1;
    }

    public Team getTeam2() {
        return team2;
    }

    public void setTeam2(Team team2) {
        this.team2 = team2;
    }

    public Team getTeam3() {
        return team3;
    }

    public void setTeam3(Team team3) {
        this.team3 = team3;
    }

    @Override
    public String toString() {
        return "Player{" +
                "playerId=" + playerId +
                ", playerName='" + playerName + '\'' +
                ", playerNum=" + playerNum +
                ", teamId=" + teamId +
                ", team1=" + team1 +
                ", team2=" + team2 +
                ", team3=" + team3 +
                '}';
    }
}

10.1.2 添加对应的mapper接口

package com.lxy.mapper;

import com.lxy.pojo.Player;

public interface PlayerMapper {
    Player queryById(int playerId);
    Player queryById1(int playerId);
    Player queryById2(int playerId);
    Player queryById3(int playerId);
}

10.1.3 对一映射方式1:通过关联对象打点调用属性的方式

要求:两表的连接查询

<?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.lxy.mapper.PlayerMapper">

    <resultMap id="baseResultMap" type="com.lxy.pojo.Player">
        <id column="playerId" property="playerId"></id>
        <result column="playerName" property="playerName"></result>
        <result column="playerNum" property="playerNum"></result>
        <result column="teamId" property="teamId"></result>
    </resultMap>
    
    <select id="queryById1" resultMap="joinTeamResult1">
        select * from player p inner join team t
        on p.teamId=t.teamId where playerId=#{id}
    </select>
    <resultMap id="joinTeamResult1" type="Player" extends="baseResultMap">
        <result column="teamId" property="team1.teamId"></result>
        <result column="location" property="team1.location"></result>
        <result column="teamName" property="team1.teamName"></result>
        <result column="createTime" property="team1.createTime"></result>
    </resultMap>
</mapper>

编写测试类:

package com.lxy.test;

import com.lxy.mapper.PlayerMapper;
import com.lxy.pojo.Player;
import com.lxy.util.MybatisUtil;
import org.junit.Test;

public class TestPlayerMapper {
    PlayerMapper playerMapper= MybatisUtil.getSqlSession().getMapper(PlayerMapper.class);

    @Test
    public void test01(){
        Player player = playerMapper.queryById1(1);
        System.out.println(player);
    }
}

 测试结果:

10.1.4 对一映射方式2:直接引用关联对象的Mapper映射

 要求:1、两表的连接查询
            2、关联对象中已经存在被引用的resultMap

    <select id="queryById2" resultMap="joinTeamResult2">
        select * from player p inner join team t
        on p.teamId=t.teamId where playerId=#{id}
    </select>
    <resultMap id="joinTeamResult2" type="Player" extends="baseResultMap">
        <association property="team2" javaType="Team"
                     resultMap="com.lxy.mapper.TeamMapper.baseResultMap"/>
    </resultMap>

 测试类:

package com.lxy.test;

import com.lxy.mapper.PlayerMapper;
import com.lxy.pojo.Player;
import com.lxy.util.MybatisUtil;
import org.junit.Test;

public class TestPlayerMapper {
    PlayerMapper playerMapper= MybatisUtil.getSqlSession().getMapper(PlayerMapper.class);

    @Test
    public void test01(){
        Player player = playerMapper.queryById1(1);
        System.out.println(player);
    }

    @Test
    public void test02(){
        Player player = playerMapper.queryById2(1);
        System.out.println(player);
    }
}

测试结果:

10.1.5 对一映射方式3:直接引用关联对象的单独查询的方法

要求:1、不需要两表的连接查询
           2、关联对象中已经存在被引用的查询方法

    <select id="queryById3" resultMap="joinTeamResult3">
        select * from player where playerId=#{id}
    </select>
    <resultMap id="joinTeamResult3" type="Player" extends="baseResultMap">
        <association property="team3" javaType="Team"
                     select="com.lxy.mapper.TeamMapper.queryById" column="teamId"/>
    </resultMap>

测试类:

package com.lxy.test;

import com.lxy.mapper.PlayerMapper;
import com.lxy.pojo.Player;
import com.lxy.util.MybatisUtil;
import org.junit.Test;

public class TestPlayerMapper {
    PlayerMapper playerMapper= MybatisUtil.getSqlSession().getMapper(PlayerMapper.class);

    @Test
    public void test01(){
        Player player = playerMapper.queryById1(1);
        System.out.println(player);
    }

    @Test
    public void test02(){
        Player player = playerMapper.queryById2(1);
        System.out.println(player);
    }

    @Test
    public void test03(){
        Player player = playerMapper.queryById3(1);
        System.out.println(player);
    }
}

测试结果: 

10.2 对多关系的映射

修改实体类Team.java:

public class Team {
    private Integer teamId;
    private String teamName;
    private String location;
    private Date createTime;
    //一对多的体现:一方持有多方的对象
    private List<Player> playerList1;//关联对象--一个球队可以拥有多个球员
    private List<Player> playerList2;//关联对象--一个球队可以拥有多个球员

TeamMapper.java接口添加方法:

public interface TeamMapper {
    Team queryById1(int teamId);
    Team queryById2(int teamId);

PlayerMapper.java接口中添加方法:

public interface PlayerMapper {
    List<Player> queryByTeamId(int teamId);

10.2.1 方式1:连接查询+引用关联对象的结果映射

TeamMapper.xml添加:

    <select id="queryById1" resultMap="joinResult1">
        select * from team t join player p
        on t.teamId=p.teamId where t.teamId=#{id};
    </select>
    <!--方式1:
        对多的连接查询:对多使用collection
        property="关联对象的集合名称"
        javaType="关联对象的集合类型"
        ofType="关联对象的集合的泛型"
        resultMap="引用关联对象的结果映射"
    -->
    <resultMap id="joinResult1" type="Team" extends="baseResultMap">
        <collection property="playerList1" javaType="java.util.ArrayList" ofType="player"
                    resultMap="com.lxy.mapper.PlayerMapper.baseResultMap"/>
    </resultMap>

添加测试类:

    @Test
    public void test11(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        Team team = mapper.queryById1(1001);
        System.out.println(team);
    }

测试结果:

10.2.2 方式2:引用关联对象的单独查询的方法

 TeamMapper.xml添加:

    <select id="queryById2" resultMap="joinResult2">
        select * from team where teamId=#{id};
    </select>
    <!--方式2:
        对多的连接查询:对多使用collection
        property="关联对象的集合名称"
        javaType="关联对象的集合类型"
        ofType="关联对象的集合的泛型"
        select="引用关联对象的单独查询的方法":使用的前提是关联对象中该方法可用
        column="引用关联对象的单独查询的方法的参数,一般是外键"
    -->
    <resultMap id="joinResult2" type="Team" extends="baseResultMap">
        <collection property="playerList2" javaType="java.util.ArrayList" ofType="player"
                    select="com.lxy.mapper.PlayerMapper.queryByTeamId" column="teamId"/>
    </resultMap>

PlayerMapper.xml添加如下内容:

<select id="queryByTeamId" resultType="Player">
    select * from player where teamId=#{id}
</select>

测试类:

    @Test
    public void test12(){
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        Team team = mapper.queryById2(1001);
        System.out.println(team);
    }

测试结果:

十一、动态SQL

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

之前学习过 JSTL,所以动态 SQL 元素会让你感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少

11.1 where标签在select中的使用

(1)原先的多条件查询做法

/* 原有的多条件分析:都是通过java中的字符串拼接实现 
String sql="select * from team where 1 = 1 "; 
// 如果用户输入了名称,就模糊查询 
and teamName like '%?%' 
// 如果用户输入了日期,按照日期区间查询 
and createTime> ? and createTime< ? 
//如果输入了地区,按照地区查询 
and location =?";*/ 
 
if(vo.getName()!=null && !"".equals(vo.getName().trim())){ 
    sql+=" and teamName like '%"+vo.getName().trim()+"%'"; 
}
 
if(vo.getBeginTime()!=null ){ 
    sql+=" and getEndTime>"+vo.getBeginTime(); 
}
 
if(vo.getBeginTime()!=null ){ 
    sql+=" and createTime<="+vo.getEndTime(); 
}
 
if(vo.getLocation()!=null && !"".equals(vo.getLocation().trim())){ 
    sql+=" and location ="+vo.getLocation().trim(); 
} 

(2)自己封装的查询条件类QueryTeamVO.java

package com.lxy.pojo;

import java.util.Date;

public class QueryTeamVO {
    private String name;
    private Date beginTime ;
    private Date endTime;
    private String location;

    public String getName() {
        return name;
    }

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

    public Date getBeginTime() {
        return beginTime;
    }

    public void setBeginTime(Date beginTime) {
        this.beginTime = beginTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }
}

(3)TeamMapper.java接口添加:

List<Team> queryByVO(QueryTeamVO vo);

(4)TeamMapper.xml映射文件添加:

    <!--多条件查询:
        模糊查询的写法可以使用3种方式:
        方式1: and teamName like #{name} ,传递参数的时候带上%,例如vo.setName("%人%")
        方式2: and teamName like ‘%${name}%’ 传递参数的时候没有%,例如vo.setName("人")
        方式3:  and teamName like concat(concat('%',#{name}),'%')  例如vo.setName("人")
        concat(str1,str2)函数是字符串拼接使用-->
    <select id="queryByVO" parameterType="QueryVO" resultMap="baseResultMap">
        select * from team
        <where>
            <!-- 如果用户输入了名称,就模糊查询   and teamName like '%?%'-->
            <if test="name!=null ">
                and teamName like concat(concat('%',#{name}),'%')
            </if>
            <if test="beginTime!=null ">
                and createTime>=#{beginTime}
            </if>
            <if test="endTime!=null ">
                and createTime&lt;=#{endTime}
            </if>
            <if test="location!=null ">
                and location=#{location}
            </if>
        </where>
    </select>

(5)测试方法

package com.lxy.test;

import com.lxy.mapper.TeamMapper;
import com.lxy.pojo.QueryTeamVO;
import com.lxy.pojo.Team;
import com.lxy.util.MybatisUtil;
import org.junit.Test;

import java.util.Date;
import java.util.List;

public class TestSql {
    private TeamMapper mapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);

    @Test
    public void test01(){
        QueryTeamVO vo = new QueryTeamVO();
        vo.setName("球");
        vo.setEndTime(new Date());
        vo.setLocation("北京");
        List<Team> teams = mapper.queryByVO(vo);
        teams.forEach(team -> System.out.println(team));
    }
}

测试结果: 

11.2 set标签在update中的使用

之前的做法如果没给其余属性赋值,那么null值会直接覆盖之前属性的值,所以并不合理

11.2.1 更新的原有写法

TeamMapper.java接口中的方法:

int update(Team team);

TeamMapper.xml映射文件对应的内容:

<update id="update" parameterType="com.kkb.pojo.Team">
    update team set teamName=#{teamName},location=#{location},createTime=#{createTime}
    where teamId=#{teamId}
</update>

 测试类中添加测试方法:

@Test
public void test2(){
    Team team=new Team();
    team.setTeamId(1055);
    team.setTeamName("lina");
    int update = teamMapper.update(team);
    MybatisUtil.getSqlSession().commit();
    System.out.println(update);
}

测试结果:

11.2.2 使用set标签构建动态的SQL语句

TeamMapper.java接口中添加方法:

int update1(Team team);

TeamMapper.xml映射文件对应的内容:

    <update id="update1" parameterType="com.lxy.pojo.Team">
        update team
        <set>
            <if test="teamName!=null">
                teamName=#{teamName}
            </if>
            <if test="location!=null">
                location=#{location},
            </if>
            <if test="createTime!=null">
                createTime=#{createTime},
            </if>
            where teamId=#{teamId}
        </set>
    </update>

测试类:

    @Test
    public void test02(){
        Team team=new Team();
        team.setTeamId(1020);
        team.setTeamName("lina");
        int update = mapper.update1(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println(update);
    }

测试结果: 

11.3 forEach标签

11.3.1 批量添加

 TeamMapper.java接口中添加方法:

void addList(List<Team> list);

TeamMapper.xml映射文件对应的内容: 

    <!--批量添加-->
    <insert id="addList" parameterType="arrayList">
            insert into team (teamName,location) values
            <!--collection:要遍历的集合:参数是集合类型,直接写list
            item:遍历的集合中的每一个数据
            separator:将遍历的结果用 , 分割开-->
            <foreach collection="list" item="t" separator=",">
                (#{t.teamName},#{t.location})
            </foreach>
    </insert>

编写测试类:

    @Test
    public void test03(){
        List<Team> list=new ArrayList<>();
        for(int i=1;i<=3;i++){
            Team team=new Team();
            team.setTeamName("lina"+i);
            team.setLocation("bj"+i);
            list.add(team);
        }
        mapper.addList(list);
        MybatisUtil.getSqlSession().commit();
    }

测试结果:

11.3.2 批量删除

 TeamMapper.java接口中添加方法:

void delList(List<Integer> list);

TeamMapper.xml映射文件对应的内容: 

<delete id="delList" >
    delete from team where teamId in

    <!--collection:要遍历的集合;参数是集合类型,直接写list
    item:遍历的集合中的每一个数据
    separator:将遍历的结果用,分割
    open="(" close=")":表示将遍历结果用open close包裹起来-->

    <foreach collection="list" item="teamId" separator="," open="(" close=")">
        #{teamId}
    </foreach>
</delete>

 编写测试类:

    @Test
    public void test04(){
        List<Integer> list = new ArrayList<>();
        list.add(1021);
        list.add(1022);
        list.add(1023);
        mapper.delList(list);
        MybatisUtil.getSqlSession().commit();
    }

测试结果:

十二、分页插件

12.1 jar依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.10</version>
</dependency>

12.2 在Mybatis全局配置文件中添加插件配置 

<!-- 引入 pageHelper插件 -->
    <!--注意这里要写成PageInterceptor, 5.0之前的版本都是写PageHelper, 5.0之后要换成PageInterceptor-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!--reasonable:分页合理化参数,默认值为false,直接根据参数进行查询。
                当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数        时),会查询最后一页。
                方言可以省略,会根据连接数据的参数url自动推断-->
            <!--<property name="reasonable" value="true"/>-->
    </plugin>
</plugins>

12.3 使用插件

    @Test
    public void test5() {
        // PageHelper.startPage 必须紧邻查询语句,而且只对第一条查询语句生效
        PageHelper.startPage(2,5);
        List<Team> teams = mapper.queryAll();//查询语句结尾不能有分号
        teams.forEach(team-> System.out.println(team));
        PageInfo<Team> info=new PageInfo<>(teams);
        System.out.println("分页信息如下:");
        System.out.println("当前页:"+info.getPageNum());
        System.out.println("总页数:"+info.getPages());
        System.out.println("前一页:"+info.getPrePage());
        System.out.println("后一页:"+info.getNextPage());
        System.out.println("navigatepageNums:"+info.getNavigatepageNums());
        for (int num : info.getNavigatepageNums()) {
            System.out.println(num);
        }
    }

 测试结果:

PageInfo.java的部分源码:

package com.github.pagehelper;
import java.util.Collection;
import java.util.List;
/**
* 对Page<E>结果进行包装
* <p/>
* 新增分页的多项属性,主要参考:http://bbs.csdn.net/topics/360010907
*
* @author liuzh/abel533/isea533
* @version 3.3.0
* @since 3.2.2
* 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class PageInfo<T> extends PageSerializable<T> {
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总页数
private int pages;
//前一页
private int prePage;
//下一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;

十三、Mybatis缓存

13.1 缓存作用

 缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。将经常查询的数据存在缓存(内存)中,用户查询该数据的时候不需要从磁盘(关系型数据库文件)上查询,而是直接从缓存中查询,提高查询效率,解决高并发问题。

MyBatis 也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口。

Mybatis的缓存结构体系:

 

13.2 一级缓存:自动开启,SqlSession级别的缓存 

在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。

当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。


Mybatis默认开启一级缓存,存在内存中(本地缓存)不能被关闭,可以调用clearCache()来清空本地缓存,或者改变缓存的作用域。

13.2.1 一级缓存分析 

工作原理图:

当用户发起第一次查询team=1001的时候,先去缓存中查找是否有team=1001的对象;如果没有,继续向数据中发送查询语句,查询成功之后会将teamId=1001的结果存入缓存中;


当用户发起第2次查询team=1001的时候,先去缓存中查找是否有team=1001的对象,因为第一次查询成功之后已经存储到缓存中,此时可以直接从缓存中获取到该数据,意味着不需要再去向数据库发送查询语句。


如果SqlSession执行了commit(有增删改的操作),此时该SqlSession对应的缓存区域被整个清空,目的避免脏读。

前提:SqlSession未关闭。

测试:

    @Test
    public void test13(){
        Team t1=sqlSession.selectOne("com.lxy.mapper.TeamMapper.queryById",1001);//第一次查询,先查缓存,此时缓存中没有,继续向数据库发送查询语句
        System.out.println(t1);//查询完毕之后数据被自动存入缓存区域
        Team t2=sqlSession.selectOne("com.lxy.mapper.TeamMapper.queryById",1001);//第二次查询,因为缓存中已经有了该数据,可以直接获取,不需要发送查询语句
        System.out.println(t2);
        MybatisUtil.closeSqlSession();//关闭连接,缓存清空
        sqlSession=MybatisUtil.getSqlSession();//再次获取连接,此时缓存为空
        Team t3=sqlSession.selectOne("com.lxy.mapper.TeamMapper.queryById",1001);//新连接下第一次查询,肯定发送查询语句
        System.out.println(t3);//查询完毕之后数据被自动存入缓存区域
        int num=sqlSession.delete("com.lxy.mapper.TeamMapper.del",10000);
        sqlSession.commit();//提交之后缓存被整个清空
        System.out.println("删除结果:"+num);
        Team t4=sqlSession.selectOne("com.lxy.mapper.TeamMapper.queryById",1001);//第二次查询,因为缓存已经被上一次的提交清空了,所以还是需要发送查询语句
        System.out.println(t4);
        sqlSession.close();
    }

 测试结果:

13.2.2 清空缓存的方式

1、 session.clearCache( ) ;
2、 execute update(增删改) ;
3、 session.close( );
4、 xml配置 flushCache="true" ;
5、 rollback;
6、 commit。

13.3 二级缓存:Mapper级别的缓存

多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。 

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace。

不同的sqlSession两次执行相同namespace下的sql语句参数相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。

Mybatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存。

如果缓存中有数据就不用从数据库中获取,大大提高系统性能。

二级缓存原理图:

13.3.1 使用二级缓存步骤 

二级缓存是mapper范围级别的,默认不启用。

1、在Mybatis框架的全局配置文件中开启二级缓存

<!--是否开启二级缓存,默认false-不开启, true:开启-->
<setting name="cacheEnabled" value="true"/>

2、在需要二级缓存的Mapper中添加缓存标志

3、实体类必须实现Serializable接口

4、测试二级缓存

如果两个session不是从同一个Factory获取,那么二级缓存将不起作用。

    @Test
    public void test2() {
        SqlSession sqlSession1 = MybatisUtil.getSqlSession();
        Team t1 = sqlSession1.selectOne("com.lxy.mapper.TeamMapper.queryById", 1001);//先查缓存,没有,先数据库,查询完毕写入二级缓存
        System.out.println(t1);
        MybatisUtil.closeSqlSession();//关闭连接,一级缓存清空,二级缓存存在
 
        SqlSession sqlSession2 = MybatisUtil.getSqlSession();
        Team t2 = sqlSession2.selectOne("com.lxy.mapper.TeamMapper.queryById", 1001);//先查缓存,有,直接获取,不需要查询数据库
        System.out.println(t2);
        MybatisUtil.closeSqlSession();//关闭连接,一级缓存清空,二级缓存存在
 
        SqlSession sqlSession3 = MybatisUtil.getSqlSession();
        int num = sqlSession3.delete("com.lxy.mapper.TeamMapper.del", 10000);//删除成功
        System.out.println("删除的结果:" + num);
        sqlSession3.commit();//提交之后清空二级缓存
        MybatisUtil.closeSqlSession();//关闭连接,缓存清空
 
        SqlSession sqlSession4 = MybatisUtil.getSqlSession();
        Team t3 = sqlSession4.selectOne("com.lxy.mapper.TeamMapper.queryById", 1001);先查缓存,曾经有,但是上一个提交已经清空了缓存,所以只能去数据库中查询,查询完毕写入二级缓存
        System.out.println(t3);
        MybatisUtil.closeSqlSession();//关闭连接,缓存清空
    }

13.3.2 二级缓存的禁用 

对于变化比较频繁的SQL,可以禁用二级缓存。


在开始了二级缓存的XML中对应的statement中设置useCache=false禁用当前Select语句的二级缓存,意味着该SQL语句每次只需都去查询数据库,不会查询缓存。


useCache默认值是true。对于一些很重要的数据尽不放在二级缓存中。

13.3.3 缓存的属性配置 

<cache>
    <property name="eviction" value="LRU"/><!--回收策略为LRU-->
    <property name="flushInterval" value="60000"/><!--自动刷新时间间隔为60S-->
    <property name="size" value="1024"/><!--最多缓存1024个引用对象-->
    <property name="readOnly" value="true"/><!--只读-->
</cache>

源码:

@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.TYPE) 
public @interface CacheNamespace { 
    Class<? extends Cache> implementation() default PerpetualCache.class; 
    Class<? extends Cache> eviction() default LruCache.class; 
    long flushInterval() default 0; 
    int size() default 1024; 
    boolean readWrite() default true; 
    boolean blocking() default false; 
    Property[] properties() default {}; 
}
 
/**属性介绍: 
1.映射语句文件中的所有select语句将会被缓存; 
2.映射语句文件中的所有CUD操作将会刷新缓存; 
3.缓存会默认使用LRU(Least Recently Used)算法来收回; 
3.1、LRU – 最近最少使用的:移除最长时间不被使用的对象。 
3.2、FIFO – 先进先出:按对象进入缓存的顺序来移除它们。 
3.3、SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 
3.4、WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 
4.缓存会根据指定的时间间隔来刷新(默认情况下没有刷新间隔,缓存仅仅调用语句时刷新);
 
5.缓存会存储列表集合或对象(无论查询方法返回什么),默认存储1024个对象。 
6.缓存会被视为是read/write(可读/可写)的缓存,意味着检索对象不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
**/

如果想在命名空间中共享相同的缓存配置和实例,可以使用cache-ref 元素来引用另外一个缓存。

<cache-ref namespace="com.lxy.mapper.TeamMapper" />
//引用TeamMapper命名空间中的cache。

十四、反向生成插件 

14.1 插件的配置

在pom.xml文件中的中中添加如下插件配置:

            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <!--配置文件的路径-->
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.5</version>
                    </dependency>
                </dependencies>
            </plugin>

generatorConfig.xml内容:

其内容需要自己改一部分

<?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>
    <!--1、数据库驱动jar:添加自己的jar路径 -->
    <classPathEntry
            location="D:\repository\mysql\mysql-connector-java\8.0.23\mysql-connector-java-8.0.23.jar" />
    <context id="MyBatis" targetRuntime="MyBatis3">
        <!--去除注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--2、数据库连接 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=GMT"
                        userId="root"
                        a="???">
        </jdbcConnection>
        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer;
        为 true时把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
        <!--3、生成实体类 指定包名 以及生成的地址 (可以自定义地址,但是路径不存在不会自动创建
        使用Maven生成在target目录下,会自动创建) -->
        <javaModelGenerator targetPackage="org.xzk.pojo"
                            targetProject="src\main\java">
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!--4、生成SQLmapper.xml映射文件 -->
        <sqlMapGenerator targetPackage="org.xzk.mapper"
                         targetProject="src\main\resources">
        </sqlMapGenerator>
        <!--5、生成Dao(Mapper)接口文件,-->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="org.xzk.mapper"
                             targetProject="src\main\java">
        </javaClientGenerator>
        <!--6、要生成哪些表(更改tableName和domainObjectName就可以) -->
        <!-- tableName:要生成的表名
        enableCountByExample:Count语句中加入where条件查询,默认为true开启
        enableUpdateByExample:Update语句中加入where条件查询,默认为true开启
        enableDeleteByExample:Delete语句中加入where条件查询,默认为true开启
        enableSelectByExample:Select多条语句中加入where条件查询,默认为true开启
        selectByExampleQueryId:Select单个对象语句中加入where条件查询,默认为true开启
        -->

        <table tableName="team">
            <property name="useActualColumnNames" value="true"/>
        </table>
        <table tableName="player">
            <property name="useActualColumnNames" value="true"/>
        </table>
        <table tableName="gameRecord">
            <property name="useActualColumnNames" value="true"/>
        </table>
    </context>
</generatorConfiguration>

注意只能运行一次,运行完毕显示BUILD SUCCESS即为成功。

 如果想重新生成,需要将之前生成的删除掉

14.2 反向生成插件的使用

1.使用时需要注意,生成后所使用的mapper需要在mybatis.xml中注册

2.使用时还需注意,如果其他数据库的表名和你需要生成的表名相同,那么你有可能会生成两个baseResultMap,使用时一点会报错

package com.lxy.test;

import com.lxy.util.MybatisUtil;
import org.junit.Test;
import org.xzk.mapper.TeamMapper;
import org.xzk.pojo.Team;
import org.xzk.pojo.TeamExample;

import java.util.List;

/**
 * 测试反向生成的内容
 */
public class TestGenerator {
    private TeamMapper mapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);

    @Test
    public void test1(){
        Team team = mapper.selectByPrimaryKey(1001);
        System.out.println(team);
    }
    @Test
    public void test2(){
        Team team = new Team();
        team.setTeamName("LALALA");
        team.setLocation("bjjj");
        int i = mapper.insertSelective(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println(i);
    }
    @Test
    public void test3(){
        //理解为为多条件、排序等服务的类
        TeamExample example = new TeamExample();
        //理解为盛放条件的容器
        TeamExample.Criteria criteria = example.createCriteria();
        //向容器中添加条件
        criteria.andTeamNameLike("%队%");
        //criteria.andTeamIdBetween(1001,1020);
        example.setOrderByClause("teamId asc");
        List<Team> teams = mapper.selectByExample(example);
        for (Team team : teams) {
            System.out.println(team);
        }
    }
}

如果你读到了这里,那请给博主点个攒吧!如果你有什么问题,可以在评论区留言,我会及时回答你。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月亮被咬碎成星星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值