提示:本文中,我们只给了部分示例代码。
如果你需要完整的代码,请点击:https://github.com/mengyunzhi/sampleUpdateTableWithJpa/tree/1
why to do
在版本的迭代中,我们毕然会面临数据表更新的问题。而这些更新,有些是可以通过spring jpa
进行自动更新的,有些更新spring jpa
则表式无能无力,所以只能采用手动的方法。
本文将实现以下功能:
假设当前共有3个发布的版本。分别为1.1,1.2,1.3
,每个版本都有对应的应用程序及数据库。
实现功能1:1.2
版本的程序运行在1.1
版本的数据库上时,自动将其更新为1.2
版本所对应的数据库结构。
实现功能2:1.3
版本的程序运行在1.2
版本的数据库时,自动将其更新为1.3
版本的数据库。
实现功能3:1.3
版本的程序运行在1.1
版本的数据库时,自动将期更新为1.3
版本的数据库。
实现功能4:开发时,我们使用的为H2
数据库,生产环境使用mysql
数据库。当使用h2
数据库时,不做任何数据库版本的更新。
准备知识
spring boot
在系统启动时,对数据表的操作顺序如下(实际顺序并不见得如此,只以下流程图只为实现本文功能)。
先想明白
由上面的图,我们得知,如果想实现我们的功能。需要从以下几方面入手。
- 需要设置几个属性。
- 需要单独为
mysql
来定制脚本。 - 需要一张记录数据库版本的表。
- 需要通过
sql
脚本,来判断数据库版本,然后执行不同的语句。 - 当数据库为
H2
时,不增加任何脚本。
好了,想明白了上面的问题。其它的事情就显得简单了。
实施
准备
准备好mysql,并建立相应的数据库。在pom.xml
中加入适应的依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mengyunzhi</groupId>
<artifactId>sample-update-table-with-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sample-update-table-with-jpa</name>
<description>进行版本迭代过程中,使用spring jpa来完美解决数据表更新的问题</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置spring boot
spring:
profiles:
active: mysql
---
# h2环境
spring:
profiles: h2
---
# mysql环境
spring:
profiles: mysql
datasource:
url: jdbc:mysql://localhost/sampleUpdateJpa
username: root
password:
platform: mysql
separator: //
initialization-mode: always
jpa:
hibernate:
ddl-auto: update
建立测试文件
在resources
文件夹下,建立schema-mysql.sql
文件。并随便写两行错误的语句,然后启动应用,得到报错信息,说明该文件并成功执行。比如,我随便写了如下几行。
-- 先于hibernate执行
sdfsdfsdf
组织sql语句
先判断,是否存在记录版本的数据表,如果没有,则创建一个数据表,并加入一条数据,将版本设置为1.1。
-- 重写 ; 为 // ,在spring中,注释掉下面一行,应该我们在配置文件中的 separator: // 便是起的该作用
-- DELIMITER //
-- 如果存在函数,则先删除
DROP PROCEDURE IF EXISTS `FUN20180628` //
-- 定义函数FUN20180628
CREATE PROCEDURE `FUN20180628` ()
BEGIN
DECLARE hasDataTable INT;
SELECT count(*) INTO hasDataTable FROM information_schema.tables WHERE (table_schema = 'sampleUpdateJpa') AND (table_name= 'version');
IF hasDataTable = 0 THEN
CREATE TABLE `sampleUpdateJpa`.`version` (
`version` float NOT NULL COMMENT '版本号',
PRIMARY KEY (`version`)
) COMMENT='版本号';
insert into `sampleUpdateJpa`.`version` ( `version`) values ( '1.1');
END IF;
END
//
-- 调用函数
CALL FUN20180628() //
-- 恢复重写的;,以免影响其它的function
-- DELIMITER ;
我们在上面只所以要先删除原来的同名函数,主要目的是为了这个函数的随时升级。
相信有了上面的语句基础,实现当版本为1时,升级为2;为2时,升级为3,就简单了。
如果你只在意解决方案,那么本节内容到此结束。
我是如何做到的?
前面,我们已经给出了实现的方法,如果你还对“我是如何做到的”感兴趣,请继续阅读以下内容。
阅读官方文档
在spring官方网站,我们找到共当家花旦spring-boot
, 然后在介绍的下方,点击参考手册。
然后我们将得到一个很长很长,当然也是很有用很有用的手册,我期待自己能够有充分的时间,可以在假期的时候尝试从头到尾的读一遍。来到第82章 -- Database Initialization
按我们的需求,简单的记录下,我们所要的资料:
82.1 说,有两种方式控制开与关,分别是:spring.jpa.generate-ddl(boolean)
,及spring.jpa.hibernate.ddl-auto(enum)
结论:我们并不需要在程序启动时,执行相关的import
语句。
82.2介绍了使用Hibernate初始化数据库的过程,并对spring.jpa.hibernate.ddl-auto
的几种属性和默认属性做了说明。
然后接着又说:在系统启动时,如果ddl-auto
设置的为create
或create-drop
,那么一个名存在于classpath的名为import.sql
,将会被自动执行。并指定,这对我们提供DOME
非常有帮助,谁说不是呢?我们完成在系统完成后,添加dome
数据,并将其导出为import.sql
文件,以使得在程序部署其它dome
程序时被执行,不是吗?
结论:上面的信息,对我们本次的任务没有帮助。
82.3 Initialize a Database初始化一个数据库。
大概讲了:spring boot
可以分别由classpath
中的schema.sql
和data.sql
中加载数据。然后又指出,如果存在schema-${platform}.sql
和data-${platform}.sql
时,spring boot
也会执行。其中,platform
是指在spring.datasource.platform
设置的值,比如:我们将spring.datasource.platform
的值设置为:hsqldb
, h2
, oracle
, mysql
, postgresql
或者其它的。
然后又给出3点,第一点说Spring Boot
会自动为我们创建一个内置的数据库,在启动内置数据库时,默认会加载sql
脚本(意思是其它的数据库则不会),我们可以使用spring.datasource.initialization-mode
来定义它的属性,以使得在使用其它数据库时,也默认加载这些脚。又说:在程序启时,如果执行sql scripts
出错,那么程序就报错退出来了,然后我们可以通过更改spring.datasource.continue-on-error
的值来改变。最后点说:我们可以选择是让Hiberante为我们自己动生成数据表还是执行schema.sql
来生成数据表,但是不能二者全部选择,如果我们想使用schema.sql
,那么就要禁用spring.jpa.hibernate.ddl-auto
。(在82.2中,指出了设置为create或是create-drop为启动,其它为禁用)
迭代式开发
我们很难一次的将sql
的函数写正确,如果你直接在schema.sql
中写,无异于自己挖坑。
- 在创建
sql
函数的过程中,我们首先找到navicat
,连接数据库后,新建函数,并保存测试,直至该函数可以通过,并且实现我们的功能。 - 有了函数,我们再把这个函数的内容复制到查询中,在询中,对
;
进行转义。比如,我们转义为\\
。然后运行查询,也确认查询没有错误。 - 查询测试完了,最后我们把
sql
的脚本才正式的放到schema.sql
中,并且注释到转义的部分。
进阶
spring
的官方文档还给出了进阶的方法。即使用第三方库来进行更加人性化,简单的版本升级工作。请参阅:82.5 Use a Higher-level Database Migration Tool
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。