一、mapper参数映射
当mapper.xml在接受java的mapper接口传输过来的参数时,我们如果只传输一个参数时我们可以这么传输:
public User queryById(Integer id);
同时我们可以这么接收参数:
<!--
resultType是遍历后得到的结果的类型,可自定义,
parameterType是传入的参数的类型,也可以是自己写的实体类
-->
<select id="queryById" parameterType="int" resultType="com.fs.entity.User">
select * from tb_user where id = #{id}
</select>
编译测试类:
public class UserMapperTest {
private UserMapper userMapper;
private SqlSession sqlSession;
@Before
public void setUp() throws Exception {
//怎么得到Mapper接口的实现类的对象
// sqlSession.getMapper(Mapper接口的Class类型) 得到Mapper接口的实现类的对象
//加载解析: mybatis的主配置文件:mybatis-config.xml
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//通过第一步解析的结果,创建SqlSessionFactory对象, 构建者模式: SqlSessionFactoryBuilder
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory对象创建SqlSession(使用的核心对象)
sqlSession = sqlSessionFactory.openSession();
userMapper =sqlSession.getMapper(UserMapper.class);
}
@Test
public void queryById() {
// userMapper.queryById(1);
System.out.println(userMapper.queryById(1));
}
}
测试结果:
这样是不会有任何问题的,但是当我们要传输多个参数时:
public User queryByNameAndPwd(String username,String password);
<!--
resultType是遍历后得到的结果的类型,可自定义,
parameterType是传入的参数的类型,也可以是自己写的实体类
-->
<select id="queryByNameAndPwd" resultMap="userResultMap">
SELECT * FROM tb_user WHERE username = #{username} AND PASSWORD=#{password}
</select>
运行后,出现报错,情况如下!
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [0, 1, param1, param2]
那么这个问题怎么解决?这里我们有以下几种种解决方法:
①在xml中引用参数的地方设置为#{0}, #{1},.....等(这是从0开始):
<!--
resultType是遍历后得到的结果的类型,可自定义,
parameterType是传入的参数的类型,也可以是自己写的实体类
-->
<select id="queryByNameAndPwd" resultMap="userResultMap">
SELECT * FROM tb_user WHERE username = #{0} AND PASSWORD=#{1}
</select>
②在xml中引用参数的地方设置为#{param1} ,#{param2}.....等(这是从1开始):
<!--
resultType是遍历后得到的结果的类型,可自定义,
parameterType是传入的参数的类型,也可以是自己写的实体类
-->
<select id="queryByNameAndPwd" resultMap="userResultMap">
SELECT * FROM tb_user WHERE username = #{param1} AND PASSWORD=#{param1}
</select>
③引用注解:@Param()给参数起个别名(推荐),因为这样具有更高的代码可读性
public User queryByNameAndPwd(@Param("username") String username, @Param("password")String password);
然后在xml中我们只需要使用别名代表参数即可!
<!--
resultType是遍历后得到的结果的类型,可自定义,
parameterType是传入的参数的类型,也可以是自己写的实体类
-->
<select id="queryByNameAndPwd" resultMap="userResultMap">
SELECT * FROM tb_user WHERE username = #{username} AND PASSWORD=#{password}
</select>
上述三种均可成功,在这就展示一次结果就好了!
④不同于以上几种,我们可以选择不用一个一个地写入参数,我们可以利用JAVA的封装思想,将参数封装成一个实体类再作为参数传入!(举例)
实体类:
import lombok.Data;
@Data
public class User {
private Integer id;
private String username;
private String password;
}
测试类:
@Test
public void queryById() {
// userMapper.queryById(1);
User user = new User();
user.setId(1);
user.setUsername("admin");
System.out.println(userMapper.queryById(user));
}
接口方法:
public User queryById(User user);
statement:
<!--
resultType是遍历后得到的结果的类型,可自定义,
parameterType是传入的参数的类型,也可以是自己写的实体类
-->
<select id="queryById" parameterType="int" resultType="com.fs.entity.User">
select * from user where id = #{id} and username = #{username}
</select>
结果:
这正是因为mybatis会在statement运行的时候,将里面的#{id}和#{username}自动与我们传入的User对象中的id和username进行映射,然后根据user对象中的信息进行查询!
但是哈,我说但是,如果你在传输的时候啊,加上了@Param()的话,那就会不太一样了!
然后mapper的java接口里这么写:
public User queryById(@Param("user") User user);
好家伙,那你可真是给自己找了麻烦,如果你这样写,那你就需要在你的statement里修改成这样:user.username和user.id
<!--
resultType是遍历后得到的结果的类型,可自定义,
parameterType是传入的参数的类型,也可以是自己写的实体类
-->
<select id="queryById" parameterType="com.fs.entity.User" resultType="com.fs.entity.User">
select * from user where id = #{user.id} and username = #{user.username}
</select>
不改?还是写#{id}和#{username}?来,下面这个找不到参数的错误就是你不改的后果哦!
改完运行test方法,OK搞定:
二、resultMap结果集自定义
(一)基本的resultMap讲解
有时候啊,当我们查询的结果不是我们现有的实体类,或者字段名和自己写的实体类名有冲突,而你又不想再建一个实体类也不想改动现有的实体类咋办啊?(经典实体类设计者和数据库表设计者大型纷争现场)
那就自己定义一个结果集呗!
举个例子:
①我数据库中的图片路径的字段名是img_path:
然后我给的实体类的图片路径名又是:gd_img:
得了,这回mybatis想救你都难了,只能自己手动映射一个结果集了:
其中:
①resultMap 里的 id 是这个结果集的名称,直接在你的statement中的resultType中填入就行
②resultMap 里的 type 就是实体类名称
③result 中的 id 是表示的唯一主键
④result 中的 property 是 实体类中对应的参数名
⑤result 中的 column 是 数据库中查询出来的对应的列名
<!--
id是这个结果集的名称,到时候直接在你的statement中的resultType中填入就行
type就是实体类名称
-->
<resultMap id="GoodsContent" type="GoodsContent">
<id property="gd_id" column="gd_id"/>
<!--
这里就是映射:
property 是实体类中对应的参数名
column 是数据库中查询出来的对应的列明
-->
<result property="gd_img" column="img_path"/>
<result property="price" column="gd_price"/>
<result property="s_id" column="s_id"/>
<result property="s_name" column="s_name"/>
<result property="g_data" column="g_data"/>
<result property="g_datail" column="g_datail"/>
<result property="g_name" column="g_name"/>
</resultMap>
然后在statement中这么写,只需要在statement中的resultMap中填入上面的resultMap的id即可:
<select id="queryGoodContent" resultMap="GoodsContent">
SELECT GD.gd_id,GD.gd_price,STO.s_id,STO.s_name,GS.g_data,GS.g_datail,GS.g_name,img.img_path
FROM good_detail GD
JOIN store STO ON GD.s_id = STO.s_id
JOIN goods GS ON GS.g_id = GD.G_ID
JOIN img_path img ON GD.gd_id = img.gd_id
WHERE GD.gd_id = #{gdId}
</select>
然后执行一下接口测试(controller和service就省略了,只展示结果,请忽略我的乱码,懒得改编码了):
(二)resultMap的使用:结果集中嵌套多层list实体类结果集
很多人想问,为什么我查个数据要嵌套多层结果集啊?
那么假设你现在想要只能使用一个statement接口实现一个功能:查询一个用户的所有的订单(多条),每条订单里又包括了多条订单详情,然后每个订单详情中又包含了多个商品
[ 一 ]resultMap嵌套一个实体类:
想要在resultMap中嵌套多层list集合实体类的话,我们首先需要的是这几个实体类(这里偷懒写在一个代码块中,但是实际是分开的):
//User实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
//用户表
public class User implements Serializable {
private int id;
private String username;
private List<Orders> ordersList = new ArrayList<>();
}
//Orders实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Orders {
private Integer oId;
private String OData;
private Integer uidV;
private List<OrderDetail> orderDetailList = new ArrayList<>();
}
//OrderDetail实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderDetail {
private Integer odId;
private String odData;
private int gidV;
private List<Goods> goodsList = new ArrayList<>();
}
//Goods商品类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
private Integer gId;
private String gName;
private double gPrice;
}
然后我们的mapper接口只需要传入一个用户id即可:
public User queryUserAndOrderDetailByUid(@Param("uid")Integer uid);
接着我们去写mapper.xml中resultMap:
这里就要引出我们resultMap中的另一个标签:<collection></collection>
这个标签可以当做是用于在一个结果集中嵌套生成了另一个结果集
而这个结果集中的property是指当前这个collection所表示实体类中的list集合的属性名
例如第一个collection中的property是ordersList,那代表的就是User实体类中的ordersList
然后我们配置好字段名和属性名的映射,我们的resultMap就完成了!
<resultMap id="UserAndAllOrderDetail" type="User">
<!-- 先映射的是User的属性 -->
<id property="id" column="id"/>
<result property="username" column="username"/>
<!--配置关联属性: orderDetailList
集合属性: collection
属性:
property: 关联属性名
ofType: 集合的元素的类型, 映射的与集合的元素映射
-->
<collection property="ordersList" ofType="Orders">
<id column="oid" property="oId"/>
<result column="OData" property="OData"/>
<collection property="orderDetailList" ofType="OrderDetail">
<id property="odId" column="odId"/>
<result column="odData" property="odData"/>
<collection property="goodsList" ofType="Goods">
<id column="gId" property="gId" />
<result property="gName" column="gName"/>
<result property="gPrice" column="gPrice"/>
</collection>
</collection>
</collection>
</resultMap>
然后我们去写mapper.xml中的statement:
<!-- 别学着我写*,不然会被项目组长骂(吧?) -->
<select id="queryUserAndOrderDetailByUid" parameterType="int" resultMap="UserAndAllOrderDetail">
SELECT * FROM USER u
JOIN ORDERS o
ON u.id = o.uidV
JOIN orderdetail OD
ON OD.oidV = o.oid
JOIN GOODS G
ON G.odIdV = OD.odid
WHERE U.ID = #{uid}
</select>
最后我们去写测试类Test:
public class UserOrderMapperTest {
private UserOrderMapper userOrderMapper;
private SqlSession sqlSession;
@Before
public void setUp() throws Exception {
//怎么得到Mapper接口的实现类的对象
// sqlSession.getMapper(Mapper接口的Class类型) 得到Mapper接口的实现类的对象
//加载解析: mybatis的主配置文件:mybatis-config.xml
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//通过第一步解析的结果,创建SqlSessionFactory对象, 构建者模式: SqlSessionFactoryBuilder
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory对象创建SqlSession(使用的核心对象)
sqlSession = sqlSessionFactory.openSession();
userOrderMapper =sqlSession.getMapper(UserOrderMapper.class);
}
@Test
public void querayUserAndAllOrderDetailById(){
System.out.println(userOrderMapper.queryUserAndOrderDetailByUid(1));
}
}
好了,到这里我们就可以运行了,结果:
为了直观点看我复制出来理一下顺序:
User(id=1, username=admin,
ordersList=[
Orders(oId=1, OData=简介1, uidV=null,
orderDetailList=[
OrderDetail(odId=1, odData=订单详情1, gidV=0,
goodsList=[
Goods(gId=1, gName=苹果, gPrice=10.0),
Goods(gId=2, gName=香蕉, gPrice=5.0)]),
OrderDetail(odId=2, odData=订单详情2, gidV=0,
goodsList=[
Goods(gId=3, gName=葡萄, gPrice=7.0),
Goods(gId=4, gName=橘子, gPrice=6.0)])]),
Orders(oId=2, OData=简介2, uidV=null,
orderDetailList=[
OrderDetail(odId=3, odData=订单详情3, gidV=0,
goodsList=[
Goods(gId=5, gName=水蜜桃, gPrice=3.0),
Goods(gId=6, gName=冬枣, gPrice=8.0)]),
OrderDetail(odId=4, odData=订单详情4, gidV=0,
goodsList=[
Goods(gId=7, gName=西瓜, gPrice=20.0),
Goods(gId=8, gName=哈密瓜, gPrice=10.0)
])])])
那么到这里,mabatis的入门学习使用也就到一段落了,感谢各位赏脸浏览,不知道我讲清楚没有,望大佬轻喷,感恩戴德!