1、建立测试数据模型
1.1 业务模型分析
该测试案例模拟一个银行批量购买理财产品的业务。用户在网银理财购买页面勾选多款理财产品(如10款),然后网银系统生成一个批次号,该批次号对应这一批(如10款)的理财产品数据,一批中的每一款理财产品会有一个产品号,对应该理财产品的信息。
批量转账的业务逻辑涉及4张表,分别是用户表、批次表、批次明细表和理财产品表。
这里的customer表对应购买的理财产品的用户信息表。batch表存放购买的一批理财产品的批次信息。finacial_products表中存储了每一个理财产品的详细信息。batchdetail表为批次明细表,它是一张中间表,指定对应的一批购买包含了哪些理财产品,对应的理财产品属于哪个购买批次。
在实体-关系模型中,一个用户可以购买多个批次的理财产品,多个批次的产品也能被同一个用户购买;一个产品批次中包含多个理财产品,而一个理财产品也能被多个产品批次包含。
1.2 根据业务创建测试表
创建finacial_products表(理财产品表),并插入如下测试数据,具体SQL语句代码如下:
DROP TABLE IF EXISTS `finacial_products`;
CREATE TABLE `finacial_products` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '理财产品名称',
`price` float(10,1) NOT NULL COMMENT '理财产品定价',
`detail` text COMMENT '理财产品描述',
`pic` varchar(64) DEFAULT NULL COMMENT '理财产品图片',
`invasttime` datetime NOT NULL COMMENT '理财产品收益日期',
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*Data for the table `finacial_products` */
insert into `finacial_products`(`product_id`,`name`,`price`,`detail`,`pic`,`invasttime`) values (1,'一起富',5000.0,'投资少,风险小','img001','2017-06-21 00:00:00'),(2,'惠薪富',10000.0,'收益稳健','img002','2017-05-03 00:00:00'),(3,'安富尊容',15000.0,'年收益率提升5%','img003','2017-07-18 00:00:00'),(4,'富津利',2000.0,'企划收益率','img004','2017-04-11 00:00:00');
创建后的数据表如下所示:
创建customer表(用户表),并插入如下测试数据,具体SQL语句代码如下:
DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
`cus_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`acno` varchar(32) DEFAULT NULL COMMENT '卡号',
`gender` varchar(4) DEFAULT NULL COMMENT '性别',
`phone` varchar(256) DEFAULT NULL COMMENT '电话',
PRIMARY KEY (`cus_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*Data for the table `customer` */
insert into `customer`(`cus_id`,`username`,`acno`,`gender`,`phone`) values (1,'刘云','6228286666666','男','13800000000'),(2,'李健','622848111111','男','13811111111'),(3,'张丽丽','622848333333','女','13822222222');
创建后的数据表如下所示:
创建batch表(批次表),并插入如下测试数据,具体SQL语句代码如下:
DROP TABLE IF EXISTS `batch`;
CREATE TABLE `batch` (
`batch_id` int(11) NOT NULL AUTO_INCREMENT,
`cus_id` int(11) NOT NULL COMMENT '创建批次用户id',
`number` varchar(32) NOT NULL COMMENT '批次编码',
`createtime` datetime NOT NULL COMMENT '创建批次时间',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`batch_id`),
KEY `FK_batch_1` (`cus_id`),
CONSTRAINT `FK_batch_id` FOREIGN KEY (`cus_id`) REFERENCES `customer` (`cus_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
/*Data for the table `batch` */
insert into `batch`(`batch_id`,`cus_id`,`number`,`createtime`,`note`) values (1,1,'00001','2017-07-22 00:00:00','首次购买'),(2,3,'00002','2017-03-11 00:00:00','委托购买');
创建后的数据表如下所示:
创建batchdetail表(批次明细表),并插入如下测试数据,具体SQL语句代码如下:
DROP TABLE IF EXISTS `batchdetail`;
CREATE TABLE `batchdetail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`batch_id` int(11) NOT NULL COMMENT '批次id',
`product_id` int(11) NOT NULL COMMENT '理财产品id',
`product_num` int(11) DEFAULT NULL COMMENT '理财产品购买数量',
PRIMARY KEY (`id`),
KEY `FK_batchdetail_1` (`batch_id`),
KEY `FK_batchdetail_2` (`product_id`),
CONSTRAINT `FK_batchdetai_1` FOREIGN KEY (`batch_id`) REFERENCES `batch` (`batch_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_batchdetai_2` FOREIGN KEY (`product_id`) REFERENCES `finacial_products` (`product_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
/*Data for the table `batchdetail` */
insert into `batchdetail`(`id`,`batch_id`,`product_id`,`product_num`) values (1,1,1,2),(2,1,2,1),(3,1,3,1),(4,2,1,2),(5,2,2,1);
创建后的数据表如下所示:
1.3 创建实体类
在com.ccff.mybatis.model包下创建名为“FinacialProduct”的实体类,具体代码如下:
package com.ccff.mybatis.model;
import java.util.Date;
import java.util.Set;
public class FinacialProduct {
private int product_id;
private String name;
private float price;
private String detail;
private String pic;
private Date invasttime;
private Set<Batch> batches;
public int getProduct_id() {
return product_id;
}
public void setProduct_id(int product_id) {
this.product_id = product_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public String getPic() {
return pic;
}
public void setPic(String pic) {
this.pic = pic;
}
public Date getInvasttime() {
return invasttime;
}
public void setInvasttime(Date invasttime) {
this.invasttime = invasttime;
}
public Set<Batch> getBatches() {
return batches;
}
public void setBatches(Set<Batch> batches) {
this.batches = batches;
}
@Override
public String toString() {
return "FinacialProduct{" +
"product_id=" + product_id +
", name='" + name + '\'' +
", price=" + price +
", detail='" + detail + '\'' +
", pic='" + pic + '\'' +
", invasttime=" + invasttime +
", batches=" + batches +
'}';
}
}
在com.ccff.mybatis.model包下创建名为“Customer”的实体类,具体代码如下:
package com.ccff.mybatis.model;
import java.util.Set;
public class Customer {
private int cus_id;
private String username;
private String acno;
private String gender;
private String phone;
private Set<Batch> batches;
public int getCus_id() {
return cus_id;
}
public void setCus_id(int cus_id) {
this.cus_id = cus_id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAcno() {
return acno;
}
public void setAcno(String acno) {
this.acno = acno;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Set<Batch> getBatches() {
return batches;
}
public void setBatches(Set<Batch> batches) {
this.batches = batches;
}
@Override
public String toString() {
return "Customer{" +
"cus_id=" + cus_id +
", username='" + username + '\'' +
", acno='" + acno + '\'' +
", gender='" + gender + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
在com.ccff.mybatis.model包下创建名为“Batch”的实体类,具体代码如下:
package com.ccff.mybatis.model;
import java.util.Date;
import java.util.Set;
public class Batch {
private int batch_id;
private String number;
private Date createtime;
private String note;
private Customer customer;
private Set<FinacialProduct> finacialProducts;
public int getBatch_id() {
return batch_id;
}
public void setBatch_id(int batch_id) {
this.batch_id = batch_id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Date getCreatetime() {
return createtime;
}
public void setCreatetime(Date createtime) {
this.createtime = createtime;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Set<FinacialProduct> getFinacialProducts() {
return finacialProducts;
}
public void setFinacialProducts(Set<FinacialProduct> finacialProducts) {
this.finacialProducts = finacialProducts;
}
@Override
public String toString() {
return "Batch{" +
"batch_id=" + batch_id +
", number='" + number + '\'' +
", createtime=" + createtime +
", note='" + note + '\'' +
", customer=" + customer +
'}';
}
}
这里需要注意的是,在定义实体时,若定义的是双向关联,即双方的属性中均有对方对象作为域属性出现,那么它们在定义各自的toString方法时需要注意,只让某一方可以输出另一方即可,不要让双方的toString方法均可输出对方。这样会形成递归调用,程序出错。
1.3 创建接口Dao
在com.ccff.mybatis.dao包下创建名为“IFinacialDao”的接口类,用于之后的测试接口方法的编写。具体代码如下:
package com.ccff.mybatis.dao;
public interface IFinacialDao {
}
1.4 创建SQL映射文件
在config/sqlmap文件夹下创建名为“FinacialMapper”的SQL映射文件,具体代码如下:
<?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.ccff.mybatis.dao.IFinacialDao">
</mapper>
并将创建好的SQL映射文件添加到MyBatis的全局配置文件中,具体代码如下:
<mappers>
<mapper resource="sqlmap/UserMapper.xml"/>
<mapper resource="sqlmap/StudentMapper.xml"/>
<mapper resource="sqlmap/BasketballPlayerMapper.xml"/>
<mapper resource="sqlmap/FinacialMapper.xml"/>
</mappers>
1.5 创建测试类
在com.ccff.mybatis.test包下创建名为“FinacialTest”的测试类,具体代码如下:
package com.ccff.mybatis.test;
import com.ccff.mybatis.dao.IFinacialDao;
import com.ccff.mybatis.datasource.DataConnection;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import java.io.IOException;
public class FinacialTest {
private SqlSession sqlSession;
private IFinacialDao finacialDao;
@Before
public void init() {
DataConnection dataConnection = new DataConnection();
try {
sqlSession = dataConnection.getSqlSession();
finacialDao = sqlSession.getMapper(IFinacialDao.class);
} catch (IOException e) {
e.printStackTrace();
}
}
@After
public void tearDown(){
if (sqlSession != null)
sqlSession.close();
}
}
2、一对多查询
一对多关联查询是指,在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。
一对多查询测试案例的需求是:根据用户id查询该用户信息及用户所购买的理财产品批次信息。
2.1 修改实体类
修改Customer实体类,重写该类的toString方法,具体代码如下:
@Override
public String toString() {
return "Customer{" +
"cus_id=" + cus_id +
", username='" + username + '\'' +
", acno='" + acno + '\'' +
", gender='" + gender + '\'' +
", phone='" + phone + '\'' +
", batches=" + batches +
'}';
}
2.2 修改Dao接口
修改IFinacialDao接口,添加selectCustomerByIdUnion方法和selectCustomerByIdAlone方法,具体代码如下:
package com.ccff.mybatis.dao;
import com.ccff.mybatis.model.Customer;
public interface IFinacialDao {
//一对多查询:根据用户id查询该用户信息及用户所购买的理财产品批次信息。
Customer selectCustomerByIdUnion(int cus_id);
Customer selectCustomerByIdAlone(int cus_id);
}
2.3 修改SQL映射文件
2.3.1 通过多表连接查询方式实现
首先采用多表连接查询的方式实现, 修改SQL映射文件,添加相应的SQL语句,具体配置如下:
<?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.ccff.mybatis.dao.IFinacialDao">
<resultMap id="CustomerMapper" type="Customer">
<id column="cus_id" property="cus_id" />
<result column="username" property="username" />
<result column="acno" property="acno" />
<collection property="batches" ofType="Batch">
<id column="batch_id" property="batch_id" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
</collection>
</resultMap>
<select id="selectCustomerByIdUnion" resultMap="CustomerMapper">
select customer.cus_id,customer.username,customer.acno,batch.batch_id,batch.number,batch.createtime,batch.note
from customer,batch
where customer.cus_id = batch.cus_id and customer.cus_id = #{cus_id}
</select>
</mapper>
在上述的SQL配置文件中,使用resultMap可以将数据库字段映射到名称不一样的相应式题类属性,重要的是,可以映射实体类中包裹的其他实体类。这里需要注意的是使用resultMap时,尽管字段名与属性名相同也要写出它们的映射关系,因为MyBatis框架是依据这个resultMap标签封装对象的。
另外,在映射文件中使用collection标签体现出两个实体对象之间的关联关系。其中属性含义如下:
- property:指定关联属性,即实体类中的集合属性
- ofType:实体类中集合属性的泛型类型
2.3.2 通过多表单独查询方式实现
其次采用多表单独查询的方式实现, 多表连接查询方式是将多张表进行连接,连为一张表后进行查询。其查询的本质是一张表。而多表单独查询方式是多张表各自查询各自的相关内容,需要多张表的联合数据时,则将主表的查询结果联合其它表的查询结果,封装为一个对象。当然,这多个查询是可以跨越多个映射文件的,即是可以跨越多个namespace的。在使用其它namespace的查询时,添加上其所在的namespace即可。
修改SQL映射文件,添加相应的SQL语句,具体配置如下:
<!--根据外键cus_id查询batch表-->
<select id="selectBatchByCustomerId" resultType="Batch">
select batch_id,number,createtime,note from batch where cus_id = #{cus_id}
</select>
<resultMap id="CustomerMapperAlone" type="Customer">
<id column="cus_id" property="cus_id" />
<result column="username" property="username" />
<result column="acno" property="acno" />
<!-- 关联属性的映射关系 -->
<!--集合的数据来自于指定的select查询,而该select查询的恭泰参数来自于column指定的字段值-->
<collection property="batches" ofType="Batch" select="selectBatchByCustomerId" column="cus_id" />
</resultMap>
<!--根据cus_id查询customer表-->
<select id="selectCustomerByIdAlone" resultMap="CustomerMapperAlone">
select cus_id,username,acno from customer where cus_id = #{cus_id}
</select>
其中,映射文件中两个select标签之间的联系为:关联属性collection的数据来自于另一个查询selectBatchByCustomerId。而该查询selectBatchByCustomerId的动态参数“cus_id = #{cus_id}”的值来自于查询selectCustomerByIdAlone的查询结果字段cus_id。如图所示:
2.4 修改测试类
在测试类FinacialTest中添加测试方法TestSelectCustomerByIdUnion,具体代码如下:
@Test
public void TestSelectCustomerByIdUnion(){
Customer customer = finacialDao.selectCustomerByIdUnion(1);
System.out.println(customer);
}
执行该测试方法后,即可在控制台看到如下日志信息,说明测试通过。
在测试类FinacialTest中添加测试方法TestSelectCustomerByIdAlone,具体代码如下:
@Test
public void TestSelectCustomerByIdAlone(){
Customer customer = finacialDao.selectCustomerByIdAlone(3);
System.out.println(customer);
}
执行该测试方法后,即可在控制台看到如下日志信息,说明测试通过。
3、多对一查询
这里的多对一关联查询是指,在查询多方对象的时候,同时将其所关联的一方对象也查询出来。
由于在查询多方对象时也是一个一个查询,所以多对一关联查询,其实就是一对一关联查询。即一对一关联查询的实现方式与多对一的实现方式是相同的。
多对一查询测试案例的需求是:查询batch批次表,并在查询时通过外键cus_id查询中customer用户表的数据。
3.1 修改实体类
将Customer实体类中的toString方法中的batches属性删除,具体代码如下:
@Override
public String toString() {
return "Customer{" +
"cus_id=" + cus_id +
", username='" + username + '\'' +
", acno='" + acno + '\'' +
", gender='" + gender + '\'' +
", phone='" + phone + '\'' +
'}';
}
修改Batch实体类,重写其toString方法如下:
@Override
public String toString() {
return "Batch{" +
"batch_id=" + batch_id +
", number='" + number + '\'' +
", createtime=" + createtime +
", note='" + note + '\'' +
", customer=" + customer +
'}';
}
3.2 修改Dao接口
修改IFinacialDao接口,添加selectCustomerById方法,具体代码如下:
package com.ccff.mybatis.dao;
import com.ccff.mybatis.model.Batch;
import com.ccff.mybatis.model.Customer;
import java.util.List;
public interface IFinacialDao {
//一对多查询:根据用户id查询该用户信息及用户所购买的理财产品批次信息。
Customer selectCustomerByIdUnion(int cus_id);
Customer selectCustomerByIdAlone(int cus_id);
//多对一查询:查询batch批次表,并在查询时通过外键cus_id查询中customer用户表的数据
List<Batch> selectAllBatchUnion();
List<Batch> selectAllBatchAlone();
}
3.3 修改SQL配置文件
3.3.1 通过多表连接查询实现
修改SQL映射文件,添加相应的SQL语句,具体配置如下:
<!-- 多对一查询——多表连接查询方式 -->
<resultMap id="BatchMapperUnion" type="Batch">
<id column="batch_id" property="batch_id" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<association property="customer" javaType="Customer">
<id column="cus_id" property="cus_id" />
<result column="username" property="username" />
<result column="acno" property="acno" />
<result column="gender" property="gender" />
<result column="phone" property="phone" />
</association>
</resultMap>
<select id="selectAllBatchUnion" resultMap="BatchMapperUnion">
select batch.batch_id,batch.number,batch.createtime,batch.note,customer.cus_id,customer.username,customer.acno,customer.gender,customer.phone
from customer,batch
where customer.cus_id = batch.cus_id
</select>
3.3.2 通过多表单独查询实现
修改SQL映射文件,添加相应的SQL语句,具体配置如下:
<!-- 多对一查询——多表单独查询方式 -->
<select id="selectCustomer" resultType="Customer">
select cus_id,username,acno,gender,phone from customer where cus_id = #{cus_id}
</select>
<resultMap id="BatchMapperAlone" type="Batch">
<id column="batch_id" property="batch_id" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<association property="customer" javaType="Customer" select="selectCustomer" column="cus_id" />
</resultMap>
<select id="selectAllBatchAlone" resultMap="BatchMapperAlone">
select batch_id,number,createtime,note,cus_id from batch
</select>
3.4 修改测试类
在测试类FinacialTest中添加测试方法TestSelectAllBatchUnion和TestSelectAllBatchAlone,具体代码如下:
@Test
public void TestSelectAllBatchUnion(){
List<Batch> batches = finacialDao.selectAllBatchUnion();
for (Batch batch : batches){
System.out.println(batch);
}
}
@Test
public void TestSelectAllBatchAlone(){
List<Batch> batches = finacialDao.selectAllBatchAlone();
for (Batch batch : batches){
System.out.println(batch);
}
}
执行TestSelectAllBatchUnion方法后,即可在控制台看到如下日志信息,说明测试通过。
执行TestSelectAllBatchAlone方法后,即可在控制台看到如下日志信息,说明测试通过。
4、多对多查询
所谓多对多关系,其实是由两个胡凡的一对多关系组成的。一般情况下,多对多关系都会通过一个中间表建立,例如在测试案例业务场景中的batchdetail表。
通过之前对业务的分析可知,一个批次的理财产品可以包含多个理财产品,而一个理财产品也可能被多个批次包含,所以理财产品的批次和理财产品之间具有多对多的关联关系。
多对一查询测试案例的需求是:查询finacial_products表,及每个理财产品所被包含的批次信息。
4.1 修改实体类
在FinacialProduct实体类中重写toString方法,具体代码如下:
@Override
public String toString() {
return "FinacialProduct{" +
"product_id=" + product_id +
", name='" + name + '\'' +
", price=" + price +
", detail='" + detail + '\'' +
", pic='" + pic + '\'' +
", invasttime=" + invasttime +
", batches=" + batches +
'}';
}
4.2 修改Dao接口
修改IFinacialDao接口,添加selectAllFinacialProduct方法,具体代码如下:
package com.ccff.mybatis.dao;
import com.ccff.mybatis.model.Batch;
import com.ccff.mybatis.model.Customer;
import com.ccff.mybatis.model.FinacialProduct;
import java.util.List;
public interface IFinacialDao {
//一对多查询:根据用户id查询该用户信息及用户所购买的理财产品批次信息。
Customer selectCustomerByIdUnion(int cus_id);
Customer selectCustomerByIdAlone(int cus_id);
//多对一查询:查询batch批次表,并在查询时通过外键cus_id查询中customer用户表的数据
List<Batch> selectAllBatchUnion();
List<Batch> selectAllBatchAlone();
//多对多查询:查询finacial_products表,及每个理财产品所被包含的批次信息
List<FinacialProduct> selectAllFinacialProduct();
}
4.3 修改SQL配置文件
修改SQL映射文件,添加相应的SQL语句,具体配置如下:
<!-- 多对多查询 -->
<resultMap id="FinacialProductMapper" type="FinacialProduct">
<id column="product_id" property="product_id" />
<result column="name" property="name" />
<result column="price" property="price" />
<result column="detail" property="detail" />
<result column="pic" property="pic" />
<result column="invasttime" property="invasttime" />
<collection property="batches" ofType="Batch">
<id column="batch_id" property="batch_id" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
</collection>
</resultMap>
<select id="selectAllFinacialProduct" resultMap="FinacialProductMapper">
select finacial_products.*,batch.* from finacial_products,batch,batchdetail
where finacial_products.product_id = batchdetail.product_id and batch.batch_id = batchdetail.batch_id
</select>
4.4 修改测试类
在测试类FinacialTest中添加测试方法TestSelectAllFinacialProduct,具体代码如下:
@Test
public void TestSelectAllFinacialProduct(){
List<FinacialProduct> finacialProducts = finacialDao.selectAllFinacialProduct();
for (FinacialProduct finacialProduct : finacialProducts){
System.out.println(finacialProduct);
}
}
执行TestSelectAllBatchAlone方法后,即可在控制台看到如下日志信息,说明测试通过。
5、自关联查询
5.1 综述
所谓自关联查询是指,自己即充当一方,又充当多方,是1:n或n:1的变型。例如对于新闻栏目NewsLabel,可以充当一方,即父栏目,也可以充当多方,即子栏目。而反映到数据中的数据表中,只有一张表,这张表具有一个外键,用于表示该栏目的父栏目。一级栏目没有父栏目,所以可以将其外键设置为0,而子栏目则具有外键值。
为了便于理解,将自关联分为两种情况来讲解。一种是当做1:n讲解,即当前类作为一方,其包含多方的集合域属性。一种是当做n:1讲解,即当前类作为多方,其包含一方的域属性。
5.2 创建数据表
在mybatis数据库中创建名为“newslabel”的数据表用于测试自关联查询。建表SQL语句以及插入测试数据的SQL语句如下所示:
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `newslabel`
-- ----------------------------
DROP TABLE IF EXISTS `newslabel`;
CREATE TABLE `newslabel` (
`id` int(5) NOT NULL auto_increment,
`name` varchar(20) default NULL,
`pid` int(5) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of newslabel
-- ----------------------------
INSERT INTO `newslabel` VALUES ('1', '娱乐新闻', '0');
INSERT INTO `newslabel` VALUES ('2', '体育新闻', '0');
INSERT INTO `newslabel` VALUES ('3', 'NBA', '2');
INSERT INTO `newslabel` VALUES ('4', 'CBA', '2');
INSERT INTO `newslabel` VALUES ('5', '火箭', '3');
INSERT INTO `newslabel` VALUES ('6', '湖人', '3');
INSERT INTO `newslabel` VALUES ('7', '北京金隅', '4');
INSERT INTO `newslabel` VALUES ('8', '浙江广厦', '4');
INSERT INTO `newslabel` VALUES ('9', '青岛双星', '4');
INSERT INTO `newslabel` VALUES ('10', '港台明星', '1');
INSERT INTO `newslabel` VALUES ('11', '内地明星', '1');
5.3 定义实体类
在com.ccff.mybatis.model包下创建名为“NewsLabel”的实体类,具体代码如下:
package com.ccff.mybatis.model;
import java.util.Set;
public class NewsLabel {
private int id;
private String name;
private Set<NewsLabel> children;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<NewsLabel> getChildren() {
return children;
}
public void setChildren(Set<NewsLabel> children) {
this.children = children;
}
@Override
public String toString() {
return "INewsLabelDao{" +
"id=" + id +
", name='" + name + '\'' +
", children=" + children +
'}';
}
}
5.4 创建Dao接口
在com.ccff.mybatis.dao包下创建名为“INewsLabelDao”的接口,具体代码如下:
package com.ccff.mybatis.dao;
import com.ccff.mybatis.model.NewsLabel;
import java.util.List;
public interface INewsLabelDao {
}
5.5 创建SQL映射文件
在config/sqlmap文件夹下创建名为“NewsLabelMapper”的SQL映射文件,具体代码如下:
<?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.ccff.mybatis.dao.INewsLabelDao">
</mapper>
创建SQL映射文件完成后,需要将NewsLabelMapper.xml配置文件配置到MyBatis的全局配置文件SqlMapConfig中,具体配置如下:
<mappers>
<mapper resource="sqlmap/UserMapper.xml"/>
<mapper resource="sqlmap/StudentMapper.xml"/>
<mapper resource="sqlmap/BasketballPlayerMapper.xml"/>
<mapper resource="sqlmap/FinacialMapper.xml"/>
<mapper resource="sqlmap/NewsLabelMapper.xml" />
</mappers>
5.6 创建测试类
在com.ccff.mybatis.test包下创建名为“NewsLabelTest”的测试类,实现初始化操作及后续操作,具体代码如下:
package com.ccff.mybatis.test;
import com.ccff.mybatis.dao.INewsLabelDao;
import com.ccff.mybatis.datasource.DataConnection;
import com.ccff.mybatis.model.NewsLabel;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
public class NewsLabelTest {
private SqlSession sqlSession;
private INewsLabelDao newsLabelDao;
@Before
public void init() {
DataConnection dataConnection = new DataConnection();
try {
sqlSession = dataConnection.getSqlSession();
newsLabelDao = sqlSession.getMapper(INewsLabelDao.class);
} catch (IOException e) {
e.printStackTrace();
}
}
@After
public void tearDown(){
if (sqlSession != null)
sqlSession.close();
}
}
5.7 以一对多方式处理
以一对多方式处理,即一方可以看到多方。该处理方式的应用场景比较多,例如在页面上点击父栏目,显示出其子栏目。再如,将鼠标定位在窗口中的某菜单项上回显示其所有子菜单项等。
根据查询需求的不同,有可以分为两种情况:一种是查询出指定栏目的所有子孙栏目,一种是查询出指定栏目及其所有子孙栏目。
5.7.1 查询指定栏目的所有子孙栏目
根据指定的id,仅查询出其所有子栏目。当然,包括其所有备份的孙子栏目。即,给出的查询id实际为父栏目id。
在SQL映射文件中具体配置如下:
<!--根据pid查询其子栏目-->
<resultMap id="newsLabelMapper" type="NewsLabel">
<id column="id" property="id" />
<result column="name" property="name" />
<collection property="children" ofType="NewsLabel" select="selectChildrenByParentId" column="id" />
</resultMap>
<select id="selectChildrenByParentId" parameterType="int" resultMap="newsLabelMapper">
select id,name from newslabel where pid = #{pid}
</select>
这里通过select语句的递归调用实现查询所有下级栏目的功能。查询结果的集合数据collection来自于递归调用的selectChildrenByParentId查询。与第一次进行该查询不同的是,第一次的pid动态参数值来自于调用方法传递来的实参,而collection中查询语句的pid动态参数来自于上一次的查询结果的id值。
在测试类NewsLabelTest中添加测试方法TestSelectChildrenByParentId,具体代码如下:
@Test
public void TestSelectChildrenByParentId(){
List<NewsLabel> newsLabels = newsLabelDao.selectChildrenByParentId(1);
for (NewsLabel newsLabel : newsLabels){
System.out.println(newsLabel);
}
}
运行测试方法TestSelectChildrenByParentId后在控制台得到如下日志输出信息,说明测试通过。
5.7.2 查询指定栏目及其所有子孙栏目
这里的查询结果,既要包含指定id的当前栏目,还包括其所有辈分的孙子栏目。即给出的id实际为当前要查询的栏目的id。
修改SQL映射文件,具体配置如下:
<mapper namespace="com.ccff.mybatis.dao.INewsLabelDao">
<!--以一对多方式处理:查询指定栏目的所有子孙栏目-->
<resultMap id="newsLabelMapper" type="NewsLabel">
<id column="id" property="id" />
<result column="name" property="name" />
<collection property="children" ofType="NewsLabel" select="selectChildrenByParentId" column="id" />
</resultMap>
<select id="selectChildrenByParentId" parameterType="int" resultMap="newsLabelMapper">
select id,name from newslabel where pid = #{pid}
</select>
<!--以一对多方式处理:查询指定栏目及其所有子孙栏目-->
<resultMap id="newsLabelMapper1" type="NewsLabel">
<id column="id" property="id" />
<result column="name" property="name" />
<collection property="children" ofType="NewsLabel" select="selectChildrenByParentId" column="id" />
</resultMap>
<select id="selectNewsLabelById" parameterType="int" resultMap="newsLabelMapper1">
select id,name from newslabel where id = #{id}
</select>
</mapper>
在测试类NewsLabelTest中添加测试方法TestSelectChildrenByParentId,具体代码如下:
@Test
public void TestSelectNewsLabelById(){
NewsLabel newsLabel = newsLabelDao.selectNewsLabelById(1);
System.out.println(newsLabel);
}
运行测试方法TestSelectNewsLabelById后在控制台得到如下日志输出信息,说明测试通过。
5.8 以多对一方式处理
以多对一方式处理,即多方可以看到一方。该处理方式的应用场景,例如在网页上显示当前页面的站内位置。
测试案例需求:根据pid查询某一父栏目的全部信息。
5.8.1 修改实体类
修改NewsLabel,使其变成多方可以看到一方的形式,具体代码如下:
package com.ccff.mybatis.model;
public class NewsLabel {
private int id;
private String name;
private NewsLabel parent;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public NewsLabel getParent() {
return parent;
}
public void setParent(NewsLabel parent) {
this.parent = parent;
}
@Override
public String toString() {
return "NewsLabel{" +
"id=" + id +
", name='" + name + '\'' +
", parent=" + parent +
'}';
}
}
5.8.2 修改SQL映射文件
修改SQL映射文件,具体配置如下:
<!--以多对一方式处理:根据pid查询某一父栏目的全部信息-->
<resultMap id="newsLabelMapper2" type="NewsLabel">
<id column="id" property="id" />
<result column="name" property="name" />
<association property="parent" javaType="NewsLabel" select="selectParentByParentId" column="pid" />
</resultMap>
<select id="selectParentByParentId" parameterType="int" resultMap="newsLabelMapper2">
select id,name,pid from newslabel where id = #{pid}
</select>
5.8.3 修改测试类
在测试类NewsLabelTest中添加测试方法TestSelectParentByParentId,具体代码如下:
@Test
public void TestSelectParentByParentId(){
NewsLabel newsLabel = newsLabelDao.selectParentByParentId(3);
System.out.println(newsLabel);
}
运行测试方法TestSelectParentByParentId后在控制台得到如下日志输出信息,说明测试通过。