接着上一个学习做,内容比较多,resultMap,一对多,多对一
别名
在mybaits-conf.xml,加入如下代码即可将包中的内容变成别名,别名可以在resultType中使用
<typeAliases>
<package name="cn.hd.po" />
</typeAliases>
别名在Spring中的配置是sqlSessionFactory中的
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:com/test/mapping/*.xml"></property>
resultMap
如果查询出来的结果和表数据一样,就没有必要用到resultMap。但有时候会出现页面显示比表中数据多。比如年龄。比如多表查询。
我这里就用多表查询算了,用一个一对一的数据做个resultMap的试验了。
加入了2个表,后面一对多和多对一都可以用
create table T_DEPT
(
DID NUMBER not null,
DEPT_NAME VARCHAR2(20),
PID NUMBER
);
create table T_U_JOB
(
JID NUMBER not null,
USER_ID NUMBER not null,
JOB_NAME VARCHAR2(20) not null
)
部门和工作,一个人可以有多个任职,一个人只能一个部门。
package cn.hd.vo;
import cn.hd.po.User;
public class UserVo extends User {
private String deptName;
private String jobName;
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
}
一个vo,值对象,好了,现在就是重头戏了,UserMapper.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="cn.hd.dao.UserMapper">
<span style="white-space:pre"> </span><!-- ++++++++多表查询+++++++++++++多表查询++++++++++++++多表查询++++++++++++ -->
<span style="white-space:pre"> </span><resultMap type="cn.hd.vo.UserVo" id="userResultMap">
<span style="white-space:pre"> </span><!-- 返回值主键,并不是数据库主键 -->
<id column="id" property="userid" />
<!-- column是结果集的列名,并不是字段名称,property是返回对象中的字段名称 -->
<result column="dept_name" property="deptName"/>
<result column="depid" property="jobName" />
</resultMap>
<span style="white-space:pre"> </span><!-- 使用resultType查询,所以这里需要使用别名讲字段名称改为和对象中的一致 -->
<span style="white-space:pre"> </span><select id="queryVo" resultType="cn.hd.vo.UserVo">
<span style="white-space:pre"> </span>select u.*,d.dept_name as deptName from t_user u,t_dept d where u.did=d.did
</select>
<!-- 因为resultMap中已经映射的了字段名称,所以这里不需要别名 -->
<span style="white-space:pre"> </span><select id="queryResultMap" resultMap="userResultMap">
<span style="white-space:pre"> </span>select u.*,d.dept_name from t_user u,t_dept d where u.did=d.did
</select>
<span style="white-space:pre"> </span><!-- ++++++++基本查询+++++++++++++基本查询++++++++++++++基本查询++++++++++++ -->
<!--根据id查询得到一个user对象 List<Userbean>-->
<select id="queryAll" resultType="cn.hd.po.User">
<span style="white-space:pre"> </span>select * from t_user
</select>
<select id="findById" parameterType="int" resultType="cn.hd.po.User">
<span style="white-space:pre"> </span><!-- 只有一个参数时,#{这里可以随便写} -->
select * from t_user where userid=#{vlues}
</select>
</mapper>
测试代码加入如下,这里引入了一个jar包fastjson-1.1.41.jar,用于输出对象。
@Test
public void testFindVo() {
List<UserVo> user=session.selectList("cn.hd.dao.UserMapper.queryVo");
for(UserVo u :user)
{
System.out.println(JSON.toJSONString(u));
}
}
@Test
public void testFindResultMap() {
List<UserVo> user=session.selectList("cn.hd.dao.UserMapper.queryResultMap");
for(UserVo u :user)
{
System.out.println(JSON.toJSONString(u));
}
}
2个结果都是输出了这个结果,那么搞定了。
{"deptName":"信息部","did":1,"jid":1,"password":"123","realname":"张飞","userid":1,"username":"张三"}
{"deptName":"教务部","did":2,"jid":2,"password":"321","realname":"李世民","userid":2,"username":"李四"}
总结,resultType是使用的现有的对象,resultMap是需要定义一个userResultMap,在这个map中人为去干涉字段映射。
resultType是使用约定,resultMap使用配置。约定规范,配置灵活。
resultMap的配置可以比使用的多,也可以少。多的字段不会启用,少的配置相应的字段数据会丢失,如果字段名称配置错误了,数据也会丢失。(这里并没有报错!!!)
一对多,多对一
一对一的上面已经写过了,现在看一对多,多对一。我就直接用resultType了,resultMap还需要配置collection 。算了一起写吧!好好学习,天天向上。
事实证明偷的懒始终是需要补回来的,上面偷懒没有写po,现在补上呗。
package cn.hd.po;
public class Job {
private Long jid;
private Long userId;
private String jobName;
public Long getJid() {
return jid;
}
public void setJid(Long jid) {
this.jid = jid;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
}
package cn.hd.po;
public class Dept {
private Long deptId;
private String deptName;
private Long pid;
public Long getDeptId() {
return deptId;
}
public void setDeptId(Long deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public Long getPid() {
return pid;
}
public void setPid(Long pid) {
this.pid = pid;
}
}
package cn.hd.vo;
import java.util.List;
import cn.hd.po.Dept;
import cn.hd.po.Job;
import cn.hd.po.User;
public class UserVoO2M extends User {
private Dept dept;
private List<Job> listJob;
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public List<Job> getListJob() {
return listJob;
}
public void setListJob(List<Job> listJob) {
this.listJob = listJob;
}
}
ok,现在几个java文件就做完成了。mapper中就一个接口,就不贴出来了,没必要了。新增一个xml叫UserMapperO2M.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="cn.hd.dao.UserMapperO2M">
<!-- ++++++一对多,多对一+++++++++++++++一对多,多对一+++++++++++++++一对多,多对一+++++++ -->
<resultMap type="cn.hd.vo.UserVoO2M" id="userO2M">
<!-- 返回值主键,并不是数据库主键-->
<id column="userid" property="userid" />
<result column="username" property="username"/>
<!-- 这个其实省略了很多字段,因为使用约定,所以就不写了 非常奇怪,这里使用了association之后就不适用约定了-->
<!-- 一对一 property中对应到成员变量 javaType是类型-->
<association property="dept" javaType="cn.hd.po.Dept">
<id column="did" property="deptId"/>
<result column="dept_name" property="deptName"/>
</association>
<!-- 1对多使用collection property对应到1的成员变量 ofType是成员变量的范型 -->
<collection property="listJob" ofType="cn.hd.po.Job">
<id column="jobid" property="jid"/>
<result column="user_id" property="userId"/>
<result column="job_name" property="jobName"/>
</collection>
</resultMap>
<!-- 使用resultType查询,所以这里需要使用别名讲字段名称改为和对象中的一致 -->
<select id="queryVo" resultMap="userO2M">
select u.*,j.jid as jobid,j.user_id,j.job_name from t_user u,t_u_job j where u.userid = j.user_id
</select>
</mapper>
然后测试类和执行结果
package cn.hd.test;
import java.util.List;
import org.junit.Test;
import com.alibaba.fastjson.JSON;
import cn.hd.vo.UserVoO2M;
public class TestMyBatisO2M extends Junit{
@Test
public void testFindVo() {
List<UserVoO2M> user=session.selectList("cn.hd.dao.UserMapperO2M.queryVo");
for(UserVoO2M u :user)
{
System.out.println(JSON.toJSONString(u));
}
}
}
{"did":0,"jid":0,"listJob":[{"jid":1,"jobName":"会长","userId":2},{"jid":2,"jobName":"主席","userId":2},{"jid":3,"jobName":"皇帝","userId":2}],"userid":2,"username":"李四"}
{"did":0,"jid":0,"listJob":[{"jid":4,"jobName":"战士","userId":1},{"jid":5,"jobName":"大将军","userId":1},{"jid":6,"jobName":"狮子吼","userId":1}],"userid":1,"username":"张三"}
好,现在结果对了,但是这里的约定没有生效,User对象中的很多字段都没有写入进去,好奇怪,回头在研究了。
遇到的问题:
1、首先是一个,“Error querying database. Cause: java.sql.SQLException: ORA-00911: 无效字符”,这个问题太坑,我开始以为是字段错了,找了半天,后来才发现原来吧分号复制进去了。去掉分号就正常了。
2、第二个就是约定不生效的问题。一定要写result的配置。尚未解决。
3、第三个就是字段名称一样,导致数据错误,我user中有一个jid,t_u_job中也有一个jid。
然后结果就变成这样了。
{"dept":{"deptId":2,"deptName":"教务部"},"did":0,"jid":0,"listJob":[{"jid":2,"jobName":"会长","userId":2}],"userid":2,"username":"李四"}
{"dept":{"deptId":1,"deptName":"信息部"},"did":0,"jid":0,"listJob":[{"jid":1,"jobName":"战士","userId":1}],"userid":1,"username":"张三"}
明显可以看到listJob中少了好几条数据就是因为Jid重复的问题。我这里将Jid写了个别名了。
4、我发现一个好玩的东西,就是在包下,同名的select id,是有关系的,应该是使用了接口代理的原因吧!我说下表现。
大家可以发现我在UserMapper.xml中有一个queryVo ,在UserMapperO2M.xml 也有一个queryVo 。好,这个是吧UserMapper.xml中queryVo改一下
<select id="queryVo" resultType="cn.hd.vo.UserVo">
select u.*,d.dname as deptName from t_user u,t_dept d where u.did=d.did
</select>
好,那么这个是去测试UserMapperO2M.xml中的queryVo方法,“Error querying database. Cause: java.sql.SQLException: ORA-00904: "D"."DNAME": 标识符无效”。
其实UserMapperO2M中的方法明显是对的,但是这里还是出错了。
后面一直重现不了,看来还是没找对原因。
总结:
半自动化配置,配置数据库与JAVA类的映射。
一对多时,一对一时 在id标签中的是确定对象是否重复的关键。如果id不重复,那么就会被认为是1对多种的1。
association 一对一
collection 一对多