持久层框架MyBatis 多表查询 一对多
往期内容:持久层框架MyBatis 多表查询 一对一
需求
商品分类表
商品详情表
SELECT category.category_id,category.category_name,item_name
FROM category,item
WHERE category.category_id=1 AND item.category_id=category.category_id
1.表的关系
2.分析
3.实现步骤
1.在mybatis04项目pojo包中创建Item、category实体类
2.在mapper包中创建CategoryMapper接口{Category select(Integer categoryld)}
3.在mapper包中创建CategoryMapper.xml
result
select
4.在controller包中创建CategoryController
@autowired
CategoryMapper categoryMapper
@requestmMapping(/select)
Category select (){
return categoryMapper.select()
}
1.打开eclipse,找到mybatis04中的pojo包
2.创建Item类、Category类
package com.tedu.DbDemo.pojo;
//对应Item表
public class Item {
String itemName;//对应item_name列
//set()、get()
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
}
package com.tedu.DbDemo.pojo;
import java.util.List;
//对应Category表
public class Category {
Integer categoryId;//对应Category_id列
String categoryName;//对应Category_name列
//一个分类下有多个商品
List <Item> itemList;//体现出一对多关系
//set、get
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public List<Item> getItemList() {
return itemList;
}
public void setItemList(List<Item> itemList) {
this.itemList = itemList;
}
}
3.在mapper包中创建接口CategoryMapper类、CategoryMapper.xml
package com.tedu.DbDemo.mapper;
import com.tedu.DbDemo.pojo.Category;
public interface CategoryMapper {
//查询员工分类和分类下的所有商品
public Category selectById(Integer categoryId);
}
<?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">
<!-- namespace="接口的包名.接口名" -->
<mapper namespace="com.tedu.DbDemo.mapper.CategoryMapper">
<!-- type=com.tedu.DbDemo.pojo.Category -->
<resultMap type="com.tedu.DbDemo.pojo.Category" id="map1">
<!-- 顺序是result、colleciton -->
<result column="category_id" property="categoryId"/>
<result column="category_name" property="categoryname"/>
<!-- 结果集中有商品信息,分类和商品是一对多 -->
<!-- 一对多用collection 一对一用assiocation -->
<!-- Category{List<Item> itemList} -->
<collection property="itemList" ofType="com.tedu.DbDemo.pojo.Item">
<result column="item_name" property="itemName"/>
</collection>
</resultMap>
<!-- resultType=Category
resultMap=map
parameterType=Integer
parameterMap
-->
<select id="selectById" resultMap="map1" parameterType="Integer">
SELECT category.category_id,category_name,item_name
FROM category,item
WHERE category.category_id=#{categortId}
AND item.category_id=category.category_id
</select>
</mapper>
select元素有很多属性(这里说用的比较多的):
id:命名空间唯一标识,可以被用来引用这条语句
<font face=“宋体” color=#FF0000size=5> parameterType:将会传入这条语句的参数类的完全限定名或者别名
resultType:从这条语句要返回的期望类型的类的完全限定名或别名(这里注意下集合类型,应该是集合可以包含的类型,不能是集合本身),重要:使用resultType或resultMap,但不能同时使用。
resultMap:命名引用外部的resultMap,其名称要和外部的resultMap元素的ID名称一致,用于映射其结果到实体类指定对象中。
4.在controller包中创建CategoryController类
package com.tedu.DbDemo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.tedu.DbDemo.mapper.CategoryMapper;
import com.tedu.DbDemo.pojo.Category;
@RestController
public class CategoryController {
//@mapperScan(mapper) mybatis框架会自动为mapper包下的
//接口创建代理类$proxy51,为代理类创建代理对象
//放在spring的ioc 容器中
//autowired得到代理对象
//执行selectById(),mybatis框架会从xml中找到select语句
//执行sql得到的是结果集,把每一行转成Category对象
//把对象放在list中,结果集中就一行数据,返回一个对象
//由jackson框架把数据转成json
@Autowired
CategoryMapper categoryMapper;
@RequestMapping("/category")
public Category selectById() {
Category category=categoryMapper.selectById(1);
return category;
}
}
Run As执行:
http://localhost:8080/category
4.参数占位符
mybatis本质就是拼接SQL语句。
#{name}
使用的jdbc的ParpareStatment。
#{} 很赞,防止SQL注入;如果参数是一个字符串类型。
chen 拼接SQL语句时会根据类型,自动加相关符号。例如字符串类型’chen’。
${orderby}
${} 原样输出,很危险,有SQL注入风险。
测试:
查看控制台输出的SQL语句:
==> Preparing: select category.category_id,category_name,item_name from category,item where category.category_id=1 and item.category_id=category.category_id
> Parameters:
< Total: 2
工作中必须用#{},因为他安全,防止SQL注入!
5.复杂搜索案例
Baidu高级搜索
github.com/search/advanced
6.动态SQL语句
6.1 在mapper包下创建ItemMapper{listselect(Item)},pojo包下的Item添加categoryld
6.2 在mapper包下面创建ItemMapper.xml
doctype
mapper namespace
不用写resultMap
select
select category_id as categoryld,item_name as itemName
where
<if test="categoryId!=null">
category_id=#{
categoryId}
</if>
6.3打开Item类,进行修改
package com.tedu.DbDemo.pojo;
//对应Item表
public class Item {
String itemName;//对应item_name列
Integer categoryId;
//set(),get()
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
}
7.在mapper包中创建接口 ItemMapper类、ItemMapper.xml
创建ItemMapper.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">
<!-- namespace="接口的包名.接口名" -->
<mapper namespace="com.tedu.DbDemo.mapper.ItemMapper">
<!-- List<Item> select1() -->
<!-- 如果方法返回的是集合,resultType的值是集合中数据的类型,resultType指定的是结果集中的每一行转成哪个类 -->
<select id="select1" parameterType="com.tedu.DbDemo.pojo.Item"
resultType="com.tedu.DbDemo.pojo.Item">
SELECT category_id AS categoryId,item_name AS itemName FROM item
</select>
</mapper>
8.在controller包中创建ItemController类
package com.tedu.DbDemo.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.tedu.DbDemo.mapper.ItemMapper;
import com.tedu.DbDemo.pojo.Item;
@RestController
public class ItemController {
@Autowired
ItemMapper itemMapper;
@RequestMapping("/item/select1")
public List<Item> select1(Item item){
List<Item> itemList=itemMapper.select1(item); //打上断点
return itemList;
}
}
Debug AS执行:
==> Preparing: SELECT category_id AS categoryId,item_name AS itemName FROM item
> Parameters:
< Total: 3
9.动态SQL语句 ItemMapper.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">
<!-- namespace="接口的包名.接口名" -->
<mapper namespace="com.tedu.DbDemo.mapper.ItemMapper">
<!-- list<Item> select1()
如果方法返回的是集合,resultType的值是集合中数据的类型
resultType指定的是结果集中的每一行转成那个类
参数item中有categroryId,itemName属性-->
<select id="select1" parameterType="com.tedu.DbDemo.pojo.Item" resultType="com.tedu.DbDemo.pojo.Item">
SELECT category_id AS categoryId,item_name AS itemName
FROM item
<!-- 写的是where,不传参数 生成的sql是from item where
前面的参数不传,sql是from item where and item_name like
解决方法是吧where改成<where> -->
<where>
<if test="categoryId!=null">
<!-- category_id是列名 ,categoryId是参数item中的属性 -->
category_id=#{categoryId}
</if>
<if test="itemName != null">
<!-- concat是mysql内置的函数,拼接字符串 -->
and item_name like concat('%',#{itemName},'%')
</if>
</where>
</select>
</mapper>
Run As测试:
http://localhost:8080/item/select1?categoryId=1&itemName=新
错误一:
http://localhost:8080/item/select1
SQL: SELECT category_id AS categoryId ,item_name AS itemName FROM item where
错误二:
http://localhost:8080/item/select1?itemName=%E6%96%B0
SQL: SELECT category_id AS categoryId ,item_name AS itemName FROM item where and item_name like concat(’%’,?,’%’)
10 集合参数
动态sql生成in
Item,java增加itemld属性,增加set()、get()
ltemMapper接口中增加listselectByList(Listlist)
ItemMapper.xml增加select id=selectByList
生成生成in(2,3,4) in (2)
ItemController增加selectByList
测试sql
SELECT
item_id AS itemid,category_id AS categoryId,
item_name AS itemName
FROM item
WHERE item_id IN(2,3,4)
给Item增加一个itemId,get()set()
package com.tedu.DbDemo.pojo;
//对应Item表
public class Item {
String itemName;//对应item_name列
Integer itemId;
Integer categoryId;
//set(),get()
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public Integer getItemId() {
return itemId;
}
public void setItemId(Integer itemId) {
this.itemId = itemId;
}
}
ItemMapper 类增加一个方法商品编号查询
package com.tedu.DbDemo.mapper;
import java.util.List;
import com.tedu.DbDemo.pojo.Item;
public interface ItemMapper {
//参数item中可以包含categoryId,itemName
//如果item中的某个属性值不为空,就根据属性值查找
public List<Item> select1(Item item);
//根据商品编号查询
public List<Item> selectByList(List<Integer> list);
}
创建动态生成: ItemMapper.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">
<!-- namespace="接口的包名.接口名" -->
<mapper namespace="com.tedu.DbDemo.mapper.ItemMapper">
<!-- list<Item> select1()
如果方法返回的是集合,resultType的值是集合中数据的类型
resultType指定的是结果集中的每一行转成那个类
参数item中有categroryId,itemName属性-->
<select id="select1" parameterType="com.tedu.DbDemo.pojo.Item" resultType="com.tedu.DbDemo.pojo.Item">
SELECT category_id AS categoryId,item_name AS itemName
FROM item
<!-- 写的是where,不传参数 生成的sql是from item where
前面的参数不传,sql是from item where and item_name like
解决方法是吧where改成<where> -->
<where>
<if test="categoryId!=null">
<!-- category_id是列名 ,categoryId是参数item中的属性 -->
category_id=#{categoryId}
</if>
<if test="itemName != null">
<!-- concat是mysql内置的函数,拼接字符串 -->
and item_name like concat('%',#{itemName},'%')
</if>
</where>
</select>
<!-- public List<Item> selectByList(List<Integer> list); -->
<!-- parameterType parameterMap -->
<select id="selectByList" parameterType="Integer" resultType="com.tedu.DbDemo.pojo.Item">
SELECT
item_id AS itemid,category_id AS categoryId,
item_name AS itemName
FROM item
WHERE item_id IN
<!-- 生成(2,3,4) -->
<!-- foreach遍历集合
collection指定集合的名称
item指定从集合中取出的数据放在id中 -->
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>
修改ItemController 类进行测试:
package com.tedu.DbDemo.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.tedu.DbDemo.mapper.ItemMapper;
import com.tedu.DbDemo.pojo.Item;
@RestController
public class ItemController {
@Autowired
ItemMapper itemMapper;
@RequestMapping("/item/selectByList")
public List<Item> selectByList(){
ArrayList<Integer> list=new ArrayList<Integer>();
list.add(2);
list.add(3);
list.add(4);
List<Item> itemList=itemMapper.selectByList(list);
return itemList;
}
@RequestMapping("/item/select1")
public List<Item> select1(Item item){
List<Item> itemList=itemMapper.select1(item);
return itemList;
}
}
Run As执行
http://localhost:8080//item/selectByList?categoryId=1&itemName=新
小结:
在xml中通过标签生成sql
if标签、where标签、foreach标签
查看逆向工程生成的动态sql
找到mybatis02项目中的mapper包下UserMapper.xml
拓展:优化,使代码更加简洁
前面讲了CRUD的全部测试,下面是对CRUD示例的优化:
可以在配置文件中调用的地方都进行include引用
<sql id="cols">id,name,birthday,address</sql>
<select id="list" resultType="user">
select <include refid="cols"/> from user
</select>
SQL中有特殊字符
当SQL中有特殊字符,mybatis不能正常解析时,用CDATA括起来就解决了
<![CDATA[ and age<=#{age} ]]>总结
MVC和SSM的关系
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码。
Controller在小型项目中作用不明显,controller相当于厨师长,分配谁来做菜。