MyBatis?多表聯(lián)合查詢及優(yōu)化方法
這篇文章我打算來簡單的談?wù)?mybatis 的多表聯(lián)合查詢。起初是覺得挺簡單的,沒必要拿出來寫,畢竟 mybatis 這東西現(xiàn)在是個開發(fā)的都會用,而且網(wǎng)上的文章也是一搜羅一大堆,根本就用不著我來重復(fù)。但是吧,就我前幾天在做一個多表聯(lián)合查詢的時候,竟然出了很多意想不到的問題,而且這些問題的出現(xiàn),并不是對 mybatis 不了解,而是在用的過程中會或多或少的忽略一些東西,導(dǎo)致提示各種錯誤。
背景
老規(guī)矩,開始之前,還是要先說說這件事的背景。也就是最近幾天,公司要做一個后臺的管理平臺,由于之前的一些限制,這次要做成單獨的項目進行部署,因此就要重新考慮很多東西。索性這幾天有時間,就做了一個小 Demo ,實現(xiàn) mybatis 的多表聯(lián)合查詢的,由于之前用的是 Hibernate 做的聯(lián)合查詢,眾所周知,Hibernate 是全自動的數(shù)據(jù)庫持久層框架,它可以通過實體來映射數(shù)據(jù)庫,通過設(shè)置一對多、多對一、一對一、多對多的關(guān)聯(lián)來實現(xiàn)聯(lián)合查詢。
正文
下面就來說一下 mybatis 是通過什么來實現(xiàn)多表聯(lián)合查詢的。首先看一下表關(guān)系,如圖:

這里,我已經(jīng)搭好了開發(fā)的環(huán)境,用到的是 SpringMVC + Spring + MyBatis,當(dāng)然,為了簡單期間,你可以不用搭前端的框架,只使用 Spring + MyBatis 就可以,外加 junit 測試即可。環(huán)境我就不帶大家搭了,這里只說涉及到聯(lián)合查詢的操作。
設(shè)計好表之后,我用到了 mybatis 的自動生成工具 mybatis generator 生成的實體類、mapper 接口、以及 mapper xml 文件。由于是測試多表聯(lián)合查詢,因此需要自己稍加改動。
下面是 User 和 Role 的實體類代碼:
User
<span style="font-family:Comic Sans MS;font-size:12px;">package com.sica.domain;
import java.io.Serializable;
import java.util.List;
public class User implements Serializable {
private String id;
private String username;
private String password;
private List<Role> roles;
private static final long serialVersionUID = 1L;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id == null ? null : id.trim();
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username == null ? null : username.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
User other = (User) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getUsername() == null ? other.getUsername() == null : this.getUsername().equals(other.getUsername()))
&& (this.getPassword() == null ? other.getPassword() == null : this.getPassword().equals(other.getPassword()));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getUsername() == null) ? 0 : getUsername().hashCode());
result = prime * result + ((getPassword() == null) ? 0 : getPassword().hashCode());
return result;
}
}</span>Role
<span style="font-family:Comic Sans MS;font-size:12px;">package com.sica.domain;
import java.io.Serializable;
public class Role implements Serializable {
private String id;
private String name;
private String jsms;
private String bz;
private Integer jlzt;
private String glbm;
private String userid;
private static final long serialVersionUID = 1L;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id == null ? null : id.trim();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
public String getJsms() {
return jsms;
}
public void setJsms(String jsms) {
this.jsms = jsms == null ? null : jsms.trim();
}
public String getBz() {
return bz;
}
public void setBz(String bz) {
this.bz = bz == null ? null : bz.trim();
}
public Integer getJlzt() {
return jlzt;
}
public void setJlzt(Integer jlzt) {
this.jlzt = jlzt;
}
public String getGlbm() {
return glbm;
}
public void setGlbm(String glbm) {
this.glbm = glbm == null ? null : glbm.trim();
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid == null ? null : userid.trim();
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
Role other = (Role) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName()))
&& (this.getJsms() == null ? other.getJsms() == null : this.getJsms().equals(other.getJsms()))
&& (this.getBz() == null ? other.getBz() == null : this.getBz().equals(other.getBz()))
&& (this.getJlzt() == null ? other.getJlzt() == null : this.getJlzt().equals(other.getJlzt()))
&& (this.getGlbm() == null ? other.getGlbm() == null : this.getGlbm().equals(other.getGlbm()))
&& (this.getUserid() == null ? other.getUserid() == null : this.getUserid().equals(other.getUserid()));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
result = prime * result + ((getJsms() == null) ? 0 : getJsms().hashCode());
result = prime * result + ((getBz() == null) ? 0 : getBz().hashCode());
result = prime * result + ((getJlzt() == null) ? 0 : getJlzt().hashCode());
result = prime * result + ((getGlbm() == null) ? 0 : getGlbm().hashCode());
result = prime * result + ((getUserid() == null) ? 0 : getUserid().hashCode());
return result;
}
}</span>首先講一下業(yè)務(wù),這里用到的 User 、Role 的對應(yīng)關(guān)系是,一個用戶有多個角色,也就是 User : Role 是 1 : n 的關(guān)系。因此,在 User 的實體中加入一個 Role 的屬性,對應(yīng)一對多的關(guān)系。
然后就是 mapper 接口和 xml 文件了:
mapper接口
UserMapper
<span style="font-family:Comic Sans MS;font-size:12px;">package com.sica.mapper;
import com.sica.domain.User;
import java.util.List;
public interface UserMapper {
int deleteByPrimaryKey(String id);
int insert(User record);
int insertSelective(User record);
User selectByPrimaryKey(String id);
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
List<User> queryForList();
}</span>mapper xml文件
UserMapper
<span style="font-family:Comic Sans MS;font-size:12px;"><?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="com.sica.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.sica.domain.User">
<id column="id" property="id" jdbcType="VARCHAR"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="password" property="password" jdbcType="VARCHAR"/>
</resultMap>
<resultMap id="queryForListMap" type="com.sica.domain.User">
<id column="id" property="id" jdbcType="VARCHAR"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="password" property="password" jdbcType="VARCHAR"/>
<collection property="roles" javaType="java.util.List" ofType="com.sica.domain.Role">
<id column="r_id" property="id" jdbcType="VARCHAR" />
<result column="r_name" property="name" jdbcType="VARCHAR" />
<result column="r_jsms" property="jsms" jdbcType="VARCHAR" />
<result column="r_bz" property="bz" jdbcType="VARCHAR" />
<result column="r_jlzt" property="jlzt" jdbcType="INTEGER" />
<result column="r_glbm" property="glbm" jdbcType="VARCHAR" />
</collection>
</resultMap>
<select id="queryForList" resultMap="queryForListMap">
SELECT
u.id,
u.username,
u.password,
r.id r_id,
r.name r_name,
r.jsms r_jsms,
r.bz r_bz,
r.jlzt r_jlzt,
r.glbm r_glbm
FROM
user u
LEFT JOIN
role r
ON
u.id = r.userid
</select>
<sql id="Base_Column_List">
id, username, password
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String">
select
<include refid="Base_Column_List"/>
from user
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from user
where id = #{id,jdbcType=VARCHAR}
</delete>
<insert id="insert" parameterType="com.sica.domain.User">
insert into user (id, username, password
)
values (#{id,jdbcType=VARCHAR}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="com.sica.domain.User">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="username != null">
username,
</if>
<if test="password != null">
password,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="username != null">
#{username,jdbcType=VARCHAR},
</if>
<if test="password != null">
#{password,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.sica.domain.User">
update user
<set>
<if test="username != null">
username = #{username,jdbcType=VARCHAR},
</if>
<if test="password != null">
password = #{password,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="com.sica.domain.User">
update user
set username = #{username,jdbcType=VARCHAR},
password = #{password,jdbcType=VARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper></span>之后,我擴展了一個 Dao 接口,當(dāng)然,你也可以直接使用 mapper 接口,都是一樣的。
Dao 接口
IUserDao
<span style="font-family:Comic Sans MS;font-size:12px;">package com.sica.dao;
import com.sica.mapper.UserMapper;
/**
* Created by IntelliJ IDEA.
* Package: com.sica.dao
* Name: IUserDao
* User: xiang.li
* Date: 2015/5/22
* Time: 15:25
* Desc: To change this template use File | Settings | File Templates.
*/
public interface IUserDao extends UserMapper {
}</span>下面就是 service 和實現(xiàn)層的代碼了。
IUserService
<span style="font-family:Comic Sans MS;font-size:12px;">package com.sica.service;
import com.sica.domain.User;
import java.util.List;
/**
* Created by xiang.li on 2015/1/31.
*/
public interface IUserService {
/**
* 根據(jù)Id查詢用戶對象
* @param id 編號
* @return 用戶對象
*/
User getUserById(String id);
/**
* 根據(jù)用戶名查詢用戶對象
* @return List
*/
List<User> queryUserList();
}</span>UserServiceImpl
<span style="font-family:Comic Sans MS;font-size:12px;">package com.sica.service.impl;
import com.sica.dao.IUserDao;
import com.sica.domain.User;
import com.sica.service.IUserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by xiang.li on 2015/1/31.
*/
@Service("userService")
public class UserServiceImpl implements IUserService {
@Resource
public IUserDao userDao;
@Override
public User getUserById(String id) {
return this.userDao.selectByPrimaryKey(id);
}
@Override
public List<User> queryUserList() {
return userDao.queryForList();
}
}</span>當(dāng)然,還有所謂的 applicationContext.xml 配置,不過,我這里叫 spring-mybatis.xml。
<span style="font-family:Comic Sans MS;font-size:12px;"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自動掃描 -->
<context:component-scan base-package="com.sica"/>
<!-- 引入配置文件 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:jdbc.properties"
/>
<!-- 配置數(shù)據(jù)庫連接池 -->
<!-- 初始化連接大小 -->
<!-- 連接池最大數(shù)量 -->
<!-- 連接池最大空閑 -->
<!-- 連接池最小空閑 -->
<!-- 獲取連接最大等待時間 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driver}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
p:initialSize="${jdbc.initialSize}"
p:maxActive="${jdbc.maxActive}"
p:maxIdle="${jdbc.maxIdle}"
p:minIdle="${jdbc.minIdle}"
p:maxWait="${jdbc.maxWait}"
/>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" lazy-init="default"
p:dataSource-ref="dataSource"
p:mapperLocations="classpath:com/sica/mapping/*.xml"
/>
<!-- DAO接口所在包名,Spring會自動查找其下的類 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"
p:basePackage="com.sica.dao"
p:sqlSessionFactoryBeanName="sqlSessionFactory"
/>
<!-- (事務(wù)管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"
/>
</beans></span>最后,我用到的是 junit 進行的測試,測試代碼如下。
GetUserTest
<span style="font-family:Comic Sans MS;font-size:12px;">package com.sica.user;
import com.alibaba.fastjson.JSON;
import com.sica.domain.User;
import com.sica.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by xiang.li on 2015/2/1.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-mybatis.xml")
public class GetUserTest {
private static String UUID = "3";
@Resource
private IUserService userService;
private static Logger logger = LoggerFactory.getLogger(GetUserTest.class);
@Test
public void test() {
User user = userService.getUserById(UUID);
logger.info(JSON.toJSONString(user));
}
/**
* 測試聯(lián)合查詢
*/
@Test
public void test2() {
List<User> users = userService.queryUserList();
logger.info(JSON.toJSONString(users));
}
}</span>測試結(jié)果

可以看到,所有的用戶和用戶對應(yīng)的角色都全部查出來了,這說明,這次的測試很成功。
關(guān)于優(yōu)化
對于優(yōu)化嘛,我這里簡單的提幾點,大家可以考慮一下。首先,就是對表的設(shè)計,在設(shè)計表初期,不僅僅要考慮到數(shù)據(jù)庫的規(guī)范性,還好考慮到所謂的業(yè)務(wù),以及對性能的影響,比如,如果從規(guī)范性角度考慮的話,可能就會分多個表,但是如果從性能角度來考慮的話,龐大的數(shù)據(jù)量在多表聯(lián)合查詢的時候,相對于單表來說,就會慢很多,這時,如果字段不是很多的話,可以考慮冗余幾個字段采用單表的設(shè)計。
其次嘛,就是在 sql 上下功夫了,對于聯(lián)合查詢,sql 的優(yōu)化是很有必要的,到底是采用 INNER JOIN,還是采用 LEFT JOIN 亦或是 RIGHT JOIN 、OUTTER JOIN 等,都是要在滿足業(yè)務(wù)需求之后,通過測試性能得出的結(jié)論。
再次嘛,就是在程序中調(diào)用的時候了,是采用懶加載,還是采用非懶加載的方式,這也算是一個因素吧,具體的還是要考慮業(yè)務(wù)的需要。
最后嘛,就要用到數(shù)據(jù)庫的緩存了,或者在數(shù)據(jù)庫與程序的中間再加一層緩存。不過,還是建議用好數(shù)據(jù)庫本身自帶的緩存功能。
到此這篇關(guān)于MyBatis 多表聯(lián)合查詢及優(yōu)化的文章就介紹到這了,更多相關(guān)MyBatis 多表聯(lián)合查詢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java應(yīng)用服務(wù)器之tomcat部署的詳細教程
這篇文章主要介紹了Java應(yīng)用服務(wù)器之tomcat部署,本文通過實例圖文相結(jié)合給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
Java代碼如何判斷l(xiāng)inux系統(tǒng)windows系統(tǒng)
這篇文章主要介紹了Java代碼如何判斷l(xiāng)inux系統(tǒng)windows系統(tǒng)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01
關(guān)于@RequestParam的使用所遇到的404問題
這篇文章主要介紹了關(guān)于@RequestParam的使用所遇到的404問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
java.lang.NullPointerException出現(xiàn)的幾種原因及解決方案
這篇文章主要介紹了java.lang.NullPointerException出現(xiàn)的幾種原因及解決方案,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05

