文章目录
1、结果处理
1.1、简单类型输出映射!
简单类型,比如int,注意哦,一些类型的resultType,mybatis已经帮我们定义好了,我们直接用人家定义好的就行了!
详情见:https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases
public interface CarsMapper {
int findAllCars();
}
<select id="findAllCars" resultType="int">
select count(*) from cars
</select>
@Test
public void findAllCars(){
System.out.println(mapper.findAllCars());
}
[DEBUG] 2021-04-08 16:35:02,038 ==> Preparing: select count(*) from cars
[DEBUG] 2021-04-08 16:35:02,084 ==> Parameters:
[DEBUG] 2021-04-08 16:35:02,233 <== Total: 1
3
1.2、pojo对象输出映射!
之前,我们测试了,如果列名和我们的属性名一致的话,那么mybatis会自动映射,将查询结果封装到对象中。
那如果我们数据库中的列名是game_desc呢? 单独列名desc是通不过的,那我们只能起game_desc,那我们pojo的Game对象呢,总不能起game_desc把,这样就不规范了,我们标准是不是驼峰啊,gameDesc,那么问题来了,我们按照原来的方法是不是就映射不了啊,mybatis虽然厉害,但他也不能明白你的意思,对不对,他只能对队列名和属性名一致的来进行封装数据!
一种解决方法是我们在mybatis-config.xml文件中配置
<setting name=“mapUnderscoreToCamelCase” value=“true”/>,
这个时候,他才会为我们自动映射!
那好,我们来测试一下
创建game表
CREATE TABLE game(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
game_desc VARCHAR(10)
)
创建Game实体类
看不懂注解,请移位:1.4.3小节!
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Game {
private int id;
private String name;
private String gameDesc;
}
GameMapper接口!
public interface GameMapper {
List<Game> queryAllGameInfo();
}
GameMapper.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tian.mapper.GameMapper">
<select id="queryAllGameInfo" resultType="game">
select * from game
</select>
</mapper>
记得一定要在mapper注册GameMapper.xml ,一定要记得!!!
测试
@Test
public void queryAllGameInfos(){
System.out.println(gameMapper.queryAllGameInfo());
}
结果! 是不是,我们的gameDesc经过配置后,也可以封装到poji对象了!
[DEBUG] 2021-04-08 16:57:33,651 ==> Preparing: select * from game
[DEBUG] 2021-04-08 16:57:33,687 ==> Parameters:
[DEBUG] 2021-04-08 16:57:33,724 <== Total: 4
[Game(id=1, name=lol, gameDesc=塔防类), Game(id=2, name=cf, gameDesc=射击类), Game(id=3, name=dnf, gameDesc=地下类), Game(id=4, name=qq, gameDesc=聊天类)]
如果我们去了呢,来试下嘛,肯定是有问题的,这不用说!
看嘛,是不是获取不到gameDesc, gameDesc=null
[DEBUG] 2021-04-08 17:00:39,544 ==> Preparing: select * from game
[DEBUG] 2021-04-08 17:00:39,588 ==> Parameters:
[DEBUG] 2021-04-08 17:00:39,619 <== Total: 4
[Game(id=1, name=lol, gameDesc=null), Game(id=2, name=cf, gameDesc=null), Game(id=3, name=dnf, gameDesc=null), Game(id=4, name=qq, gameDesc=null)]
1.3、定义resultMap
首先再学resultMap之前,思考一下我们什么要定义resultMap,定义它是要来解决与什么的?
- 在我们多表关联查询的时候,如果不加别名,查询的结果是不是可能有重复,我们自己写的可能不会太懵,那别人来看呢,别人调试一下你的代码,唉,这什么啊,为什么查出来有两个id,name啊,这是那个的name,id啊!
- 诶,这个时候问题就很严重了,起了别名是不是和数据库的属性又不对应了,即使我们上边设置了<setting name=“mapUnderscoreToCamelCase” value=“true”/> ,但是还是保不齐不发建立映射关系,那这个时候我们需要定义一个resultMap来帮我们解决这个问题!
案例
创建表 person,game如下
CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
game_id INT,
CONSTRAINT game_id_fk FOREIGN KEY(game_id) REFERENCES game(id)
)
CREATE TABLE game(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
game_desc VARCHAR(10)
)
Person实体类
看不懂注解,请移位:1.4.3小节!
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private int id;
private String name;
private Game game;
}
Game实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Game {
private int id;
private String name;
private String gameDesc;
}
我们先来跑一下不加别名的sql查询
SELECT
p.id,
p.name,
g.id,
g.name,
g.game_desc game_Desc
FROM
person p
LEFT JOIN game g
ON p.game_id = g.id
WHERE p.id = 1
是不是一头雾水啊,人都傻了!
我们起别名之后,就豁然开朗了,瞬间清晰了很多有没有
清晰归清晰,我们查出来能映射吗? 我们查询最终是以别名为准,别名和我们pojo属性一致吗?
- 很显然,并不一致,这个时候就有小伙伴说了,诶,我们将person属性设置的和别名一致不就好了吗,
- 答案毋庸置疑是可以的,但是你考虑过我们如果不只查这个表呢,我们又该怎么办,是不是还映射不出来,那这个时候,我们显然选择映射前者即字段名和属性名一致,我们定义resultMap解决映射!
ok我们来写查询语句!
PersonMapper接口
public interface PersonMapper {
List<Person> queryAll();
}
PersonMapper.xml文件
- select标签中的id对应我们接口中的方法名
- select标签中的resultMap对应我们定义的resultMap的id
- resultMap的type对应我们查的表对应的java实体类类型
- person表关联game表,我们association标签是用来封装game对象的
- association中的property对应person类中的 private Game game; 属性 即game
- association中javaType对应java实体类类型
- id标签的column,很明显,对应数据库列名,
- id标签的property,那就对应实体类中的属性喽!
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tian.mapper.PersonMapper">
<resultMap id="queryAllPersonInfo" type="Person">
<id column="pid" property="id"/>
<id column="pname" property="name"/>
<!-- 封装game对象 -->
<association property="game" javaType="Game">
<id column="gid" property="id"/>
<id column="gname" property="name"/>
<id column="game_desc" property="gameDesc"/>
</association>
</resultMap>
<select id="queryAll" resultMap="queryAllPersonInfo" >
SELECT
p.id pid,
p.name pname,
g.id gid,
g.name gname,
g.game_desc game_Desc
FROM
person p
LEFT JOIN game g
ON p.game_id = g.id
WHERE p.id=2
</select>
</mapper>
至此,我们就写完了,来测试一下
bingo! 我们的值是不是封装到了我们的实体类中,也就是说我们的列名和属性达到了映射!
[DEBUG] 2021-04-08 17:24:08,277 ==> Preparing: SELECT p.id pid, p.name pname, g.id gid, g.name gname, g.game_desc game_Desc FROM person p LEFT JOIN game g ON p.game_id = g.id WHERE p.id=2
[DEBUG] 2021-04-08 17:24:08,311 ==> Parameters:
[DEBUG] 2021-04-08 17:24:08,338 <== Total: 1
[Person(id=2, name=张三, game=Game(id=2, name=cf, gameDesc=射击类))]
1.4、resultMap使用(association,collection)注意!
当我们关联查询时候
-
如果实体类封装的是对象,我们使用association,
-
当我们封装的是对象集合的时候,用collection,
1.4.1、association
案例!
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private int id;
private String name;
private Game game; // 对象!
}
这个时候我们得用association
如果你非得对象用collection,会出现下述错误:简单的说就是注入不进去!用association就好了!
org.apache.ibatis.exceptions.PersistenceException:
Error querying database. Cause: org.apache.ibatis.executor.ExecutorException: Error instantiating collection property for result ‘game’. Cause: org.apache.ibatis.reflection.ReflectionException: Could not set property ‘game’ of ‘class com.tian.pojo.Person’ with value ‘[]’ Cause: java.lang.IllegalArgumentException: argument type mismatch
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tian.mapper.PersonMapper">
<resultMap id="queryAllPersonInfo" type="Person">
<id column="pid" property="id"/>
<id column="pname" property="name"/>
<!-- 封装game对象 -->
<association property="game" javaType="Game">
<id column="gid" property="id"/>
<id column="gname" property="name"/>
<id column="game_desc" property="gameDesc"/>
</association>
</resultMap>
<select id="queryAll" resultMap="queryAllPersonInfo" >
SELECT
p.id pid,
p.name pname,
g.id gid,
g.name gname,
g.game_desc game_Desc
FROM
person p
LEFT JOIN game g
ON p.game_id = g.id
WHERE p.id=2
</select>
</mapper>
安全通过!
[DEBUG] 2021-04-08 17:33:11,578 ==> Preparing: SELECT p.id pid, p.name pname, g.id gid, g.name gname, g.game_desc game_Desc FROM person p LEFT JOIN game g ON p.game_id = g.id WHERE p.id=2
[DEBUG] 2021-04-08 17:33:11,614 ==> Parameters:
[DEBUG] 2021-04-08 17:33:11,636 <== Total: 1
[Person(id=2, name=张三, game=Game(id=2, name=cf, gameDesc=射击类))]
1.4.2、collection
当我们用的是对象的集合时,注意,这里 写法有些区稍稍不一样!
我们管理查询的时候,需要变动一下,由原来的 javaType=“Game” 变为 --> javaType=“list” ofType=“Game”
-
javaType是我们 private List game; 返回的类型,list已经返回已经被mybatis定义了,我们直接小写list即可,或者 java.util.List ,写全类名!
-
ofType是 private List<Game> game; 的泛型,即关联表映射的pojo类类型!
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private int id;
private String name;
private List<Game> game; // 对象的集合
}
这里我们得用collection
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tian.mapper.PersonMapper">
<resultMap id="queryAllPersonInfo" type="Person">
<id column="pid" property="id"/>
<id column="pname" property="name"/>
<!-- 封装game集合 -->
<collection property="game" javaType="list" ofType="Game">
<id column="gid" property="id"/>
<id column="gname" property="name"/>
<id column="game_desc" property="gameDesc"/>
</collection>
</resultMap>
<select id="queryAll" resultMap="queryAllPersonInfo" >
SELECT
p.id pid,
p.name pname,
g.id gid,
g.name gname,
g.game_desc game_Desc
FROM
person p
LEFT JOIN game g
ON p.game_id = g.id
WHERE g.id=2
</select>
</mapper>
如果我们用association的话,还是会出一样的错误,出现这类错误,我们就能定位到是哪里出现了错误!
Cause: org.apache.ibatis.reflection.ReflectionException: Could not set property ‘game’ of ‘class com.tian.pojo.Person’ with value ‘Game(id=2, name=cf, gameDesc=射击类)’ Cause: java.lang.IllegalArgumentException: argument type mismatch
为了确保准确,这里我们查询条件换为g.id = 2,
[DEBUG] 2021-04-08 17:36:36,243 ==> Preparing: SELECT p.id pid, p.name pname, g.id gid, g.name gname, g.game_desc game_Desc FROM person p LEFT JOIN game g ON p.game_id = g.id WHERE g.id=2
[DEBUG] 2021-04-08 17:36:36,278 ==> Parameters:
[DEBUG] 2021-04-08 17:36:36,405 <== Total: 2
[Person(id=2, name=张三, game=[Game(id=2, name=cf, gameDesc=射击类)]), Person(id=5, name=李四, game=[Game(id=2, name=cf, gameDesc=射击类)])]
1.5、懒加载
-
需要查询关联信息时,使用 Mybatis 懒加载特性可有效的减少数据库压力,
-
首次查询只查询主表信息,关联表的信息在用户获取时再加载。
-
Mybatis 一对一关联的 association 和一对多的 collection 可以实现懒加 载。
-
懒加载时要使用 resultMap,不能使用 resultType。
懒加载
设置项 | 描述 | 允许值 | 默认值 |
---|---|---|---|
lazyLoadingEnabled | 是否开启懒加载模式 | true | false | false |
启动懒加载
在mybatis-config.xml中的settings标签中设置
<setting name="lazyLoadingEnabled" value="true"/>
在关联表即从表设置
fetchType="lazy"
如果是这样,表示不启动懒加载
fetchType="eager"
<association property="game" javaType="Game" fetchType="lazy"
select="findGameByID" column="game_id"></association>
(1). Select:指定关联查询懒加载对象的 Mapper Statement ID 为
findGameByID
(2). column=“game_id”:关联查询时将 game_id列的值传入 findGameByID,
并将 findGameByID查询的结果映射到 Person的 Game属性中
(3).collection 和 association 都需要配置 select 和 column 属性,两者配置方法
相同