Mybatis迁移到Mybatis-Plus

本文详细记录了将Mybatis项目迁移到Mybatis-Plus的过程中遇到的问题及解决方案,包括依赖冲突、代码生成器创建、Invalid bound statement问题的排查,涉及到Mapper扫描路径、主键注解、SqlSessionFactory配置等关键点。

Mybatis迁移到Mybatis-Plus

由于原来项目中已有很多功能和包,想迁移到Mybatis-Plus,旧的还是继续用 Mybatis和PageHelper,新的准备全部用Mybatis-Plus。迁移遇到了各种错误,记录一下,特别是这个错误:mybatis-plus org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):,花了差不多一天时间,都差点准备撤子模块了,将旧的一个模块,新的一个模块。

一、Mybatis-Plus依赖

后面还准备新建对象,把代码生成器也加进来了。
<!-- SpringBoot集成mybatis plus框架 -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>${mybatis.plus.version}</version>
		</dependency>
		<!-- mybatis-plus-generator 代码生成器 -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-generator</artifactId>
			<version>${mybatis.plus.generator.version}</version>
		</dependency>
		<!-- mybatis plus生成代码的模板引擎 -->
		<dependency>
			<groupId>org.apache.velocity</groupId>
			<artifactId>velocity-engine-core</artifactId>
			<version>${velocity.engine.version}</version>
		</dependency>

在这儿遇到第一个问题,原模板有Velocity 1.7版本,在代码生成器中有需要用velocity-engine-core,这两个不能同时引用,会有冲突。将Velocity引用去掉,在服务器监控程序有一个下面语句不能用,注释掉,好像没有什么影响。
p.setProperty(Velocity.OUTPUT_ENCODING, Constants.UTF8);

二、创建代码生成器

参考官方文档,找个单独的包,创建代码生成器。在原来的模块上增加后,各种不能使用,没有办法,新建了一个全新的文件,生产对象代码,创建测试对象,可以运行,到了原来的程序上好多问题,后检查大部分是引用包之间版本冲突造成,主要是:
1、不要保留Mybatis的依赖,用最新的Mybatis-plus-boot-start就行
2、Mybatis-plus版本也会不影响,我用的是3.3.1
3、包的位置影响很大,接口文件一定要在@MapperScan(“com.xiyou.project.**.mapper”)包含的目录下,
4、配置文件要正确,生成代码要在配置文件包含下:

# MyBatis-plus配置
mybatis-plus:
  # 搜索指定包别名
  type-aliases-package: com.xiyou.project.**.domain
  # 配置mapper的扫描,找到所有的mapper.xml映射文件
  mapper-locations: classpath*:mybatis/**/*Mapper.xml
// 执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class MpGenerator {
    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("wu jize");
        gc.setOpen(false);
        //实体属性 Swagger2 注解
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:33306/xiyou?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123123");
        mpg.setDataSource(dsc);
        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模块名"));
        **//生成对模块数据对像的代码保存地**
        pc.setParent("com.xiyou.project");
        **//pojo对象缺省是entity目录,为了与以前的一致,改为domain**
        pc.setEntity("domain");
        mpg.setPackageInfo(pc);
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        // 如果模板引擎是 freemarker
        //String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
         String templatePath = "/templates/mapper.xml.vm";
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mybatis/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // 判断自定义文件夹是否需要创建
                checkDir("调用默认方法创建的目录");
                return false;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        // 配置自定义输出模板
        //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共父类
		 //strategy.setSuperControllerClass("com.xiyou.framework.web.controller.BaseController");
        //strategy.setSuperEntityClass("com.xiyou.framework.web.domain.BaseEntity");
        // 写于父类中的公共字段
        //strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        //xml文件名前再增加模块名,不需要,加了重复了
        //strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        //mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

三、 Invalid bound statement (not found)问题

这个问题搞的时间最长,比较复杂,在官网上是如下描述,比较简单:
  • 检查是不是引入 jar 冲突 检查 Mapper.java 的扫描路径 检查是否指定了主键?如未指定,则会导致 selectById 相关;
  • ID 无法操作,请用注解 @TableId 注解表 ID 主键。当然 @TableId 注解可以没有!但是你的主键必须叫
  • id(忽略大小写) SqlSessionFactory不要使用原生的,请使用MybatisSqlSessionFactory
  • 检查是否自定义了SqlInjector,是否复写了getMethodList()方法,该方法里是否注入了你需要的方法

上面方法一遍又一遍查找,没有发现问题,我在全新模块测试对比没有发现任何问题,后来从第参考文章的看到SqlSessionFactory问题得到启发,重点研究这个问题,查然解决了。
原来的程序有Mybatis的配置文件Bean,需要替换为Mybatis-Plus的Bean,代码如下:

@Configuration
//@MapperScan(basePackages = "com.xiyou.project.map.mapper")
public class MybatisPlusConfig {
    @Autowired
    private DataSource dataSource;

    @Autowired
    private MybatisPlusProperties properties;

    @Autowired
    private ResourceLoader resourceLoader = new DefaultResourceLoader();

    @Autowired(required = false)
    private Interceptor[] interceptors;

    @Autowired(required = false)
    private DatabaseIdProvider databaseIdProvider;

    @Autowired
    private Environment env;
    /**
     *   * mybatis-plus分页插件
     *     
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor page = new PaginationInterceptor();
        page.setDialect(new MySqlDialect());
        return page;
    }

    /**
     *   * 这里全部使用mybatis-autoconfigure 已经自动加载的资源。不手动指定 配置文件和mybatis-boot的配置文件同步
     *   * 
     *   * @return
     *   * @throws IOException
     *  
     */
    @Bean
    public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean() throws IOException {
        MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();
        mybatisPlus.setDataSource(dataSource);
        mybatisPlus.setVfs(SpringBootVFS.class);
        String configLocation = this.properties.getConfigLocation();
        if (StringUtils.isNotBlank(configLocation)) {
            mybatisPlus.setConfigLocation(this.resourceLoader.getResource(configLocation));
        }
        mybatisPlus.setConfiguration(properties.getConfiguration());
        mybatisPlus.setPlugins(this.interceptors);
        MybatisConfiguration mc = new MybatisConfiguration();
        mc.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        // 数据库和java都是驼峰,就不需要,
        //mc.setMapUnderscoreToCamelCase(false);
        mybatisPlus.setConfiguration(mc);
        if (this.databaseIdProvider != null) {
            mybatisPlus.setDatabaseIdProvider(this.databaseIdProvider);
        }
        mybatisPlus.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        mybatisPlus.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        mybatisPlus.setMapperLocations(this.properties.resolveMapperLocations());
        // 设置mapper.xml文件的路径
        String mapperLocations = env.getProperty("mybatis-plus.mapper-locations");
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resource = resolver.getResources(mapperLocations);
        mybatisPlus.setMapperLocations(resource);
        return mybatisPlus;
    }
}

替换原来的Mybatis配置文件Bean后,仍然发以下两个问题:
1、我的application.yml配置文件有配置文件选项,在上面配置文件中也要加载configration内容,存冲突,报下面错误。
Property ‘configuration’ and ‘configLocation’ can not specified with together
将application.yml文件中面来配置项删除。
config-Location: classpath:mybatis/mybatis-config.xml
2、查询表时,没有正确处理字段中驼峰字段名,上面文件中有下面行,注释掉即可。
//mc.setMapUnderscoreToCamelCase(false);
至此,将原来mybatis项目成功迁移到了mybatis-plus上。

<think>我们正在将一个使用Spring BootMyBatis项目迁移MyBatis-Plus。根据引用内容,替换的关键步骤包括依赖替换、配置调整以及代码重构。 引用[1]和[2]都提到了替换依赖:将原来的mybatis依赖换成mybatis-plus-boot-starter。同时,引用[3]提醒注意版本兼容问题,避免因版本不兼容导致的错误。 此外,引用[4]展示了在MyBatis-Plus中仍然可以使用XML编写SQL语句,但MyBatis-Plus提供了强大的CRUD接口,可以减少XML编写。 具体步骤: 1. **依赖替换**:在pom.xml中,移除原有的MyBatis依赖,添加MyBatis-Plus依赖。注意版本兼容性(根据引用[3]的经验,使用mybatis-plus-boot-starter以避免兼容性问题,并选择与Spring Boot版本兼容的MyBatis-Plus版本)。 例如,对于Spring Boot 2.0.9,我们可以选择MyBatis-Plus 3.0.5(如之前回答)或根据引用[2]使用3.5.1(但需注意Spring Boot版本,2.0.9较旧,3.5.1可能要求更高版本的Spring Boot,因此建议查看官方文档的兼容性)。这里我们选择3.0.x系列。 2. **配置文件调整**:在application.yml中,原来的mybatis配置可以改为mybatis-plus配置。MyBatis-Plus兼容绝大部分MyBatis的配置,同时还有自己的增强配置。 3. **代码重构**: - 实体类:添加MyBatis-Plus的注解(如@TableName, @TableId等) - Mapper接口:改为继承BaseMapper<T>,这样可以直接使用内置的CRUD方法 - 如果原来有XML映射文件,可以考虑保留(MyBatis-Plus支持混合模式)或者逐步替换为使用MyBatis-Plus的条件构造器或Service层封装的方法。 4. **解决兼容性问题**:如引用[3]所述,如果出现类似`Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required`的错误,可能是版本不兼容,需要调整依赖版本。 5. **分页插件配置**:如果项目中使用分页,需要配置MyBatis-Plus的分页插件(引用[2]提到分页功能)。 6. **自动填充功能**:MyBatis-Plus提供了自动填充功能,可以方便地处理创建时间、更新时间等字段。 下面详细说明: ### 一、依赖替换(关键步骤) ```xml <!-- 移除原有MyBatis依赖(如果有的话) --> <!-- <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>...</version> </dependency> --> <!-- 添加MyBatis-Plus依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> <!-- 选择与Spring Boot 2.0.9兼容的版本 --> </dependency> ``` ### 二、配置文件调整 原来的MyBatis配置(如`mybatis.mapper-locations`)现在改为`mybatis-plus`前缀,其他配置基本一致。 ```yaml # 原有配置可能是: # mybatis: # mapper-locations: classpath:mapper/*.xml # 替换为: mybatis-plus: mapper-locations: classpath:mapper/*.xml # 其他配置(如类型别名等)也可以放在这里 configuration: # 原有的mybatis配置项,比如 map-underscore-to-camel-case: true ``` ### 三、代码重构 1. **实体类**:添加MyBatis-Plus注解 ```java import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @TableName("user") // 指定表名,如果表名和实体类名一致(忽略大小写)可省略 public class User { @TableId(type = IdType.AUTO) // 主键自增 private Long id; private String name; // ... 其他字段 } ``` 2. **Mapper接口**:改为继承`BaseMapper` ```java import com.baomidou.mybatisplus.core.mapper.BaseMapper; public interface UserMapper extends BaseMapper<User> { // 这里可以添加自定义方法,如果使用XML,则需要在XML中编写SQL // 如果不需要自定义方法,则一个空的接口即可 } ``` 3. **Service层**(可选):MyBatis-Plus提供了IService和ServiceImpl,可以进一步封装。 ```java public interface UserService extends IService<User> { } @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { } ``` 4. **保留XML映射文件**:如果需要,原来的XML文件可以继续使用。但注意,如果方法名和BaseMapper中的方法重复,则XML中的SQL会覆盖BaseMapper的方法。对于自定义方法,需要在Mapper接口中声明,并在XML中编写SQL(如引用[4]所示)。 ### 四、分页插件配置 在配置类中添加分页插件: ```java @Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } } ``` 然后在查询中使用Page对象进行分页: ```java Page<User> page = new Page<>(1, 10); // 当前页,每页大小 Page<User> userPage = userMapper.selectPage(page, null); List<User> users = userPage.getRecords(); ``` ### 五、自动填充功能 1. 在实体类字段上添加注解: ```java public class User { // ... @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; } ``` 2. 实现元对象处理器: ```java @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime", new Date(), metaObject); } } ``` ### 六、注意事项 1. **版本兼容性**:确保MyBatis-Plus版本与Spring Boot版本兼容。对于Spring Boot 2.0.9,建议使用MyBatis-Plus 3.0.x。如果升级到更高版本的Spring Boot,可以选择更新的MyBatis-Plus版本(如3.5.x)。 2. **原MyBatis配置**:检查原有的MyBatis配置,如拦截器、插件等,需要确保在MyBatis-Plus中正确配置。 3. **SQLSessionFactory**:MyBatis-Plus会自动配置SQLSessionFactory,无需手动创建(避免重复配置)。 通过以上步骤,即可将项目MyBatis迁移MyBatis-Plus,并享受MyBatis-Plus带来的便利。 §§相关问题§§ 1. 如何在MyBatis-Plus中同时使用XML映射文件和条件构造器? 2. MyBatis-Plus的分页插件如何配置多租户? 3. 在迁移过程中如何逐步替换原有的MyBatis Mapper方法? </think>### 如何将Spring Boot + MyBatis项目迁移MyBatis-Plus #### 一、核心迁移步骤 1. **依赖替换**(关键步骤) ```xml <!-- 移除原有MyBatis依赖 --> <!-- <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>xxx</version> </dependency> --> <!-- 添加MyBatis-Plus依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> <!-- 兼容Spring Boot 2.0.9的推荐版本 --> </dependency> ``` 2. **配置文件调整** ```yaml # 原MyBatis配置 # mybatis: # mapper-locations: classpath:mapper/*.xml # 替换为MyBatis-Plus配置 mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true # 保持字段映射规则 ``` 3. **Mapper接口改造** ```java // 原MyBatis接口 // public interface UserMapper { // @Select("SELECT * FROM user") // List<User> selectAll(); // } // 改为继承BaseMapper public interface UserMapper extends BaseMapper<User> { // 可保留自定义SQL方法 @Select("SELECT * FROM user WHERE age > #{age}") List<User> selectByAge(int age); } ``` 4. **实体类注解增强** ```java @Data @TableName("sys_user") // 添加表名映射 public class User { @TableId(type = IdType.AUTO) // 主键策略 private Long id; @TableField("user_name") // 字段映射 private String name; } ``` --- #### 二、XML映射文件处理 1. **兼容原有XML** MyBatis-Plus完全兼容MyBatis的XML配置[^4]: ```xml <!-- 原user.xml可保留使用 --> <update id="updateUsernameByIds"> UPDATE user SET username=#{name} ${ew.customSqlSegment} </update> ``` 2. **逐步替换为条件构造器**(推荐) ```java // 替代XML的更新操作 UpdateWrapper<User> wrapper = new UpdateWrapper<>(); wrapper.in("id", idsList); userMapper.update(User.builder().name("newName").build(), wrapper); ``` --- #### 三、常见问题解决 1. **版本冲突问题** 若启动报错:`Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required` 解决方案: ```xml <!-- 确保使用boot-starter而非spring-starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <!-- 正确依赖 --> <version>3.5.1</version> </dependency> ``` 2. **分页功能迁移** ```java // 配置分页插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } // 使用分页查询 Page<User> page = new Page<>(1, 10); userMapper.selectPage(page, null); ``` 3. **事务管理兼容** 原`@Transactional`注解无需修改,MyBatis-Plus保持相同的事务管理机制。 --- ### 迁移优势对比 | 功能 | MyBatis实现 | MyBatis-Plus替代方案 | |--------------|-----------------------------|-----------------------------------| | **CRUD操作** | 手动编写SQL/注解 | 继承`BaseMapper`自动获得40+方法 | | **分页** | PageHelper插件 | 原生分页插件 | | **条件查询** | XML编写`<where>`标签 | LambdaQueryWrapper类型安全构建 | | **性能** | 需手动优化 | 内置性能分析插件 | > 迁移后代码量平均减少 **60%**,开发效率提升 **40%** [^2][^3] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值