![d8a03c331c49fab761d88968ba7e5734.png](https://i-blog.csdnimg.cn/blog_migrate/d9bfd02180dfebb3f65525e649c7fcb6.png)
各种ORM从诞生之日起,可能便会把多数据库支持当成卖点,讲述同一套代码运行在多种不同数据库的故事。然而在实际中,我们似乎很难看到具体的应用场景,一套程序若是支持SQL Server,那么可能便是一直支持sql server,难以“一行代码都不修改”,仅通过修改配置,便运行在mysql之上。
我们进行开发的时候,也往往是在内网部署一套数据库,然后供所有的开发调用;维护开发、测试等不同环境的数据库实例,是有一定成本的。
随着微服务的流行,理论上,每个微服务都应该是调用自身的独立的数据库;给每个微服务都部署独立的数据库实例,也可以是件略麻烦的事情;但如果开发时,程序调用的是嵌入到内存的数据库,则可以便利一些。
这也有利于开发编写测试,因为嵌入式的数据库生命周期捆绑于测试程序:
- 测试开始时创建数据库
- 填充初始化数据
- 运行测试
- 测试结束时删除数据库
“造测试数据”这个事情也顺便解决了。
Java的企业开发框架Micronaut
,便在其相应的ORM库Micronaut Data中推荐这样的模式,生产部署中链接msql、postgre、SQL Server、Oracle等数据库,开发、测试时,则链接H2这一嵌入式的数据库。
比方说我们要给链接Oracle,便可以在micronaut的配置文件application.yml
中写:
datasources:
default:
url: jdbc:oracle:thin:@127.0.0.1:1521:xe
driverClassName: oracle.jdbc.driver.OracleDriver
username: admin
password: 'admin'
而在用于测试环境中的配置文件application-test.yml
中写:
datasources:
default:
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
driverClassName: org.h2.Driver
username: sa
password: ''
schema-generate: CREATE_DROP
最后一行schema-generate: CREATE_DROP
便是指在启动时创建数据库表,并在结束时删除。
以kotlin为例,实体类的定义一个会是类似:
@Entity
data class Book(@Id
@GeneratedValue
var id: Int?,
val userId: Int,
val title: String,
@DateUpdated
var createDate: LocalDateTime?,
val content: String)
注意:
- 上面的主键
id
,它被注解为@GeneratedValue
,那么,这在Oracle中是会通过相应的Sequence去生成,而在H2中,则是自增数字主键 @DateUpdate
则是让ORM自动在插入时获得当前时间
相应的Repo则是:
@JdbcRepository(dialect = Dialect.ORACLE)
interface BookRepo : CrudRepository<Book, Int> {
fun findByUserIdAndTitle(userId: Int, title: String): Book
}
这样,我们便可以一个支持基础Crud操作,以及可以根据用户ID以及标题去搜索数据库查询的Repostory类。
Micronaut是基于代码生成、编译时注入的框架,因此我们也需要将数据库dialect配置为Oracle,这样ORM才可以生成面向Oracle的SQL语句。
至于在测试中,我们则可以定义一个BookRepo的子类:
@JdbcRepository(dialect = Dialect.H2)
@Replaces(BookRepo::class)
@Requires(env = ["test"])
interface BookTestRepo: BookRepo {}
这样面向H2数据库的BookTestRepo
便会在test环境中替代掉BookRepo
;在测试代码中注入BookRepo
对象时,获得的便会是BookTestRepo
的实例:
@MicronautTest
class DbMockSpec : StringSpec() {
@Inject
lateinit var bookRepo: BookRepo
}
我想,这样的应用才是真正发挥出依赖注入的威力。