最近在使用Mybatis的过程中遇到了一些问题,感觉有必要总结一下。前面一段时间简单的看了一点Mybatis的源码,对Mybatis在项目启动过程中以及启动后执行查询的工作流程有了一个大概的了解。这周开始我又到另一个项目,这是一个微服务项目,不过我只参与了一个服务的开发,项目使用的是Mybatis,但是并不是使用xml的方式,而是注解的方式,这也是我第一次在项目中使用注解的方式开发,中间也遇到了一些问题,这里做一个总结。
其实在前面Mybatis源码分析(一)的过程中有提到过Mybatis的注解,但是我一直习惯使用xml方式,所以并没有专门去了解注解的方式。这次总结主要还是专注于使用中遇到的问题。
开发这么久也是我第一次需要使用一对多查询,为了方便我自己写了一点简单的代码,来模拟这样一个问题。首先我有2个model,分别是order和product,从业务上来讲我一个订单可能有多个product,所以model的定义如下:
@Data
public class Order {
private String userid;
private String province;
private String city;
private String street;
private String orderid;
}
@Data
public class Product {
private String orderid;
private String productname;
private Integer count;
private String price;
private String skucode;
}
@Data
public class CustomOrder {
private String userid;
private String province;
private String city;
private String street;
private String orderid;
private List productList;
}
CustomOrder中有一个productList的属性,存储的是某订单所有Product的skucode属性。当然productList也可以是所有商品的全部属性。
1、使用Mapper.xml文件 1.1 多表查询其实首先容易想到的就是关联表查询,sql如下:
select o.*,p.skucode from t_order o left join t_product p on o.orderid = p.orderid where o.userid = ?;
查询的结果如下:
图-1.png
其实这个结果并不是我想要的,我希望最后一列的skucode多行数据转一行数据,即根据userid等列分组,也就是这种结果:
图-2.png
这个方式可以直接通过sql的方式实现,我们如果使用Mybatis的xml方式又该如何实现呢?其实查询的sql并不需要改变,只需要在进行结果映射的时候将查询的skucode放入到映射的model的productList属性即可,即映射到CustomOrder的productList属性中,xml如下:
userid, province, city, street, orderid select o.*,p.skucode from t_order o left join t_product p on o.orderid = p.orderid where o.userid = #{userId}
上面的xml中最主要的就是"
图-3.png
,
下面是控制台输出的sql:
图-4.png
其实根据上图输出的sql就可以看出来,这种方式和我们一开始使用sql进行查询的结果是完全一样的(废话,sql的都一样,结果肯定一样),唯一的区别就是Mybati在输出结果的映射上帮我们进行了处理,即将skucode这一列的结果帮我们转成了一个List
另外xml中除了"
如果productList存放的就是Product的所有属性,CustomOrder的productList改为List
userid, province, city, street, orderid
再测试一次,结果如下:
图-5.png
根据上图的结果可以看出也映射成功了,这个方式其实和普通的关联表查询没有任何不同,唯一区别在于对查询结果的映射处理上。这是使用xml文件第一种方式。
1.2 单表查询除了使用上面的关联查询之外还有另外一种方式,就是正宗的一对多查询。之所以这么说是因为我们上面只是将查询结果进行了不同的映射处理,本质上上面就是普通的关联查询。下面看看Mybatis的一对多到底是怎么样的,修改OrderMapper.xml文件如下:
userid, province, city, street, orderid
根据上面的OrderMapper.xml文件可以看出,sql变成了单表查询,之查询t_order表,而在结果映射里面依然是使用"
orderid, productname, count, price, skucode
接下来我们再通过测试来检验一下结果,结果如下:
图-6.png
然后在看下控制台输出的sql是怎样的:
图-7.png
根据输出sql可以看出有两次查询,首先根据入参(userid)先查询t_order,然后用查询出来的orderid作为入参,执行第二个查询。感觉这里应该是一个查询嵌套,也就是说第二个查询返回结果之后,第一次查询才跟着结束,并返回结果集。而并不是第一个查询先结束再执行第二个查询。不知道这么理解对不对,关于一对多查询的问题就到这里,其实就两种方式来讲感觉关联查询更好一些,毕竟只需要查询一次数据库,第二种方式则需要查多次数据库,性能上可能会差一点。
二、使用Mybatis注解
其实我本身是不喜欢使用注解开发的,因为注解一样需要sql,一样需要做结果映射,显得代码会比较凌乱,但是因为架构定下的要求,所以自己还是要遵守的。下面说下注解开发遇到的一点小问题。
1、使用注解一对多查询上面主要使用xml文件方式实现一对多查询,那么如果使用注解方式如何实现一对多呢??先修改相关的mapper文件,添加相关的注解,OrderMapper如下:
public interface OrderMapper {
@Results(id = "customOrder",value = {
@Result(column = "userid",property = "userid",javaType = String.class,jdbcType = JdbcType.VARCHAR),
@Result(column = "orderid",property = "orderid",javaType = String.class,jdbcType = JdbcType.VARCHAR),
@Result(column = "province",property = "province",javaType = String.class,jdbcType = JdbcType.VARCHAR),
@Result(column = "city",property = "city",javaType = String.class,jdbcType = JdbcType.VARCHAR),
@Result(column = "street",property = "street",javaType = String.class,jdbcType = JdbcType.VARCHAR),
@Result(column = "orderid",property = "productList",many = @Many(select = "com.ypc.mybatis.mapper.ProductMapper.selectByOrderId",fetchType = FetchType.EAGER))
})
@Select("select * from t_order o where o.userid = #{userId}")
CustomOrder selectByUserId(@Param("userId") String userId);
}
同样我需要在ProductMapper文件添加一个相关的sql,代码如下:
public interface ProductMapper {
@Results(id = "product",value = {
@Result(column = "orderid",property = "orderid",javaType = String.class,jdbcType = JdbcType.VARCHAR),
@Result(column = "productname",property = "productname",javaType = String.class,jdbcType = JdbcType.VARCHAR),
@Result(column = "count",property = "count",javaType = Integer.class,jdbcType = JdbcType.INTEGER),
@Result(column = "price",property = "price",javaType = String.class,jdbcType = JdbcType.VARCHAR),
@Result(column = "skucode",property = "skucode",javaType = String.class,jdbcType = JdbcType.VARCHAR)
})
@Select("select * from t_product where orderid = #{orderId}")
List selectByOrderId(@Param("orderId") String orderId);
@ResultMap("product")
@Select("select * from t_product where skucode = #{skucode}")
Product selectBySkuCode(@Param("skucode")String skucode);
}
在注解中,@Results注解和xml中的"resultMap"标签作用是相同的,主要就是实现表的列名和model的属性进行映射,而且这个结果也是可以共用的,比如上面的ProductMapper中selectBySkuCode方法,只需要指定结结果集的id就可以了。@Result注解相当与xml中的"result"标签一样,主要是针对某一列的映射关系。使用注解方式实现一对多的时候不能使用多表查询。所以注解实现一对多查询只有一种方式,就是OrderMapper中的方法。这里就不再测试了,因为和xml完全相同,如果测试的话记得注掉配置文件中的的xml的位置配置,即不让程序读取xml文件。
2、注解中使用动态sql 2.1使用") List""}) // 使用"," @Select({""})
如果是这种方式我不太建议使用 "+"分割sql,因为我在开发中遇到过使用"+"报错的问题,改成","就没问题了,但是刚才在我电脑上使用也是正常的,不知道是不是因为Mybatis版本问题,因为我在开发中确实遇到过使用"+"sql异常改成","正常的情况,这里只是提醒一下可能会出现问题,也算是一种解决问题的思路吧。
2.2 使用Provider实现关于动态sql除了直接使用"script"之外,Mybatis还提供了使用Provider的方式,将我们的mapper文件修改如下:
@SelectProvider(type = OrderProvider.class,method = "queryByUserIdAndStatus")
List queryByUserIdAndStatus(@Param("userid") String userid, @Param("status") String status);
@SelectProvider表示的是执行查询的提供者,即sql提供方,需要注意的一点是,提供的sql并不是真正意义上的sql脚本,而是Mybatis处理之前的sql,即有OGNL表达式的sql,这点需要注意。然后创建一个queryByUserIdAndStatus方法,这个方法返回结果就是所说的sql,返回类型是String,代码如下:
public class OrderProvider {
public String queryByUserIdAndStatus(String userid,String status) {
SQL sql = new SQL();
sql.SELECT("user_id as userid,order_id as orderid,province as province,city as city,street as street,status as status");
sql.FROM("t_order");
sql.WHERe("user_id = #{userid}");
if (status != null && status != "") {
sql.WHERe("status = #{status}");
}
System.out.println("sql: " + sql.toString());
return sql.toString();
}
}
Provider的方法主要其实就是针对动态sql的问题,它本身不进行赋值,只是对传入的参数进行一些判断从而决定具体执行的sql而已。这里就不再进行测试了,感兴趣的可以自己试一下。
三、总结
关于自己开发中遇到的一点问题就到这里了,当然这只是工作的一部分,还有些知识点是自己没有涉及到的,只能有需要的时候再去学习吧。通过自己在实际开发中遇到的问题,以及自己不断查找资料解决,也不断刷新自己对Mybatis的认识。作为一个应用广泛的ORM框架,Mybatis确实非常的优秀。自己以前确实有很多方面没有涉及到,这次开发感觉收获还是蛮大的(不止是Mybatis),另外还有像mysql的一些处理json类型函数等等。关于一对多查询,除了上面的所说的方法之外还有其他的方式也可以达到这个目的,使用数据库的内置函数,但是这个方法相对比较麻烦,需要自定义TypeHandler,有兴趣的可以试试。
学习更多JAVA知识与技巧,关注与私信博主(666)
如果本文对你有帮助,别忘记给我个3连 ,点赞,转发,评论,
,咱们下期见!答案获取方式:已赞 已评 已关~



