前期准备
关联映射就是多表连接查询,MyBaits可以帮助我们对查询结果进行封装。所以我们首先需要两张表
关联关系:
1.数据库主外键关联。
2.通过程序逻辑让数据产生关联。比如在一个类中维护了另一个类的对象。
商品类型表
create table gtype(
typeid int PRIMARY key auto_increment,
typename varchar(255) not null
)
商品表
create table goods(
gid int PRIMARY key auto_increment,
gname varchar(200) not null,
gprice float(10,2) default 0.0 ,
gdecs varchar(500) ,
typeid int
)
我们知道数据库的表之间是可以存在关系的,一对一、一对多以及多对多,接下来就以这两张表演示这三种关系的关联映射。
环境搭建如下
首先需要导入依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
mybatis-config.xml配置文件
<?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"/>
<typeAliases>
<package name="com.young.model"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
</configuration>
db.properties配置文件
db.url=jdbc:mysql://localhost:3306/mybatis_db?serverTimezone=UTC
db.username=root
db.password=123
db.driverClassName=com.mysql.jdbc.Driver
一对一关系
为两张表绑定一对一的关系
ALTER TABLE goods ADD CONSTRAINT UNIQUE(typeid)
创建对应的实体类
Good类
public class Good {
private Integer gid;
private String gname;
private Float gprice;
private String gdecs;
//Good类中维护了Gtype类对象
private Gtype type;
//以下省略了所有成员变量的get和set方法......
}
Gtype 类
public class Gtype {
private Integer typeid;
private String typename;
//以下省略了所有成员变量的get和set方法......
}
GoodMapper接口
public interface GoodMapper {
Good selectByGid(Integer gid);
}
假设GoodMapper.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">
<select id="selectByGid" resultType="Good">
SELECT
gtype.typeid,
gtype.typename,
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs,
goods.typeid
FROM
goods
INNER JOIN gtype ON goods.typeid = gtype.typeid
WHERE
goods.gid=#{gid}
</select>
</mapper>
测试方法
@Test
public void testMapper() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sessionFactory.openSession();
GoodMapper goodMapper = session.getMapper(GoodMapper.class);
Good good = goodMapper.selectByGid(1);
System.out.println(JSON.toJSONString(good,true));
}
运行结果:
{
"gdecs":"让你成为职业选手的第一步",
"gid":1,
"gname":"补兵的艺术",
"gprice":50.0
}
因为Good实体类中维护了Gtype类的对象,所以返回的结果无法和Gtype对象关联
通常使用<resultMap>标签来配置表和实体类的关联关系,然后在<select>引用配置的关联关系
GoodMapper.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="com.young.mapper.GoodMapper">
<!--Good selectByGid(Integer gid);-->
<!--强大的结果映射标签-->
<resultMap id="goodResult" type="Good">
<!--如果列名和属性名不一致,可以通过<id>和<result>将列和属性关联-->
<!--<id>标签代表主键;<result>标签代表其他类名-->
<!--column代表数据的列名;property代表类的属性名-->
<id column="gid" property="gid"/>
<!--属性-->
<result column="gname" property="gname"/>
<result column="gprice" property="gprice"/>
<result column="gdecs" property="gdecs"/>
<!--关联对象的属性-->
<result column="typeid" property="type.typeid"/>
<result column="typename" property="type.typename"/>
</resultMap>
<select id="selectByGid" resultMap="goodResult">
SELECT
gtype.typeid,
gtype.typename,
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs,
goods.typeid
FROM
goods
INNER JOIN gtype ON goods.typeid = gtype.typeid
WHERE
goods.gid=#{gid}
</select>
</mapper>
使用<association>标签将列名与Good类中维护的Gtype对象的属性关联
<?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.young.mapper.GoodMapper">
<resultMap id="goodResult" type="Good">
<id column="gid" property="gid"/>
<result column="gname" property="gname"/>
<result column="gprice" property="gprice"/>
<result column="gdecs" property="gdecs"/>
<!--property表示Good类中的属性;javaType属性type对应的类型,可以写别名-->
<association property="type" javaType="com.young.model.Gtype">
<!--将列typeid与Gtype类中typeid属性关联-->
<id column="typeid" property="typeid"/>
<!--将列typename与Gtype类中typename属性关联-->
<result column="typename" property="typename"/>
</association>
</resultMap>
<select id="selectByGid" resultMap="goodResult">
SELECT
gtype.typeid,
gtype.typename,
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs,
goods.typeid
FROM
goods
INNER JOIN gtype ON goods.typeid = gtype.typeid
WHERE
goods.gid=#{gid}
</select>
</mapper>
以上两种方式运行测试方法,输出结果如下:
MyBatis会将列名与Good类中维护的Gtype对象的属性关联,输出结果可以看出键为"type",值为对象。
{
"gdecs":"让你成为职业选手的第一步",
"gid":1,
"gname":"补兵的艺术",
"gprice":50.0,
"type":{
"typeid":1,
"typename":"游戏教学"
}
}
如果列名与属性名一致,可以省略<id>和<result>标签,只需要将不一致列名和属性名通过这两个标签关联起来,但是需要设置autoMapping属性开启自动映射。
<resultMap id="goodResult" type="Good" autoMapping="true">
<association property="type" javaType="com.young.model.Gtype" autoMapping="true">
</association>
</resultMap>
MyBatis还可以设置省略列名的下划线,即使列名"g_id"与属性名"gid"自动关联起来,但是配置可能会出错,不推荐。
使用<association>标签来配置映射关系可以设置单独的查询语句来进行分段查询,此时支持延迟加载
<association>标签中存在select属性用于设置查询语句
<?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.young.mapper.GoodMapper">
<resultMap id="goodResult" type="Good" autoMapping="true">
<!--fetchType,表示加载模式,lazy表示懒加载
如果没有访问到Good类中的type属性时,不会执行selectByTypeid方法中的查询语句
select可以指定一个查询语句的Sqlid(namespace+id),将查询结果映射到Gtype对象的属性中
column表示列名,将查询结果中的typeid列的数据作为参数传入select属性指定的select语句中-->
<!--fetchType="lazy"-->
<association property="type"
column="typeid"
select="com.young.mapper.GtypeMapper.selectByTypeid"
fetchType="lazy" javaType="com.young.model.Gtype" autoMapping="true"/>
</resultMap>
<!--resultMap="goodResult"-->
<select id="selectByGid" resultMap="goodResult" >
SELECT
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs,
goods.typeid
FROM
goods
WHERE
goods.gid=#{gid}
</select>
</mapper>
需要新建一个GtypeMapper接口实现查询gtype表的方法
public interface GtypeMapper {
Gtype selectByTypeid(Integer id);
}
对应的GtypeMapper.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="com.young.mapper.GtypeMapper">
<select id="selectByTypeid" resultType="Gtype" >
select * from gtype where typeid=#{typeid}
</select>
</mapper>
需要将该接口交给MyBatis框架
<mappers>
<mapper class="com.young.mapper.GoodMapper"/>
<mapper class="com.young.mapper.GtypeMapper"/>
</mappers>
运行测试方法结果
{
"gdecs":"让你成为职业选手的第一步",
"gid":1,
"gname":"补兵的艺术",
"gprice":50.0,
"type":{
"typeid":1,
"typename":"游戏教学"
}
}
日志记录内容
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 10:18:22 DEBUG [selectByGid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Preparing: SELECT goods.gid, goods.gname, goods.gprice, goods.gdecs, goods.typeid FROM goods WHERE goods.gid=?
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 10:18:22 DEBUG [selectByGid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: 1(Integer)
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 10:18:22 DEBUG [selectByGid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <== Total: 1
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 10:18:22 DEBUG [selectByTypeid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Preparing: select * from gtype where typeid=?
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 10:18:22 DEBUG [selectByTypeid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: 1(Integer)
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 10:18:22 DEBUG [selectByTypeid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <== Total: 1
过程解析:
1.通过"selectByGid"的查询语句查询出结果并将结果封装到resultMap中
2.将结果中的goods.typeid的值作为参数传入"selectByTypeid"查询语句中
3.将"selectByTypeid"查询语句查询出的结果与Good类中的维护的Gtype对象中的属性进行关联
如果在测试方法中只访问Good类对象的gname属性,并允许方法
Good good = goodMapper.selectByGid(1);
System.out.println(good.getGname());
日志记录
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 19:56:03 DEBUG [selectByGid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Preparing: SELECT goods.gid, goods.gname, goods.gprice, goods.gdecs, goods.typeid FROM goods WHERE goods.gid=?
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 19:56:03 DEBUG [selectByGid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: 1(Integer)
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 19:56:03 DEBUG [selectByGid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <== Total: 1
通过日志可以得知没有执行了<association>标签中的select语句
如果在测试方法中
Good good = goodMapper.selectByGid(1);
System.out.println(good.getType().getTypename());
日志记录
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 20:04:20 DEBUG [selectByGid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Preparing: SELECT goods.gid, goods.gname, goods.gprice, goods.gdecs, goods.typeid FROM goods WHERE goods.gid=?
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 20:04:20 DEBUG [selectByGid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: 1(Integer)
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 20:04:20 DEBUG [selectByGid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <== Total: 1
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 20:04:20 DEBUG [selectByTypeid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Preparing: select * from gtype where typeid=?
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 20:04:20 DEBUG [selectByTypeid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: 1(Integer)
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/25 20:04:20 DEBUG [selectByTypeid] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <== Total: 1
通过日志可以得知执行了<association>标签中的select语句,这就是延迟加载
注意:如果查询语句只有一条就不存在延迟加载
补充:延迟加载的问题记录
当前使用的是mybatis3.4.6,而该版本使用javassist操作字节码文件方式不被JDK11所允许(mybatis3.3之后默认采用javassist方式)
如果运行测试方法出现如下异常:
Caused by: org.apache.ibatis.executor.ExecutorException: Error creating lazy proxy. Cause: java.lang.NullPointerException
解决方法:
使用使用CGLib代理
导入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
在mybatis-config.xml配置文件中增加如下配置:
<settings>
<!--配置允许懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--指定动态代理的实现方式-->
<!--指定Mybatis创建可延迟加载对象所用到的代理工具。-->
<setting name="proxyFactory" value="CGLIB" />
</settings>
如果不想使用以上解决方法,可以降低JDK版本,比如使用JDK1.8
以上是一对一关系的单向关联,即Good类中维护了Gtype对象,但是Gtype类中没有维护Good对象。
如果是双向关联,那么需要更改一些配置,如下:
GtypeMapper接口
public interface GtypeMapper {
Gtype selectByTypeid(Integer id);
Gtype selectByTypeid2(Integer id);
}
GtypeMapper.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="com.young.mapper.GtypeMapper">
<select id="selectByTypeid" resultType="Gtype" >
select * from gtype where typeid=#{typeid}
</select>
<resultMap id="gtypeResult" type="Gtype" autoMapping="true">
<association property="good" javaType="com.young.model.Good" autoMapping="true"/>
</resultMap>
<select id="selectByTypeid2" resultMap="gtypeResult">
SELECT
gtype.typeid,
gtype.typename,
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs,
goods.typeid
FROM
goods
INNER JOIN gtype ON goods.typeid = gtype.typeid
WHERE
gtype.typeid=#{typeid}
</select>
</mapper>
测试方法
@Test
public void testMapper() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sessionFactory.openSession();
GtypeMapper gtypeMapper = session.getMapper(GtypeMapper.class);
System.out.println(JSON.toJSONString(gtypeMapper.selectByTypeid2(1),true));
session.close();
}
运行结果
{
"good":{
"gdecs":"让你成为职业选手的第一步",
"gid":1,
"gname":"补兵的艺术",
"gprice":50.0
},
"typeid":1,
"typename":"游戏教学"
}
一对多关系
修改数据库关系,去除唯一索引
ALTER TABLE `goods` DROP INDEX `typeid`;
一种商品类型可以对应多个商品,一个商品只能对应一种商品
因为关联关系改变,所以Java实体类也需要改变
Gtype类
public class Gtype {
private Integer typeid;
private String typename;
//通过元素为Good类对象的集合来体现一对多的关系
private List<Good> goods;
//以下省略了所有成员变量的get和set方法......
}
public class Good {
private Integer gid;
private String gname;
private Float gprice;
private String gdecs;
private Gtype type;
//以下省略了所有成员变量的get和set方法......
}
单向关联:通过商品类型(一)找商品(多)
GtypeMapper接口
public interface GtypeMapper {
Gtype selectByTypeid(Integer id);
}
GtypeMapper.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="com.young.mapper.GtypeMapper">
<resultMap id="gtypeResult" type="Gtype" autoMapping="true">
<id column="typeid" property="typeid"/>
<!--一对多关系中,实体类Gtype(一)中维护了一个存储Good对象的集合,
所以需要使用<collection>标签来关联属性和列名,用法与<association>类似-->
<!--property:Gtype类中维护的其他类对象属性名goods
ofType:集合中维护的对象的类型
select:查询语句的namespace+id
column:传递给select属性查询语句的参数
fetchType:加载方式为懒加载-->
<collection property="goods"
ofType="com.young.model.Good"
select="com.young.mapper.GoodMapper.selectByTypeid"
fetchType="lazy"
column="typeid"/>
</resultMap>
<select id="selectByTypeid" resultMap="gtypeResult" >
SELECT
gtype.typeid,
gtype.typename
FROM
gtype
WHERE
gtype.typeid = #{typeid}
</select>
</mapper>
GoodMapper接口
public interface GoodMapper {
List<Good> selectByTypeid(Integer typeid);
}
GoodMapper.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="com.young.mapper.GoodMapper">
<select id="selectByTypeid" resultType="Good" >
SELECT
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs,
goods.typeid
FROM
goods
WHERE
goods.typeid=#{typeid}
</select>
</mapper>
测试方法
@Test
public void testMapper() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sessionFactory.openSession();
GtypeMapper gtypeMapper = session.getMapper(GtypeMapper.class);
Gtype gtype = gtypeMapper.selectByTypeid(1);
System.out.println(JSON.toJSONString(gtype,true));
session.close();
}
运行结果
{
"goods":[
{
"gdecs":"让你成为职业选手的第一步",
"gid":1,
"gname":"补兵的艺术",
"gprice":50.0
},
{
"gdecs":"让你成为职业选手的必经之路",
"gid":2,
"gname":"对线的技巧",
"gprice":66.0
}
],
"typeid":1,
"typename":"游戏教学"
}
双向关联时,还需要通过商品(多)寻找商品类型(一),与一对一关系类似
修改Mapper接口和映射文件
GoodMapper接口
public interface GoodMapper {
List<Good> selectByTypeid(Integer typeid);
Good selectByGid(Integer gid);
}
GoodMapper.xml映射文件
<mapper namespace="com.young.mapper.GoodMapper">
<resultMap id="goodResult" type="Good" autoMapping="true">
<!--使用立即加载的方式,不使用延迟加载-->
<association property="type" javaType="com.young.model.Gtype" autoMapping="true"/>
</resultMap>
<select id="selectByTypeid" resultType="Good" >
SELECT
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs,
goods.typeid
FROM
goods
WHERE
goods.typeid=#{typeid}
</select>
<select id="selectByGid" resultMap="goodResult">
SELECT
gtype.typeid,
gtype.typename,
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs,
goods.typeid
FROM
goods
INNER JOIN gtype ON goods.typeid = gtype.typeid
WHERE
goods.gid = #{gid}
</select>
</mapper>
测试方法
GoodMapper goodMapper = session.getMapper(GoodMapper.class);
Good good = goodMapper.selectByGid(2);
System.out.println(JSON.toJSONString(good,true));
运行结果
{
"gdecs":"让你成为职业选手的必经之路",
"gid":2,
"gname":"对线的技巧",
"gprice":66.0,
"type":{
"typeid":1,
"typename":"游戏教学"
}
}
多对多关系
多对多关系中,一个商品可以对应多个商品类别,一个商品类别也可以有多个商品。我们需要一个中间表将多对多关系转换为一对多关系
建表语句如下:
goods(商品表)
CREATE TABLE `goods` (
`gid` int(11) NOT NULL AUTO_INCREMENT,
`gname` varchar(200) NOT NULL,
`gprice` float(10,2) DEFAULT '0.00',
`gdecs` varchar(500) DEFAULT NULL,
PRIMARY KEY (`gid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
gtype(商品类型表)
CREATE TABLE `gtype` (
`typeid` int(11) NOT NULL AUTO_INCREMENT,
`typename` varchar(255) NOT NULL,
PRIMARY KEY (`typeid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
goods_gtype(中间表)
CREATE TABLE `goods_gtype` (
`gt_id` int(11) NOT NULL,
`goodsid` int(11) DEFAULT NULL,
`gtypeid` int(11) DEFAULT NULL,
PRIMARY KEY (`gt_id`),
KEY `goodsid` (`goodsid`),
KEY `gtypeid` (`gtypeid`),
CONSTRAINT `goods_gtype_ibfk_2` FOREIGN KEY (`gtypeid`) REFERENCES `gtype` (`typeid`),
CONSTRAINT `goods_gtype_ibfk_1` FOREIGN KEY (`goodsid`) REFERENCES `goods` (`gid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Good实体类
public class Good {
private Integer gid;
private String gname;
private Float gprice;
private String gdecs;
private List<Gtype> type;
//以下省略了所有成员变量的get和set方法......
}
Gtype实体类
public class Gtype {
private Integer typeid;
private String typename;
private List<Good> goods;
//以下省略了所有成员变量的get和set方法......
}
单向关联:通过商品类别id查询类别信息,关联商品信息
立即加载模式:
GtypeMapper接口
public interface GtypeMapper {
//查询结果为多条数据,所以返回值用List
List<Gtype> selectByTypeid(Integer id);
}
GtypeMapper.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="com.young.mapper.GtypeMapper">
<resultMap id="gtypeResult" type="Gtype" autoMapping="true">
<collection property="goods"
ofType="com.young.model.Good"
autoMapping="true"/>
</resultMap>
<select id="selectByTypeid" resultMap="gtypeResult">
SELECT
gtype.typeid,
gtype.typename,
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs
FROM
goods_gtype,goods,gtype
WHERE
goods.gid=goods_gtype.goodsid
AND
gtype.typeid=goods_gtype.gtypeid
AND
gtype.typeid=#{typeid}
</select>
</mapper>
测试方法
GtypeMapper gtypeMapper = session.getMapper(GtypeMapper.class);
List<Gtype> list = gtypeMapper.selectByTypeid(2);
System.out.println(JSON.toJSONString(list,true));
运行结果
[
{
"goods":[
{
"gdecs":"成为职业选手的第一步",
"gid":1,
"gname":"补兵的艺术",
"gprice":50.0
}
],
"typeid":2,
"typename":"ADC教学"
},
{
"goods":[
{
"gdecs":"成为职业选手的必经之路",
"gid":2,
"gname":"对线技巧",
"gprice":66.0
}
],
"typeid":2,
"typename":"ADC教学"
}
]
双向关联,通过商品id查找商品信息,关联类别信息
GoodMapper接口
public interface GoodMapper {
Good selectGoodByTypeid(Integer typeid);
Good selectByGid(Integer gid);
}
GoodMapper.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="com.young.mapper.GoodMapper">
<resultMap id="goodResult" type="Good">
<!--当使用延迟加载时,可以不指定ofType属性,因为在引用的select标签上,已经指定了resultType
延迟加载时可以不指定autoMapping属性
当select属性引用的select标签在同一个xml映射文件中,可以不指定namespace-->
<collection property="type"
select="selectGtypeByGid"
fetchType="lazy"
column="gid"/>
</resultMap>
<select id="selectGoodByTypeid" resultType="Good">
SELECT
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs
FROM
goods,goods_gtype
WHERE
goods.gid=goods_gtype.goodsid
AND
goods_gtype.gtypeid=#{typeid}
</select>
<select id="selectByGid" resultMap="goodResult">
SELECT
goods.gid,
goods.gname,
goods.gprice,
goods.gdecs
FROM
goods
WHERE
goods.gid=#{gid};
</select>
<select id="selectGtypeByGid" resultType="Gtype">
SELECT
gtype.typeid,
gtype.typename
FROM
goods_gtype,gtype
WHERE
gtype.typeid=goods_gtype.gtypeid
AND
goods_gtype.goodsid=#{gid}
</select>
</mapper>
测试方法
GoodMapper goodMapper = session.getMapper(GoodMapper.class);
Good good = goodMapper.selectByGid(1);
System.out.println(JSON.toJSONString(good,true));
运行结果
{
"gdecs":"成为职业选手的第一步",
"gname":"补兵的艺术",
"gprice":50.0,
"type":[
{
"typeid":1,
"typename":"游戏教学"
},
{
"typeid":2,
"typename":"ADC教学"
}
]
}
延迟加载传入多个参数问题
在延迟加载时,如果需要传入多个参数到<association>和<collection>标签的select属性绑定的select语句中,可使用column属性,格式如下:
column={key1=值1,key2=值2...}
即:
<collection property="goods"
ofType="com.oracle.model.Goods"
fetchType="lazy"
column="{typeid=typeid,typename=typename}"
select="com.oracle.mapper.GoodsMapper.selectGoods"/>
上述配置就是将typeid和typename作为参数传入select属性引用的Sql语句中
而在Sql id为"com.oracle.mapper.GoodsMapper.selectGoods"的select标签,需要多个参数
可以使用param1、param2...来指定参数
也可以在Mapper接口中的方法中使用@Param注解指定参数,如下:
public List<Goods> selectGoods(@Param("typeid") Integer typeid,@Param("typename") String typename);