上一篇内容我们使用了Mybatis去查询数据库中的一条数据,本篇我们在上一篇的基础上对结果映射进行详细介绍一下。
如果您对查询数据不太了解,可以参考:
Mybatis查询一条数据https://blog.csdn.net/m1729339749/article/details/132469672
一、准备数据
这里我们直接使用脚本初始化数据库中的数据
-- 如果数据库不存在则创建数据库
CREATE DATABASE IF NOT EXISTS demo DEFAULT CHARSET utf8;
-- 切换数据库
USE demo;
-- 创建用户表
CREATE TABLE IF NOT EXISTS T_USER(
ID INT PRIMARY KEY,
USERNAME VARCHAR(32) NOT NULL,
AGE INT NOT NULL
);
-- 插入用户数据
INSERT INTO T_USER(ID, USERNAME, AGE)
VALUES(1, '张三', 20),(2, '李四', 22),(3, '王五', 24);
创建了一个名称为demo的数据库;并在库里创建了名称为T_USER的用户表并向表中插入了数据
二、定义实体类
在cn.horse.demo包下创建UserInfo实体类,里面包含了一个无参数构造方法和一个带有两个参数的构造方法;
为了方便打印用户的信息这里重写了ToString()方法
package cn.horse.demo;
public class UserInfo {
private Integer userId;
private String name;
private Integer age;
public UserInfo() { }
public UserInfo(int userId, String name) {
this.userId = userId;
this.name = name;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append('[');
result.append("userId: " + this.userId);
result.append(", ");
result.append("name: " + this.name);
result.append(", ");
result.append("age: " + this.age);
result.append(']');
return result.toString();
}
}
三、配置Mapper文件
这里我在resources下创建了一个demo3的目录,并在目录下创建了一个UserInfoMapper1.xml的配置文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.horse.demo.UserInfoMapper1">
<select id="findById" resultType="cn.horse.demo.UserInfo">
SELECT *
FROM T_USER
WHERE ID = #{id}
</select>
</mapper>
这里名称空间指定为cn.horse.demo.UserInfoMapper1
查询标签的编号指定为findById, 返回类型为cn.horse.demo.UserInfo,SQL语句为根据ID查询用户信息
四、引入Mapper配置
Mapper配置完成后,并没有生效;需要在mybatis-config.xml文件中配置完成后才能生效;
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="jdbc:mysql://${ip}:${port}/${database}?useUnicode=true&useSSL=false&characterEncoding=utf8"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="demo3/UserInfoMapper.xml" />
</mappers>
</configuration>
五、查询数据
// 读取mybatis配置文件
InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
// 根据配置创建SqlSession工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = null;
try {
// 创建SqlSession
sqlSession = sqlSessionFactory.openSession();
// 根据用户编号查询一条用户信息
UserInfo userInfo = sqlSession.selectOne("cn.horse.demo3.UserInfoMapper.findById", 2);
System.out.println(userInfo);
} finally {
// 关闭会话
if(Objects.nonNull(sqlSession)) {
sqlSession.close();
}
}
代码执行的结果如下:
发现userId, name没有值,只有age字段有值
分析原因
在上一篇我们提到过,查询出来的数据转换成结果类型对象需要满足两个条件
- 结果类型提供无参数的构造方法
- 查询的列需要与结果类型的字段一一对应
原因就在于从表中查询的字段有ID, USERNAME, AGE;而结果类型的字段只有age能够对应上,导致userId, name字段无法绑定值。
解决方法
- 查询表数据时指定别名
- 使用ResultMap进行结果映射
六、查询表数据指定别名
这里我们在resources的demo目录下新建UserInfoMapper2配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.horse.demo.UserInfoMapper2">
<select id="findById" resultType="cn.horse.demo.UserInfo">
SELECT
ID userId,
USERNAME name,
AGE age
FROM T_USER
WHERE ID = #{id}
</select>
</mapper>
SQL语句中我们为表中的字段指定了别名,保证别名与返回类型的字段一致;
另外我们还需要在mybatis-config.xml中引入配置文件
<mapper resource="demo/UserInfoMapper2.xml" />
测试:
findById("cn.horse.demo.UserInfoMapper2.findById", 2);
执行查询数据的代码,结果如下:
发现userId, name字段的值也查询出来了
七、使用ResultMap进行结果映射
这里我们使用ResultMap标签进行结果映射,并为select标签指定结果映射的resultMap
1、使用无参数的构造方法创建映射类型对象
这里我们在resources的demo目录下新建UserInfoMapper3配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.horse.demo.UserInfoMapper3">
<resultMap id="userInfoMap" type="cn.horse.demo.UserInfo">
<result column="USERNAME" property="name" />
<result column="ID" property="userId" />
<result column="AGE" property="age" />
</resultMap>
<select id="findById" resultMap="userInfoMap">
SELECT *
FROM T_USER
WHERE ID = #{id}
</select>
</mapper>
(1)使用resultMap建立查询列与映射类型字段的映射
resultMap标签:id是编号,相同命名空间下不同重复, type是映射类型
result标签: column是查询的列,property是映射类型的字段;通过指定column,property完成数据库列到映射类型字段的对应关系
(2)使用resultMap属性指定select标签使用的结果映射
原来select标签中使用的resultType属性修改成了resultMap属性,并使用定义的resultMap标签的属性id作为resultMap属性的值
由于这里只是指定了列与字段的对应关系,映射类型对象的创建则使用无参数的构造方法
另外我们还需要在mybatis-config.xml中引入配置文件
<mapper resource="demo/UserInfoMapper3.xml" />
测试:
findById("cn.horse.demo.UserInfoMapper3.findById", 2);
执行查询数据的代码,结果如下:
发现userId, name字段的值也查询出来了
2、使用带有两个参数的构造方法创建映射类型对象(不建议使用)
这里我们在resources的demo目录下新建UserInfoMapper4配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.horse.demo.UserInfoMapper4">
<resultMap id="userInfoMap" type="cn.horse.demo.UserInfo">
<constructor>
<arg column="USERNAME" name="name" />
<arg column="ID" name="userId" />
</constructor>
<result column="AGE" property="age" />
</resultMap>
<select id="findById" resultMap="userInfoMap">
SELECT *
FROM T_USER
WHERE ID = #{id}
</select>
</mapper>
constructor标签:代表使用指定的构造方法创建映射类型对象,具体使用哪个构造方法会根据参数的个数、类型、名称进行筛选,与参数的顺序无关;这里使用了带有两个参数的构造方法,构造方法的参数包含name和userId。
arg标签:代表的是构造方法参数的映射,column代表的是查询的列,name代表的是构造方法的参数名称。
另外我们还需要在mybatis-config.xml中引入配置文件
<mapper resource="demo/UserInfoMapper4.xml" />
测试:
findById("cn.horse.demo.UserInfoMapper4.findById", 2);
执行查询数据的代码,结果如下:
发现userId, name字段的值也查询出来了
可能出现的问题
Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in demo3/UserInfoMapper.xml
### The error occurred while processing mapper_resultMap[userInfoMap]
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.builder.BuilderException: Error in result map 'cn.horse.demo3.UserInfoMapper.userInfoMap'. Failed to find a constructor in 'cn.horse.demo3.UserInfo' by arg names [name, userId]. There might be more info in debug log.
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
at cn.horse.demo3.Main.main(Main.java:16)
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.builder.BuilderException: Error in result map 'cn.horse.demo3.UserInfoMapper.userInfoMap'. Failed to find a constructor in 'cn.horse.demo3.UserInfo' by arg names [name, userId]. There might be more info in debug log.
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:121)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:99)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78)
... 2 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.builder.BuilderException: Error in result map 'cn.horse.demo3.UserInfoMapper.userInfoMap'. Failed to find a constructor in 'cn.horse.demo3.UserInfo' by arg names [name, userId]. There might be more info in debug log.
at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:120)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.parse(XMLMapperBuilder.java:92)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:373)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:119)
... 4 more
Caused by: org.apache.ibatis.builder.BuilderException: Error in result map 'cn.horse.demo3.UserInfoMapper.userInfoMap'. Failed to find a constructor in 'cn.horse.demo3.UserInfo' by arg names [name, userId]. There might be more info in debug log.
at org.apache.ibatis.mapping.ResultMap$Builder.build(ResultMap.java:132)
at org.apache.ibatis.builder.MapperBuilderAssistant.addResultMap(MapperBuilderAssistant.java:213)
at org.apache.ibatis.builder.ResultMapResolver.resolve(ResultMapResolver.java:47)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:285)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:252)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElements(XMLMapperBuilder.java:244)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:116)
造成的原因是编译时未保留方法的参数名
解决方法
在pom文件中新增编译参数
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
其中-parameters参数可以在编译时保留方法的参数名