首页 > 技术文章 > spring boot项目整合mybatis访问数据源

luo630 2021-11-14 21:01 原文

JAVA 8

Spring Boot 2.5.3

PostgreSQL 10 (on Windows)

org.mybatis:mybatis:3.5.7

---

 

目录

前言

试验1:连接单个数据库(源)

关于数据库连接池

试验2:连接两个数据库(源)——lib1、lib2

试验3:动态数据库——2个数据库(源)

参考文档

 

前言

mybatis百度百科

整合mybatis,可以方便地对数据库进行操作——关系型数据库

 

mybatis最新版本:3.5.7(Apr, 2021)

# Maven
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>

# Gradle
// https://mvnrepository.com/artifact/org.mybatis/mybatis
implementation group: 'org.mybatis', name: 'mybatis', version: '3.5.7'

上面的jar包,再加上对于的数据库驱动就可以使用了(尚未实践)。

 

s.b. 中,也提供了官方依赖包:

对应的依赖包如下:最新版 2.2.0 (May, 2021)

<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

// https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter
implementation group: 'org.mybatis.spring.boot', name: 'mybatis-spring-boot-starter', version: '2.2.0'

其下依赖包:

不过,本文没有使用 这个依赖包(没用过),于是,找到 参考文档1 ,使用其中的 com.baomidou:mybatis-plus-boot-starter 来进行试验。

最新版 3.4.3.4(Sep, 2021)。

<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3.4</version>
</dependency>

// https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter
implementation group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.4.3.4'

其下依赖包:

相比于官方的 mybatis-spring-boot-starter,多了 mybatis-plus包。

 

MyBatis-Plus官网:https://baomidou.com/

baomidou:苞米豆

 

添加数据库PostgreSQL的驱动,即可开始。来自博客园

s.b.官方也提供了PostgreSQL驱动:

<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>runtime</scope>
</dependency>

最新的是 42.3.1(Oct, 2021),而和 本文的 s.b. 2.5.3对应的是 42.2.23(Jul, 2021)。

 

注:

mybatis的包好多啊,除了上面的,还有 分页插件、代码生成器、通用Mapper 等,还没体验过呢!

本文不涉及这些功能,或许另文进行。

对了,还要和 Druid连接池 配合使用。

 

那么,开始吧!

 

依赖包汇总:

项目3大依赖包
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-boot-starter</artifactId>
	<version>3.4.3.4</version>
</dependency>
<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>runtime</scope>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>

 

两个数据表:guser、device

注,本来要建立 user表的,但是,user 在PostgreSQL中是关键字,无法使用。来自博客园

 

试验1:连接单个数据库(源)

guser的相关内容在 user包下,device的相关内容在 device包下。

User:

User和UserMapper
@Data
@TableName("guser") // import com.baomidou.mybatisplus.annotation.TableName; 指定表名为 guser
public class User {
	
    private Long id;
    private String name;
    private Integer age;
    private String email;
	
}

// import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {

}

 

Device:

Device和DeviceMapper
@Data
public class Device {

    private Long id;
    private Long sn;
    private String name;
    private Date createTime;
    
}

public interface DeviceMapper extends BaseMapper<Device> {
}

 

注,BaseMapper由 苞米豆 提供,包含一些方法。

 

数据源配置:

# application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/lib1
spring.datasource.username=postgres
spring.datasource.password=123456
spring.datasource.driver-class-name=org.postgresql.Driver

 

使用@MapperScans+@MapperScan:来自博客园

@SpringBootApplication
// 扫描*Mapper
@MapperScans({
	@MapperScan(value="org.lib.postgresqlhello.user"),
	@MapperScan(value="org.lib.postgresqlhello.device"),
})
public class PostgresqlHelloApplication {
	// ...入口类,省略...
}

 

启动项目:启动成功(伪成功,此时还没有建立数据表呢,,mybatis不自动检测&建立吗?Spring Data JPA会自动建立的)。

 

建立UserRunner 使用 UserMapper Bean操作数据:

@Component
@Slf4j
public class UserRunner implements ApplicationRunner {

	@Autowired
	private UserMapper um;
	
	@Override
	public void run(ApplicationArguments args) throws Exception {
		User u = new User();
		u.setName("tom");
		u.setAge(1);
		u.setEmail("tomt@lib.org");
		// 1、新增
		int ret1 = um.insert(u);
		log.warn("ret1={}, u={}", ret1, u);
		// 2、查询byId
		User findu = um.selectById(u.getId());
		log.warn("findu={}", findu);
	}

}

 

启动项目:启动失败。

启动错误日志
Caused by: org.springframework.jdbc.BadSqlGrammarException: 
### Error updating database.  Cause: org.postgresql.util.PSQLException: 错误: 关系 "guser" 不存在
  位置:13
### The error may exist in org/lib/postgresqlhello/user/UserMapper.java (best guess)
### The error may involve org.lib.postgresqlhello.user.UserMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO guser  ( id, name, age, email )  VALUES  ( ?, ?, ?, ? )
### Cause: org.postgresql.util.PSQLException: 错误: 关系 "guser" 不存在
  位置:13
; bad SQL grammar []; nested exception is org.postgresql.util.PSQLException: 错误: 关系 "guser" 不存在
  位置:13

 

建立数据表 guser、device:来自博客园

create table guser(id bigserial primary key, name varchar(100), email varchar(100), age integer);

create table device(id bigserial primary key, sn bigint, name varchar(100),create_time timestamp default now());

 

创建成功,再次启动项目:启动成功,数据添加、查询成功

启动日志:
device.DeviceRunner  : ret1=1, dv=Device(id=1459829342484152322, sn=123, name=device1, createTime=null)
device.DeviceRunner  : finddv=Device(id=1459829342484152322, sn=123, name=device1, createTime=Sun Nov 14 18:23:32 CST 2021)
llo.user.UserRunner  : ret1=1, u=User(id=1459829343234932738, name=tom, age=1, email=tomt@lib.org)
llo.user.UserRunner  : findu=User(id=1459829343234932738, name=tom, age=1, email=tomt@lib.org)

数据库查询结果:
lib1=# select * from guser;
         id          | name |    email     | age
---------------------+------+--------------+-----
 1459829343234932738 | tom  | tomt@lib.org |   1
(1 行记录)


lib1=# select * from device;
         id          | sn  |  name   |        create_time
---------------------+-----+---------+----------------------------
 1459829342484152322 | 123 | device1 | 2021-11-14 18:23:32.383509
(1 行记录)

 

注意,

数据库插入成功了,但是其 id不是像mysql一样是从1自增的,没有规律。这样看来,苞米豆 的 BaseMapper 对 PostgreSQL的支持不是太好的——需要自己写mapper的方法来实现,另文介绍。

 

总之,数据库连接成功,可以操作数据了。

 

说明:

本试验 还没有用到各个Mapper 对应的*Mapper.xml 文件,后文再介绍。

 

关于数据库连接池

s.b.默认使用 HikariCP连接池,和数据库建立10个连接。

在本试验中,如果没有 runner,默认是 不启用连接池的,此时,没有和数据库建立连接

在 启用 runner后,需要操作数据库,此时会使用HikariCP连接池——默认和数据库建立10个连接。

可以使用下面的命令查询:

select pid,datid,datname,usename,application_name,state from pg_stat_activity;

其中,application_name为“PostgreSQL JDBC Driver”的连接就是 连接池建立的。

 

现在更换为 Druid连接池:添加依赖包,即可。

<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.20</version>
</dependency>

// https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter
implementation group: 'com.alibaba', name: 'druid-spring-boot-starter', version: '1.1.20'

注,1.1.20(Aug, 2019)不是当前最新版本(1.2.8(Oct, 2021))。来自博客园

 

启动项目,可以在日志中看到“c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource”,说明已经在使用 Druid连接池了。

不过,Druid默认只建立一个 连接,可以配置。

更多内容,可以看 Druid的文档。

 

试验2:连接两个数据库(源)——lib1、lib2

两个数据库:lib1、lib2,本试验来自同一个数据库服务器。

更改配置文件:

# 试验2:多数据库(源),2个
# 数据库lib1
# 多数据源时使用 jdbc-url
#spring.datasource.one.url=jdbc:postgresql://localhost:5432/lib1
spring.datasource.one.jdbc-url=jdbc:postgresql://localhost:5432/lib1
spring.datasource.one.username=postgres
spring.datasource.one.password=123456
spring.datasource.one.driver-class-name=org.postgresql.Driver

# 数据库lib2
# 多数据源时使用 jdbc-url
#spring.datasource.two.url=jdbc:postgresql://localhost:5432/lib2
spring.datasource.two.jdbc-url=jdbc:postgresql://localhost:5432/lib2
spring.datasource.two.username=postgres
spring.datasource.two.password=123456
spring.datasource.two.driver-class-name=org.postgresql.Driver

 

注意,其中的 url 被替换为了 jdbc-url!否则,失败!

 

取消 入口类PostgresqlHelloApplication的@MapperScans注解。

 

添加2个数据源的配置类:DataSource1Config.java、DataSource2Config.java

两个类 类似,下面是 DataSource1Config.java,修改其中的 类名、DS_TAG的值即可变为 DataSource2Config.java。

关于@Primary 注解的使用:一般只在其中一个使用,但试验显示,两个都添加也行,但哪个是 主数据源呢?s.b.中的 主数据源有什么用呢?TODO

另外,入口类的 @MapperScans迁到这里了。

注意,@MapperScan有两个,但是,只启用了一个。原因是,两个数据源配置 类都扫描的话,会出现错误,导致项目启动失败。因此,数据源1 扫描user包,数据源2 扫描device包。

注意,参考文档1 使用的是 SqlSessionFactoryBean,但启动不了,改为 MybatisSqlSessionFactoryBean 就可以了。来自博客园

package org.lib.postgresqlhello.config;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.annotation.MapperScans;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;

/**
 * 数据源1
 * @author ben
 * @date 2021-11-04 20:50:28 CST
 */
@Configuration
@MapperScans({
	@MapperScan(value="org.lib.postgresqlhello.user", sqlSessionFactoryRef = DataSource1Config.DS_TAG + "SqlSessionFactory")
//	@MapperScan(value="org.lib.postgresqlhello.device", sqlSessionFactoryRef = DataSource1Config.DS_TAG + "SqlSessionFactory")
})
public class DataSource1Config {
	
	public static final String DS_TAG = "lib1";
	public static final String DS_CONFIG_PREFIX = "spring.datasource.one";
	
	public static final String DS_NAME = DS_TAG + "DataSource";
	public static final String DS_SSF = DS_TAG + "SqlSessionFactory";
	public static final String DS_SSTLT = DS_TAG + "SqlSessionTemplate";

	@Primary
	@Bean(DS_NAME)
	@ConfigurationProperties(prefix = DS_CONFIG_PREFIX)
	public DataSource dataSource() {
		return DataSourceBuilder.create().build();
	}
	
	@Primary
	@Bean(DS_SSF)
	public SqlSessionFactory sqlSessionFactory(@Qualifier(DS_NAME) DataSource dataSource) throws Exception {
		// 异常:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
//		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		// 正常
		MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		
		// 实践:下面的不一定要配置,,配置后可以在XML中写实现
		// mapper的xml形式文件位置必须要配置,不然将报错
		bean.setMapperLocations(new PathMatchingResourcePatternResolver()
				.getResources("classpath*:/mapper/" + DS_TAG + "/*.xml"));
		
		return bean.getObject();
	}
	
	@Primary
	@Bean(DS_SSTLT)
	public SqlSessionTemplate sqlSessionTemplate(@Qualifier(DS_SSF) SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

 

注意,@MapperScan注解的 sqlSessionFactoryRef属性 不要配置错了——看来,这个DS_TAG常量应该放到另外的地方。

 

好了,开启Runner,启动项目:启动成功,数据操作成功,,lib1的user表新增了一条数据,lib2的device新增了一条数据。

lib1=# select * from guser;
         id          | name |    email     | age |        create_time
---------------------+------+--------------+-----+---------------------------
 1459861260256710658 | tom  | tomt@lib.org |   1 | 2021-11-14 20:30:22.08904
(1 行记录)


lib1=#
lib1=# \c lib2
您现在已经连接到数据库 "lib2",用户 "postgres".
lib2=# select * from device;
         id          | sn  |  name   |        create_time
---------------------+-----+---------+----------------------------
 1459861259325575170 | 123 | device1 | 2021-11-14 20:30:21.992877
(1 行记录)

 来自博客园

更多:前面提到 下面的 配置可以不需要

// 实践:下面的不一定要配置,,配置后可以在XML中写实现
// mapper的xml形式文件位置必须要配置,不然将报错
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
    .getResources("classpath*:/mapper/" + DS_TAG + "/*.xml"));

无论是否配置,默认情况下,src/main/resources 下 可以没有 *Mapper.xml 文件。

当然,可以添加——以便事项更丰富的数据操作功能。

简单的示例:

<?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="org.lib.postgresqlhello.user.UserMapper">

</mapper>

具体怎么使用,需要学习mybatis的相关知识。一般情况下,这里面会写很多业务相关的 CRUD等操作。

 

小结:

1、每个数据源一个配置,比较麻烦;

2、项目开发之前,就需要设计好 哪些数据表 由哪个Mapper来操作;

 

针对上面的问题,苞米豆 提供了动态数据源配置功能——dynamic-datasource-spring-boot-starter包,下面的 试验3 来玩玩。

 

试验3:动态数据库——2个数据库(源)

在试验2 的基础上进行改造。

添加依赖包:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>2.5.6</version>
</dependency>

本文使用的是 2.5.6(Jul, 2019),最新版本是 3.4.1(Jul, 2021)。来自博客园

 

配置:

# 试验3:动态数据源
# 使用Druid数据库连接池
spring.datasource.dynamic.primary=lib1
# lib1
spring.datasource.dynamic.datasource.lib1.url=jdbc:postgresql://localhost:5432/lib1
spring.datasource.dynamic.datasource.lib1.username=postgres
spring.datasource.dynamic.datasource.lib1.password=123456
spring.datasource.dynamic.datasource.lib1.driver-class-name=org.postgresql.Driver
# lib2
spring.datasource.dynamic.datasource.lib2.url=jdbc:postgresql://localhost:5432/lib2
spring.datasource.dynamic.datasource.lib2.username=postgres
spring.datasource.dynamic.datasource.lib2.password=123456
spring.datasource.dynamic.datasource.lib2.driver-class-name=org.postgresql.Driver
# other
# 需要排除,否则会报错
spring.autoconfigure.exclude=com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure

注意,上面的主数据库为 lib1。

注意,最后一行的配置是 需要的,否则,报错,也可以使用下面的配置类替代——主类上加注解:

// baomidou-动态多数据源时,
//加exclude 效果同 spring.autoconfigure.exclude的配置
//@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
public class PostgresqlHelloApplication {

 

取消 DataSource1Config、DataSource1Config 的配置:

 

开启主类PostgresqlHelloApplication的 @MapperScans+@MapperScan的使用(同 单数据源)。

 

好了,启动项目:启动成功,两个runner给 lib1 的数据表增加了记录:

postgres=# \c lib1
您现在已经连接到数据库 "lib1",用户 "postgres".
lib1=#
lib1=# select * from guser;
         id          | name |    email     | age |        create_time
---------------------+------+--------------+-----+----------------------------
 1459866409163321346 | tom  | tomt@lib.org |   1 | 2021-11-14 20:50:49.623428
(3 行记录)


lib1=# select * from device;
         id          | sn  |  name   |        create_time
---------------------+-----+---------+----------------------------
 1459866407779201026 | 123 | device1 | 2021-11-14 20:50:49.573958
(2 行记录)

 来自博客园

怎么把数据添加到 数据库lib2 的表你?使用 com.baomidou.dynamic.datasource.annotation.DS 注解

package org.lib.postgresqlhello.user;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

// 不使用时,默认是主库 lib1
@DS("lib2")
public interface UserMapper extends BaseMapper<User> {

}

// -----------------

@DS("lib2")
public interface DeviceMapper extends BaseMapper<Device> {

}

 

启动项目:启动成功,数据被写到了 lib2 的数据表了。

lib1=# \c lib2
您现在已经连接到数据库 "lib2",用户 "postgres".
lib2=#
lib2=# select * from guser;
         id          | name |    email     | age |        create_time
---------------------+------+--------------+-----+----------------------------
 1459867136388501506 | tom  | tomt@lib.org |   1 | 2021-11-14 20:53:43.003151
(1 行记录)


lib2=# select * from device;
         id          | sn  |  name   |        create_time
---------------------+-----+---------+----------------------------
 1459867134962438145 | 123 | device1 | 2021-11-14 20:53:42.948146
(3 行记录)

 

小结,

好像,很方便了啊!写的代码少很多了!

 

》》》全文完《《《

 

只是用起来了,不过不太精通的啊!还需深入了解才是。来自博客园

mybatis生态中,还有很多没玩呢,继续。

 

其实,spring boot手册中 有一个 “Data Access”章,里面介绍了 访问多数据源的问题的。待试验,TODO

目前的 spring boot手册中的内容如下:

https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.data-access

变成第 8章 了,大家可以下载pdf下来学习的。来自博客园

 

当然,把本文的 PostgreSQL 改为 MySQL 可以更好地玩耍吧

 

参考文档

1、springboot-整合多数据源配置

作者:AizenSousuke

2、

 

推荐阅读