目录
1.关联映射概述
1.1关联映射关系
在关系型数据库中,表与表之间存在着三种关联映射关系,分别为一对一关系、一对多关系和多对多关系。
一对一关系
一个数据表中的一条记录最多可以和另一个数据表中的一条记录相关。例如,现实生活中学生与校园卡就属于一对一的关系,一个学生只能拥有一张校园卡,一张校园卡只能属于一个学生。
一对多关系
主键数据表中的一条记录可以和另外一个数据表的多条记录相关。但另外一个数据表中的记录只能与主键数据表中的某一条记录相关。例如,现实中班级与学生的关系就属于一对多的关系,一个班级可以有很多学生,但一个学生只能属于一个班级。
多对多关系
一个数据表中的一条记录可以与另外一个数据表任意数量的记录相关,另外一个数据表中的一条记录也可以与本数据表中任意数量的记录相关。例如,现实中学生与教师属于多对多的关系,一名学生可以由多名教师授课,一名教师可以为多名学生授课。
Java对象如何描述事物之间的关系
数据表之间的关系实质上描述的是数据之间的关系,除了数据表,在Java中,还可以通过对象来描述数据之间的关系。通过Java对象描述数据之间的关系,其实就是使对象的属性与另一个对象的属性相互关联。
一对一
A类中定义B类对象b作为属性,在B类中定义A类对象a作为属性。
一对多
定义在A类中,定义一个B类对象的集合作为A类的属性;在B类中,定义A类对象a作为B类的属性
多对多
在A类中定义B类类型的集合作为属性,在B类中定义A类类型的集合作为属性。
1.2一对一查询
<association>元素
在现实生活中,一对一关联关系是十分常见的。例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人。人与身份证之间的关联关系如图。
在MyBatis中,通过<association>元素来处理一对一关联关系。<association>元素提供了一系列属性用于维护数据表之间的关系。
<association>元素属性
<association>元素的配置方式
<association>元素是<resultMap>元素的子元素,它有两种配置方式,嵌套查询方式和嵌套结果方式,下面对这两种配置方式分别进行介绍。
a.嵌套查询方式
嵌套查询是指通过执行另外一条SQL映射语句来返回预期的复杂类型。
<association property="card" column="card_id"
javaType="com.itheima.pojo.IdCard"
select="com.itheima.mapper.IdCardMapper.findCodeById" />
b.嵌套结果方式
嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集。
<association property="card" javaType="com.itheima.pojo.IdCard">
<id property="id" column="card_id" />
<result property="code" column="code" />
</association>
a.具体实现
<?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为映射的根节点,用来管理DAO接口
namespace指定DAO接口的完整类名,表示mapper配置文件管理哪个DAO接口(包.接口名)
mybatis会依据这个接口动态创建一个实现类去实现这个接口,而这个实现类是一个Mapper对象
-->
<mapper namespace="com.it.mapper.PersonMapper">
<!--
id = "接口中的方法名"
parameterType = "接口中传入方法的参数类型"
resultType = "返回实体类对象:包.类名" 处理结果集 自动封装
注意:sql语句后不要出现";"号
查询:select标签
增加:insert标签
修改:update标签
删除:delete标签
-->
<select id="findPersonById" parameterType="Integer" resultMap="IdCardWithPersonResult">
SELECT * from tb_person where id=#{id} </select>
<resultMap type="Person" id="IdCardWithPersonResult">
<!-- 主键列使用id-->
<id property="id" column="id" />
<!-- 非主键列使用result-->
<result property="name" column="name" />
<result property="age" column="age" />
<result property="sex" column="sex" />
<!-- association来配置一对一映射-->
<association property="card" column="card_id" javaType="IdCard"
select="com.it.mapper.IdCardMapper.findCodeById"/>
</resultMap>
</mapper>
<?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为映射的根节点,用来管理DAO接口
namespace指定DAO接口的完整类名,表示mapper配置文件管理哪个DAO接口(包.接口名)
mybatis会依据这个接口动态创建一个实现类去实现这个接口,而这个实现类是一个Mapper对象
-->
<mapper namespace="com.it.mapper.IdCardMapper">
<!--
id = "接口中的方法名"
parameterType = "接口中传入方法的参数类型"
resultType = "返回实体类对象:包.类名" 处理结果集 自动封装
注意:sql语句后不要出现";"号
查询:select标签
增加:insert标签
修改:update标签
删除:delete标签
-->
<select id="findCodeById" parameterType="Integer" resultType="IdCard">
SELECT * from tb_idcard where id=#{id}
</select>
</mapper>
<?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" >
<!--配置mybatis环境-->
<configuration>
<!--配置连接使用的相关参数
default为默认使用的环境:development 测试环境
product 生产环境
-->
<properties resource="jdbc.properties"></properties>
<typeAliases>
<package name="com.it.pojo"/>
</typeAliases>
<environments default="development">
<!--测试环境-->
<environment id="development">
<!--事务管理类型:指定事务管理的方式 JDBC-->
<transactionManager type="JDBC"/>
<!--数据库连接相关配置,动态获取conZ fig.properties文件里的内容-->
<!--数据源类型:POOLED 表示支持JDBC数据源连接池
UNPOOLED 表示不支持数据源连接池
JNDI 表示支持外部数据源连接池
-->
<dataSource type="POOLED">
<!--此处使用的是MySQL数据库,使用Oracle数据库时需要修改,仔细检查各项参数是否正确,里面配置了时区、编码方式、SSL,用以防止中文查询乱码,导致查询结果为null及SSL警告等问题-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url"
value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--下面编写mapper映射文件↓↓↓↓↓ 参考格式:<mapper resource="dao/UserMapper.xml"/> -->
<mapper resource="com/it/mapper/StudentMapper.xml"></mapper>
<mapper resource="com/it/mapper/IdCardMapper.xml"></mapper>
<mapper resource="com/it/mapper/PersonMapper.xml"></mapper>
</mappers>
</configuration>
@Test
public void findPersonByIdTest()
{
// 1、通过工具类获取SqlSession对象
SqlSession session = MyBatisUtils.getSession();
// 2.使用MyBatis嵌套查询的方式查询id为1的人的信息
Person person = session.selectOne("com.it.mapper.PersonMapper.findPersonById", 1);
// 3、输出查询结果信息
System.out.println(person);
// 4、关闭SqlSession
session.close();
}
b.具体实现
<select id="findPersonById2" parameterType="Integer" resultMap="IdCardWithPersonResult2">
SELECT * ,p.id pid,
c.id cid
from tb_person p,
tb_idcard c
where
p.card_id=c.id
and p.id=#{id}
</select>
<resultMap type="Person" id="IdCardWithPersonResult2">
<!-- 主键列使用id-->
<id property="id" column="pid" />
<!-- 非主键列使用result-->
<result property="name" column="name" />
<result property="age" column="age" />
<result property="sex" column="sex" />
<!-- association来配置一对一映射-->
<association property="card" javaType="IdCard">
<id property="id" column="cid"></id>
<result property="code" column="code"></result>
</association>
</resultMap>
@Test
public void findPersonById2Test()
{
// 1、通过工具类获取SqlSession对象
SqlSession session = MyBatisUtils.getSession();
Person person = session.selectOne("com.it.mapper.PersonMapper.findPersonById2", 2);
// 3、输出查询结果信息
System.out.println(person);
// 4、关闭SqlSession
session.close();
}
MyBatis延迟加载的配置
在使用MyBatis嵌套查询方式进行MyBatis关联映射查询时,使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在mybatis-config.xml中的<settings>元素内进行配置。
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 将积极加载改为消息加载,即按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
1.3一对多查询
与一对一的关联关系相比,接触更多的关联关系是一对多(或多对一)。例如一个用户可以有多个订单,多个订单也可以归一个用户所有。用户和订单的关联关系如图。
<collection>元素
在MyBatis中,通过<collection>元素来处理一对多关联关系。<collection>元素的属性大部分与<association>元素相同,但其还包含一个特殊属性一ofType。ofType属性与javaType属性对应,它用于指定实体类对象中集合类属性所包含的元素的类型。
<collection>元素的配置方式
<collection>元素是<resultMap>元素的子元素,<collection >元素有嵌套查询和嵌套结果两种配置方式。
a.嵌套查询方式
<collection property="ordersList"
column="id"
ofType="com.itheima.pojo.Orders"
select=" com.itheima.mapper.OrdersMapper.selectOrders"/>
b.嵌套结果方式
<collection property="ordersList"ofType="com.itheima.pojo.Orders">
<id property="id" column="orders_id" />
<result property="number" column="number" />
</collection>
<?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为映射的根节点,用来管理DAO接口
namespace指定DAO接口的完整类名,表示mapper配置文件管理哪个DAO接口(包.接口名)
mybatis会依据这个接口动态创建一个实现类去实现这个接口,而这个实现类是一个Mapper对象
-->
<mapper namespace="com.it.mapper.UsersMapper">
<!--
id = "接口中的方法名"
parameterType = "接口中传入方法的参数类型"
resultType = "返回实体类对象:包.类名" 处理结果集 自动封装
注意:sql语句后不要出现";"号
查询:select标签
增加:insert标签
修改:update标签
删除:delete标签
-->
<select id="findUserWithOrdersById" parameterType="Integer" resultMap="UserWithOrdersResult">
select *,u.id uid, o.id oid
from tb_user u
,tb_orders o
where
u.id=o.user_id
and
u.id=#{id}
</select>
<resultMap type="Users" id="UserWithOrdersResult">
<id column="uid" property="id" ></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<collection property="ordersList" javaType="list" ofType="Orders">
<id property="id" column="oid"/>
<result property="number" column="number"/>
</collection>
</resultMap>
</mapper>
@Test
public void findUserWithOrdersById()
{
SqlSession session = MyBatisUtils.getSession();
Users users= session.selectOne("com.it.mapper.UsersMapper.findUserWithOrdersById",1);
System.out.println(users);
session.close();
}
1.4多对多查询
订单和商品多对多关系图
在实际项目开发中,多对多的关联关系非常常见。以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多个订单,订单和商品属于多对多关联关系,订单和商品之间的关联关系如图
在数据库中,多对多的关联关系通常使用一个中间表来维护,中间表中的订单id作为外键关联订单表的id,中间表中的商品id作为外键关联商品表的id。这三个表之间的关系如图。
a.嵌套查询
Orders类重写toString()方法
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", number='" + number + '\'' +
", userId=" + userId +
", productList=" + productList +
'}';
}
ProductMapper.xml
<select id="findProductById"
parameterType="Integer"
resultType="product"
>
SELECT * FROM tb_product WHERE id IN(
SELECT product_id FROM tb_ordersitem WHERE orders_id =#{id})
</select>
OrdersMapper.xml
<mapper namespace="com.it.mapper.OrdersMapper">
<!--
id = "接口中的方法名"
parameterType = "接口中传入方法的参数类型"
resultType = "返回实体类对象:包.类名" 处理结果集 自动封装
注意:sql语句后不要出现";"号
查询:select标签
增加:insert标签
修改:update标签
删除:delete标签
-->
<select id="findOrdersById"
parameterType="Integer"
resultMap="orderWithProductResult"
>
SELECT * FROM tb_orders WHERE id=#{id};
</select>
<resultMap id="orderWithProductResult" type="Orders">
<id property="id" column="id"></id>
<result property="number" column="number"></result>
<!--商品信息的映射-->
<collection property="productList"
column="id"
javaType="list"
ofType="product"
select="com.it.mapper.ProductMapper.findProductById"
>
</collection>
</resultMap>
</mapper>
测试:
@Test
public void findOrderWithProductById()
{
SqlSession session=MyBatisUtils.getSession();
Orders orders=session.selectOne("com.it.mapper.OrdersMapper.findOrdersById",1);
System.out.println(orders);
session.close();
}
b.嵌套结果
OrdersMapper.xml
<select id="findOrdersById2"
parameterType="Integer"
resultMap="orderWithProductResult2"
>
SELECT o.*,
p.*,
o.id oid,
p.id pid
FROM
tb_orders o,
tb_product p,
tb_ordersitem oi
WHERE
o.id=oi.orders_id
AND p.id=oi.product_id
AND o.id=#{id}
</select>
<resultMap id="orderWithProductResult2" type="orders">
<id column="oid" property="id"></id>
<result property="number" column="number"></result>
<collection property="productList"
javaType="list"
ofType="product"
>
<id property="id" column="pid"></id>
<result property="name" column="name"></result>
<result property="price" column="price"></result>
</collection>
</resultMap>
测试
@Test
public void findOrderWithProductById2()
{
SqlSession session=MyBatisUtils.getSession();
Orders orders=session.selectOne("com.it.mapper.OrdersMapper.findOrdersById2",1);
System.out.println(orders);
session.close();
}
2.缓存机制
2.1一级缓存
一级缓存级别
MyBatis的一级缓存是SqlSession级别的缓存。如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis会将查询结果写入到一级缓存中,此后,如果程序没有执行插入、更新、删除操作,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不用再去数据库查询,从而提高了数据库的查询效率。
举例
例如,存在数据表tb_book,从表中多次查询id为1的图书信息,当程序第一次查询id为1的图书信息时,程序会将查询结果写入MyBatis一级缓存,当程序第二次查询id为1的图书信息时,MyBatis直接从一级缓存中读取,不再访问数据库进行查询。当程序对数据库执行了插入、更新、删除操作,MyBatis会清空一级缓存中的内容以防止程序误读。
查询过程
数据库准备
USE mybatis;
CREATE TABLE tb_book(
id INT PRIMARY KEY AUTO_INCREMENT,
bookName VARCHAR(255),
price DOUBLE,
author VARCHAR(40) );
INSERT INTO tb_book(bookName,price,author)
VALUES('Java基础入门',45.0,' 传智播客高教产品研发部');
INSERT INTO tb_book(bookName,price,author)
VALUES('Java基础案例教程',48.0,'黑马程序员');
INSERT INTO tb_book(bookName,price,author)
VALUES('JavaWeb程序设计任务教程',50.0,'黑马程序员');
实例类
public class Book {
private int id;
private String bookName;
private double price;
private String author;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", bookName='" + bookName + '\'' +
", price=" + price +
", author='" + author + '\'' +
'}';
}
}
映射文件
<mapper namespace="com.it.mapper.BookMapper">
<!--
id = "接口中的方法名"
parameterType = "接口中传入方法的参数类型"
resultType = "返回实体类对象:包.类名" 处理结果集 自动封装
注意:sql语句后不要出现";"号
查询:select标签
增加:insert标签
修改:update标签
删除:delete标签
-->
<select id="findBookById" parameterType="Integer"
resultType="Book">
SELECT * from tb_book where id=#{id} </select>
<!-- 根据id更新图书信息 -->
<update id="updateBook"
parameterType="Book">
update tb_book set bookName=#{bookName},price=#{price}
where id=#{id} </update>
</mapper>
日志依赖(搞坐标)
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
文件 log4j.properties
#全局日志配置
log4j.rootLogger=DEBUG, Console
#控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
#日志输出级别,只展示了一个
log4j.logger.java.sql.PreparedStatement=DEBUG
测试
@Test
public void findBookByIdTest1()
{
SqlSession session=MyBatisUtils.getSession();
System.out.println("******第一次查询******");
Book book=session.selectOne("com.it.mapper.BookMapper.findBookById",1);
System.out.println(book);
System.out.println("******第一次查询******");
Book book2=session.selectOne("com.it.mapper.BookMapper.findBookById",1);
System.out.println(book2);
session.close();
}
MyBatis如何防止程序误读
当程序对数据库执行了插入、更新、删除操作后,MyBatis会清空一级缓存中的内容,以防止程序误读。MyBatis一级缓存被清空之后,再次使用SQL查询语句访问数据库时,MyBatis会重新访问数据库。例如上面的例子,首先查询id为1的图书信息,然后使用更新语句对数据库中的图书信息进行更改,更改之后,再次对id为1的图书信息进行查询时,MyBatis依然会从数据库中查询。
2.2二级缓存
使用二级缓存的好处
由4.5.1节的内容可知,相同的Mapper类,相同的SQL语句,如果SqlSession不同,则两个SqlSession查询数据库时,会查询数据库两次,这样也会降低数据库的查询效率。为了解决这个问题,就需要用到MyBatis的二级缓存。MyBatis的二级缓存是Mapper级别的缓存,与一级缓存相比,二级缓存的范围更大,多个SqlSession可以共用二级缓存,并且二级缓存可以自定义缓存资源。
执行过程
在MyBatis中,一个Mapper.xml文件通常称为一个Mapper,MyBatis以namespace区分Mapper,如果多个SqlSession对象使用同一个Mapper的相同查询语句去操作数据库,在第一个SqlSession对象执行完后,MyBatis会将查询结果写入二级缓存,此后,如果程序没有执行插入、更新、删除操作,当第二个SqlSession对象执行相同的查询语句时,MyBatis会直接读取二级缓存中的数据。
默认的二级缓存可实现功能
- 映射文件中所有select语句将会被缓存。
- 映射文件中的所有insert、update和delete语句都会刷新缓存。
- 缓存会使用LRU算法回收。
- 没有刷新间隔,缓存不会以任何时间顺序来刷新。
- 缓存会存储列表集合或对象的1024个引用。
- 缓存是可读/可写的缓存,这意味着对象检索不是共享的,缓存可以安全的被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
<cache>元素的属性
如果需要调整级缓存的特性,可通过<cache>元素的属性来实现。
实现接口
测试
@Test
public void findBookByIdTest2()
{
SqlSession sqlSession1 = MyBatisUtils.getSession();
SqlSession sqlSession2= MyBatisUtils.getSession();
System.out.println("******第一次查询******");
Book book1=sqlSession1.selectOne("com.it.mapper.BookMapper.findBookById",1);
System.out.println(book1);
System.out.println("******第一次查询******");
//释放sqlSession,将数据写入到二级缓存
sqlSession1.close();
System.out.println("第二次查询");
Book book2=sqlSession2.selectOne("com.it.mapper.BookMapper.findBookById",1);
System.out.println(book2);
sqlSession2.close();
}
注意:在第一次查询完要释放sqlSession,将数据写入到二级缓存
如果在二次查询前进行了增删改
Cache Hit Ratio(缓存命中率)
终端用户访问缓存时,如果在缓存中查找到了要被访问的数据,就叫做命中。如果缓存中没有查找到要被访问的数据,就是没有命中。当多次执行查询操作时,缓存命中次数与总的查询次数(缓存命中次数+缓存没有命中次数)的比,就叫作缓存命中率,即缓存命中率=缓存命中次数/总的查询次数。当MyBatis开启二级缓存后,第一次查询数据时,由于数据还没有进入缓存,所以需要在数据库中查询而不是在缓存中查询,此时,缓存命中率为0。第一次查询过后,MyBatis会将查询到的数据写入缓存中,当第二次再查询相同的数据时,MyBatis会直接从缓存中获取这条数据,缓存将命中,此时的缓存命中率为0.5(1/2)。当第三次查询相同的数据,则缓存命中率为0.66666(2/3),以此类推。
两个级别缓存的不同
MyBatis的二级缓存需要手动开启,开启二级缓存通常要完成以下两个步骤。
与使用二级缓存前,需要在MyBatis的核心配置mybatis-config.xml文件中通过<settings>元素开启二级缓存的全局配置。
<settings>
<setting name="cacheEnabled" value="true" />
</settings>
开启当前Mapper的namespace下的二级缓存,可以通过MyBatis映射文件中的<cache>元素来完成。
<!-- 开启当前Mapper的namespace下的二级缓存-->
<cache>
</cache>
案例:商品的类别
完成商品的类别案例,能够根据表1和表2在数据库分别创建一个商品表和一个商品类别表, 查询商品类别为白色家电的商品的信息
详情
数据准备
USE mybatis;
# 创建一个名称为category的表
CREATE TABLE category (
id INT(32) PRIMARY KEY AUTO_INCREMENT,
typename VARCHAR(40)
);
# 插入2条数据
INSERT INTO category VALUES (1, '黑色家电');
INSERT INTO category VALUES (2, '白色家电');
# 创建一个名称为product的表
CREATE TABLE product(
id INT PRIMARY KEY AUTO_INCREMENT,
goodsname VARCHAR(20),
price DOUBLE,
typeid INT
);
INSERT INTO product VALUES(NULL,'电视机',5000,1);
INSERT INTO product VALUES(NULL,'冰箱',4000,2);
INSERT INTO product VALUES(NULL,'空调',3000,2);
INSERT INTO product VALUES(NULL,'洗衣机',2000,2);
实体类
import java.util.List;
public class Category {
private int id;
private String typename;
private List<Product2> product2list;
@Override
public String toString() {
return "Category{" +
"id=" + id +
", typename='" + typename + '\'' +
", product2List=" + product2list +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTypename() {
return typename;
}
public void setTypename(String typename) {
this.typename = typename;
}
public List<Product2> getProduct2list() {
return product2list;
}
public void setProduct2List(List<Product2> product2list) {
this.product2list = product2list;
}
}
public class Product2 {
private int id;
private String goodsname;
private double price;
private int typeid;
@Override
public String toString() {
return "Product2{" +
"id=" + id +
", goodsname='" + goodsname + '\'' +
", price=" + price +
", typeid=" + typeid +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getGoodsname() {
return goodsname;
}
public void setGoodsname(String goodsname) {
this.goodsname = goodsname;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getTypeid() {
return typeid;
}
public void setTypeid(int typeid) {
this.typeid = typeid;
}
}
映射文件
<mapper namespace="com.it.mapper.CategoryMapper">
<!--
id = "接口中的方法名"
parameterType = "接口中传入方法的参数类型"
resultType = "返回实体类对象:包.类名" 处理结果集 自动封装
注意:sql语句后不要出现";"号
查询:select标签
增加:insert标签
修改:update标签
删除:delete标签
-->
<select id="findCategoryWithProduct"
resultMap="categoryWithProduct"
>
select
p.id pid,
p.goodsname,
p.price,
c.id cid,
c.typename
from category c,product p
where c.id=p.typeid
and c.id=#{cid};
</select>
<resultMap id="categoryWithProduct" type="Category">
<id column="cid" property="id"></id>
<result column="typename" property="typename"></result>
<!-- 一对多映射-->
<collection property="product2list" javaType="list" ofType="product2">
<id column="pid" property="id"></id>
<result column="goodsname" property="goodsname"></result>
<result column="price" property="price"></result>
</collection>
</resultMap>
</mapper>
测试
@Test
public void findCategoryWithProductTest()
{
SqlSession session=MyBatisUtils.getSession();
Category category=session.selectOne("com.it.mapper.CategoryMapper.findCategoryWithProduct",2);
session.close();
System.out.println(category);
}
resultMap和下面的resultMap要对应,上面的不要手快写成resultType!