Mybatis的映射文件Mapper.xml获取applicaition.properties配置文件中定义的属性值
问题现象:今天在项目中想到了一个问题:当数据源中的表和数据都不变,仅仅是修改了数据源(库)名,服务是否还能正常使用?也可以理解为服务是否兼容其他环境下的数据源?问题分析:这个问题其实就是为了满足不同环境下的需求:1.数据库环境兼容假如我的服务已经可以在服务器1上,正常地运行并访问该服务器上的数据库1(访问数据库1所用的用户名和该数据库名一样)!此时我在另一台服务器上面也部署了我的服务和数据库2(访
目录
有很多坑是他们不知道或者说完全没有提及到的,当然也有很多文章是照搬别人的文章,没有经过自己思考的,这里我是不推荐这样做的!
所以,要注意不要双重动态绑定,mybatis配置文件只能获取 application.properties配置文件某个属性的值,而这个值不能使用动态绑定符(占位符:${})修饰,否则会失败!!!
问题现象:
今天在项目中想到了一个问题:
当数据源中的表和数据都不变,仅仅是修改了数据源(库)名,服务是否还能正常使用?也可以理解为服务是否兼容其他环境下的数据源?
问题分析:
这个问题其实就是为了满足不同环境下的需求:
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配置文件,真的很好用,强烈推荐!!!
更多推荐
所有评论(0)