文章目录

  • 关联查询、多表
    • 数据库表关系
    • Java对象之间的关系
    • 前提条件
    • 一对多
      • 单向关联
      • 延迟加载
      • 双向关联
      • 延迟加载
      • 延迟加载中传递多个参数

关联查询、多表

数据库表关系

表—>约束(主键约束、唯一性、非空、默认值、check约束)保证数据的准确性和完整性;

多张表,表与表之间存在外键约束。

外键约束,是关系型数据库典型的一个特点,是指一张表中的列参考/来源于另一张表的某个列的值。

比如,学生信息表,电脑表,一台电脑属于一个学生。

学生信息表,主表,父表。 电脑表,子表,外键表。

学生编号 学生编号

数据库表之间建立关联关系后,在数据层面会有三种体现。

一对一、一对多、多对多。(多对多需要额外的一张关联关系表)

Java对象之间的关系

一对一

public class S {学生编号学生姓名联系电话
}
public class C {电脑编号电脑品牌电脑价格学生编号 //属于字段关联,就不是面向对象
}

面向对象

public class S {学生编号学生姓名联系电话电脑对象
}
public class C {电脑编号电脑品牌电脑价格学生对象
}

一对多

public class S {学生编号学生姓名联系电话List<C> 电脑集合
}
public class C {电脑编号电脑品牌电脑价格学生对象
}

多对多(没有中间关联关系表的对象)

public class S {学生编号学生姓名联系电话List<C> 电脑集合
}
public class C {电脑编号电脑品牌电脑价格List<S> 学生对象
}

前提条件

案例的前提条件:多表连接查询的sql要清楚

多表连接查询:产生笛卡尔积。

所以我们要编写连接条件。

内连接:两表都有的数据;

左连接:左表(主表)的全部数据;

右连接:右表(主表)的全部数据。

一对多

搞清楚mybatis是如何处理一对多的,那么一对一和多对多就都会处理了。

因为一对多,多的一方,维护了一的一方的对象。而一对一中,双方都是维护了对方的一个对象。所以一对一对应的就是一对多中多的一方的配置。

一对多中,一的一方,维护了多的一方的集合。而多对多的双方维护的都是对方的集合。所以搞清楚一对多一的一方的配置,就会配置多对多的双方的配置。

环境准备

DROP TABLE IF EXISTS account;CREATE TABLE IF NOT EXISTS account (aid INT NOT NULL AUTO_INCREMENT,aname VARCHAR(50) NOT NULL,apassword VARCHAR(50) NOT NULL,a_nickname VARCHAR(50) NOT NULL,PRIMARY KEY (aid)
);CREATE TABLE IF NOT EXISTS books (bookid INT NOT NULL AUTO_INCREMENT,bookname VARCHAR(50) NOT NULL,bookprice FLOAT NOT NULL,accountid INT DEFAULT NULL,PRIMARY KEY (bookid),CONSTRAINT fk_books_account FOREIGN KEY (accountid) REFERENCES account(aid)
);INSERT INTO account VALUES(NULL, 'zhangsan', 'nasgnahz', '张三');
INSERT INTO account VALUES(NULL, 'lisi', 'isil', '李四');INSERT INTO books VALUES(NULL, 'Java从入门到精通', 99.9, 1);
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account implements Serializable {private static final long serialVersionUID = 6623184271851977540L;private Integer aid;private String aname;private String apassword;private String anickname;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books implements Serializable {private static final long serialVersionUID = 3301442300858007177L;private Integer bookid;private String bookname;private Float bookprice;private Account account;
}

单向关联

先完成一对多单向关联在多的一方查询一的一方的信息

也就是通过电脑找到用户

package org.westos.mapper;import org.westos.model.Books;/*** @author lwj* @date 2020/9/7 14:59*/
public interface BooksMapper {Books selectByPrimaryKey(int bookid);
}

第一种方式:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.westos.mapper.BooksMapper"><!--关联对象查询第一种方式--><resultMap id="resultMapBook1" type="Books"><id property="bookid" column="bookid"/><result property="bookname" column="bookname"/><result property="bookprice" column="bookprice"/><!--可以设置关联对象--><result property="account.aid" column="aid"/><result property="account.aname" column="aname"/><result property="account.apassword" column="apassword"/><result property="account.anickname" column="a_nickname"/></resultMap><select id="selectByPrimaryKey" resultMap="resultMapBook1">SELECT a.*, b.*FROM account aINNER JOIN books bON a.`aid` = b.`accountid`WHERE b.`bookid` = #{bookid};</select>
</mapper>

测试:

@Test
public void testSelectByPrimaryKey() {try (SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession()) {BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);Books books = mapper.selectByPrimaryKey(1);System.out.println(JSON.toJSONString(books, true));} catch (IOException e) {e.printStackTrace();}
}
{"account":{"aid":1,"aname":"zhangsan","anickname":"张三","apassword":"nasgnahz"},"bookid":1,"bookname":"Java从入门到精通","bookprice":99.9
}

第二种方式:

<!--关联对象查询第二种方式-->
<resultMap id="resultMapBook2" type="Books" autoMapping="true"><!--可以设置关联对象--><result property="account.aid" column="aid"/><result property="account.aname" column="aname"/><result property="account.apassword" column="apassword"/><result property="account.anickname" column="a_nickname"/>
</resultMap>

第三种方式:

(在没有设置mapUnderscoreToCamelCase属性为true的情况下)

<!--关联对象查询第三种方式-->
<resultMap id="resultMapBook3" type="Books" autoMapping="true"><!--可以设置关联对象--><!--通过association标签关联映射对象--><!--property:当前Books对象中的属性--><!--javaType:关联对象的类型--><!--autoMapping:自动映射--><association property="account" javaType="Account" autoMapping="true"/>
</resultMap>

查询结果:

{"account":{"aid":1,"aname":"zhangsan","apassword":"nasgnahz"},"bookid":1,"bookname":"Java从入门到精通","bookprice":99.9
}

不会自动封装a_nickname。

第四种方式:

<!--关联对象查询第四种方式-->
<resultMap id="resultMapBook4" type="Books" autoMapping="true"><association property="account" javaType="Account" autoMapping="true"><result property="anickname" column="a_nickname"/></association>
</resultMap>

查询结果:

{"account":{"aid":1,"aname":"zhangsan","anickname":"张三","apassword":"nasgnahz"},"bookid":1,"bookname":"Java从入门到精通","bookprice":99.9
}

延迟加载

以上的关联关系中,存在一个问题:

  1. sql比较复杂,必须是一个多表连接查询的sql;
  2. 查询书,然后用户信息不管用不用,都会查询

能不能实现,先查询书,访问了书中的关联用户属性值,才进行关联用户的查询。

mybatis支持的,延迟加载

延迟加载:就是访问到延迟加载属性(account)的时候才执行。

那么延迟加载,也是基于动态代理实现的,因为只有创建动态代理类,才可以对原有目标的业务进行增强处理。

特别说明:mybatis 3.4.6 和 JDK11 对懒加载的支持有问题。
问题的体现是jdk11对反射API进行了改版,不允许不安全的类访问。
解决这个问题的方式是:将mybatis的代理方式设置为cglib,或者更换mybatis版本。
设置jdk8是没问题的。
public interface AccountMapper {Account selectByPrimaryKey(Integer aid);
}
<mapper namespace="org.westos.mapper.AccountMapper"><select id="selectByPrimaryKey" resultType="Account">select * from accountwhere aid = #{aid}</select>
</mapper>

在查询封装Account时,查询的是*,所以在全局setting中进行设置,这样就不用设置as或者resultMap。

<settings><!--忽略下划线--><setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
Books selectByPrimaryKey2(int bookid);

BooksMapper.xml

<resultMap id="resultMapBook5" type="Books" autoMapping="true"><!--关联对象--><!--关联对象是单条数据用association,比如在Books中关联Account属性,并不是List集合--><!--select就是说,查询的sql Id, ID = namespace + sql ID--><!--如果查询的select语句不在当前mapper中,必须写namespace + sql Id--><!--如果查询的select语句在当前mapper中,写sql Id即可--><!--column是select语句执行的参数值,来源于上一条sql中的列--><!--fetchType lazy 是懒加载,所谓懒加载,就是用的时候才加载(当不查询account信息时不加载)--><!--当配置了select语句,javaType无效--><association property="account"  column="accountid"select="org.westos.mapper.AccountMapper.selectByPrimaryKey" fetchType="lazy"/>
</resultMap><!--以下代码支持延迟加载-->
<select id="selectByPrimaryKey2" resultMap="resultMapBook5">select * from books where bookid = #{bookid}
</select>

当我在测试类中访问Books类的bookname属性时,是不会执行AccountMapper查询的sql的。

@Test
public void testSelectByPrimaryKey2() {try (SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession()) {BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);Books books = mapper.selectByPrimaryKey2(1);//查询bookname字段System.out.println(books.getBookname());} catch (IOException e) {e.printStackTrace();}
}
Opening JDBC Connection
Created connection 832279283
Setting autocommit to false on JDBC Connection
Preparing: select * from books where bookid = ?
Parameters: 1(Integer)
Total: 1
Java从入门到精通

只有当我在测试类中访问Books类的account属性时,才会执行AccountMapper查询的sql。

@Test
public void testSelectByPrimaryKey2() {try (SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession()) {BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);Books books = mapper.selectByPrimaryKey2(1);System.out.println(books.getBookname());System.out.println(books.getAccount());} catch (IOException e) {e.printStackTrace();}
}
Preparing: select * from books where bookid = ?
Parameters: 1(Integer)Total: 1
Java从入门到精通
Preparing: select * from account where aid = ?
Parameters: 1(Integer)Total: 1
Account(aid=1, aname=zhangsan, apassword=nasgnahz, anickname=张三)

延迟加载之后得到的Books是一个代理对象。

@Test
public void testSelectByPrimaryKey2() {try (SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession()) {BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);Books books = mapper.selectByPrimaryKey2(1);System.out.println(books);//延迟加载之后,拿到的books是一个代理对象System.out.println(books.getBookname());System.out.println(books.getAccount());} catch (IOException e) {e.printStackTrace();}
}

双向关联

一对多的一的一方的配置。

一对多,学生和书的关系,一的一方就是学生,学生对象中维护着书的集合。

之前aid=1的学生只有一本书,现在多加一本。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account implements Serializable {private static final long serialVersionUID = 6623184271851977540L;private Integer aid;private String aname;private String apassword;private String anickname;private List<Books> books;
}

一对多,一的一方,维护多的一方的集合;多的一方,维护一的一方的对象。

public interface AccountMapper {Account selectById(Integer aid);
}

不支持延迟加载的关联方式

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.westos.mapper.AccountMapper"><!--Account类除了List的books属性外,其他属性都已经自动映射了,包括a_nickname -> anickname,因为在全局setting中已经配置了--><resultMap id="resultMapAccount1" type="Account" autoMapping="true"><!--在一对多结果映射中,如果sql是多表连接,需要设置id标签,否则数据无法合并--><!--用sql查询出来,会有两条记录,但是只有Books所有字段不同,Account字段都是相同的,我们不能把他封装为List<Account>--><!--而是应该封装为Account{List<Books>} 用id来归并数据--><id column="aid" property="aid"/><!--前面是配置关联的对象用association,现在配置关联的集合,用collection--><!--property:关联的属性ofType:关联对象的类型--><collection property="books" autoMapping="true" ofType="Books"/></resultMap><select id="selectById" resultMap="resultMapAccount1">select a.*, b.*from account aleft join books bon a.aid = b.accountidwhere a.aid = #{aid}</select>
</mapper>

测试:

@Test
public void testSelectById() {try (SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession()) {AccountMapper mapper = sqlSession.getMapper(AccountMapper.class);Account account = mapper.selectById(1);System.out.println(JSON.toJSONString(account, true));} catch (IOException e) {e.printStackTrace();}
}
{"aid":1,"aname":"zhangsan","anickname":"张三","apassword":"nasgnahz","books":[{"bookid":1,"bookname":"Java从入门到精通","bookprice":99.9},{"bookid":2,"bookname":"HTML5深入浅出","bookprice":55.5}]
}

延迟加载

//延迟加载
Account selectByPrimary(Integer aid);
<resultMap id="resultMapAccount2" type="Account" autoMapping="true"><collection property="books" autoMapping="true" column="aid" select="selectBooksById" fetchType="lazy"/>
</resultMap>
<!--延迟加载-->
<select id="selectByPrimary" resultMap="resultMapAccount2">select *from accountwhere aid = #{aid}
</select><select id="selectBooksById" resultType="Books">select *from bookswhere accountid = #{accountid}
</select>

测试:

@Test
public void testSelectByPrimary() {try (SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession()) {AccountMapper mapper = sqlSession.getMapper(AccountMapper.class);Account account = mapper.selectByPrimary(1);System.out.println(account.getAname());System.out.println(JSON.toJSONString(account, true));} catch (IOException e) {e.printStackTrace();}
}
Preparing: select * from account where aid = ?
Parameters: 1(Integer)
Total: 1
zhangsan
Preparing: select * from books where bookid = ?
Parameters: 1(Integer)
Total: 2
结果在下面
{"aname":"zhangsan","anickname":"张三","apassword":"nasgnahz","books":[{"bookid":1,"bookname":"Java从入门到精通","bookprice":99.9},{"bookid":2,"bookname":"HTML5深入浅出","bookprice":55.5}]
}

延迟加载中传递多个参数

<resultMap id="resultMapAccount2" type="Account" autoMapping="true"><collection property="books" autoMapping="true" column="{aid=aid,aname=aname}" select="selectBooksById" fetchType="lazy"/>
</resultMap>
<!--延迟加载-->
<select id="selectByPrimary" resultMap="resultMapAccount2">select *from accountwhere aid = #{aid}
</select><select id="selectBooksById" resultType="Books">select *from bookswhere accountid = #{aid} or bookname = #{aname}
</select>

注意观察:resultMap标签中的column属性,多个参数之间用,隔开,key与value之间用=,还有id=selectBooksById的select标签中sql的where语句的or条件。

查询结果:

{"aid":1,"aname":"zhangsan","anickname":"张三","apassword":"nasgnahz","books":[{"bookid":1,"bookname":"Java从入门到精通","bookprice":99.9},{"bookid":2,"bookname":"HTML5深入浅出","bookprice":55.5}]
}

6、数据库表的关系、Java对象的关系、关联查询(一对多)、延迟加载访问相关推荐

  1. Oracle数据库表,数据量很少,但是查询很慢

    Oracle数据库表,数据量很少,但是查询很慢 1.起因 1.1.原因排查 1.2.原因分析 2.解决方式 2.1.删表重建这张表(95%解决一切问题) 2.2.建索引(目前用的) 1.起因 一开始是 ...

  2. BOS12——多对多添加方法,多对多页面需要字段问题(不多的话直接提供get方法),修改Realm中授权方法(查询数据库),缓存Java对象的方法,加载左侧菜单(ztree提供pId)...

    1.多对多添加方法 @Override public void add(Role model, String functionIds) {// 1.先将角色保存到数据库roleDao.save(mod ...

  3. 根据数据库表动态生成java实体类

    前言,本次记录重点在读取数据库表结构,及解析转换成java实体类需要字段. 主要包含,表 列名.类型.注释的读取解析 至于代码生成,可以基于已有的代码生成模板 整合进去即可 pom: <!-- ...

  4. Apex——SOQL在多个对象上进行关联查询

    SOQL在多个对象的关联查询 引言 重要的reference类型 代码示例 引言 最开始接触SOQL语句的时候,其实觉得和java等里的SQl没有什么区别,但是当真正在多个对象上进行查询时就不再是ja ...

  5. java动态是如何根据实体建表_传入Java对象 自动创建动态表 并录入数据

    看到Hibernate你给一个对象,他就能动态的创建配置文件里面指定的表名,然后把数据录入到数据库,当初感觉是很神奇,不过,好像 Hibernate不能动态的分表创建表和录入数据 我这里写了一个公用的 ...

  6. iBatis数据库字段映射到Java对象。

    数据库执行完SQL后会返回执行结果,iBatis如何将查询出来的记录设置到account对象中呢? 和ParameterMap类似,填充返回信息需要的资源都已经包含在ResultMap中.当有了保存返 ...

  7. Java如何监控数据库表的变化,java监听数据库表变化

    南京理工大学泰州科技学院 实验报告书课程名称: <Java 面向对象程序设计> 实验题目: 实验七 输入输出流,数据库编程 班学姓级: 号: 名: 09 计算机(2) ...... 一.实 ...

  8. java maven 项目依赖关系,java – 关于依赖关系共享的Maven多模块项目组合

    有几个类似的问题,但没有这样的.你如何处理这种情况(典型情况): 一个由8-11个子项目组成的项目,具有父工件/项目和一个主要项目,主要使用/声明其他项目作为模块. 问题是所有项目"严格&q ...

  9. 数据库表的映射与java类映射处理

    数据库表的映射与java类映射处理 1.数据库表与简单java类的映射处理 依据数据库表中的信息设计简单的java类,其实利用了上次课学习java,类间关联以及自身关联和合成设计模式实现.以下通过案例 ...

最新文章

  1. 原生JavaScript实现字符串长度截取
  2. Struts2与Webwork2的区别
  3. r语言导入ggplot2_R语言 可视化之三大绘图系统概述:base、lattice和ggplot2 | 第7讲...
  4. Matlab 中常用的直线与点形表示属性
  5. 语音信号的分帧加窗的matlab实现
  6. DNS高级部署使用RSYNC部署搭建DNS view主从服务
  7. CSS3 元素基础知识
  8. 智能问答:LSTM 句子相似度分析
  9. SecureCRT或XShell软件
  10. WPS怎么统计相同名称的数据_群发邮件平台的数据统计怎么用
  11. 按平均成绩排行c语言文件操作,学生成绩管理系统(c语言结构体以及文件操作)实验报告精选.doc...
  12. 安装CentOS步骤
  13. 1048: 谭浩强C语言(第三版)习题6.4
  14. 飞控算法-姿态解算之互补滤波
  15. Windows conda ImportError: DLL load failed while importing shell
  16. swb-2润湿平衡测试仪_自动化测试
  17. ui设计移动端字体适配_移动端界面设计之尺寸篇
  18. 全球及中国玄武岩增强纤维发展前景及投资潜力预测报告2021~2026年
  19. Guide哥|我写了四年博客,收获了20w+读者。我为什么推荐你写博客?
  20. Java面试题总结-2022版

热门文章

  1. 前端面试笔记-CSS篇
  2. Tikz作图教程:说说图形颜色填充那些事儿
  3. 最新macOS Big Sur11.1新功能介绍
  4. SaltStack常用模块之file
  5. [BZOJ3252][长链剖分]攻略
  6. webrtc中mid rid
  7. IT产品是计算机类产品吗,IT产品是什么
  8. 安科瑞企业能源管理系统在水泥企业中的设计与应用-Susie 周
  9. 孤岛危机安装时出现dll错误
  10. 译码器设计——Verilog HDL语言