多学习。

mybatis中的多表查询

1、表之间的关系

一对一:双方主键互为彼此外键,但并没有创两张表的必要,我创一张表两个属性放在一起不就行了
多对一:多的那方外键为少的那方的主键
一对多:同上
多对多:创立一张新表,把对应两表的主键拿来作为共有主键,同时新表的两个主键是对应两表的外键
举例:
一个用户可以下多个订单(1:m),多个订单属于一个用户(m:1)
一个人只能有一个身份证号(1:1),一个身份证只能属于一个人(1:1)
一个学生可以被多个老师教(n:m), 一个老师可以教多个学生(n:m)
特例:
如果拿出每一个订单,它都只能属于一个用户,所以mybatis就把多对一看成了一对一。

2、mybatis中的一对一查询

示例:用户和账户
一个用户可以有多个账户
一个账户只能属于一个用户(多个账户也可以属于同一个账户)
步骤:
1.先建立两张表:用户表 账户表
账户表的外键是用户表的主键
2.建立两个实体类:用户实体类和账户实体类
让用户和账户的实体类体现出一对多的关系
3.建立两个配置文件:
用户的配置文件
账户的配置文件
4.实现配置:
当我们查询用户时,可以同时得到用户下所包含的账户信息
当我们查询账户时,可以同时得到账户的所属用户信息
多表查询的sql语句:

 select * from account,user where account.UID = user.id;

方法一:通过Account子类实现多表查询(不常用)

Account类

package com.czy.domain;

import java.io.Serializable;

public class Account implements Serializable {
    private Integer ID;
    private Integer UID;
    private Double MONEY;

    @Override
    public String toString() {
        return "Account{" +
                "ID=" + ID +
                ", UID=" + UID +
                ", MONEY=" + MONEY +
                '}';
    }

    public Integer getID() {
        return ID;
    }

    public void setID(Integer ID) {
        this.ID = ID;
    }

    public Integer getUID() {
        return UID;
    }

    public void setUID(Integer UID) {
        this.UID = UID;
    }

    public Double getMONEY() {
        return MONEY;
    }

    public void setMONEY(Double MONEY) {
        this.MONEY = MONEY;
    }
}

Account子类:AccountUser

package com.czy.domain;

import java.io.Serializable;

public class AccountUser extends Account implements Serializable {
    private String username;
    private String address;

    @Override
    public String toString() {
        return super.toString()+" AccountUser{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

多表查询account包含其相应user的username和address

    <select id="findAllAccount" resultType="AccountUser">
        select account.*,username,address from account,user where account.UID = user.id
    </select>

测试类

    @Test
    public void testFindAllAccount(){
        List<AccountUser> users = dao.findAllAccount();
        for(AccountUser user : users)
            System.out.println(user);
    }
//成功输出结果

疑问:私有属性不是不会继承吗?为什么可以这样做

简单讲:子类表面上没有继承父类的私有属性和方法,实际在内存父类的一切都继承了(子类是在先调用父类的构造函数的情况下再构造自己的),故我们可以通过public的getter和setter获取和修改父类的属性,
而mybatis的封装也正是使用getter和setter(OGNL表达式)。
详细说:
一、观点引出: 
观点一: 官方文档的解释(标准)      
 A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass. "       A nested class has access to all the private members of its enclosing class—both fields and methods. Therefore, a public or protected nested class inherited by a subclass has indirect access to all of the private members of the superclass. 详细链接:https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html 
意思是说:子类不继承private其父类的成员。但是,如果超类具有用于访问其私有字段的公共或受保护的方法,则子类也可以使用这些方法。嵌套类可以访问其封闭类的所有私有成员,包括字段和方法。因此,子类继承的公共或受保护的嵌套类可以间接访问超类的所有私有成员 
这是官方的观点,当然有人就有其他的理解 
观点二: 父类的任何成员变量都是会被子类继承下去的。子类继承父类,子类拥有了父类的所有属性和方法。父类的私有属性和方法子类是无法直接访问的。当然私有属性可以通过public修饰的get和set方法访问到的,但是私有方法不行。父类的private属性,会被继承并且初始化在子类父对象中,只不过对外不可见。   详解:分析内存后,会发现,当一个子类被实例化的时候,默认会先调用父类的构造方法对父类进行初始化,即在内存中创建一个父类对象,然后再父类对象的外部放上子类独有的属性,两者合起来成为一个子类的对象。   所以:子类继承了父类的所有属性和方法或子类拥有父类的所有属性和方法是对的,只不过父类的私有属性和方法,子类是无法直接访到的。即只是拥有,但是无法使用。 (这里不考虑Java反射机制)
二、辨析: 
从继承的概念来说,private和final不被继承。Java官方文档上是这么说的。 
从内存的角度来说,父类的一切都被继承(从父类构造方法被调用就知道了,因为new一个对象,就会调用构造方法,子类被new的时候就会调用父类的构造方法,所以从内存的角度来说,子类拥有一个完整的父类)。子类对象所引用的内存有父类变量的一份拷贝。   如图所示,父类为Person类,子类为Student类。首先明确子类不能继承父类的构造方法。这就是为什么子类的默认的构造方法会自动调用父类的默认的构造方法。   在子类的构造方法中通过super()方法调用父类的构造方法。也就是,在构造子类的同时,为子类构造出跟父类相同的域。如此就在子类的对象中,也拥有了父类声明的域了。

方法二:建立实体类关系

Account类中添加User引用

package com.czy.domain;

import java.io.Serializable;

public class Account implements Serializable {
    private Integer ID;
    private Integer UID;
    private Double MONEY;

    //从表实体应该包含一个主表实体的对象引用(相当于外键的定义)
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Account{" +
                "ID=" + ID +
                ", UID=" + UID +
                ", MONEY=" + MONEY +
                '}';
    }

    public Integer getID() {
        return ID;
    }

    public void setID(Integer ID) {
        this.ID = ID;
    }

    public Integer getUID() {
        return UID;
    }

    public void setUID(Integer UID) {
        this.UID = UID;
    }

    public Double getMONEY() {
        return MONEY;
    }

    public void setMONEY(Double MONEY) {
        this.MONEY = MONEY;
    }
}

配置resultMap,并修改resultType为resultMap

<?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.czy.dao.AccountDao">
    
    <!--定义和封装account和user的resultMap-->
    <resultMap id="accountUserMap" type="account">
        <id property="ID" column="aid"></id>
        <result property="UID" column="UID"></result>
        <result property="MONEY" column="MONEY"></result>

        <!-- 一对一的关系映射,配置封装user的内容 -->
        <association property="user" column="UID" javaType="com.czy.domain.User">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
        </association>

    </resultMap>
    
    <!--查询所有-->
    <select id="findAll" resultMap="accountUserMap" >
       select u.*,a.id aid,a.UID,a.MONEY  from user u,account a where u.id = a.UID
    </select>

    <select id="findAllAccount" resultType="AccountUser">
        select account.*,username,address from account,user where account.UID = user.id
    </select>
</mapper>

测试类

    @Test
    public void testFindAll(){
        List<Account> accounts = dao.findAll();
        for(Account account : accounts){
            System.out.println("------------每个Account的信息-----------------");
            System.out.println(account);
            System.out.println(account.getUser());
        }
    }
//结果正确

3、一对多查询

示例:用户和账户
一个用户可以有多个账户
一个账户只能属于一个用户(多个账户也可以属于同一个账户)
查询所有用户,同时获取到用户下所有账户的信息
sql语句
需要用左联结,不然没有联结不会查询用户信息

select u.*,a.id aid,a.uid,a.money from user u left join account a on a.uid = u.id

User类

package com.czy.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    //一对多关系映射:主表实体应该包含从表实体的集合
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

UserMapper

<?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.czy.dao.UserDao">

    <!--定义User的resultMap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

        <!--配置user对象中的account集合-->
        <collection property="accounts" ofType="account">
            <id column="aid" property="ID"></id>
            <result column="uid" property="UID"></result>
            <result column="money" property="MONEY"></result>
        </collection>
    </resultMap>

    <!--查询所有-->
    <select id="findAll" resultMap="userAccountMap">
        select u.*,a.id as aid,a.uid,a.money from user u left join account a on a.uid = u.id
    </select>


    <!--根据id查询单个用户-->
    <select id="findById" resultType="com.czy.domain.User" parameterType="INT">
        select * from user where id = #{id};
    </select>


</mapper>

测试

    @Test
    public void testFindAll() throws Exception{

        List<User> list = dao.findAll();
        for(User user : list){
            System.out.println("---------每个用户的信息-----------");
            System.out.println(user);
            System.out.println(user.getAccounts());
        }
    }

3、多对多查询

示例:用户和角色
一个用户可以有多个角色
一个橘色可以赋予多个用户
步骤:
1.建立两张表:用户表,角色表
让用户和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表是外键,也是共有主键
2.建立两个实体类,用户实体类和角色实体类
让用户和角色的实体类能体现出多对对的关系
各自包含对方一个集合引用
3.建立两个配置文件
用户的配置文件
角色的配置文件
4.实现配置文件:
当我们查询用户时,可以同时得到用户所包含的角色信息
当我们查询角色时,可以同时得到角色的所属用户信息
sql语句

select u.*,r.id rid,ROLE_NAME,ROLE_DESC from role r left join  user_role ur on r.id = ur.rid left join user u on u.id = ur.uid

Role类的实现和配置文件
Role类:

package com.czy.domain;

import java.io.Serializable;
import java.util.List;

public class Role implements Serializable {
    private Integer roleId;
    private String roleName;
    private String roleDesc;


    //实现多对多的映射集合
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }
}

RoleMapper.xml:记得用left join,这样才能让无对应user的role查询得到

<?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.czy.dao.RoleDao">
    <resultMap id="roleMapper" type="role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="ROLE_NAME"></result>
        <result property="roleDesc" column="ROLE_DESC"></result>

        <collection property="users" ofType="user">
            <id property="id" column="id"></id>
            <result property="birthday" column="birthday"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="username" column="username"></result>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="roleMapper">
           /* select u.*,r.id rid,ROLE_NAME,ROLE_DESC from role r ,user u , user_role  ur where r.id = ur.rid and u.id = ur.uid*/
           select u.*,r.id rid,ROLE_NAME,ROLE_DESC from role r left join  user_role ur on r.id = ur.rid left join user u on u.id = ur.uid
    </select>
</mapper>

测试:

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class RoleTest {

    private InputStream in;
    private SqlSession session;
    private RoleDao dao;

    @Before
    public void init() throws Exception{
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        session = new SqlSessionFactoryBuilder().build(in).openSession();
        dao = session.getMapper(RoleDao.class);
    }


    @After
    public void destroy() throws Exception{
        session.commit();
        in.close();
        session.close();
    }

    @Test
    public void testFindAll(){
        List<Role> roles = dao.findAll();
        for(Role role : roles){
            System.out.println("------每个角色--------");
            System.out.println(role);
            System.out.println(role.getUsers());
            System.out.println(role.getUsers().size());
        }
    }
}

user到role同理

posted @ 2022-03-18 22:39  czyaaa  阅读(235)  评论(0)    收藏  举报