SpringBoot 正确集成Mybatis

抽象思想

一般我们集成mybatis是不是要 引入mybatis的一些包,然后配置pojo的包路径,mapper接口的路径等, 非常之头晕。

我们今天把上面这些操作都封装到一个starter里面,后续我们新加springboot服务只需引入制作好的starter就可以完成数据库功能了。

对mybatis方便操作数据库有两个框架: 通用mapper(tk) + mybatisplus插件 。 今天我们两个都会实践,大家动手跟我敲就行了。


一. 通用 mapper(tk) 方式的starter制作

先看下我们封装好的 mybatis-spring-boot-starter ,怎么用, 3个步骤:

  • 引入starter包:
<dependency>
		<groupId>com.hadluo</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>0.0.1-SNAPSHOT</version>
</dependency>

2. 启动类加MybatisConf注解:

@SpringBootApplication
@MybatisConf(pojo = "com.hadluo.order.pojo", mapper = "com.hadluo.order.mapper")
public class OrderApplication {
	public static void main(String[] args) {
		SpringApplication.run(OrderApplication.class, args);
	}
}

3. application配置文件指定数据库地址:

spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/hadluo-blog?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=xxxx

就这三个步骤就可集成了mybatis+ tk 了,非常方便!


mybatis-spring-boot-starter 的制作

新建 mybatis-spring-boot-starter项目 , maven文件:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.hadluo</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<!-- 换成自己公司的parent -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.10</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!-- tk mybatis -->
		<dependency>
			<groupId>tk.mybatis</groupId>
			<artifactId>mapper-spring-boot-starter</artifactId>
			<version>2.1.5</version>
		</dependency>
	</dependencies>
</project>

新建MybatisConf 注解,用于指定pojo+mapper接口 路径:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(AutoConfiguredMapperScannerRegistrar.class)
public @interface MybatisConf {
	/***
	 * pojo 的扫描路径
	 * 
	 * @return
	 */
	public String pojo() default "";

	/***
	 * mapper接口的扫描路径
	 * 
	 * @return
	 */
	public String mapper() default "";
}

注意上面有个Import注解,这个注解是springboot提供给我们注入bean的一个方式。

我们新建AutoConfiguredMapperScannerRegistrar类:

public class AutoConfiguredMapperScannerRegistrar
		implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

	private BeanFactory beanFactory;
	private ResourceLoader resourceLoader;
	private Environment environment;
	// poj路径
	public static String pojoPack = null;
	public static String mapperPack = null;

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
		scanner.setMapperProperties(environment);
		try {
			if (this.resourceLoader != null) {
				scanner.setResourceLoader(this.resourceLoader);
			}
			// 获取 扫描的 base路径
			List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
			// 获取 MybatisConf 注解
			Map<String, Object> mapper = importingClassMetadata.getAnnotationAttributes(MybatisConf.class.getName());
			if (!mapper.containsKey("pojo") || !StringUtils.hasLength(mapper.get("pojo").toString())) {
				throw new RuntimeException("MybatisConf 注解的pojo不允许为空!");
			}
			if (!mapper.containsKey("mapper") || !StringUtils.hasLength(mapper.get("mapper").toString())) {
				throw new RuntimeException("MybatisConf 注解的pojo不允许为空!");
			}
			// 赋值 pojo
			AutoConfiguredMapperScannerRegistrar.pojoPack = mapper.get("pojo").toString();
			AutoConfiguredMapperScannerRegistrar.mapperPack = mapper.get("mapper").toString();
			// mapper接口
			packages.add(mapper.get("mapper").toString());
			scanner.registerFilters();
			scanner.doScan(StringUtils.toStringArray(packages));
		} catch (IllegalStateException ex) {
		}
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = beanFactory;
	}

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

        private static String MYBATIS_CONFIG = "/mybatis-config.xml";
	/** mybatis mapper resource */
	private static String MAPPER_PATH = "/mapper/**.xml";
	@Autowired
	private DataSource dataSource;

	@Bean
	// 不存在 SqlSessionFactoryBean 就注册
	@ConditionalOnMissingBean(SqlSessionFactoryBean.class)
	public SqlSessionFactoryBean createSqlSessionFactoryBean() throws Exception {
		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
		sqlSessionFactoryBean.setConfigLocation(new ClassPathResource(MYBATIS_CONFIG));
		PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + MAPPER_PATH;
		// mapper xml 路径
		sqlSessionFactoryBean.setMapperLocations(pathMatchingResourcePatternResolver.getResources(packageSearchPath));
		// 数据源
		sqlSessionFactoryBean.setDataSource(dataSource);
		sqlSessionFactoryBean.setTypeAliasesPackage(AutoConfiguredMapperScannerRegistrar.pojoPack);
		System.err.println("【Mybatis】>> mapper xml:classpath:mapper/**.xml");
		System.err.println("【Mybatis】>> mapper scan:" + AutoConfiguredMapperScannerRegistrar.mapperPack);
		System.err.println("【Mybatis】>> pojo scan:" + AutoConfiguredMapperScannerRegistrar.pojoPack);
		return sqlSessionFactoryBean;
	}
}

上面代码有两块逻辑:

第一: 获取 MybatisConf 注解的 mapper接口路径, 然后加入到tk.mybatis.spring.mapper.ClassPathMapperScanner 类的扫描中去, ClassPathMapperScanner这个类 会动态生成mapper接口的代理,这个属于mybatis内部逻辑了。

第二: 注册一个SqlSessionFactoryBean 的bean,这样我们就可以成功用mybatis了。


然后引入我们的mybatis-config.xml 公共配置(放到resources目录下即可):

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<!-- 这个配置使全局的映射器启用或禁用缓存 -->
		<setting name="cacheEnabled" value="false" />
		<!-- 全局启用或禁用延迟加载当禁用时所有关联对象都会即时加载 -->
		<setting name="lazyLoadingEnabled" value="true" />
		<!-- 当启用时有延迟加载属性的对象在被调用时将会完全加载任意属性否则每种属性将会按需要加载 -->
		<setting name="aggressiveLazyLoading" value="true" />
		<!-- 允许或不允许多种结果集从一个单独的语句中返回需要适合的驱动 -->
		<setting name="multipleResultSetsEnabled" value="true" />
		<!-- 使用列标签代替列名不同的驱动在这方便表现不同参考驱动文档或充分测试两种方法来决定所使用的驱动 -->
		<setting name="useColumnLabel" value="true" />
		<!-- 允许JDBC支持生成的键需要适合的驱动如果设置为true则这个设置强制生成的键被使用尽管一些驱动拒绝兼容但仍然有效比如Derby -->
		<setting name="useGeneratedKeys" value="true" />
		<!-- 指定MyBatis如何自动映射列到字段/属性PARTIAL只会自动映射简单没有嵌套的结果FULL会自动映射任意复杂的结果嵌套的或其他情况 -->
		<setting name="autoMappingBehavior" value="PARTIAL" />
		<!--当检测出未知列或未知属性如何处理默认情况下没有任何提示这在测试的时候很不方便不容易找到错误 NONE : 不做任何处理 
			(默认值) WARNING : 警告日志形式的详细信息 FAILING : 映射失败抛出异常和详细信息 -->
		<setting name="autoMappingUnknownColumnBehavior"
			value="WARNING" />
		<!-- 配置默认的执行器SIMPLE执行器没有什么特别之处REUSE执行器重用预处理语句BATCH执行器重用语句和批量更新 -->
		<setting name="defaultExecutorType" value="SIMPLE" />
		<!-- 设置超时时间它决定驱动等待一个数据库响应的时间 -->
		<setting name="defaultStatementTimeout" value="50000" />
		<!--设置查询返回值数量可以被查询数值覆盖 -->
		<setting name="defaultFetchSize" value="100" />
		<!-- 允许在嵌套语句中使用分页 -->
		<setting name="safeRowBoundsEnabled" value="false" />
		<!--是否开启自动驼峰命名规则映射即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射 -->
		<setting name="mapUnderscoreToCamelCase" value="false" />
		<!--MyBatis利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询 默认值为 
			SESSION这种情况下会缓存一个会话中执行的所有查询 若设置值为 STATEMENT本地会话仅用在语句执行上对相同 SqlSession 
			的不同调用将不会共享数据 -->
		<setting name="localCacheScope" value="SESSION" />
		<!-- 当没有为参数提供特定的 JDBC 类型时为空值指定 JDBC 类型 某些驱动需要指定列的 JDBC 类型多数情况直接用一般类型即可比如 
			NULLVARCHAROTHER -->
		<setting name="jdbcTypeForNull" value="OTHER" />
		<!-- 指定哪个对象的方法触发一次延迟加载 -->
		<setting name="lazyLoadTriggerMethods"
			value="equals,clone,hashCode,toString" />
		<!--设置启用数据库字段下划线映射到java对象的驼峰式命名属性默认为false -->
		<setting name="mapUnderscoreToCamelCase" value="true" />
	</settings>
	<typeAliases>
		<typeAlias alias="Integer" type="java.lang.Integer" />
		<typeAlias alias="Long" type="java.lang.Long" />
		<typeAlias alias="HashMap" type="java.util.HashMap" />
		<typeAlias alias="LinkedHashMap"
			type="java.util.LinkedHashMap" />
		<typeAlias alias="ArrayList" type="java.util.ArrayList" />
		<typeAlias alias="LinkedList" type="java.util.LinkedList" />
	</typeAliases>
</configuration>

到此我们的starter已经制作完成。 注意: 我们并不是通过spring.factorys的方式,而是通过 @Import注解的方式。

二. mybatisplus插件 方式的starter制作

新建 mybatis-spring-boot-starter项目 , maven文件:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.hadluo</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<!-- 换成自己公司的parent -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.10</version>
	</parent>

	<properties>
		<java.version>8</java.version>
	</properties>
	<dependencies>
		 <!--mybatis-plus的springboot支持 -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.1.1</version>
		</dependency>
		<!--mysql驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
	</dependencies>
</project>

META-INF/spring.factories 自动配置类:

@Configuration
public class MybatisAutoConfiguration {
	/**
	 * 分页插件
	 */
	@Bean
	public PaginationInterceptor paginationInterceptor() {
		return new PaginationInterceptor();
	}
}

这里就注入了个分页插件。

接下来客户端引入我们的包后指定MapperScan注解就可以用了,所以mybatisplus还是很简单的。

@SpringBootApplication
@MapperScan("com.hadluo.order.mapper")
public class OrderApplication {
	public static void main(String[] args) {
		SpringApplication.run(OrderApplication.class, args);
	}
}

其他的一些 配置可以在application配置中改变:

mybatis-plus:
    #要扫描的xml路径
    mapper-locations:
      classpath:mapping/*.xml
    #要扫描的包路径
    type-aliases-package: com.example.demo
    configuration:
      #开启驼峰命名
      map-underscore-to-camel-case: true
      #关缓存
      cache-enabled: false
      #打印sql信息到控制台开发时用
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

推荐一个Java架构师博客,带你一起写架构:

JAVA架构师修炼

支付宝打赏 微信打赏

如果文章对您有帮助,您可以鼓励一下作者