SSM框架学习——MyBatis关联映射

MyBatis关联映射

为什么要关联映射

实际开发中,对数据库操作常常会涉及多张表,所以在OOP中就涉及对象与对象的关联关系。针对多表操作,MyBatis提供关联映射。

关联关系概述

  • 一对一:A类中定义B类的属性b,B类中定义A类属性a。
  • 一对多:一个A类类型对应多个B类。
  • 多对多:在A类中定义B的集合,B中定义A的集合。

嵌套查询与嵌套结果

嵌套查询是通过执行另外一条SQL映射语句来返回预期复杂类型:

  • 嵌套查询是在查询SQL嵌套一个子查询
  • 嵌套查询会执行多条SQL语句
  • 嵌套查询SQL语句编写相对简单

嵌套结果是使用嵌套结果映射来处理的联合结果子集:

  • 嵌套结果是一个嵌套的多表查询SQL
  • 嵌套结果只会执行一条复杂的SQL语句
  • 嵌套结果SQL语句写起来相对麻烦

对于嵌套查询有一个问题,那就是执行多条SQL语句导致性能开销很大。于是就有了MyBatis的延迟加载——fetchType。

实践

创建表

我们还是用之前的账户连接数据库,终端命令可以如下

mysql -u db_mybatis -p

输入密码敲击回车,然后切换数据库到db_mybatis

Windows用户可以用终端或者sqlyog执行下面语句

如果你没有这个数据库请回到前面的章节创建它。

USE db_mybatis;

我们要使用一个新的customer表,所以把之前项目留下的删除掉

DROP TABLE IF EXISTS customer;

重新创建它

CREATE TABLE customer(`id` int(32) NOT NULL AUTO_INCREMENT,`username` varchar(50) DEFAULT NULL,`jobs` varchar(50) DEFAULT NULL,`phone` varchar(16) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

给这张表插入两条数据方便我们接下来写代码用

INSERT  INTO customer(`id`,`username`,`jobs`,`phone`) VALUES (1,'zhangsan','teacher','11111111111'),(2,'lisi','student','11111111112');

创建一张idcard表,并设置主键(主码)为id

如果之前存在请删除

DROP TABLE IF EXISTS `idcard`;
CREATE TABLE idcard(`id` int(11) NOT NULL AUTO_INCREMENT,`code` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

同样插入两条数据(身份证号是我瞎编的,为了防止巧合不一定符合真实格式,但长度与现实一样)

INSERT  INTO idcard(`id`,`code`) VALUES(1,'37010219800X051118'),(2,'370203199X09092222');

创建persion表,并插入数据

DROP TABLE IF EXISTS person;CREATE TABLE person(`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(50) DEFAULT NULL,`age` int(11) DEFAULT NULL,`card_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `FK_person` (`card_id`),CONSTRAINT `FK_person` FOREIGN KEY (`card_id`) REFERENCES `idcard` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;INSERT INTO person(`id`,`name`,`age`,`card_id`) values (1,'张三',18,1),(2,'李四',18,2);

创建product表并插入数据

DROP TABLE IF EXISTS product;CREATE TABLE product(`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(32) DEFAULT NULL,`price` double DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;INSERT INTO product(`id`,`name`,`price`) values (1,'笔记本电脑',8888.8),(2,'华为手机',6666.6);

创建user表并插入数据

DROP TABLE IF EXISTS user;CREATE TABLE user(`id` int(11) NOT NULL,`name` varchar(20) DEFAULT NULL,`password` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO user(`id`,`name`,`password`) values (1,'zhangsan','654321'),(2,'lisi','123456');

创建orders

DROP TABLE IF EXISTS orders;
CREATE TABLE orders(`id` int(11) NOT NULL AUTO_INCREMENT,`number` varchar(32) DEFAULT NULL,`user_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `FK_orders` (`user_id`),CONSTRAINT `FK_orders` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;INSERT INTO `orders`(`id`,`number`,`user_id`) VALUES (1,'201901',1);

创建orderitem表,还是一样,如果之前存在先删除。这个表一定放后面,否则创建外键会报错。

DROP TABLE IF EXISTS orderitem;CREATE TABLE orderitem(`id` int(11) NOT NULL AUTO_INCREMENT,`orders_id` int(11) DEFAULT NULL,`product_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `FK_orderitem` (`orders_id`),KEY `FK_orderitem_product` (`product_id`),CONSTRAINT `FK_orderitem_product` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`),CONSTRAINT `FK_orderitem` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;INSERT INTO orderitem(`id`,`orders_id`,`product_id`) VALUES (1,1,1),(2,1,2);

构建项目

我们新创建一个Maven项目top.cairbin.test5

pom.xml添加依赖包

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>top.cairbin</groupId><artifactId>test5</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>test5</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.4</version></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core --><dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-core</artifactId><version>1.4.0</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency></dependencies>
</project>

创建与src平级的resources目录,并右键->Build Path->Use as Source Folder

在里面添加一个数据库配置文件db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_mybatis
jdbc.username=db_mybatis
jdbc.password=db_mybatis

配置MyBatis,在同目录下创建mybatis-config.xml,这个文件会使用db.properties里面的配置,以${...}的方式调用,在<properties resource="db.properties" />里声明位置。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><properties resource="db.properties" /><settings><!-- 延迟加载的开关 --><setting name="lazyLoadingEnabled" value="false" /><!-- 设置积极加载(true)或按需加载(false) --><setting name="aggressiveLazyLoading" value="true" /><setting name="mapUnderscoreToCamelCase" value="true" /><setting name="logImpl" value="STDOUT_LOGGING" /></settings><typeAliases><package name="top.cairbin.test5" /></typeAliases><!--1.配置环境 ,默认的环境id为mysql --><environments default="mysql"><!--1.2.配置id为mysql的数据库环境 --><environment id="mysql"><!-- 使用JDBC的事务管理 --><transactionManager type="JDBC" /><!--数据库连接池 --><dataSource type="POOLED"><!-- 数据库驱动 --><property name="driver" value="${jdbc.driver}" /><!-- 连接数据库的url --><property name="url" value="${jdbc.url}" /><!-- 连接数据库的用户名 --><property name="username" value="${jdbc.username}" /><!-- 连接数据库的密码 --><property name="password" value="${jdbc.password}" /></dataSource></environment></environments><!--2.配置Mapper的位置 --><mappers><mapper resource="top/cairbin/test5/mapper/CustomerMapper.xml" /><mapper resource="top/cairbin/test5/mapper/UserMapper.xml" /><mapper resource="top/cairbin/test5/mapper/IdCardMapper.xml" /><mapper resource="top/cairbin/test5/mapper/OrdersMapper.xml" /><mapper resource="top/cairbin/test5/mapper/PersonMapper.xml" /><mapper resource="top/cairbin/test5/mapper/ProductMapper.xml" /></mappers>
</configuration>

注意上方的<mapper>的目录,我们一会介绍。

然后添加log4j的配置文件log4j.properties

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.top.cairbin.test5=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

Dao层

我们先把对应的类创建了再说,到src/main/javatop.cairbin.test5创建一个子包,名称为top.cairbin.test5.dao

在我们新创建的包下面编写若干个类

首先是Person类,此时我们的IdCard还没创建,IDE提示错误很正常

package top.cairbin.test5.dao;public class Person {private Integer id;private String name;private Integer age;private Integer cardId;private IdCard card;public void setId(Integer id) {this.id = id;}public Integer getId() {return this.id;}public void setName(String name) {this.name = name;}public String getName() {return this.name;}public void setAge(Integer age) {this.age = age;}public Integer getAge() {return this.age;}public Integer getCardId() {return cardId;}public void setCardId(Integer cardId) {this.cardId = cardId;}public void setCard(IdCard card) {this.card = card;}public IdCard getCard() {return this.card;}	
}

接下来创建IdCard

package top.cairbin.test5.dao;public class IdCard {private Integer id;private String code;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getCode() {return code;}public void setCode(String code) {this.code = code == null ? null : code.trim();}
}

然后是Customer

package top.cairbin.test5.dao;public class Customer {private Integer id;private String username;private String jobs;private String phone;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 == null ? null : username.trim();}public String getJobs() {return jobs;}public void setJobs(String jobs) {this.jobs = jobs == null ? null : jobs.trim();}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone == null ? null : phone.trim();}
}

接着是Orders

package top.cairbin.test5.dao;package top.cairbin.test5.dao;import java.util.List;public class Orders {private Integer id;private String number;private Integer userId;private List<Product> productList;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getNumber() {return number;}public void setNumber(String number) {this.number = number == null ? null : number.trim();}public Integer getUserId() {return userId;}public void setUserId(Integer userId) {this.userId = userId;}public void setProductList(List<Product> productList) {this.productList = productList;}public List<Product> getProductList(){return this.productList;}   
}

Product

package top.cairbin.test5.dao;public class Product {private Integer id;private String name;private Double price;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name == null ? null : name.trim();}public Double getPrice() {return price;}public void setPrice(Double price) {this.price = price;}
}

User

package top.cairbin.test5.dao;import java.util.List;public class User {private Integer id;private String name;private String password;private List<Orders> ordersList;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name == null ? null : name.trim();}public String getPassword() {return password;}public void setPassword(String password) {this.password = password == null ? null : password.trim();}public void setOrdersList(List<Orders> ordersList) {this.ordersList = ordersList;}public List<Orders> getOrdersList(){return this.ordersList;}   
}

Util层

这一层我们来放置一些工具类。创建包top.cairbin.test5.util

然后在下面创建MyBatisHelper

package top.cairbin.test5.util;import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisHelper {private static SqlSessionFactory sqlSessionFactory = null;// 初始化SqlSessionFactory对象static {try {// 使用MyBatis提供的Resources类加载MyBatis的配置文件Reader reader = Resources.getResourceAsReader("mybatis-config.xml");// 构建SqlSessionFactory工厂sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);} catch (Exception e) {e.printStackTrace();}}// 获取SqlSession对象的静态方法public static SqlSession getSession() {return sqlSessionFactory.openSession();}
}

Mapper层

接下来我们再为top.cairbin.test5创建一个子包top.cairbin.test5.mapper,该层主要实现数据持久化。

与上面不一样的是,我们这里定义的是接口

首先是CustomerMapper接口

package top.cairbin.test5.mapper;import top.cairbin.test5.dao.Customer;public interface CustomerMapper {int deleteByPrimaryKey(Integer id);int insert(Customer record);int insertSelective(Customer record);Customer selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(Customer record);int updateByPrimaryKey(Customer record);
}

到这里你可能会问,谁去实现它们呢,还记得mybatis-config.xml里那些<mapper>吗,它们每一个都指定resource到Mapper层,也就是说我们要在这里写XML文件让MyBatis实现这些方法。

第一个是CustomerMapper.xml,注意此处的namespace<resultMap>id,下文同理。

<?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="top.cairbin.test5.mapper.CustomerMapper"><resultMap id="BaseResultMap" type="top.cairbin.test5.dao.Customer"><id column="id" jdbcType="INTEGER" property="id" /><result column="username" jdbcType="VARCHAR" property="username" /><result column="jobs" jdbcType="VARCHAR" property="jobs" /><result column="phone" jdbcType="VARCHAR" property="phone" /></resultMap><sql id="Base_Column_List">id, username, jobs, phone</sql><select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from customerwhere id = #{id,jdbcType=INTEGER}</select><delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">delete from customerwhere id = #{id,jdbcType=INTEGER}</delete><insert id="insert" parameterType="top.cairbin.test5.dao.Customer">insert into customer (id, username, jobs, phone)values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{jobs,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR})</insert><insert id="insertSelective" parameterType="top.cairbin.test5.dao.Customer">insert into customer<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="username != null">username,</if><if test="jobs != null">jobs,</if><if test="phone != null">phone,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id,jdbcType=INTEGER},</if><if test="username != null">#{username,jdbcType=VARCHAR},</if><if test="jobs != null">#{jobs,jdbcType=VARCHAR},</if><if test="phone != null">#{phone,jdbcType=VARCHAR},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.Customer">update customer<set><if test="username != null">username = #{username,jdbcType=VARCHAR},</if><if test="jobs != null">jobs = #{jobs,jdbcType=VARCHAR},</if><if test="phone != null">phone = #{phone,jdbcType=VARCHAR},</if></set>where id = #{id,jdbcType=INTEGER}</update><update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.Customer">update customerset username = #{username,jdbcType=VARCHAR},jobs = #{jobs,jdbcType=VARCHAR},phone = #{phone,jdbcType=VARCHAR}where id = #{id,jdbcType=INTEGER}</update>
</mapper>

对了,请检查你XML中的方法的parameterType属性以及resultType对应类型的包是否正确!!!

然后是IdCardMapper接口

package top.cairbin.test5.mapper;import top.cairbin.test5.dao.IdCard;public interface IdCardMapper {int deleteByPrimaryKey(Integer id);int insert(IdCard record);int insertSelective(IdCard record);IdCard selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(IdCard record);int updateByPrimaryKey(IdCard record);
}

同样在IdCardMapper.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="top.cairbin.test5.mapper.IdCardMapper"><resultMap id="BaseResultMap" type="top.cairbin.test5.dao.IdCard"><id column="id" jdbcType="INTEGER" property="id" /><result column="code" jdbcType="VARCHAR" property="code" /></resultMap><sql id="Base_Column_List">id, code</sql><select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from idcardwhere id = #{id,jdbcType=INTEGER}</select><delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">delete from idcardwhere id = #{id,jdbcType=INTEGER}</delete><insert id="insert" parameterType="top.cairbin.test5.dao.IdCard">insert into idcard (id, code)values (#{id,jdbcType=INTEGER}, #{code,jdbcType=VARCHAR})</insert><insert id="insertSelective" parameterType="top.cairbin.test5.dao.IdCard">insert into idcard<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="code != null">code,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id,jdbcType=INTEGER},</if><if test="code != null">#{code,jdbcType=VARCHAR},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.IdCard">update idcard<set><if test="code != null">code = #{code,jdbcType=VARCHAR},</if></set>where id = #{id,jdbcType=INTEGER}</update><update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.IdCard">update idcardset code = #{code,jdbcType=VARCHAR}where id = #{id,jdbcType=INTEGER}</update>
</mapper>

然后是ProductMapper接口以及ProductMapper.xml

package top.cairbin.test5.mapper;
import top.cairbin.test5.dao.Product;public interface ProductMapper {int deleteByPrimaryKey(Integer id);int insert(Product record);int insertSelective(Product record);Product selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(Product record);int updateByPrimaryKey(Product record);
}
<?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="top.cairbin.test5.mapper.ProductMapper"><resultMap id="BaseResultMap" type="top.cairbin.test5.dao.Product"><id column="id" jdbcType="INTEGER" property="id" /><result column="name" jdbcType="VARCHAR" property="name" /><result column="price" jdbcType="DOUBLE" property="price" /></resultMap><sql id="Base_Column_List">id, name, price</sql><select id="findProductByOrderId" parameterType="Integer"   resultType="Product">SELECT * from product where id IN(SELECT product_id FROM orderitem  WHERE orders_id = #{id})</select><select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from productwhere id = #{id,jdbcType=INTEGER}</select><delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">delete from productwhere id = #{id,jdbcType=INTEGER}</delete><insert id="insert" parameterType="top.cairbin.test5.dao.Product">insert into product (id, name, price)values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{price,jdbcType=DOUBLE})</insert><insert id="insertSelective" parameterType="top.cairbin.test5.dao.Product">insert into product<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="name != null">name,</if><if test="price != null">price,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id,jdbcType=INTEGER},</if><if test="name != null">#{name,jdbcType=VARCHAR},</if><if test="price != null">#{price,jdbcType=DOUBLE},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.Product">update product<set><if test="name != null">name = #{name,jdbcType=VARCHAR},</if><if test="price != null">price = #{price,jdbcType=DOUBLE},</if></set>where id = #{id,jdbcType=INTEGER}</update><update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.Product">update productset name = #{name,jdbcType=VARCHAR},price = #{price,jdbcType=DOUBLE}where id = #{id,jdbcType=INTEGER}</update>
</mapper>

对于剩下来的几个Dao层类对应的Mapper需要特别注意下,因为它们的类里都包含复合类型,这也是我们这一章节真正要学习的东西——关联映射。

PersonMapper接口

package top.cairbin.test5.mapper;import top.cairbin.test5.dao.Person;public interface PersonMapper {int deleteByPrimaryKey(Integer id);int insert(Person record);int insertSelective(Person record);Person selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(Person record);int updateByPrimaryKey(Person record);
}

PersonMapper.xml文件,请重点关注findPersonByIdfindPersonById2这两个地方。

<?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="top.cairbin.test5.mapper.PersonMapper"><resultMap id="BaseResultMap" type="top.cairbin.test5.dao.Person"><id column="id" jdbcType="INTEGER" property="id" /><result column="name" jdbcType="VARCHAR" property="name" /><result column="age" jdbcType="INTEGER" property="age" /><result column="card_id" jdbcType="INTEGER" property="cardId" /></resultMap><sql id="Base_Column_List">id, name, age, card_id</sql><!-- 嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型 --><select id="findPersonById" parameterType="Integer" resultMap="IdCardWithPersonResult">SELECT * from person where id=#{id}</select><resultMap type="Person" id="IdCardWithPersonResult"><id property="id" column="id" /><result property="name" column="name" /><result property="age" column="age" /><!-- 一对一:association使用select属性引入另外一条SQL语句 --><association property="card" column="card_id" javaType="top.cairbin.test5.dao.IdCard"select="top.cairbin.test5.mapper.IdCardMapper.selectByPrimaryKey" /></resultMap><!-- 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集 --><select id="findPersonById2" parameterType="Integer" resultMap="IdCardWithPersonResult2">SELECT p.*,idcard.codefrom person p,idcard idcardwhere p.card_id=idcard.id and p.id= #{id}</select><resultMap type="Person" id="IdCardWithPersonResult2"><id property="id" column="id" /><result property="name" column="name" /><result property="age" column="age" /><association property="card" javaType="IdCard"><id property="id" column="id" /><result property="code" column="code" /></association></resultMap><select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from personwhere id = #{id,jdbcType=INTEGER}</select><delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">delete from personwhere id = #{id,jdbcType=INTEGER}</delete><insert id="insert" parameterType="top.cairbin.test5.dao.Person">insert into person (id, name, age, card_id)values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{cardId,jdbcType=INTEGER})</insert><insert id="insertSelective" parameterType="top.cairbin.test5.dao.Person">insert into person<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="name != null">name,</if><if test="age != null">age,</if><if test="cardId != null">card_id,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id,jdbcType=INTEGER},</if><if test="name != null">#{name,jdbcType=VARCHAR},</if><if test="age != null">#{age,jdbcType=INTEGER},</if><if test="cardId != null">#{cardId,jdbcType=INTEGER},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.Person">update person<set><if test="name != null">name = #{name,jdbcType=VARCHAR},</if><if test="age != null">age = #{age,jdbcType=INTEGER},</if><if test="cardId != null">card_id = #{cardId,jdbcType=INTEGER},</if></set>where id = #{id,jdbcType=INTEGER}</update><update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.Person">update personset name = #{name,jdbcType=VARCHAR},age = #{age,jdbcType=INTEGER},card_id = #{cardId,jdbcType=INTEGER}where id = #{id,jdbcType=INTEGER}</update>
</mapper>

对于上面这么长的文件,最重要的地方是这里

<!-- 一对一:association使用select属性引入另外一条SQL语句 -->
<association property="card" column="card_id"   javaType="top.cairbin.test5.dao.IdCard"
select="top.cairbin.test5.mapper.IdCardMapper.selectByPrimaryKey" />

为了看到效果,我们在mybatis-config.xml里将延迟加载关掉。

<settings><!-- 延迟加载的开关 --><!-- 为了让大家看到关联关系的效果,我们在这里关闭了延迟加载 -->  <setting name="lazyLoadingEnabled" value="false" />  <setting name="aggressiveLazyLoading" value="true"/>  
</settings>

创建UserMapper接口和UserMapper.xml

package top.cairbin.test5.mapper;import top.cairbin.test5.dao.User;public interface UserMapper {int deleteByPrimaryKey(Integer id);int insert(User record);int insertSelective(User record);User selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(User record);int updateByPrimaryKey(User record);
}
<?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="top.cairbin.test5.mapper.UserMapper"><resultMap id="BaseResultMap" type="top.cairbin.test5.dao.User"><id column="id" jdbcType="INTEGER" property="id" /><result column="name" jdbcType="VARCHAR" property="name" /><result column="password" jdbcType="VARCHAR" property="password" /><collection property="ordersList" column="id" ofType="Orders" select="top.cairbin.test5.mapper.OrdersMapper.findOrdersWithUser"></collection></resultMap><sql id="Base_Column_List">id, name, password</sql><select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from userwhere id = #{id,jdbcType=INTEGER}</select><delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">delete from userwhere id = #{id,jdbcType=INTEGER}</delete><insert id="insert" parameterType="top.cairbin.test5.dao.User">insert into user (id, name, password)values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR})</insert><insert id="insertSelective" parameterType="top.cairbin.test5.dao.User">insert into user<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="name != null">name,</if><if test="password != null">password,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id,jdbcType=INTEGER},</if><if test="name != null">#{name,jdbcType=VARCHAR},</if><if test="password != null">#{password,jdbcType=VARCHAR},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.User">update user<set><if test="name != null">name = #{name,jdbcType=VARCHAR},</if><if test="password != null">password = #{password,jdbcType=VARCHAR},</if></set>where id = #{id,jdbcType=INTEGER}</update><update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.User">update userset name = #{name,jdbcType=VARCHAR},password = #{password,jdbcType=VARCHAR}where id = #{id,jdbcType=INTEGER}</update>
</mapper>

最后OrdersMapperOrdersMapper.xml

package top.cairbin.test5.mapper;import top.cairbin.test5.dao.Orders;public interface OrdersMapper {int deleteByPrimaryKey(Integer id);int insert(Orders record);int insertSelective(Orders record);Orders selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(Orders record);int updateByPrimaryKey(Orders record);
}
<?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="top.cairbin.test5.mapper.OrdersMapper"><resultMap id="BaseResultMap" type="top.cairbin.test5.dao.Orders"><id column="id" jdbcType="INTEGER" property="id" /><result column="number" jdbcType="VARCHAR" property="number" /><result column="user_id" jdbcType="INTEGER" property="userId" /></resultMap><sql id="Base_Column_List">id, number, user_id</sql><select id="findOrdersWithUser" parameterType="Integer" resultMap="BaseResultMap">select * from orders WHERE user_id=#{id}	</select><select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from orderswhere id = #{id,jdbcType=INTEGER}</select><delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">delete from orderswhere id = #{id,jdbcType=INTEGER}</delete><insert id="insert" parameterType="top.cairbin.test5.dao.Orders">insert into orders (id, number, user_id)values (#{id,jdbcType=INTEGER}, #{number,jdbcType=VARCHAR}, #{userId,jdbcType=INTEGER})</insert><insert id="insertSelective" parameterType="top.cairbin.test5.dao.Orders">insert into orders<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="number != null">number,</if><if test="userId != null">user_id,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id,jdbcType=INTEGER},</if><if test="number != null">#{number,jdbcType=VARCHAR},</if><if test="userId != null">#{userId,jdbcType=INTEGER},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.Orders">update orders<set><if test="number != null">number = #{number,jdbcType=VARCHAR},</if><if test="userId != null">user_id = #{userId,jdbcType=INTEGER},</if></set>where id = #{id,jdbcType=INTEGER}</update><update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.Orders">update ordersset number = #{number,jdbcType=VARCHAR},user_id = #{userId,jdbcType=INTEGER}where id = #{id,jdbcType=INTEGER}</update>
</mapper>

我们编写测试类,在src/test/java下的top.cairbin.test5

PersonTest类来测试一对一的关系

package top.cairbin.test5;import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import top.cairbin.test5.dao.Person;
import top.cairbin.test5.util.*;public class PersonTest {@Testpublic void findPersonByIdTest() {// 1、通过工具类生成SqlSession对象SqlSession session = MyBatisHelper.getSession();// 2.使用MyBatis嵌套查询的方式查询id为1的人的信息Person person = session.selectOne("top.cairbin.test5.mapper." + "PersonMapper.selectByPrimaryKey", 1);// 3、输出查询结果信息System.out.println(person);// 4、关闭SqlSessionsession.close();}//测试一对一(嵌套结果):@Testpublic void findPersonByIdTest2() {// 1、通过工具类生成SqlSession对象SqlSession session = MyBatisHelper.getSession();// 2.使用MyBatis嵌套查询的方式查询id为1的人的信息Person person = session.selectOne("top.cairbin.test5.mapper." + "PersonMapper.findPersonById2", 1);// 3、输出查询结果信息System.out.println(person);// 4、关闭SqlSessionsession.close();}
}

点击运行,两个方法所用时间差距不小。

我们再编写测试类UserTest来测试一对多

package top.cairbin.test5;import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import top.cairbin.test5.dao.User;
import top.cairbin.test5.util.MyBatisHelper;public class UserTest {@Testpublic void findUserTest() {// 1、通过工具类生成SqlSession对象SqlSession session = MyBatisHelper.getSession();// 2、查询id为1的用户信息,注意包名为mapper层的User user = session.selectOne("top.cairbin.test5.mapper."+ "UserMapper.selectByPrimaryKey", 1);// 3、输出查询结果信息System.out.println(user);// 4、关闭SqlSessionsession.close();}}

最后编写OrdersTest来测试多对多

package top.cairbin.test5;import org.apache.ibatis.session.SqlSession;
import org.junit.Test;import top.cairbin.test5.dao.Orders;
import top.cairbin.test5.util.MyBatisHelper;public class OrdersTest {@Testpublic void findOrdersTest(){// 1、通过工具类生成SqlSession对象SqlSession session = MyBatisHelper.getSession();// 2、查询id为1的订单中的商品信息Orders orders = session.selectOne("top.cairbin.test5.mapper."+ "OrdersMapper.selectByPrimaryKey", 1);// 3、输出查询结果信息System.out.println(orders);// 4、关闭SqlSessionsession.close();}
}

总结

在本章节,我们学习了MyBatis的关联映射。此外,你会发现我们写代码不再像以前一样只放在一个包里,而是创建好几个子包分层放置不同的代码,这有利于代码逻辑清晰并且方便维护,这种分层设计已经有一个正式项目的雏形了,当你写完这些东西的时候项目目录应该与我差不多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/785632.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MCU友好过渡MPU,米尔基于STM32MP135开发板裸机开发应用笔记

以前微处理器&#xff08;MPU&#xff09;与微控制器&#xff08;MCU&#xff09;是截然不同的两种设备&#xff0c;MPU支持丰富的软件系统&#xff0c;如Linux和相关的软件堆栈&#xff0c;而MCU通常将专注于裸机和RTOS。近年来&#xff0c;随着MCU的性能越来越高&#xff0c;…

【Spring源码】WebSocket做推送动作的底层实例

一、前瞻 Ok&#xff0c;开始我们今天的对Spring的【模块阅读】。 那就挑Web里的WebSocket模块&#xff0c;先思考下本次阅读的阅读线索&#xff1a; WebSocket在Spring里起到什么作用这个模块采用了什么设计模式我们都知道WebSocket可以主动推送消息给用户&#xff0c;那做推…

halcon图像膨胀

1、原理&#xff1a; 使用结构元素在图像上移动&#xff0c;如果结构元素中有任意一个像素和图像上的非零像素重叠&#xff0c;则保留此时结构元素中心所在位置&#xff0c;并将其像素值设置为非零。 2、halcon代码 其中圆形结构元素可设置半径&#xff0c;矩形结构元素设置…

MySQL故障排查与生产环境优化

一、MySQL单实例常见故障 1.逻辑架构图 MySQL逻辑架构图客户端和连接服务核心服务功能存储引擎层数据存储层 2.故障一 故障现象 ERROR 2002 (HY000): Cant connect to local MySQL server through socket/data/mysql/mysql.sock(2) 问题分析 数据库未启动或者数据库端口…

Yolo 自制数据集dect训练改进

上一文请看 Yolo自制detect训练-CSDN博客 简介 如下图&#xff1a; 首先看一下每个图的含义 loss loss分为cls_loss, box_loss, obj_loss三部分。 cls_loss用于监督类别分类&#xff0c;计算锚框与对应的标定分类是否正确。 box_loss用于监督检测框的回归&#xff0c;预测框…

蓝桥杯真题:成绩统计

这题思路简单&#xff0c;但是输出结果的位置容易出错&#xff0c;题目要求四舍五入&#xff0c;所以要用Math.round&#xff08;&#xff09;的方法

Python快速入门系列-7(Python Web开发与框架介绍)

第七章:Python Web开发与框架介绍 7.1 Flask与Django简介7.1.1 Flask框架Flask的特点Flask的安装一个简单的Flask应用示例7.1.2 Django框架Django的特点Django的安装一个简单的Django应用示例7.2 前后端交互与数据传输7.2.1 前后端交互7.2.2 数据传输格式7.2.3 示例:使用Flas…

解决GNU Radio+USRP实现OFDM收发在接收端存在误码问题

文章目录 前言一、OFDM 收发流程1、OFDM 收端流程2、OFDM 收端流程 二、问题所在1、find_trigger_signal 函数解读2、general_work 函数3、问题所在 三、修改源码四、运行结果1、频谱2、传输数据测试 五、调试小技巧六、资源自取 前言 在使用 GNU Radio 时使用官方例程搭建 GN…

git clone没有权限的解决方法

一般情况 git clone时没有权限&#xff0c;一般是因为在代码库平台上没有配置本地电脑的id_rsa.pub 只要配置上&#xff0c;一般就可以正常下载了。 非一般情况 但是也有即使配置了id_rsa.pub后&#xff0c;仍然无法clone代码的情况。如下 原因 这种情况是因为ssh客户端…

前端常用代码整理— js,jquery篇(3)

目录 1.判断是否是json字符串 2.获取当前网址 3.将文本复制到剪贴板 4.获取一个月的天数 5.展平数组 6.要修改getRandomItem函数以返回数组中的随机两个元素&#xff0c;可以尝试以下代码 1.判断是否是json字符串 const isJson str > {try {JSON.parse(str);return …

通过 Cookie、Session 和 Spring 拦截器技术,实现对用户登录状态的持有和清理(一)

本篇博客对应“2.3 会话管理”小节 视频名称&#xff1a;会话管理 视频链接 什么是HTPP协议&#xff1f; HTTP&#xff0c;Hpyer Text Transfer Protocl&#xff1a;定义了浏览器怎样从&#xff08;万维网客户进程&#xff09;怎样向Web服务器&#xff08;万维网服务器&#…

vue 文件下载

1.返回路径下载 注: 针对一些浏览器无法识别的文件格式&#xff08;如pdf、xls、ppt&#xff09;。可以直接在地址栏上输入URL即可触发浏览器的下载功能。 情况1 //地址栏输入文件URLwindow.location.href URLwindow.open(URL) 注:该方式将下载逻辑放在后端处理&#xff0c…

Mysql的高级语句3

目录 一、子查询 注意&#xff1a;子语句可以与主语句所查询的表相同&#xff0c;但是也可以是不同表。 1、select in 1.1 相同表查询 1.2 多表查询 2、not in 取反&#xff0c;就是将子查询结果&#xff0c;进行取反处理 3、insert into in 4、update…

IO练习题

1&#xff1a;使用 dup2 实现错误日志功能 使用 write 和 read 实现文件的拷贝功能&#xff0c;注意&#xff0c;代码中所有函数后面&#xff0c;紧跟perror输出错误信息&#xff0c;要求这些错误信息重定向到错误日志 err.txt 中 #include <myhead.h>//文件IO实现文件拷…

实现offsetof宏以及交换一个整数二进制奇偶位的宏

目录 1. offsetof宏2. 交换奇偶位 1. offsetof宏 我们想用宏来实现offsetof函数,首先要了解这个函数的用法。 1.1 offsetof函数的介绍及用法 &#xff08;1&#xff09;功能&#xff1a;用来计算结构体中一个成员在该结构体中的相对起始位置的偏移量&#xff0c;单位是字节。 …

ClamAV:Linux服务器杀毒扫描工具

Clam AntiVirus&#xff08;ClamAV&#xff09;是免费而且开放源代码的防毒软件&#xff0c;软件与病毒码的更新皆由社群免费发布。ClamAV在命令行下运行&#xff0c;它不将杀毒作为主要功能&#xff0c;默认只能查出系统内的病毒&#xff0c;但是无法清除。需要用户自行对病毒…

C语言之位段

1.位段的声明 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1.位段的成员必须是 int、unsigned int 或signed int 。 2.位段的成员名后边有一个冒号和一个数字。 比如&#xff1a; struct A {int _a:2;int _b:5;int _c:10;int _d:30; }; A 就是一个位段类型…

【上海大学计算机组成原理实验报告】二、数据传送实验

一、实验目的 了解在模型机中算术、逻辑运算单元的控制方法。学习机器语言程序的运行过程。通过人工译码&#xff0c;加深对译码器基本工作原理的理解。 二、实验原理 根据实验指导书的相关内容&#xff0c;本次实验所要用的CP226实验仪在手动方式下&#xff0c;运算功能通过…

LeetCode-热题100:160. 相交链表

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&…

Go语言学习Day6:数组与切片

名人说&#xff1a;莫愁千里路&#xff0c;自有到来风。 ——钱珝 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 1. 数组① 什么是数组② 数组的声明③ 初始化数组的几种方式④ 遍历数组元素⑤ 数组为值类型⑥ 数…