10、查询专题

一、准备工作

  • 创建新的模块: mybatis-008-select

  • 该模块操作的还是 t_car 表,所以我们直接将之前的Car、jdbc.properties、SqlSessionUtil、logback.xml拿过来用

  • 利用我们之前自制的idea模板创建核心配置文件和映射文件

  • 我们在核心配置文件中 将pojo类统一起别名、并统一将所有的映射文件导入【映射文件与对应结构在一个目录下】

  • 该模块整体的目录结构如下:

     

  • 本篇概述:【查询的各种返回类型接收问题、起别名问题、查询数据总条数】

     


二、返回Car与List

  • 概述:这部分的讨论都是通过三个文件来说明问题的【如何使用】
  • 三个文件:映射文件Mapper、对应的接口、单元测试方法

🌔 1、标题有什么含义?

  • 代表的是查询结果的两种封装方式
  • Car 代表通过我们定义的pojo类的对象封装
  • List 代表利用集合来封装我们的查询结果,List的类型为Car

🌔 2、通过几个案例来区别如何使用他们?

(1)根据 id 查询一条数据,返回 Car 的对象

接口中的方法:

/**
* 根据id查询汽车信息
* @param id
* @return
*/
Car selectById(Long id);

映射文件中的SQL语句:

<select id="selectById" resultType="car">
        select
                id,
                car_num as carNum,
                brand,
                guide_price as guidePrice,
                produce_time as produceTime,
                car_type as carType
            from
                t_car
            where
                id = #{

     id}
</select>

测试类中的test方法:

@Test
public void testSelectById(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    Car car = mapper.selectById(27L);
    System.out.println(car);
    sqlSession.close();
}

测试结果: 成功显示我们根据 id 想要查询的汽车信息
&nbsp;

(2)查询全部汽车数据,返回 List<Car> 的对象

接口中的方法:

/**
* 获取所有的汽车信息
* @return
*/
List<Car> selectAll();

映射文件中的SQL语句:

<select id="selectAll" resultType="car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from
            t_car
</select>

测试类中的test方法:

@Test
public void testSelectAll()
{

    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    List<Car> cars = mapper.selectAll();
    cars.forEach(car -> System.out.println(car));
    sqlSession.close();
}

测试结果: 成功显示数据库表中的全部汽车信息
&nbsp;

(3)根据品牌模糊查询,利用Car来接收查询结果

接口中的方法:

/**
* 根据品牌模糊查询
* 查询结果可能有多个,但是我们采用一个pojo对象来接收 >> 检测是否会产生问题
* 测试结果:TooManyResultsException异常
* @param brand
* @return
*/
Car selectByBrandLike(String brand);

映射文件中的SQL语句:

<select id="selectByBrandLike" resultType="car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from
            t_car
        where
            brand like "%"#{

     brand}"%"
</select>

测试类中的test方法:

@Test
public void testSelectByBrandLike(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    Car car = mapper.selectByBrandLike("奔驰");
    System.out.println(car);
    sqlSession.close();
}

测试结果: 抛出TooManyResultsException异常,因为一个Car的对象不能接收多个查询结果
&nbsp;

(4)根据id查询汽车数据,利用List来接收查询结果

接口中的方法:

/**
* 根据id查询汽车数据,然后用一个集合来接收
* 就是测试一条查询结果能不能用集合来接收
* 测试结果:必然是可以的
* @param id
* @return
*/
List<Car> selectById2(Long id);

映射文件中的SQL语句:

<select id="selectById2" resultType="car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from
            t_car
        where
            id = #{

     id}
</select>

测试类中的test方法:

@Test
public void testSelectById2(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    List<Car> cars = mapper.selectById2(28L);
    cars.forEach(car -> System.out.println(car));
    sqlSession.close();
}

测试结果: 测试成功,可以正常输出我们指定 id 的汽车信息
&nbsp;


三、利用Map封装查询结果

🌔 1、在上面的内容无论是否采用集合的形式,我们都是通过pojo类来封装数据,那么如果没有合适的pojo类怎么办呢?

  • 我们可以采用Map集合的方式来存储我们的查询结果
  • key 对应表中字段名,value 对应属性值
  • 一个 map 的对象可以存储一条数据记录

🌔 2、我们尝试根据 id 查询一条汽车信息,利用 Map 集合来接收查询结果【根据id查询只能获得一条记录】

接口中的方法:

/**
* 根据id查询汽车信息,将汽车信息封装到Map集合中
* @param id
* @return
*/
Map<String, Object> selectByIdRetMap(Long id);

映射文件中的SQL语句:

<!--此处封装查询结果的类型使用了 java.util.Map 的别名-->
<select id="selectByIdRetMap" resultType="map">
    select * from t_car where id = #{

     id}
</select>

测试类中的test方法:

@Test
public void testSelectByIdRetMap(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    Map<String, Object> car = mapper.selectByIdRetMap(28L);
    System.out.println(car);
    sqlSession.close();
}

测试结果: 可以正常获得指定 id 的汽车信息
&nbsp;

🌔3、如果查询结果为多条数据,我们可以用List集合嵌套Map集合来接收查询结果

接口中的方法:

/**
* 查询所有的Car信息,返回一个存放Map集合的List集合
* @return
*/
List<Map<String, Object>> selectAllRetListMap();

映射文件中的SQL语句:

<select id="selectAllRetListMap" resultType="map">
    select * from t_car;
</select>

测试类中的test方法:

@Test
public void testSelectAllRetListMap(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    List<Map<String, Object>> cars = mapper.selectAllRetListMap();
    cars.forEach(car -> System.out.println(car));
    sqlSession.close();
}

测试结果: 可以正常获得全部的汽车信息
&nbsp;

🌔 4、采用这种方式如果我们想取一条指定id的记录很不方便,那是否可以采用其他存储结构呢?

  • 我们可以采用大Map套小Map的形式,小Map和上面的一样
  • Mapkey我们可以存储表的主键,value可以通过map存储具体的一条记录信息

接口中的方法:

/**
* 查询所有的汽车信息,返回一个大Map集合
* Map集合的key是每条记录的主键[此处为id],value为每条记录
* 需要通过注解 @MapKey("主键名")指定大Map的key
* @return
*/
@MapKey("id")
Map<Long, Map<String, Object>> selectAllRetMap();

映射文件中的SQL语句:

<select id="selectAllRetMap" resultType="map">
    select * from t_car;
</select>

测试类中的test方法:

@Test
public void testSelectAllRetMap(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    Map<Long, Map<String, Object>> map = mapper.selectAllRetMap();
    System.out.println(map);
    sqlSession.close();
}

测试结果: 打印显示的是 key1 = value1, key2 = value2,…的形式
&nbsp;


四、ResultMap结果映射

  • 这部分主要阐述的是:如果表中字段名与类中属性名对不上,可以采用三种方法
  • 方案一:在SQL语句中,我们直接通过关键字 as 来起别名 【不在此部分进行举例使用了】
  • 方案二:在映射文件中通过 resultMap标签提前配置好别名,在SQL语句中使用 resultMap 声明即可
  • 方案三:如果字段的命名和属性的命名都使用指定的规则,可以通过在核心配置文件中开启全局驼峰命名映射自动处理

🌔1、如果通过 resultMap 配置别名信息?

(1)找到我们的Mapper映射文件,在其中添加 resultMap 标签

<!--
        我们需要提前在mapper标签中定义一个结果映射[数据库表中字段名——Java类属性名]
        通过 type属性指定POJO类的类名[可以使用我们在核心文件中配置的别名],通过id属性定义resultType的唯一标识
    -->
    <resultMap id="carResultMap" type="Car">
        <!--
            在resultMap标签内部我们可以通过 result 标签来配置具体的别名信息
            property属性指定我们在pojo类中的属性名
            column属性指定了我们在数据库表中的字段名
        -->
        <!--主键值我们通过id标签配置,可以提高效率-->
        <id property="id" column="id" />
        <!--如果属性名和字段名一致,我们可以不配置-->
        <result property="carNum" column="car_num" />
        <result property="guidePrice" column="guide_price"/>
        <result property="produceTime" column="produce_time" />
        <result property="carType" column="car_type" />
        <!--还可以指定javaType[属性名类型]和jdbcType[字段类型]可以提高效率-->
    </resultMap>

(2)在上面代码段里我写了一些注解,在此提炼一下

  • id 代表我们这个映射的唯一标识,type 代表我们要对哪个pojo类进行映射【可以用别名】

  • 对于主键我们最好用 id 标签声明,这样可以提高mybatis的效率

  • 其余内部需要起别名的字段,我们通过 result 标签来完成

  • property 代表我们pojo类属性名

  • column 代表表中的列名

🌔2、如何使用我们配置的 resultMap 呢?

  • 在写我们的 select 语句时,不需要指定 resultType,替换成 resultMap = “别名映射的id” 即可
<select id="selectAllByResultMap" resultMap="carResultMap">
    select * from t_car
</select>

对应的测试程序:

@Test
public void testSelectAllByResultMap(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    List<Car> cars = mapper.selectAllByResultMap();
    cars.forEach(car -> System.out.println(car));
    sqlSession.close();
}

对应的接口方法:

/**
* 查询所有的汽车信息,使用resultMap标签进行结果映射
* @return
*/
List<Car> selectAllByResultMap();

运行结果如下:
&nbsp;

🌔 3、如何开启驼峰命名自动映射?

  • 找到我们本模块的核心配置文件 mybatis-config.xml
  • properties标签下面添加, settings 标签,在其内部配置具体的 setting 标签
<!--通过全局设置,开启驼峰映射代替起别名-->
<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
  • 使用驼峰命名自动映射机制有一个前提:属性名遵循Java命名规范、列名遵循SQL命名规范

  • Java 命名规范:首字母小写,后面每个单词首字母大写,遵循驼峰命名方式

  • SQL 命名规范:全部小写,单词之间采用下划线分割

🌔 4、如何使用驼峰命名自动映射?

(1)开启驼峰命名自动映射机制,编写接口方法

/**
* 查询全部汽车数据,但是启用了驼峰命名自动映射机制
* @return
*/
List<Car> selectAllByMapUnderscoreToCamelCase();

(2)编写SQL语句

<select id="selectAllByMapUnderscoreToCamelCase" resultType="Car">
    select * from t_car
</select>

(3)编写测试程序

@Test
public void testSelectAllByMapUnderscoreToCamelCase(){

    SqlSession sqlSession = SqlSessionUtil.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);
    List<Car> cars = mapper.selectAllByMapUnderscoreToCamelCase();
    cars.forEach(car -> System.out.println(car));
    sqlSession.close();
}

(4)运行测试 >> 可以正常使用
&nbsp;
🌔 5、补充知识点:统计表中所有数据的条数

接口方法:

/**
* 获取汽车信息的总记录条数
* @return
*/
Long selectTotal();

SQL 语句: 如果count指定的列名,那么如果存在空就不会统计当前这条数据

<select id="selectTotal" resultType="long">
    select count(*) from t_car
</select>

测试结果: 统计成功
&nbsp;