flyway的简单使用 2019-12-12 程序之旅 8 条评论 7641 次阅读 ## flyway的简单使用 ### flyway简述 Flyway是一个开源的数据库迁移工具。与配置相比,Flyway极力主张简单和约定。它仅基于7个基本命令: [迁移](https://flywaydb.org/documentation/command/migrate), [清理](https://flywaydb.org/documentation/command/clean), [信息](https://flywaydb.org/documentation/command/info), [验证](https://flywaydb.org/documentation/command/validate), [撤消](https://flywaydb.org/documentation/command/undo), [基线](https://flywaydb.org/documentation/command/baseline)和 [修复](https://flywaydb.org/documentation/command/repair)。 迁移可以用[SQL](https://flywaydb.org/documentation/migrations#sql-based-migrations) (支持特定于数据库的语法(例如PL / SQL,T-SQL等)或[Java](https://flywaydb.org/documentation/migrations#java-based-migrations) (适用于高级数据转换或处理LOB)的方式编写。 > 支持的数据库包括 Oracle, SQL Server(包括Amazon RDS和Azure SQL数据库), DB2, MySQL(包括Amazon RDS,Azure数据库和Google Cloud SQL), Aurora MySQL, MariaDB, Percona XtraDB群集, PostgreSQL(包括Amazon RDS,Azure数据库) ,Google Cloud SQL和Heroku), Aurora PostgreSQL, Redshift, CockroachDB, SAP HANA, Sybase ASE, Informix, H2, HSQLDB, Derby, Snowflake, SQLite和 Firebird。 ### flyway实际使用 #### spring boot 2.x 集成flyway ##### 搭建环境 - spring boot-2.2.2.RELEASE - mysql 5.7 ##### maven配置 ```xml org.springframework.boot spring-boot-starter org.flywaydb flyway-core 5.2.1 org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 8.0.13 com.alibaba druid-spring-boot-starter 1.1.16 org.springframework.boot spring-boot-maven-plugin org.flywaydb flyway-maven-plugin 5.0.3 ``` ##### yml配置 ```yaml # application.yml server: port: 8088 spring: profiles: active: dev # application-dev.yml spring: datasource: url: jdbc:mysql://localhost:3306/testdb?serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver flyway: baseline-on-migrate: true # locations: classpath:db/migration # 迁移脚本的路径 enabled: true # 是否开启flyway validationQuery: SELECT 1 type: com.alibaba.druid.pool.DruidDataSource ``` > flyway迁移脚本(.sql文件)默认放在classpath: db/migration文件夹中 > > ![image-20191212134148323.png](https://mufeng-blog.oss-cn-beijing.aliyuncs.com/typecho/2019/12/12/327453314084392/image-20191212134148323.png) 启动项目flyway创建版本管理表`flyway_schema_history`,并遍历`db.migraion`目录下数据库sql文件。 ![image-20191212134600710.png](https://mufeng-blog.oss-cn-beijing.aliyuncs.com/typecho/2019/12/12/327461721587827/image-20191212134600710.png) #### 数据库执行顺序 如果项目中使用到`mybatis`或`hibernate`等ORM框架,需要修改数据库连接池和创建数据库连执行顺序,需要手动的配置flyway执行顺序。 ##### 创建flyway的配置类 ```java package com.newgrand.common.config; import com.google.errorprone.annotations.concurrent.LazyInit; import org.flywaydb.core.Flyway; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Lazy; import org.springframework.core.annotation.Order; import javax.annotation.PostConstruct; import javax.sql.DataSource; /** * @author LiuRui * @version 1.0 * @date 2019/12/12 17:20 * @description */ @Configuration public class FlywayConfig { @Autowired private DataSource dataSource; @PostConstruct public void migrate() { Flyway flyway = Flyway.configure() .dataSource(dataSource) .locations("classpath:db/migration") .baselineOnMigrate(true) .load(); flyway.migrate(); } } ``` 在数据库连接工厂前添加方法执行以来,添加DependsOn注解,保证被依赖的bean先于当前bean被容器创建。 ![image-20191212203945613.png](https://mufeng-blog.oss-cn-beijing.aliyuncs.com/typecho/2019/12/12/555032342839010/image-20191212203945613.png) ### 注意 flyway在执行脚本时,会在源数据表中检查checksum值,并确定上次运行运行到哪一个脚本文件,本次执行时从下一条脚本文件开始执行。 所以需要注意下: 编写脚本的时候不要去修改原有的脚本内容 新的脚本版本号要连续,如果需要重复修改执行脚本可以使用repeatable migration(可重复迁移),编写的sql语句为可重复升级语句。 > **这里建议多人开发时,先使用`repeatable migration`模式对一个脚本进行开发,当应用程序发版的时候在定一个`Versioned Migrations`模式的版本。** ### Flyway工作模式 文件名格式规定 - 版本控制:`V`+`版本号`+`__`+`描述`+`.sql` - 可重复调用:`R`+`__`+`描述`+`.sql` 具体要求: - 版本号和版本描述之间,使用两个下划线分隔。 - 版本描述之间,使用一个下划线分隔单词。 - 版本号唯一:不允许多个脚本文件有相同的版本号。 使用Flyway升级,flyway会自动创建一张历史记录表:flyway_schema_history。 ### flyway遇到的问题 #### 1、时区问题 ``` java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support. ``` ##### 解决方案 在数据库连接地址后边加上时区,例如 ```yaml url: jdbc:mysql://localhost:3306/testdb?serverTimezone=GMT%2B8 ``` #### 2、首次创建库问题 程序第一次运行时,并没有手动创建数据库(schema),但是flyway的操作是在已存在的数据库中进行,否则会报错。 ##### 解决方案 可以使用druid配合创建。 ```java package com.felton.springbootflyway.config; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; @Configuration @Primary //在同样的DataSource中,首先使用被标注的DataSource public class DataSourceConfig { // private Logger log = LoggerFactory.getLogger(DataSourceConfig.class); @Value("${spring.datasource.url}") //jdbc:mysql://127.0.0.1:3306/insight?useUnicode=true&characterEncoding=utf8&failOverReadOnly=false&allowMultiQueries=true private String datasourceUrl; @Value("${spring.datasource.driver-class-name}") private String driverClassName; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Bean //声明其为Bean实例 public DataSource dataSource() { DruidDataSource datasource = new DruidDataSource(); datasource.setUrl(datasourceUrl); datasource.setUsername(username); datasource.setPassword(password); datasource.setDriverClassName(driverClassName); try { Class.forName(driverClassName); String url01 = datasourceUrl.substring(0, datasourceUrl.indexOf("?")); String url02 = url01.substring(0, url01.lastIndexOf("/"))+"?serverTimezone=GMT%2B8"; String datasourceName = url01.substring(url01.lastIndexOf("/") + 1); // 连接已经存在的数据库,如:mysql Connection connection = DriverManager.getConnection(url02, username, password); Statement statement = connection.createStatement(); // 创建数据库 statement.executeUpdate("create database if not exists `" + datasourceName + "` default character set utf8 COLLATE utf8_general_ci"); statement.close(); connection.close(); } catch (Exception e) { e.printStackTrace(); } return datasource; } } ``` #### 3、flyway_schema_history显示中文乱码 如果版本文件名称中包含中文字符,数据库创建使用UTF-8字符集,数据库连接传输后改变字符集,导致数据库存储中文乱码问题 ##### 解决方案 数据库连接添加编码设置 ``` url: jdbc:mysql://localhost:3306/testdb?serverTimezone=GMT%2B8&characterEncoding=utf-8 ``` #### 4、出现checksum不匹配问题 原有的数据库脚本文件的checksum与数据库中存储的不一致,导致程序报错无法启动。 ``` # BeanCreationException org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayConfig': Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Validate failed: Migration checksum mismatch for migration version 2.0 -> Applied to database : 142306046 -> Resolved locally : -943842776 ``` ##### 问题分析 数据库脚本文件内容没有改变,但是可能文件在拷贝或传输过程中大小会被修改,导致数据库中的`flyway_schema_history.checksum`数据与当前文件不匹配(Migration checksum mismatch for migration version X.X)。 ##### 解决方案 使用flyway的修复功能,flyway.repair()。项目数据库迁移前先对`flyway_schema_history.checksum`进行修复 打赏: 微信, 支付宝 标签: mysql, flyway 本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
这个♥是什么鬼。。。
啥❤
点一下出个♥,不应该是富强、民主、文明、和谐吗。。(/◔ ◡ ◔)/
不是的呀,猪头 (((o(゚▽゚)o)))♡
这个阅读数的增加算法不太靠谱啊
你小子 搞事情
最近这几篇没有实际业务切入点、重量不重质的博客,花一辈子都上不了的阅读量哥哥几分钟给你刷出来,是礼物还是搞事你心里没点B数么
该应用已经实际用在工作开发中,阅读量这种东西就数据库上的一个数字,谢谢你的好意