单数据源
微服务中,通常一个服务只会使用一个数据库。
搭建项目框架
在IDEA中创建一个Maven项目,名称为dbandcache,代码结构如图4-1所示。

搭建好项目框架之后,进行数据库和数据表的创建。先在机器上安装MySQL。推荐MySQL的可视化客户端:Navicat。
建库和建表
首先,创建数据库,库名为microservicedb1,之后执行如下的SQL语句建表t_user,并插入一条测试数据。

建好表之后,就可以开始项目的开发了。
使用MyBatis-Generator生成数据访问层
这里使用MyBatis-Generator来生成数据访问层的模板代码。
首先,下载myBatis-generator-core-x.x.x.jar和mysql-connector-java-x.x.x.jar,并且创建MyBatis-Generator的配置文件generatorConfig-user.xml。最后将这些文件组成如图4-2所示的目录结构。

之后,填写generatorConfig-user.xml的内容如下:

该文件中指定了数据库连接四要素(driver、connectionurl、username、password),生成的model类、mapper接口的位置以及映射文件的存储目录,最后指定了哪一张表(tableName)对应哪一个model(domainObjectName)。注意,model类和mapper类的位置一定要和如图4-1所示的项目代码结构相对应(即包名要写对),否则在代码生成之后,还需要进行修改。
之后,从终端进入MyBatis_Generator目录,执行如下语句:

之后,在src目录下我们会看到User类、UserMapper接口及UserMapper.xml文件。将这些文件复制到相应的目录下即可。如果在执行语句时出现错误,则需要先创建src文件夹。
Spring Boot集成MyBatis
MyBatis作为一个半自动的ORM框架。将会使用Spring Boot来集成MyBatis,之后通过MyBatis来操作数据库。
先来看一下pom.xml文件的内容:



在该文件中引入了与数据库相关的5个包:spring-boot-starter-jdbc、druid、mysql-connector-java、mybatis及mybatis-spring。其中,mysql-connector-java的scope是runtime,使用的数据源是阿里巴巴的Druid。
然后看一下启动主类com.iafoot.dbandcache.Application的代码:

前面生成了三个数据访问层的模板文件,下面分别来看一下。首先是模板类com.iafoot.dbandcache.model.User,代码如下:

然后是mapper接口com.iafoot.dbandcache.mapper.UserMapper,代码如下:

MyBatis-Generator生成的第三个模板文件是src/main/resources/mapping/UserMapper.xml,代码如下:



接下来在src/main/resources/application.properties文件中配置数据库信息,配置如下:

一切准备就绪之后,开始集成MyBatis,代码如com.iafoot.dbandcache.config.MyBatisConfig类所示:


这个类是整个项目中最重要的类。其中,@MapperScan注解用来指定扫描的mapper接口所在的包,读取配置文件是org.springframework.core.env.Environment实例。整个类的流程为:首先根据数据库配置创建一个DataSource单例,之后根据该DataSource实例和SqlSessionFactionBean创建一个SqlSessionFactory单例。之后SqlSessionFactory创建出SqlSession,再使用SqlSession获取相应的Mapper实例,然后通过Mapper实例就可以肆意地操作数据库了。SqlSessionFactoryBean中的TypeAliasesPackage用来指定domain类的基包,即指定其在xxxMapper.xml文件中可以使用简名来代替全类名:MapperLocations用来指定xxxMapper.xml文件所在的位置,如果MyBatis完全使用注解,则也可以不设置这两个参数。这里使用的数据源是阿里巴巴的Druid,而Spring Boot默认使用的是tomcat-jdbc数据源。
Spring Boot与MyBatis集成之后,遵循最基本的分层架构。设计了三个类:一个dao、一个service和一个controller。 首先是com.microservice.dbandcache.dao.User.Dao,代码如下:

然后是com.microservice.dbandcache.service.UserService,代码如下:

最后是com.microservice.dbandcache.controller.DbAndCacheController,代码如下:

多数据源
假设我们现在有两个数据源microservicedb1和microservicedb2,用户信息表t_user存在microservicedb1中,用户车的信息表t_car存在microservicedb2中。dbandcache这个服务根据用户ID查询出用户信息的同时也要查询出该用户车的信息,一起返回给前端。这个时候,就出现了一个服务需要访问两个数据源的情况。下面我们开始完成这个需求。
建库和建表
首先需要创建库和表,并初始化数据。microservicedb1及其中的t_user表在上面已经创建好了,接下来创建库microservicedb2,之后在其中执行如下的SQL语句:

该表中有一个字段owner,其对应t_user表中的id,可以将其理解为逻辑外键。
建好表并初始化好数据之后,使用MyBatis-Generator来生成car相关的model类、mapper接口及xml文件,之后将这些类复制到dbandcache服务中,再新增加一些类,并修改一些类即可。
使用MyBatis-Generator生成数据访问层
MyBatis-Generator生成的com.miroservice.dbandcache.model.Car代码如下:

MyBatis-Generator生成的com.microservice.dbandcache.mapper.CarMapper代码如下:

这里将多余的不使用的方法删掉了。 MyBatis-Generator生成的src/main/resources/mapping/CarMapper.xml代码如下。


在该配置文件中新增了microservicedb2数据库的配置。做好准备工作之后,开始实现多数据源!
结合AbstractRoutingDataSource实现动态数据源
首先定义一个枚举类,com.microservice.dbandcache.config.DatabaseType代码如下:

该类列出所有的数据源key。之后创建一个数据源key持有类com.microservice.dbandcache.config.DatabaseContextHolder,代码如下:

然后集成springjdbc的AbstractRoutingDataSource,实现动态数据源com.microservice.dbandcache.config.DynamicDataSource,代码如下:

然后,在com.microservice.dbandcache.config.MyBatisConfig类中,构造动态数据源,集成MyBatis,代码如下:



然后依然根据分层规则,编写dao、service和controller类。 com.microservice.dbandcache.dao.CarDao代码如下:


这里只列出关键代码。其中,UserAndCar类代码如下:

最后看一下com.microservice.dbandcache.controller.DbAndCacheController的代码:

至此,我们就实现了微服务中对多数数据源的使用。然后运行程序,使用Swagger进行测试即可。但是如果一个服务中的dao类比较多,那么可能需要写很多遍如下的代码:DatabaseContextHolder.setDatabaseType(DatabaseType.microservicedb2);。有没有什么好办法,让所有的dao类还是按照之前一个数据源时的写法,即不要在每个dao类中都写上这句代码呢?答案是肯定的,使用AOP来实现。
实现多数据源的步骤总结
最后简要总结一下用以上方法实现多数据源的步骤。
- DatabaseType列出所有的数据源key作为第5步中所说的key。
- DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法。
- DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用DatabaseContextHolder获取当前线程的DatabaseType。
- 在MyBatisCOnfig中生成两个数据源DataSource的bean作为第5步中所说的value。
- 在MyBatisConfig中将第1步中的key和第4步中的value组成的key-value对写入DynamicDataSource动态数据源的targetDataSources属性中(当然,同时也会设置两个数据源其中的一个到DynamicDataSource的defalutTargetDataSource属性中)。
- 将DynamicDataSource作为primary数据源注入SqlSessionFactory的dataSource属性中。
- **使用的时候,在dao层或service层先使用DatabaseContextHolder设置将要使用的数据源key(当然也可以使用Spring AOP去做),**然后再调用mapper层进行相应的操作。在mapper层进行操作的时候,会先调用determineCurrentLookupKey()方法获取一个数据源(获取数据源的方法:先根据设置去targetDataSources中找,若没有,则选择defaultTargetDataSource),之后再进行数据库操作。