当前位置: 首页 > article >正文

从0开始搭建一个生产级SpringBoot2.0.X项目(二)SpringBoot应用连接数据库集成mybatis-plus

前言

最近有个想法想整理一个内容比较完整springboot项目初始化Demo。

连接Oracle数据库集成mybatis-plus,自定义WrapperFactory。配置代码生成器

一、引入jar包

 <!--oracle驱动 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.oracle.ojdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>19.3.0.0</version>
        </dependency>
        <!-- oracle8 所需的字符集 -->
        <dependency>
            <groupId>cn.easyproject</groupId>
            <artifactId>orai18n</artifactId>
            <version>12.1.0.2.0</version>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>
        <!--mybatis-plus-->

二、新建配置文件application-dev.yaml

以根据不同的环境配置不同的属性文件,application-{profile} 表示环境名称。
例如,application-dev.yaml用于开发环境,application-prod.yaml用于生产环境。

增加数据库配置 hikari连接池配置

spring:
  datasource:
    driver-class-name: oracle.jdbc.OracleDriver
    url: jdbc:oracle:thin:@//127.0.0.1:1521/orcl
    username: murg_demo
    password: 654321
    hikari:
      connection-test-query: SELECT 1 FROM DUAL
      minimum-idle: 10
      maximum-pool-size: 50
      idle-timeout: 120000
      max-lifetime: 180000
      connection-timeout: 18000
logging:
  level:
    org.springframework.security: debug



启动服务看服务是否正常启动

三、自定义MyBatis 构造对象加工厂。

用于规定查询返回值格式 主要用于_处理和字段大小写规定

package com.murg.bootdemo.interceptor;

import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import java.util.Map;

/**
 * 自定义ObjectWrapperFactory 结果处理 去除_  字段大写转小写
 */
public class MapWrapperFactory implements ObjectWrapperFactory {
	@Override
	public boolean hasWrapperFor(Object object) {
		return object instanceof Map;
	}

	@Override
	public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
		return new MyMapWrapper(metaObject,(Map)object);
	}
}
package com.murg.bootdemo.interceptor;

import org.apache.commons.lang3.math.NumberUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.MapWrapper;

import java.util.Map;

/**
 * mybatis 结果处理 去除_  字段大写转小写
 */
public class MyMapWrapper extends MapWrapper {

	public MyMapWrapper(MetaObject metaObject, Map<String, Object> map) {
		super(metaObject, map);
	}

	@Override
	public String findProperty(String name, boolean useCamelCaseMapping) {
		if (useCamelCaseMapping
				&& ((name.charAt(0) >= 'A' && name.charAt(0) <= 'Z')
				|| name.contains("_"))) {
			return underlineToCamelhump(name);
		}
		return name;
	}

	/**
	 * 将下划线风格替换为驼峰风格
	 * @param inputString
	 * @return
	 */
	public String underlineToCamelhump(String inputString) {
		StringBuilder sb = new StringBuilder();
		boolean nextUpperCase = false;
		for (int i = 0; i < inputString.length(); i++) {
			char c = inputString.charAt(i);
			if (c == '_') {
				if (sb.length() > 0) {
					nextUpperCase = true;
				}
			} else {
				if (nextUpperCase) {
					if (NumberUtils.isNumber(String.valueOf(c))) {
						sb.append("_").append(c);
					} else {
						sb.append(Character.toUpperCase(c));
					}
					nextUpperCase = false;
				} else {
					sb.append(Character.toLowerCase(c));
				}
			}
		}
		return sb.toString();
	}
}

四、自定义WrapperFactoryConverter

以上配置好WrapperFactory后启动会报错

提示找不到合适的converter将string转化为ObjectWrapperFactory对象

缺少对应的converter,springboot不会提供反射机制来构建一个对象的converter,需要手写一个

package com.murg.bootdemo.interceptor;

import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
@ConfigurationPropertiesBinding
public class ObjectWrapperFactoryConverter implements Converter<String, ObjectWrapperFactory> {
	@Override
	public ObjectWrapperFactory convert(String source) {
		try {
			return (ObjectWrapperFactory) Class.forName(source).newInstance();
		} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
			throw new RuntimeException(e);
		}
	}
}

五、整合MyBatisPlus代码生成器 mybatis-plus-generator配合freemaker引擎

5.1下面这些文件直接创建在resources/generator1/templates的目录下,把内容复制进去就行了。这些文件就是代码生成的格式模板。

controller.java.ftl

package ${package.Controller};


import org.springframework.web.bind.annotation.RequestMapping;

<#if restControllerStyle>
import org.springframework.web.bind.annotation.RestController;
<#else>
import org.springframework.stereotype.Controller;
</#if>
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>

/**
 * <p>
 * ${table.comment!} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
<#if kotlin>
class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
<#else>
<#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
<#else>
public class ${table.controllerName} {
</#if>

}
</#if>

entity.java.ftl

package ${package.Entity};

<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if swagger>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Getter;
import lombok.Setter;
    <#if chainModel>
import lombok.experimental.Accessors;
    </#if>
</#if>

/**
 * <p>
 * ${table.comment!}
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if entityLombokModel>
@Getter
@Setter
    <#if chainModel>
@Accessors(chain = true)
    </#if>
</#if>
<#if table.convert>
@TableName("${schemaName}${table.name}")
</#if>
<#if swagger>
@ApiModel(value = "${entity}对象", description = "${table.comment!}")
</#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#elseif entitySerialVersionUID>
public class ${entity} implements Serializable {
<#else>
public class ${entity} {
</#if>
<#if entitySerialVersionUID>

    private static final long serialVersionUID = 1L;
</#if>
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
    <#if field.keyFlag>
        <#assign keyPropertyName="${field.propertyName}"/>
    </#if>

    <#if field.comment!?length gt 0>
        <#if swagger>
    @ApiModelProperty("${field.comment}")
        <#else>
    /**
     * ${field.comment}
     */
        </#if>
    </#if>
    <#if field.keyFlag>
        <#-- 主键 -->
        <#if field.keyIdentityFlag>
    @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
        <#elseif idType??>
    @TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
        <#elseif field.convert>
    @TableId("${field.annotationColumnName}")
        </#if>
        <#-- 普通字段 -->
    <#elseif field.fill??>
    <#-- -----   存在字段填充设置   ----->
        <#if field.convert>
    @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
        <#else>
    @TableField(fill = FieldFill.${field.fill})
        </#if>
    <#elseif field.convert>
    @TableField("${field.annotationColumnName}")
    </#if>
    <#-- 乐观锁注解 -->
    <#if field.versionField>
    @Version
    </#if>
    <#-- 逻辑删除注解 -->
    <#if field.logicDeleteField>
    @TableLogic
    </#if>
    private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ---------->

<#if !entityLombokModel>
    <#list table.fields as field>
        <#if field.propertyType == "boolean">
            <#assign getprefix="is"/>
        <#else>
            <#assign getprefix="get"/>
        </#if>
    public ${field.propertyType} ${getprefix}${field.capitalName}() {
        return ${field.propertyName};
    }

    <#if chainModel>
    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    <#else>
    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    </#if>
        this.${field.propertyName} = ${field.propertyName};
        <#if chainModel>
        return this;
        </#if>
    }
    </#list>
</#if>

<#if entityColumnConstant>
    <#list table.fields as field>
    public static final String ${field.name?upper_case} = "${field.name}";

    </#list>
</#if>
<#if activeRecord>
    @Override
    public Serializable pkVal() {
    <#if keyPropertyName??>
        return this.${keyPropertyName};
    <#else>
        return null;
    </#if>
    }

</#if>
<#if !entityLombokModel>
    @Override
    public String toString() {
        return "${entity}{" +
    <#list table.fields as field>
        <#if field_index==0>
            "${field.propertyName}=" + ${field.propertyName} +
        <#else>
            ", ${field.propertyName}=" + ${field.propertyName} +
        </#if>
    </#list>
        "}";
    }
</#if>
}

entity.kt.ftl

package ${package.Entity}

<#list table.importPackages as pkg>
import ${pkg}
</#list>
<#if swagger>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>

/**
 * <p>
 * ${table.comment}
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if table.convert>
@TableName("${schemaName}${table.name}")
</#if>
<#if swagger>
@ApiModel(value = "${entity}对象", description = "${table.comment!}")
</#if>
<#if superEntityClass??>
class ${entity} : ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
class ${entity} : Model<${entity}>() {
<#elseif entitySerialVersionUID>
class ${entity} : Serializable {
<#else>
class ${entity} {
</#if>

<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
<#if field.keyFlag>
    <#assign keyPropertyName="${field.propertyName}"/>
</#if>
<#if field.comment!?length gt 0>
<#if swagger>
        @ApiModelProperty("${field.comment}")
<#else>
    /**
     * ${field.comment}
     */
</#if>
</#if>
<#if field.keyFlag>
<#-- 主键 -->
<#if field.keyIdentityFlag>
    @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
<#elseif idType ??>
    @TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
<#elseif field.convert>
    @TableId("${field.annotationColumnName}")
</#if>
<#-- 普通字段 -->
<#elseif field.fill??>
<#-- -----   存在字段填充设置   ----->
<#if field.convert>
    @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
<#else>
    @TableField(fill = FieldFill.${field.fill})
</#if>
<#elseif field.convert>
    @TableField("${field.annotationColumnName}")
</#if>
<#-- 乐观锁注解 -->
<#if field.versionField>
    @Version
</#if>
<#-- 逻辑删除注解 -->
<#if field.logicDeleteField>
    @TableLogic
</#if>
    <#if field.propertyType == "Integer">
    var ${field.propertyName}: Int? = null
    <#else>
    var ${field.propertyName}: ${field.propertyType}? = null
    </#if>

</#list>
<#-- ----------  END 字段循环遍历  ---------->
<#if entityColumnConstant>
    companion object {
<#list table.fields as field>

        const val ${field.name?upper_case} : String = "${field.name}"

</#list>
    }

</#if>
<#if activeRecord>
    override fun pkVal(): Serializable? {
<#if keyPropertyName??>
        return ${keyPropertyName}
<#else>
        return null
</#if>
    }

</#if>
    override fun toString(): String {
        return "${entity}{" +
<#list table.fields as field>
<#if field_index==0>
        "${field.propertyName}=" + ${field.propertyName} +
<#else>
        ", ${field.propertyName}=" + ${field.propertyName} +
</#if>
</#list>
        "}"
    }
}

mapper.java.ftl

package ${package.Mapper};

import ${package.Entity}.${entity};
import ${superMapperClassPackage};
<#if mapperAnnotation>
import org.apache.ibatis.annotations.Mapper;
</#if>

/**
 * <p>
 * ${table.comment!} Mapper 接口
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if mapperAnnotation>
@Mapper
</#if>
<#if kotlin>
interface ${table.mapperName} : ${superMapperClass}<${entity}>
<#else>
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {

}
</#if>

mapper.xml.ftl

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${package.Mapper}.${table.mapperName}">

<#if enableCache>
    <!-- 开启二级缓存 -->
    <cache type="${cacheClassName}"/>

</#if>
<#if baseResultMap>
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="${package.Entity}.${entity}">
<#list table.fields as field>
<#if field.keyFlag><#--生成主键排在第一位-->
        <id column="${field.name}" property="${field.propertyName}" />
</#if>
</#list>
<#list table.commonFields as field><#--生成公共字段 -->
        <result column="${field.name}" property="${field.propertyName}" />
</#list>
<#list table.fields as field>
<#if !field.keyFlag><#--生成普通字段 -->
        <result column="${field.name}" property="${field.propertyName}" />
</#if>
</#list>
    </resultMap>

</#if>
<#if baseColumnList>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
<#list table.commonFields as field>
        ${field.columnName},
</#list>
        ${table.fieldNames}
    </sql>

</#if>
</mapper>

service.java.ftl

package ${package.Service};

import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;

/**
* <p>
    * ${table.comment!} 服务实现类
    * </p>
*
* @author ${author}
* @since ${date}
*/
@Service
public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> {

}

 5.2创建生成代码类CodeGenerator 

package com.murg.bootdemo.util;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.TemplateType;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Generator生成代码
 **/
public class CodeGenerator {

	public static void main(String[] args) {
		generator();
	}

	public static void generator() {
		final String[] moduleName = new String[1];
		FastAutoGenerator.create(new DataSourceConfig.Builder("jdbc:oracle:thin:@//127.0.0.1:1521/orcl","murg_test","654321"))
				// 全局配置
				.globalConfig((scanner, builder) -> builder.author(StringUtils.defaultString(getGitUsername(), "codeGenerator"))
						.fileOverride().outputDir(System.getProperty("user.dir") +"/src/main/java").disableOpenDir()
						.enableSwagger().dateType(DateType.ONLY_DATE))
				// 包配置
				.packageConfig((scanner, builder) -> builder.parent("com.murg.bootdemo." + scanner.apply("请输入包名(从"
								+ "com.murg.bootdemo."
								+ "开始 )?")).xml("mapper")
						.serviceImpl("service"))
				.templateConfig((builder -> builder.disable(TemplateType.SERVICE, TemplateType.CONTROLLER).serviceImpl("/templates/service.java")
						))
				// 策略配置
				.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")
								.toUpperCase()))
						.controllerBuilder().enableRestStyle().enableHyphenStyle()
						.serviceBuilder().convertServiceImplFileName((entityName -> entityName + ConstVal.SERVICE))
						.entityBuilder().enableLombok().idType(IdType.INPUT).build()).templateEngine(new FreemarkerTemplateEngine())
						//.addIgnoreColumns("prseno") 排除某个字段
				.execute();
	}

	private static String getGitUsername() {
		Runtime runtime = Runtime.getRuntime();
		Process exec;
		try {
			exec = runtime.exec("git config user.name");
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		InputStreamReader inputStreamReader = new InputStreamReader(exec.getInputStream());
		BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
		try {
			return bufferedReader.readLine();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	// 处理 all 情况
	protected static List<String> getTables(String tables) {
		return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
	}
}

5.3运行CodeGenerator 生成代码

点击左侧按钮 运行main方法控制台输出内容

按照控制台输出内容输入包名和表名,多个表名用英文逗号分隔,会在输入的包下生成文件

六、测试服务数据库连接 获取TT26相关数据

6.1启动类增加注解MapperScan 指定mapper扫描位置
@MapperScan("com.murg.bootdemo.**.mapper")

package com.murg.bootdemo;

import org.mybatis.spring.annotation.MapperScan;
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
        import org.springframework.cache.annotation.EnableCaching;

@MapperScan("com.murg.bootdemo.**.mapper")
@SpringBootApplication
public class BootdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(BootdemoApplication.class, args);
    }

}


6.2创建测试接口controller

引入Tt26Service 调用getOne方法查询数据库获取Tt26数据

getOne方法为MyBatis-Plus 通用IService使用不了解的同学可以了解一下MyBatis-Plus用法。

package com.murg.bootdemo.business.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.murg.bootdemo.business.entity.Tt26;
import com.murg.bootdemo.business.service.Tt26Service;
import com.murg.bootdemo.common.WebResult;
import lombok.RequiredArgsConstructor;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;

@RequiredArgsConstructor
@RestController
public class TestRestController {
    private final RestTemplate restTemplate;
    private final Tt26Service tt26Service;


    @RequestMapping(value = "/getTt26", method = RequestMethod.GET)
    public Tt26 getTt26(@RequestParam String code){
        return tt26Service.getOne(Wrappers.<Tt26>lambdaQuery().eq(Tt26::getCode,code))
    }

}

启动服务访问接口测试结果


http://www.kler.cn/a/372234.html

相关文章:

  • SQL语言基础
  • ffmpeg视频滤镜:网格-drawgrid
  • 省域经济高质量发展水平测算及数据2000-2021年
  • C++ —— 常见的初始化
  • helm push http: server gave HTTP response to HTTPS client
  • Android的SQLiteOpenHelper类 笔记241027
  • ElasticSearch - Bucket Script 使用指南
  • 三菱FX5U PLC使用SD存储卡固件更新的方法
  • python实现excel数据导入数据库
  • 频率限制:WAF保护网站免受恶意攻击的关键功能
  • Yolov5网络架构分析以及训练图解
  • Kafka如何控制消费的位置?
  • 数据驱动业务中的BDS对账班牛返款表集成方案
  • 前端开发设计模式——观察者模式
  • 论文题目:深度学习在自然语言处理中的应用研究
  • 开源FluentFTP实操,操控FTP文件
  • SpringBoot最佳实践之 - 使用AOP记录操作日志
  • 电子电气架构 --- 汽车以太网概述
  • 论文翻译:ICLR 2024.DETECTING PRETRAINING DATA FROM LARGE LANGUAGE MODELS
  • 免费PDF页面提取小工具
  • 《自动驾驶技术的深度思考:安全与伦理的挑战》
  • ubuntu22安装搜狗输入法不能输入中文
  • Java 代码优化 修饰器模式(Decorator Pattern)
  • 搭建 mongodb 副本集,很详细
  • 【MySQL】C语言连接MySQL数据库3——事务操作和错误处理API
  • 电子电气架构 --- 电气系统工程