mybatis多表查询
一对一、一对多、多对一、多对多
首先需要明确的是,无论是一对一还是一对多,都有三种方法,且思路一致,唯一有变化的是极个别的关键字,所以我们以一对一为例进行多表查询操作的详细了解,同时采用xml文件的形式进行多表查询而不采用注解的形式。
首先,在利用mybatis进行多表查询前,需要先熟悉mysql的多表查询操作分为以下三大类:
1.连接查询(自然连接,笛卡尔积形式的连接,左外连接,右外连接,内连接)——将两个表通过对应外键关系进行连接,横向拼接,后再去查找
2.子查询(先查询一个表当中某个属性列,作为参数再给另一个表查询)
3.联合查询(纵向拼接)一般不怎么用
而mybatis当中的三种查询本质上就都对应了前两种
方法一、
最为简单(不推荐使用)
将两个表直接合并为一个类
Person表:
IdCard表
类
package com.mybatis.entity;
import lombok.Data;
@Data
public class PersonAndIdCard {
private Integer pid;
private String pname;
private String tel;
private String sex;
private Integer cid;
private String cnum;
}
mapper接口:
既可以写在IDcard的对应mapper接口下,也可以写在people对应的mapper接口下,以写在people对应的mapper接口下为例
List<PersonAndIdCard> findPersonAndIdCard(String pname);
xml映射文件:
id对应的是mapper接口当中定义抽象方法,由mybatis自动补齐。parameterType是输入参数类型,可以不用写。resultType为返回类型,也就是我们需要去映射到的类,这个必须写。此处采用的是自动映射,需要注意的是,当mysql当中的属性名称和java当中实体类的属性名称不相同时,必须采用手动映射!
<select id="findPersonAndIdCard" parameterType="string" resultType="com.mybatis.entity.PersonAndIdCard">
select * from person p,idcard i where p.cid=i.cid and p.pname=#{pname}
</select>
当然当数据库种属性名为下划线命名法时,可以引入下划线转小驼峰的配置,就可以不用手动映射了。方法如下:
在mybatis的总配置文件下mybatis-config.xml
设置
<settings> <!-- 启用驼峰命名转换 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
这样的话,就可以自动实现mysql数据库下划线字段到类小驼峰属性的自动映射,但是在使用sql语句访问数据库时,仍然要使用原数据库字段名称,而不是使用下划线。
该方法查询本质上就是通过多表的连接查询,将两个表笛卡尔积的形式连接一起,在通过条件判断返回查询结果。我们可以采用左外连接或右外连接的方式来提高执行速度
方法二、
不将两个表新建类合并,而是将其中一个类写入另一个当中,以写入person类为例:
package com.mybatis.entity;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
*
* @TableName person
*/
@Data
public class Person implements Serializable {
private Integer pid;
private String pname;
private String tel;
private String sex;
private Idcard idcard;
}
List<Person> findPersonAndIdCard2(String pname);
此时便采用手动映射,column为mysql对应的字段名,字段名要完全和mysql一致,property为对应的java实体类的属性名,同时本质上依然是连接查询,我们同样可以改写为左外连接的方式来简化sql执行速度。手动映射association标签当种,javaType为被映射的类的全部地址名称(由于我在总配置文件中对该地址起了别名,所以可以直接写Idcard),即将查询到的字段映射到Idcard类。property则将该类输送给Person当中的内置类Idcard,为其赋值。
<resultMap id="resultMap1" type="person">
<id property="pid" column="pid"/>
<result property="pname" column="pname" />
<result property="tel" column="tel" />
<result property="sex" column="sex" />
<association property="idcard" javaType="Idcard">
<id property="cid" column="cid"/>
<result property="cnum" column="cnum"/>
</association>
</resultMap>
<select id="findPersonAndIdCard2" resultMap="resultMap1">
select * from person p,idcard i where p.cid=i.cid and p.pname=#{pname}
</select>
方法三、
该方法本质上是子查询
Person类和第二种方法相同
mapper接口也和第二种方法相同
List<Person> findPersonAndIdCard3(String pname);
实现类如下:
先根据pname查询person表,在将查询到的结果当中的索引cid传输给select属性当中的selectbyprimarykey方法,在将selectbyprimarykey查询到的结果映射到idcard
<resultMap id="resultMap2" type="person">
<id property="pid" column="pid"/>
<result property="pname" column="pname"/>
<result property="tel" column="tel"/>
<result property="sex" column="sex"/>
<association property="idcard" select="com.mybatis.mapper.IdcardMapper.selectByPrimaryKey" column="cid"/>
</resultMap>
<select id="findPersonAndIdCard3" resultMap="resultMap2">
select * from person where pname=#{pname}
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from idcard
where cid = #{cid}
</select>
这就本质上是子查询,也是嵌套查询,先查询出结果,再由这个结果当中的一些值去再深度查找对应的其他值。
举一个其他的查询实战:
package com.mybatis.entity;
import java.util.Date;
import lombok.Data;
/**
*
* @TableName employee
*/
@Data
public class Employee {
private Integer eId;
private String eName;
private Integer age;
private String position;
private Department department;
private Integer salary;
private Date hireData;
}
public class Test {
public static void main(String[] args) {
String name="%张%";
String dname="信息部";
// String position="经理";
Integer minsalary=2000;
Integer maxSalary=30000;
HashMap<String, Object> hashMap=new HashMap<>();
hashMap.put("name",name);
hashMap.put("dname",dname);
// hashMap.put("position",position);
hashMap.put("minsalary",minsalary);
hashMap.put("maxsalary",maxSalary);
SqlSession sqlSession = MybatisUtil.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
PageHelper.startPage(1,3);
List<Employee> empMoreOrderLikePage = mapper.findEmpMoreOrderLikePage(hashMap);
empMoreOrderLikePage.forEach(System.out::println);
MybatisUtil.closeSqlSession(sqlSession);
}
}
List<Employee> findEmpMoreOrderLikePage(HashMap<String,Object> hashMap);
<resultMap id="resultMap" type="com.mybatis.entity.Employee">
<id property="eId" column="e_id"/>
<result property="eName" column="e_name"/>
<result property="age" column="age"/>
<result property="position" column="position"/>
<result property="salary" column="salary"/>
<result property="hireData" column="hire_data"/>
<association property="department" javaType="com.mybatis.entity.Department">
<id property="dId" column="d_id"/>
<result property="dName" column="d_name"/>
<result property="location" column="location"/>
</association>
</resultMap>
<select id="findEmpMoreOrderLikePage" resultMap="resultMap">
select * from employee e left join department d on e.d_id=d.d_id
<where>
<if test="name != null">
e.e_name Like #{name}
</if>
<if test="position!=null">
and e.position=#{position}
</if>
<if test="dname!=null">
and d.d_name=#{dname}
</if>
<if test="min_salary!=null">
<if test="max_salary!=null">
and e.salary between #{min_salary} and #{}
</if>
</if>
</where>
ORDER BY e.salary DESC
</select>
总结:
1.接口中参数一般只能是一个,当为多个时,就不能通过#{属性名}获取了需要以下几种方法处理
1).#{param1},#{param2}……来处理
2).#{arg0},#{arg1}……来处理
3)最常用:通过hashmap来传递值,同时后面的动态sql也可以直接通过键值对当中的键名来判断是否有该参数。
4)传递实体类,同样可以通过属性名直接获取
2.此处用的是方法二,进行左外连接后查询。
要想使用方法三,需要做出如下处理:
1.通过部门名先查询到pid
2.根据查询到的pid和传过来的参数再来进行查询
可以参考下面的例子自行去写
<resultMap id="resultMap4" type="person">
<id property="pid" column="pid"/>
<result property="pname" column="pname"/>
<collection
property="bankcardList"
select="com.mybatis.mapper.BankcardMapper.selectByPidAndBalance"
column="{pid=pid, minBalance=minBalance}"/> <!-- pid 来自主查询字段,minBalance 来自 HashMap -->
</resultMap>
<select id="findPersonAndIdCard5" resultMap="resultMap4">
SELECT pid, pname
FROM person
WHERE pname = #{pname} <!-- #{pname} 从 HashMap 中获取 -->
</select>
column必须用{}的形式来接受
// BankcardMapper.java
List<Bankcard> selectByPidAndBalance(
@Param("pid") Integer pid,
@Param("minBalance") BigDecimal minBalance
);
此处必须要@param来注解,多参传过来时必须用
Map<String, Object> params = new HashMap<>();
params.put("pname", "张三");
params.put("minBalance", new BigDecimal("1000.00")); // 传递给子查询的参数
List<Person> persons = personMapper.findPersonAndIdCard5(params);
SELECT * FROM bankcard
WHERE pid = 123 -- 主查询返回的 pid
AND balance >= 1000.00; -- 来自 HashMap 的 minBalance