目录​​​​​​​

问题现象:

问题分析:

1.数据库环境兼容

2. 跨库(联库)查询

那么要如何解决这个问题呢?

有很多坑是他们不知道或者说完全没有提及到的,当然也有很多文章是照搬别人的文章,没有经过自己思考的,这里我是不推荐这样做的!

实验分析:

因此很容易就能知道:

这就是第一个坑!!!

这就是第二个坑!!!

注意:

这就是隐藏的第三个坑!!!

所以,要注意不要双重动态绑定,mybatis配置文件只能获取 application.properties配置文件某个属性的值,而这个值不能使用动态绑定符(占位符:${})修饰,否则会失败!!!

拓展(2021-05-31):

这就是隐藏的第四个坑!!!

这里我谈谈自己的观点:

那么该如何解决这个坑呢???

最终:

解决方法:

后语


问题现象:

今天在项目中想到了一个问题:

数据源中的表和数据都不变,仅仅是修改了数据源(库)名,服务是否还能正常使用?也可以理解为服务是否兼容其他环境下的数据源?


问题分析:

这个问题其实就是为了满足不同环境下的需求:

1.数据库环境兼容

假如我的服务已经可以在服务器1上,正常地运行并访问该服务器上的数据库1访问数据库1所用的用户名和该数据库名一样)!此时我在另一台服务器上面也部署了我的服务数据库2访问数据库2所用的用户名和该数据库名一样;数据库2中的表结构和数据,与服务器1中的数据库1完全一致,只是数据库名不同),现在要让服务也能正常地运行并访问该服务器上的数据库2!

平时开发中,我们都会建立测试库,或者使用单一服务器上的数据库;但如果你开发的服务(例如平台,管理系统等项目)有多个甲方要购买使用,那不可能让所有甲方都共用同一套数据库;此时就需要给每个甲方分别部署各自的数据库,并通过该服务去访问!!!因此满足这个需求的重要性就显而易见了!

2. 跨库(联库)查询

有时候,不是一个数据库就包含了我们需要的所有数据的,在很多大中型项目中,都会在同一个数据库连接下创建多个数据库分别存储对应库所规定的数据,而要拿到我们需要的数据时,很可能需要同时访问到多个数据库的表数据,因此很多时候就需要用到跨库(联库)查询!!!

那么要如何解决这个问题呢?

通过查阅网上资料,可以了解到了一些方法,基本都是大同小异,例如:

只可惜在使用的时候总是失败,最后通过不断的实验,才发现:

有很多坑是他们不知道或者说完全没有提及到的,当然也有很多文章是照搬别人的文章,没有经过自己思考的,这里我是不推荐这样做的!

实验分析:

1. 首先,这里我是通过在 application.properties(yml/yaml)配置文件中,配置数据源的方式,来访问相应的数据库的:

2.sql语句是写在映射文件(Mapper.xml)中的:

因此很容易就能知道:

1. 只要能在 映射文件(Mapper.xml) 中获取到  application.properties(yml/yaml)配置文件中的属性值即可,而由于前面说了数据库名和访问用户名一致,因此我们需要用到下面这个属性: 

application.properties 配置文件:

然后根据 mybatis 读取 application.properties(yml/yaml)配置文件 的方法,定义变量,并动态赋值为 db.run.username 的属性值,然后在 映射文件(Mapper.xml)中使用:

application.properties 配置文件:

映射文件(Mapper.xml):

controller层的接口:

然而,实验了一下发现是不行的,sql语句出现报错:

这就是第一个坑!!!

可以看出是因为这个变量(${run})没有使用成功,这里网上也没有任何资料解释这个原因,最后还是通过自己的不断实践,终于发现这样仅仅靠这种方式是不能实现我们的目标的,

于是我又用了另一种方法,通过mybatis配置文件来实现:

先在 application.properties配置文件 中添加以下配置,指定mybatis配置文件的路径:

application.properties配置文件

通过 mybatis-config.xml 文件中添加 property 来配置:

mybatis-config.xml配置文件 :

调用接口,然后出现了以下报错:

这就是第二个坑!!!

从报错信息可以得知:run这个变量的赋值过程中,找不到 'db.run.username' 这个表达式的值!!!

网上也是查不到任何这方面的解释,经过不断测试,我发现,只有两种方案可以解决这个问题:

1.property 标签的 value 属性赋值为静态值:

mybatis-config.xml配置文件 :

sql语句执行成功:

由于是静态赋值,那执行成功是在意料之内的了,但是这并不能满足我们的需求

因为这种方法,并不依赖application.properties配置文件,因此如果无法动态获取配置文件的属性值,那就还得在配置文件修改的时候,自己手动去修改这个mybatis的配置文件,这样就很麻烦!!!

所以我们使用下面这种方法:

2.保持这个变量名和变量值中引用的变量名一致,如下:

mybatis-config.xml配置文件 :

映射文件(Mapper.xml):

执行sql成功:

调用成功:

注意:

假如我在 application.properties 配置文件 中用一个新变量(myVariables.run),去动态获取另一个变量(db.run.username)的值;

然后再用

application.properties 配置文件:

mybatis-config.xml配置文件 :

映射文件(Mapper.xml):

调用接口,报错:

这就是隐藏的第三个坑!!!

因为 mybatis-config配置文件中 property标签的 属性value的值是 ${myVariables.run};

而在 application.properties配置文件中 ${myVariables.run} 的值又是一个动态获取值,它要去获取 ${db.run.username} 的值,这在配置文件中完全没问题;

然而在 mybatis-config配置文件中却不行,推测逻辑是这样的:

1. mybatis读取mybatis-config配置文件中 property标签的 属性value的值,发现是 ${myVariables.run};

2. 于是mybatis就会去 application.properties配置文件中 找到 myVariables.run 的值,发现是 ${db.run.username};

3. 于是就把这个值传递给 property标签 的 属性value

到此为止,mybatis其实已经尽力了,mybatis无法把 application.properties配置文件中  db.run.username 的值 赋值给 myVariables.run;

因为 mybatis 不会去处理 application.properties 配置文件中的动态赋值符(即我们常说的占位符:${})!!!而是直接把${db.run.username}拿来用了。

所以,要注意不要双重动态绑定,mybatis配置文件只能获取 application.properties配置文件某个属性的值,而这个值不能使用动态绑定符(占位符:${})修饰,否则会失败!!!

拓展(2021-05-31):

今天在应用过程中,遇到了如下报错:

Parameter ‘db’ not found. Available parameters are [designId , param1]

报错信息意思是说:这个名为 db 的参数找不到。现在能使用只有一个,名为 designId 的参数。

通过查看该 dao层方法,和对应的映射文件中的sql语句:

可以发现: ${db.run.username} 这个占位符我原本是为了引用 mybatis配置文件中设置的变量 db.run.username 然而却被 mybatis 识别成了调用该dao方法(delete)时,传入的一个方法参数且名为db,从而导致了报错!!!

这就是隐藏的第四个坑!!!

这里我谈谈自己的观点:

我们知道 mybatis 的映射文件中编写的 sql语句里,可以识别两种占位符 :#{} 和 $ {}。这些占位符的作用就是引用变量或方法参数!

在执行这个sql语句的时候,很显然 mybatis 把  ${db.run.username}  识别成了一个dao层方法参数!!!

很奇怪!!!

于是我又调用了其他有相同特征的dao层方法来试验,结果却出乎意料:

调用接口是成功的:

那么该如何解决这个坑呢???

根据我的实验,以下方法都试过了还是会报错

1. 把  ${db.run.username} 改成  ${db-run-username}。

2. 把  ${db.run.username} 改成  #{db_run_username}。

3. 把  ${db.run.username} 改成  ${dbRunUsername}。

4. 把  ${db.run.username} 改成  #{dbRunUsername}。

尽管都失败了,但还是有收获的,我发现了2个现象:

1. 在使用 #{} 和 ${} 时的报错是不同的,这是因为#{} 是完全取值于方法参数,${}则未必取值于方法参数。

2. 不管是 db.run,db-run 还是 db_run,都会被识别为‘db’这个参数,也就是说 (.  -   _)mybatis对于这三种符号是采取了一样的处理逻辑。

最终:

经过我2天的研究测试,终于发现原来是 mybatis的数据源配置类 出了问题:

从上面第四个坑的报错信息可知,这个数据源时无法识别我之前在 mybatis-config.xml 配置文件 中配置的变量:

这是因为我在使用 注解配置类注入数据源时 ,配置了 SqlSessionFactory ,但却忘记传入自定义的 mybatis-config.xml 配置文件的路径,从而导致了该数据源在注入为bean对象时,无法获取到mybatis-config.xml 配置文件 中配置的变量的!!!

不过正常情况下应该都不会发生这个问题,因为一般我们自定义 mybatis配置 的时候,都会采用 注解配置类 或者 xml配置文件 中的其中一种来实现,这样就不需要在 注解配置类 中把 xml配置文件 也加入进去了,确实麻烦!!!

而我这个项目中却是两种都用上了。。。这算不算是之前的同事留下的坑呢???

于是在 注解配置类 配置 SqlSessionFactory 这里, 需要这样配置:

成功:

总结:

第四个坑涉及到了 mybatis多数据源配置 的知识,后面在我学习了这方面知识后,也会自己写一篇相关的总结文章!敬请期待!!!
现在对 mybatis多数据源配置 感兴趣的小伙伴,可以看看我在网上找到的这篇文章,写得很详细,虽然有些瑕疵,但问题不大:

SpringBoot多数据源 + Atomikos事务_NickWU博客-CSDN博客


解决方法:

1. application.properties配置文件中添加自定义属性,注意不要在这个属性值上用动态绑定符(占位符:${}),而是直接赋值为一个静态值(字符串/数字等):

并指定mybatis配置文件的路径:

2. mybatis-config.xml配置文件中添加属性配置,resource指定application.properties配置文件的路径,并保证name和value的动态绑定变量名一致 :

3. 映射文件(Mapper.xml)中通过动态绑定符(占位符:${})修饰,传入 mybatis-config.xml配置文件中添加的属性变量名,即可使用:

4. 编写接口,并调用接口:


后语

        鉴于很多小伙伴在看了文章后产生的困扰,再加上我已经不再使用文章中提到方式去获取配置数据了,所以没有更深入的去研究,而是找到了更好的方法,所以今天是2022-04-13,在这里,我提议大家可以使用我现在在用的方式来实现相同的功能,而且也更加简单、方便、易懂:

继续使用我以前在文章中提到的例子:

 1、首先我的配置文件有如下配置:

2、我自定义了一个实体类 DBConfigService,用@Service注解了该类,其实用@Component注解也可以,总之就是让spring容器可以注入该类,成为一个bean对象。

接着对变量(如runSchema)使用@Value注解来获取上面配置中的变量数据,并创建变量对应的getter方法(如getRunSchema()):

 3、dao层接口类 :

建议养成通过@Param注解来绑定方法的参数名(如"schema")的习惯:

4、xml映射文件的sql中引用参数名(如${schema}):

 5、service实现类,注入上面的的方法中调用dao层方法,并调用对应的getter方法获取DBConfigService类中的变量的值,来作为方法参数传递进去:

 6、调用成功:

这种方法完全脱离了mybatis-config.xml配置文件,真的很好用,强烈推荐!!!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐