SpringBoot整合MyBatis实战总结

以下为此次项目中遇到的值得总结的问题

1. 表名为关键字的解决方法

数据库表明为index与关键字冲突,导致SQL语句执行出错,在改表名上加上’单引号即可避免该情况,举例如下:

1
2
3
4
5
6
SELECT
i.id, i.name, i.source,
a.id, a.name
FROM 'index' i
LEFT JOIN area a
ON i.id=a.id

感谢昌炬哥的指导!

2. 使用collection标签查询时结果仅有一条

在进行多表级联查询时,一对多关系(如上述SQL,一条index表数据对应多条area表数据),使用collection标签对两张表进行关联时导致查询结果仅有一条,查阅论坛得知,将互相关联的表的主键字段改为不相同即可,上述两表中主键均为id,修改表结构后,执行SQL如下:

1
2
3
4
5
6
SELECT
i.id, i.name, i.source,
a.aid, a.name
FROM 'index' i
LEFT JOIN area a
ON i.id=a.aid

上述方法也是一种解决办法,但是修改数据库表结构代价太大,解决方法如下:

1
2
3
4
5
6
7
8
9
<resulrMap id="IndexArea" typr="com.myhexin.kiv.model.Index"
extend="BaseResultMap">
//此处BaseResultMap为1-n关系中“1”这一方的resultMap
<collection property="areaList" ofType="com.myhexin.kiv.model.IndexArea">
<id column="id" property="id"/>
<result column="index_id" property="indexId"/>
<result column="area" property="area"/>
</collection>
</resultMap>

扎眼看,上述代码并无任何差错,但是执行的时候对应结果却只有一条


1
<id column="id" property="id"/>

修改为

1
<result column="id" property="id"/>

即可不用通过修改数据库表结构解决对应问题。

原因是因为被关联表的id和关联表的id(唯一主键)冲突导致,所以将被关联表的id属性设置为属性值即可解决问题

再次感谢昌炬哥!

3. Mybatis传入参数为多种类型时

基本数据类型int,string,long,Date;

案例

1
2
3
4
5
6
7
8
9
<sql id="Base_Column_List" >  
id, car_dept_name, car_maker_name, icon,car_maker_py,hot_type
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select
<include refid="Base_Column_List" />
from common_car_make
where id = #{id,jdbcType=BIGINT}
</select>

复杂数据类型 class,map

map类型案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="queryCarMakerList" resultMap="BaseResultMap" parameterType="java.util.Map">  
select
<include refid="Base_Column_List" />
from common_car_make cm
where 1=1
<if test="id != null">
and cm.id = #{id,jdbcType=DECIMAL}
</if>
<if test="carDeptName != null">
and cm.car_dept_name = #{carDeptName,jdbcType=VARCHAR}
</if>
<if test="carMakerName != null">
and cm.car_maker_name = #{carMakerName,jdbcType=VARCHAR}
</if>
<if test="hotType != null" >
and cm.hot_type = #{hotType,jdbcType=BIGINT}
</if>
ORDER BY cm.id
</select>

类类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<update id="updateByPrimaryKeySelective" parameterType="com.epeit.api.model.CommonCarMake" >  
update common_car_make
<set >
<if test="carDeptName != null" >
car_dept_name = #{carDeptName,jdbcType=VARCHAR},
</if>
<if test="carMakerName != null" >
car_maker_name = #{carMakerName,jdbcType=VARCHAR},
</if>
<if test="icon != null" >
icon = #{icon,jdbcType=VARCHAR},
</if>
<if test="carMakerPy != null" >
car_maker_py = #{carMakerPy,jdbcType=VARCHAR},
</if>
<if test="hotType != null" >
hot_type = #{hotType,jdbcType=BIGINT},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
</update>

map中包含数组的情况

1
2
3
4
5
6
7
8
9
10
11
12
<select id="selectProOrderByOrderId" resultType="com.epeit.api.model.ProOrder" parameterType="java.util.HashMap" >  
select sum(pro_order_num) proOrderNum,product_id productId,promotion_id promotionId
from pro_order
where 1=1
<if test="orderIds != null">
and
<foreach collection="orderIds" item="item" open="order_id IN(" separator="," close=")">
#{item,jdbcType=BIGINT}
</foreach>
</if>
GROUP BY product_id,promotion_id
</select>

在DAO层,如多有多个参数传入,那么一定要使用@Param注解,List类型也需要使用,否则mapping.xml将无法识别该参数

4. 控制台SQL语句的打印

直接application.properties里面定义

1
logging.level.com.abc=DEBUG

设置为开发级别即可

5. spring 中自定义工具类需要注入service

在SpringMVC框架中,我们经常要使用@Autowired注解注入Service或者Mapper接口,我们也知道,在controller层中注入service接口,在service层中注入其它的service接口或者mapper接口都是可以的,但是如果我们要在我们自己封装的Utils工具类中或者非controller普通类中使用@Autowired注解注入Service或者Mapper接口,直接注入是不可能的,因为Utils使用了静态的方法,我们是无法直接使用非静态接口的,当我们遇到这样的问题,我们就要想办法解决了。

我们有两种方法解决这个问题,第一种是注解方式,第二种是xml配置方式,下面是我们在utils中使用@Autowired注解的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component   
public class TestUtils {
@Autowired
private ItemService itemService;

@Autowired
private ItemMapper itemMapper;

public static TestUtils testUtils;

@PostConstruct
public void init() {
testUtils = this;
}

//utils工具类中使用service和mapper接口的方法例子,用"testUtils.xxx.方法" 就可以了
public static void test(Item record){
testUtils.itemMapper.insert(record);
testUtils.itemService.queryAll();
}
}

我们在init方法中使用以下注解就可以了,时间上这个init()的方法是可以自己随便定义的,注意:inti()方法里面不用写任何东西,跟我这样的就绝对ok了,不用看网上其他人瞎掰!

1
@PostConstruct

6. 使用clooection标签时报错Cause: org.apache.ibatis.reflection.ReflectionException: Could not set property ‘xx’ of ‘class xx’ with value ‘xx’

当时这个问题折磨了我整整半天时间,对应的类,以及设置的值都是完全符合类型的,但是一直报错,总结如下:

出错时collection标签配置如下:

1
2
3
4
5
6
<collection property="orderdetails" javaType="com.luchao.mybatis.first.po.Orderdetail">
<result column="id" property="id"/>
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="ordersId"/>
</collection>

通过各种查找资料得知:上面的==javaType==属性的问题,因为这个是一对多,通过反射应该映射为List,但是使用javaType会让MyBatis认为orderdetails属性为Orderdetail,所以出错,将javaType改为ofType,只是指定泛型的类型为Orderdetail。

7. Spring MVC /Boot接收并返回简单JSON 数据(不定义对应pojo情况下)

前端json类型如下:

1
{"name":"caole"}

在控制器接受参数时,单个属性没有必要再去创建一个pojo对象来接收,这里可以使用map

举例如下

1
2
public String xxxxx(@RequestBody Map map) {
List<String> list = (List) map.get("xxxxx");

且,使用Map接收参数时,注解需为@RequestBody

8. 使用SpringBoot框架PageHelper插件无法启用

由于网上有一大半的教程都是有误导性的,在这里重新总结一下

  1. 导包,使用springBoot框架时,不能使用pageHelper的基础包,需要使用针对springboot的jar包

    网上大多数说法如下:
    有两种方式,我们这里只使用原生的PageHelper导入Maven

    在这里我们要使用如下JAR:

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.3</version>
    </dependency>
  2. 具体使用

    参考作者的使用方法

    pagehelper/Mybatis-PageHelper/HowToUse.md

    PageHelper integration with Spring Boot

9. 一对多,多对多关联查询的总结

在使用pagehelper插件对查询结果进行分页时,对于单表查询效率是极高的,且分页结果会让人很满意,但是进行多表级联查询的时候,效果就不那么理想了。由于开发时间有限,没有太多时间去创建适合开发环境的分页插件,所以就只能对SQL或者resultMap加以改装去使用pagehelper插件了。

分页插件作者的方法介绍如下: Mybatis关联结果查询分页方法

总结:

我们需要达到的目的:对于关联嵌套查询,使用分页的时候,只会对主SQL进行分页查询,嵌套的查询不会被分页。

对于关联结果查询,使用分页得不到正常的结果,因为只有把数据全部查询出来,才能得到最终的结果,对这个结果进行分页才有效。因而如果是这种情况,必然要先全部查询,在对结果处理,这样就体现不出分页的作用了。

对于关联嵌套查询,使用分页的时候,只会对主SQL进行分页查询,嵌套的查询不会被分页。

如何对关联结果进行分页?

  • 针对这种情况最好的方法就是手写分页,针对主要语句进行分页,对连接的表不进行分页查询
  • 针对主要语句写count查询(不需要管连接的表)
  • 这样一来,对嵌套的结果就没有影响了

举例:

1
2
3
4
5
6
<!-- lang: sql -->
select *
from (select *
from (select a.*, rownum rw from sys_role a where rownum <= 4)
where rw > 0) a
left join sys_role_function b on a.roleid = b.roleid;

关于关联嵌套查询

嵌套查询由于都是独立的sql,主sql和分支sql都是分离的,所以使用Mybatis分页插件可以正常分页。如果你还想对关联查询的分支sql进行分页查询,基本上是不可能的,但是通过column={}这种方式传递分页参数也能实现,估计有些人看不懂这里了,就到此为止吧,不需要做这么费力不讨好的事。

然而我们就要使用别人看不懂的这个方法

举例如下:

  1. 一对多关系:

    有两张表,一张teacher,一张group,一个teacher对应多个group

    teacher的model中包括List对象

    teachermapping.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <mapper namespace="com.oasis.test.mapper.TeacherMapper">
    <resultMap id="teacherResultMap" type="com.oasis.test.entity.Teacher">
    <id column="id" property="id"/>
    <result column="teacher_name" property="name"/>
    <result column="age" property="age"/>
    <collection property="groupList" ofType="com.oasis.test.entity.Group" column="id"
    select="com.oasis.test.mapper.GroupMapper.getGroupListByTeacherId">
    </collection>
    </resultMap>
    <select id="getTeacherList" parameterType="java.lang.Long"
    resultMap="teacherResultMap">
    SELECT
    ID,TEACHER_NAME,AGE
    FROM
    t_teacher limit 0,5
    </select>

    GroupMapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <mapper namespace="com.oasis.test.mapper.GroupMapper">
    <resultMap id="groupResultMap" type="com.oasis.test.entity.Group">
    <id column="id" property="id"/>
    <result column="group_name" property="name"/>
    <result column="number" property="number"/>
    </resultMap>
    <select id="getGroupListByTeacherId" parameterType="java.lang.Long"
    resultMap="groupResultMap">
    SELECT
    ID,GROUP_NAME,NUMBER
    FROM
    t_group WHERE teacher_id=#{id}
    </select>

    </mapper>

    对应DAO中书写对应方法即可实现功能。

  2. 多对多关系

    一个指标组对应多个指标,一个指标也可能属于多个指标组

    三张表:index、index_recommend、recommend

    其中index_recommend表中仅有两个字段:index_id、recommend_id

    recommendMapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <resultMap id="selectAll" type="cn.caoler.model.Recommend">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="number" property="number"/>

    <collection property="indexList" ofType="cn.caoler.model.Index"
    colum="id" select="cn.caoler.mapper.IndexMapper.getIndexByRecommend">
    </collection>
    </resultMap>
    <select id="selectAll" resultMap="selectAll">
    SELECT *
    FROM
    recommend
    </select>

    indexMapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <resultMap id="selectAll" type="cn.caoler.model.Index">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="number" property="number"/>
    </resultMap>
    <select id="getIndexByRecommend" resultMap="selectAll">
    SELECT *
    FROM
    `index` as i
    where i.id in(
    select index_id from index_recommend
    where recommend_id=#{id}
    )
    </select>

    通过上述嵌套查询即可实现功能。

传入参数既有RequestParam,还有RequestBody的处理

举例:

post 一个json数据到

1
http://www.google.com?code=1024

RequestParams 就是 code=1024

RequestBody 就是你的json数据(题外话:注意content-type)

参考博客:

0%