mybatis关联
web项目开发中目前很多企业都是在SSM框架基础上开发。其中的M指的的mybatis(ibatis).mybatis里指的一说的是规避了传统的jdbc等的繁琐。在mybatis中我们可以只用关注sql本身。而不用太在意之个执行过程。大大简化了我们平时的开发。mybatis深究的话会有很多要说的。今天我们只来看看mybatis中提供了映射中的关联标签。
数据准备
数据结构
下表stage_order中的stage_list是有stage表中ID组成的一个字符串,之间由逗号相隔。比如stage_list=1,2,3表示该stage_order中关联着ID为1或2或3的stage。我们得分别取查询stageID=1或2或3的stage进行实体映射展示。下一节我们看看实体的构造
实体准备
基本实体(对应stage_order单表)
- 在下面的实体中出现的注解我们不需要在意,这是swagger框架的注解,在本章节中不需要理解。换句话说我们可以吧注解删掉。不会影响我们的功能的。
@ApiModel(description = "阶段顺序表")
@Table(name = "STAGE_ORDER")
public class StageOrder {
@Id
@ApiModelProperty("阶段顺序ID")
@NotNull(message = "阶段顺序ID不可为空(NULL)")
private Long stageOrderId;
@ApiModelProperty("路口编号")
@NotNull(message = "路口编号不可为空(NULL)")
private Long intersectionId;
@ApiModelProperty("相序名称")
@NotBlank(message = "相序名称不可为空白")
@ByteLength(max = 30, message = "相序名称长度不能超过{max}")
private String orderName;
@ApiModelProperty("阶段列表")
@NotBlank(message = "阶段列表不可为空白")
@ByteLength(max = 200, message = "阶段列表长度不能超过{max}")
private String stageList;
public Long getStageOrderId() {
return stageOrderId;
}
public void setStageOrderId(Long stageOrderId) {
this.stageOrderId = stageOrderId;
}
public Long getIntersectionId() {
return intersectionId;
}
public void setIntersectionId(Long intersectionId) {
this.intersectionId = intersectionId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName == null ? null : orderName.trim();
}
public String getStageList() {
return stageList;
}
public void setStageList(String stageList) {
this.stageList = stageList == null ? null : stageList.trim();
}
}
扩展实体
public class StageOrderDto extends StageOrder {
/**
* 相位列表
*/
private List<Stage> stageInfoList;
public List<Stage> getStageInfoList() {
return stageInfoList;
}
public void setStageInfoList(List<Stage> stageInfoList) {
this.stageInfoList = stageInfoList;
}
}
基本映射
一对一
- 有了上面的实体和对应的Table,那么我们如何在mybatis中将Table和实体关联呢。看如下在mapper.xml中的配置
<resultMap id="BaseResultMap" type="com.jsits.xk.dto.StageOrderDto">
<id column="STAGE_ORDER_ID" jdbcType="NUMERIC" property="stageOrderId"/>
<result column="INTERSECTION_ID" jdbcType="NUMERIC" property="intersectionId"/>
<result column="ORDER_NAME" jdbcType="VARCHAR" property="orderName"/>
<result column="STAGE_LIST" jdbcType="VARCHAR" property="stageList"/>
<association column="STAGE_LIST" property="stageInfoList" select="selectStageInfos"/>
</resultMap>
但是我们会发现resultMap中stageInfoList这个在跟数据库怎么对应呢?还有association是什么?assocaition中select又是什么?为什么assocaition中的column和上面一样?
我想对于刚接触的朋友来说心里一定会有这些疑问。上面已经说了resultmap是用来衔接实体去数据库表的桥梁。resultMap中ID属性就是一个唯一性的作用。一般只需要在当前xml中唯一就行了。因为在每个xml中有
namespace
,来确定当前的mapper.xml的唯一性。resultMap字标签id 和result都是用来映射单列值到type对应的实体或者map中属性。在使用上ID是对应数据库中主键使用的。数据库中会有联合主键,在resultMap中就用两个ID标签对应就行了。还有一点ID也是用来做唯一标识的。当和其他对象进行比较的时候这个时候ID就有作用了。这里和数据库一样主键就是代表一条数据。这里也是同样的作用。在mybatis中有缓存机制。就是通过这个ID实现的。比如mybatis这个时候需要缓存这个对象。则以ID为key ,对象的json化为值存储到缓存中。
一对多
这里一对多就用到了上面的BaseResultMap这个resultMap了。上面我们的讲的ID和result就是我们平时简单单表查询中的一对一关系处理。但是association就是用来关联查询的。association中文翻译就是联想的意思。在mybatis中association的作用就是在查询到STAGE_LIST这个字段的时候默认会将STAGE_LIST的值传到selectStageInfos这个查询语句中查询。传递进去是按照上面的写法我们不能用#{stage_list}取,得通过#{id}获取。这里是默认的必须用#{id}.但是一个健全的框架不会这么死板的。下面我们在介绍如何动态传递。
association严格上来说应该是用于selectStageInfos查询获取的也是一个基本类型数据。但是在我们上面的需求描述中我们知道STAGE_LIST,对应到stage表中其实回事多条数据。所以我们在StageOrderDto中stageInfoList也是用list集合来承载的。这个时候用association不会出错。mybatis底层却不同,底层实际上是转换成了collection标签来实现的。应为collection对应的是集合的映射处理。所以追求完美的程序员在级联查询是如果级联出的是集合就用collection标签。但是两种情况用association都是可以的。
查询应用
- 说了这么多,下面我们通过两个查询看看查询的实现代码
<select id="selectStageOrderDtoListByIntersectionId" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from stage_order
where INTERSECTION_ID=#{intersectionId}
</select>
<select id="selectStageInfos" parameterType="java.lang.String" resultType="com.jsits.xk.model.Stage">
select
<include refid="Stage_Column_List"/>
from stage
where stage_id in (#{id})
</select>
- 当我们调用selectStageOrderDtoListByIntersectionId通过intersectionId查询到数据的时候,会在通过查询到的STAGE_LIST作为条件调用selectStageInfos这个查询语句的。在selectStageInfos中的#{id}就是selectStageOrderDtoListByIntersectionId查询到的STAGE_LIST。
复杂映射
- 但是我们平时企业开发中光这样的查询有时候并不能满足我们的需求。常见复杂的查询有: 多重级联和联合主键查询
多重映射
- 多重查询说白了就是多用几次级联(association或collection),对就是这个简单。这个可能有的人没有过甚至没想过。下面贴段实际的代码。因为下面代码在联合主键章节也需要用到。
<sql id="Program_Column_List">
PHASE_LIST,PROGRAM_ID,PROGRAM_NAME,INTERSECTION_ID,STAGE_ID,STAGE_NAME,STAGE_SEQ,GREEN,RED_YELLOW,YELLOW,ALL_RED,MIN_GREEN,MAX_GREEN
</sql>
<resultMap id="weekProgramResultMap" type="com.jsits.xk.dto.WeekPlanProgramDto">
<result column="PLAN_ID" property="planId" />
<result column="PLAN_NAME" property="planName" />
<association column="{intersectionId=INTERSECTION_ID,dayPlanNo=PLAN_ID}" property="dayPlanList" select="selectDayPlanInfosByPlanNo"/>
</resultMap>
<resultMap id="dayPlanResultMap" type="com.jsits.xk.dto.DayPlanAndProgramListDto">
<result property="timeDuration" column="time_duration" />
<result property="programId" column="program_id" />
<result property="stageOrderId" column="stage_order_id" />
<result property="dayPlanId" column="day_plan_id" />
<result property="phaseList" column="phase_list" />
<result property="programName" column="program_name" />
<result property="stageOrderName" column="order_name" />
<result property="controlModeId" column="control_mode_id" />
<result property="controlModeName" column="control_mode_name" />
<result property="cycle" column="cycle" />
<association column="program_id" property="programList" select="selectProgramInfosByProgramId"/>
</resultMap>
<select id="selectProgramInfosByProgramId" parameterType="long"
resultType="com.jsits.xk.model.Program">
select
<include refid="Program_Column_List" />
from program where program_id=#{id}
</select>
<select id="selectDayPlanInfosByPlanNo" parameterType="java.util.HashMap" resultMap="dayPlanResultMap" >
select * from (select
dp.day_plan_id,
trim(REGEXP_REPLACE(to_char(dp.time_duration,'0000'),'([[:digit:]]{2})([[:digit:]]{2})','\1:\2')) as time_duration,
pg.program_id,
pg.program_name,
so.order_name,
so.stage_order_id,
cm.control_mode_id,
cm.control_mode_name,
'120' as cycle
from day_plan dp
left join control_mode cm on dp.control_mode_id=cm.control_mode_id
left join program pg on dp.program_id=pg.program_id
left join stage_order so on dp.stage_order_id=so.stage_order_id
where dp.day_plan_no=#{dayPlanNo}
and dp.intersection_id=#{intersectionId}
)
group by (day_plan_id,time_duration,program_id,program_name,order_name,stage_order_id,control_mode_id,control_mode_name,cycle)
order by time_duration asc
</select>
<select id="selectWeekInfosByIntersectionId" parameterType="long"
resultMap="weekProgramResultMap">
select #{intersectionId} as intersection_id,week.plan_name,week.plan_id from
(
select '星期一' as plan_name,intersection_id,monday as plan_id from week_plan
union
select '星期二' as plan_name,intersection_id,tuesday as plan_id from week_plan
union
select '星期三' as plan_name,intersection_id,wendesday as plan_id from week_plan
union
select '星期四' as plan_name,intersection_id,thursday as plan_id from week_plan
union
select '星期五' as plan_name,intersection_id,friday as plan_id from week_plan
union
select '星期六' as plan_name,intersection_id,saturday as plan_id from week_plan
union
select '星期日' as plan_name,intersection_id,sunday as plan_id from week_plan
)week
left join day_plan dp on dp.day_plan_no=week.plan_id
where week.intersection_id=#{intersectionId}
group by week.plan_id,week.plan_name,week.intersection_id
order by week.plan_id
</select>
联合主键查询
- 在我们很多时候数据库中的主键并不是一个字段,而是通过两个甚至多个字段组合成主键。我们习惯上称之为联合主键。在多重映射中的代码中我们可以看到
weekProgramResultMap
中级联selectDayPlanInfosByPlanNo
这个sql查询。但是有意思的是column中并不是我们熟悉的一个字段。而是通过花括号做首尾,中间加入多个字段组成的一个字符串。这个就是我们本章节引入的概念—联合主键级联查询。column属性值我们分析一下可以看出intersectionId=INTERSECTION_ID
和dayPlanNo=PLAN_ID
组成的。我们可以看成是两个map,等号前是key等号后是value。key就是我们在新的select中引用的key,及#{key}方式获取。value就是原来的select查询获得的数据。也是通过value来查询新的数据的。这里就解决了我们上面基本映射章节说必须用#{id}的局限性。所以在上面中如何我们不想用#{id},那么需要修改column的传入风格{stageId=stage_list}就可以用#{stageId}
简单说明
工作遇到的需求,mybatis还有很多神操作。以后有机会更新。在mybatis中映射并不是仅仅字段这么简单。还有点逗风格的引用。这些以后看时间在更新。
常用标签
格言
人生贵知心,定交无暮早。 —— 袁中道