后台SpringBoot + Mybatis开发遇到的一些坑

目录

一、首先介绍一下开发框架,SpringBoot+Mybatis,SpringBoot集成了Tomcat因此,不需要下载Tomcat服务运行,开发完直接build出jar包运行即可,Mybatis-Plus增加条件构造器EntityWrapper很好用,可以把需要查询的条件全部放构造器里,执行就好。

二、下面回顾一下开发中遇到的一些坑,希望互相参考

   2.1 properties配置文件错误导致无法启动

后台SpringBoot + Mybatis开发遇到的一些坑_第1张图片

比较典型就是数据库地址配置有误,导致服务启动不起来,需要我们连接检查,二报错却会显示是mapper或者sql注入等错误,需要格外注意。

 2.2 Mapper.xml文件映射出错

后台SpringBoot + Mybatis开发遇到的一些坑_第2张图片

由于字段映射出现的错误,由于Mapper作为数据库和Entity的桥梁,字段检查是必须的,随便一个写错,系统及可能跑不起来。

2.3 Mybatis-Plus 语句异常总结

​
/**
 * Copyright (c) 2011-2016, hubin ([email protected]).
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus.service.impl; import java.io.Serializable; import java.util.List; import java.util.Map; import org.apache.ibatis.binding.MapperMethod; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.session.SqlSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import com.baomidou.mybatisplus.entity.TableInfo; import com.baomidou.mybatisplus.enums.SqlMethod; import com.baomidou.mybatisplus.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.mapper.BaseMapper; import com.baomidou.mybatisplus.mapper.Condition; import com.baomidou.mybatisplus.mapper.SqlHelper; import com.baomidou.mybatisplus.mapper.Wrapper; import com.baomidou.mybatisplus.plugins.Page; import com.baomidou.mybatisplus.service.IService; import com.baomidou.mybatisplus.toolkit.CollectionUtils; import com.baomidou.mybatisplus.toolkit.MapUtils; import com.baomidou.mybatisplus.toolkit.ReflectionKit; import com.baomidou.mybatisplus.toolkit.StringUtils; import com.baomidou.mybatisplus.toolkit.TableInfoHelper; /** *

* IService 实现类( 泛型:M 是 mapper 对象,T 是实体 , PK 是主键泛型 ) *

* * @author hubin * @Date 2016-04-20 */ public class ServiceImpl, T> implements IService { private static final Log logger = LogFactory.getLog(ServiceImpl.class); @Autowired protected M baseMapper; /** *

* 判断数据库操作是否成功 *

*

* 注意!! 该方法为 Integer 判断,不可传入 int 基本类型 *

* * @param result 数据库操作返回影响条数 * @return boolean */ protected static boolean retBool(Integer result) { return SqlHelper.retBool(result); } @SuppressWarnings("unchecked") protected Class currentModelClass() { return ReflectionKit.getSuperClassGenricType(getClass(), 1); } /** *

* 批量操作 SqlSession *

*/ protected SqlSession sqlSessionBatch() { return SqlHelper.sqlSessionBatch(currentModelClass()); } /** * 获取SqlStatement * * @param sqlMethod * @return */ protected String sqlStatement(SqlMethod sqlMethod) { return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod()); } @Transactional public boolean insert(T entity) { return retBool(baseMapper.insert(entity)); } @Transactional public boolean insertAllColumn(T entity) { return retBool(baseMapper.insertAllColumn(entity)); } @Transactional public boolean insertBatch(List entityList) { return insertBatch(entityList, 30); } /** * 批量插入 * * @param entityList * @param batchSize * @return */ @Transactional public boolean insertBatch(List entityList, int batchSize) { if (CollectionUtils.isEmpty(entityList)) { throw new IllegalArgumentException("Error: entityList must not be empty"); } try (SqlSession batchSqlSession = sqlSessionBatch()) { int size = entityList.size(); String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE); for (int i = 0; i < size; i++) { batchSqlSession.insert(sqlStatement, entityList.get(i)); if (i >= 1 && i % batchSize == 0) { batchSqlSession.flushStatements(); } } batchSqlSession.flushStatements(); } catch (Throwable e) { throw new MybatisPlusException("Error: Cannot execute insertBatch Method. Cause", e); } return true; } /** *

* TableId 注解存在更新记录,否插入一条记录 *

* * @param entity 实体对象 * @return boolean */ @Transactional public boolean insertOrUpdate(T entity) { if (null != entity) { Class cls = entity.getClass(); TableInfo tableInfo = TableInfoHelper.getTableInfo(cls); if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) { Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty()); if (StringUtils.checkValNull(idVal)) { return insert(entity); } else { /* * 更新成功直接返回,失败执行插入逻辑 */ return updateById(entity) || insert(entity); } } else { throw new MybatisPlusException("Error: Can not execute. Could not find @TableId."); } } return false; } @Transactional public boolean insertOrUpdateAllColumn(T entity) { if (null != entity) { Class cls = entity.getClass(); TableInfo tableInfo = TableInfoHelper.getTableInfo(cls); if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) { Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty()); if (StringUtils.checkValNull(idVal)) { return insertAllColumn(entity); } else { /* * 更新成功直接返回,失败执行插入逻辑 */ return updateAllColumnById(entity) || insertAllColumn(entity); } } else { throw new MybatisPlusException("Error: Can not execute. Could not find @TableId."); } } return false; } @Transactional public boolean insertOrUpdateBatch(List entityList) { return insertOrUpdateBatch(entityList, 30); } @Transactional public boolean insertOrUpdateBatch(List entityList, int batchSize) { return insertOrUpdateBatch(entityList, batchSize, true); } @Transactional public boolean insertOrUpdateAllColumnBatch(List entityList) { return insertOrUpdateBatch(entityList, 30, false); } @Transactional public boolean insertOrUpdateAllColumnBatch(List entityList, int batchSize) { return insertOrUpdateBatch(entityList, batchSize, false); } /** * 批量插入修改 * * @param entityList 实体对象列表 * @param batchSize 批量刷新个数 * @param selective 是否滤掉空字段 * @return boolean */ private boolean insertOrUpdateBatch(List entityList, int batchSize, boolean selective) { if (CollectionUtils.isEmpty(entityList)) { throw new IllegalArgumentException("Error: entityList must not be empty"); } try (SqlSession batchSqlSession = sqlSessionBatch()) { int size = entityList.size(); for (int i = 0; i < size; i++) { if (selective) { insertOrUpdate(entityList.get(i)); } else { insertOrUpdateAllColumn(entityList.get(i)); } if (i >= 1 && i % batchSize == 0) { batchSqlSession.flushStatements(); } } batchSqlSession.flushStatements(); } catch (Throwable e) { throw new MybatisPlusException("Error: Cannot execute insertOrUpdateBatch Method. Cause", e); } return true; } @Transactional public boolean deleteById(Serializable id) { return retBool(baseMapper.deleteById(id)); } @Transactional public boolean deleteByMap(Map columnMap) { if (MapUtils.isEmpty(columnMap)) { throw new MybatisPlusException("deleteByMap columnMap is empty."); } return retBool(baseMapper.deleteByMap(columnMap)); } @Transactional public boolean delete(Wrapper wrapper) { return retBool(baseMapper.delete(wrapper)); } @Transactional public boolean deleteBatchIds(List idList) { return retBool(baseMapper.deleteBatchIds(idList)); } @Transactional public boolean updateById(T entity) { return retBool(baseMapper.updateById(entity)); } @Transactional public boolean updateAllColumnById(T entity) { return retBool(baseMapper.updateAllColumnById(entity)); } @Transactional public boolean update(T entity, Wrapper wrapper) { return retBool(baseMapper.update(entity, wrapper)); } @Transactional public boolean updateBatchById(List entityList) { return updateBatchById(entityList, 30); } @Transactional public boolean updateBatchById(List entityList, int batchSize) { return updateBatchById(entityList, batchSize, true); } @Transactional public boolean updateAllColumnBatchById(List entityList) { return updateAllColumnBatchById(entityList, 30); } @Transactional public boolean updateAllColumnBatchById(List entityList, int batchSize) { return updateBatchById(entityList, batchSize, false); } /** * 根据主键ID进行批量修改 * * @param entityList 实体对象列表 * @param batchSize 批量刷新个数 * @param selective 是否滤掉空字段 * @return boolean */ private boolean updateBatchById(List entityList, int batchSize, boolean selective) { if (CollectionUtils.isEmpty(entityList)) { throw new IllegalArgumentException("Error: entityList must not be empty"); } try (SqlSession batchSqlSession = sqlSessionBatch()) { int size = entityList.size(); SqlMethod sqlMethod = selective ? SqlMethod.UPDATE_BY_ID : SqlMethod.UPDATE_ALL_COLUMN_BY_ID; String sqlStatement = sqlStatement(sqlMethod); for (int i = 0; i < size; i++) { MapperMethod.ParamMap param = new MapperMethod.ParamMap<>(); param.put("et", entityList.get(i)); batchSqlSession.update(sqlStatement, param); if (i >= 1 && i % batchSize == 0) { batchSqlSession.flushStatements(); } } batchSqlSession.flushStatements(); } catch (Throwable e) { throw new MybatisPlusException("Error: Cannot execute updateBatchById Method. Cause", e); } return true; } public T selectById(Serializable id) { return baseMapper.selectById(id); } public List selectBatchIds(List idList) { return baseMapper.selectBatchIds(idList); } public List selectByMap(Map columnMap) { return baseMapper.selectByMap(columnMap); } public T selectOne(Wrapper wrapper) { return SqlHelper.getObject(baseMapper.selectList(wrapper)); } public Map selectMap(Wrapper wrapper) { return SqlHelper.getObject(baseMapper.selectMaps(wrapper)); } public Object selectObj(Wrapper wrapper) { return SqlHelper.getObject(baseMapper.selectObjs(wrapper)); } public int selectCount(Wrapper wrapper) { return SqlHelper.retCount(baseMapper.selectCount(wrapper)); } public List selectList(Wrapper wrapper) { return baseMapper.selectList(wrapper); } @SuppressWarnings("unchecked") public Page selectPage(Page page) { return selectPage(page, Condition.EMPTY); } public List> selectMaps(Wrapper wrapper) { return baseMapper.selectMaps(wrapper); } public List selectObjs(Wrapper wrapper) { return baseMapper.selectObjs(wrapper); } @SuppressWarnings({"rawtypes", "unchecked"}) public Page> selectMapsPage(Page page, Wrapper wrapper) { SqlHelper.fillWrapper(page, wrapper); page.setRecords(baseMapper.selectMapsPage(page, wrapper)); return page; } public Page selectPage(Page page, Wrapper wrapper) { SqlHelper.fillWrapper(page, wrapper); page.setRecords(baseMapper.selectPage(page, wrapper)); return page; } } ​

 

mybatisplus提供了增删改查的接口和实现类,上图,我们只需要继承,并构造实体条件就行但是要特备注意下面几个方法

  (1)

@Transactional
public boolean updateById(T entity) {
    return retBool(baseMapper.updateById(entity));
}

传入的实体类必须数据表必须有ID列,否则更新会直接报错,所以这个方法开发中并不太常用,而是用

@Transactional
public boolean update(T entity, Wrapper wrapper) {
    return retBool(baseMapper.update(entity, wrapper));
}

(2)

@Transactional
public boolean deleteById(Serializable id) {
    return retBool(baseMapper.deleteById(id));
}
这个方法也是一样,必须有列ID才行。

(3)

/**
 * 批量插入
 *
 * @param entityList
 * @param batchSize
 * @return
 */
@Transactional
public boolean insertBatch(List entityList, int batchSize) {
    if (CollectionUtils.isEmpty(entityList)) {
        throw new IllegalArgumentException("Error: entityList must not be empty");
    }
    try (SqlSession batchSqlSession = sqlSessionBatch()) {
        int size = entityList.size();
        String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
        for (int i = 0; i < size; i++) {
            batchSqlSession.insert(sqlStatement, entityList.get(i));
            if (i >= 1 && i % batchSize == 0) {
                batchSqlSession.flushStatements();
            }
        }
        batchSqlSession.flushStatements();
    } catch (Throwable e) {
        throw new MybatisPlusException("Error: Cannot execute insertBatch Method. Cause", e);
    }
    return true;
}

这个方法的,大家可以的看到,当传入空集合或者null的时候,直接抛出非法参数异常,所以在批量插入之前一定要对集合排空判断。

(4)

public Page selectPage(Page page, Wrapper wrapper) {
    SqlHelper.fillWrapper(page, wrapper);
    page.setRecords(baseMapper.selectPage(page, wrapper));
    return page;
}

Mabits的分页查询是个好方法,很方便帮我们查询出总数量和当前页集合,前端查询页面展示很方便,但是要注意,必须创建page过滤器才有效

package com.yxytech.parkingcloud.efss.config;

import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setLocalPage(true);// 开启 PageHelper 的支持

        return paginationInterceptor;
    }
}

2.4 SQL语句异常

Mybatis开发中不需要你写SQL语句,但是当涉及到多表的关联查询时候,只能通过自己写SQL了,他的EntityWrapper(条件构造器)就不支持了,所以就会出现以下几种:

(1)在Service接口以Mybatis注解的形式编写:

public interface IBlackFileStatusVersionService extends IBaseService {
    @Select("TRUNCATE TABLE U03_StatusList_Version")
    void deleteAll();

    BlackFileStatusVersion constructBlackVersion(String key, String issuerVersion, int dif, int recordCount, String zipFilePath);

    boolean insert2DB(CardStatuVersion version, String fileName);
}

如上:清空表里数据,特别注意语句是够正确,最好在数据库测试执行成功后,再粘贴到代码里

(2)在xxxMapper.xml文件里编写复杂的SQL语句(推荐)

这种方法可能需要传参数:@Param

public interface XXXInfoStateMapper{
​​​​​​​ List selectListByPage(@Param("start") long start, @Param("end") long end);
}

这种查询也很容易错,最好还是先在数据库写好执行过了再粘上来。参数#{}的方式传入xml

2.5 SpringBoot的接口

​
@RestController
@RequestMapping("/front")
public class XXXFrontController extends BaseController {
}

​

之前项目就出现调试很久,HTTP请求接口就是收不到,后来发现@RestController的问题,@Controller是可以返回视图解析器可以解析的Html,jsp页面的,而@RestController相当于@Controller和@ResponseBody结合体,返回的结果以接口方法的return的值。一般项目都是以json的格式返回的,现在很少通过服务端配置进行页面跳转,对多移动开发自由度也很不好,所以建议大家还是使用@RestController注解。

最后,在开发中还会遇到各种问题,会尽量补充上来,谢谢。

 

 


你可能感兴趣的:(后台SpringBoot + Mybatis开发遇到的一些坑)