liquibase特点:
支持代码分支和合并
支持多个开发人员
支持多种数据库类型
支持XML,YAML,JSON和SQL格式
支持与上下文相关的逻辑
集群安全的数据库升级
生成数据库更改文档
生成数据库“ 差异 ”
贯穿您的构建过程,嵌入您的应用程序或按需使用
自动生成用于DBA代码审查的SQL脚本
不需要实时数据库连接
liquibase配置文件结构:
master.xml内容:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"> <include file="classpath:liquibase/changelog/table.xml" relativeToChangelogFile="false"/> <!--<include file="classpath:liquibase/changelog/data.xml" relativeToChangelogFile="true" />--> </databaseChangeLog>
table.xml文件内容(这个要根据自己需要创建的DDL或DML改写):
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"> <property name="autoIncrement" value="true" dbms="mysql"/> <property name="now" value="now()" dbms="mysql"/> <changeSet author="zhuyoulong" id="20180503-test" context="development"> <comment>测试</comment> <createTable tableName="department" remarks="部门"> <column name="id" type="bigint" autoIncrement="${autoIncrement}"> <constraints primaryKey="true" nullable="false"/> </column> <column name="name" type="varchar(50)" remarks="名称"> <constraints nullable="false"/> </column> <column name="title" type="varchar(50)" remarks="标题"> <constraints nullable="false"/> </column> <column name="active" type="boolean" defaultValueBoolean="true" remarks="激活"/> <column name="register_time" type="datetime" defaultValueComputed="${now}" remarks="注册时间"> <constraints nullable="false"/> </column> </createTable> <modifySql dbms="mysql"> <append value="ENGINE=INNODB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci"/> </modifySql> </changeSet> </databaseChangeLog>
liquibase.properties:
url=xxx driver=xxx username=xxx password=xxx drop-first=false
maven中添加
1、依赖:
<!--liquibase依赖--> <dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-core</artifactId> <version>3.5.5</version> </dependency>
2、插件(其实这个可以不要,如果手动执行可以用这个):
<plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.5.1</version> <configuration> <!--指定数据库连接--> <propertyFile>src/main/resources/liquibase/liquibase.properties</propertyFile> <!--指定执行主文件--> <changeLogFile>src/main/resources/liquibase/master.xml</changeLogFile> <outputChangeLogFile>src/main/resources/liquibase/master.xml</outputChangeLogFile> <!-- 是否需要弹出确认框--> <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase> <!--输出文件的编码--> <outputFileEncoding>UTF-8</outputFileEncoding> <!--执行的时候是否显示详细的参数信息--> <verbose>true</verbose> <!--是否每次都重新加载properties--> <propertyFileWillOverride>true</propertyFileWillOverride> <rollbackTag>${project.version}</rollbackTag> <tag>${project.version}</tag> </configuration> </plugin>
用注解方式(笔者用这种方式):
package com.cp.config; import liquibase.integration.spring.SpringLiquibase; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; /** * * @author zhuyoulong * @date 2018年-05月-03日 * <p>Update Time: </p> * <p>Updater: </p> * <p>Update Comments: </p> */ @Configuration public class LiquibaseConfig { @Autowired @Qualifier("dataSource") private DataSource dataSource; @Bean public SpringLiquibase liquibase() { SpringLiquibase liquibase = new SpringLiquibase(); liquibase.setDataSource(espDataSource); liquibase.setChangeLog("classpath:liquibase/changelog/master.xml"); liquibase.setContexts("development,test,preproduction,production"); //如果设置为true:第一次执行不会报错,第二次将会报错,导致程序无法启动,所以第一次执行完后一定要改为:false liquibase.setShouldRun(false); return liquibase; } }
这样想起启动就会执行LiquibaseConfig.liquibase实例构造
启动后数据库建立3张表,前两张是默认创建的,记录的建表的日志
liquibase.setShouldRun(true);第二次执行会报错,所以执行完一次后要改为false:
Invocation of init method failed; nested exception is liquibase.exception.DatabaseException: Table 'databasechangelog' already exists [Failed SQL: CREATE TABLE DATABASECHANGELOG (ID VARCHAR(255) NOT NULL, AUTHOR VARCHAR(255) NOT NULL, FILENAME VARCHAR(255) NOT NULL, DATEEXECUTED datetime NOT NULL, ORDEREXECUTED INT NOT NULL, EXECTYPE VARCHAR(10) NOT NULL, MD5SUM VARCHAR(35) NULL, DESCRIPTION VARCHAR(255) NULL, COMMENTS VARCHAR(255) NULL, TAG VARCHAR(255) NULL, LIQUIBASE VARCHAR(20) NULL, CONTEXTS VARCHAR(255) NULL, LABELS VARCHAR(255) NULL, DEPLOYMENT_ID VARCHAR(10) NULL)]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1566)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:530)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1090)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5229)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5516)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1763)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:618)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:565)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
at sun.rmi.transport.Transport$1.run(Transport.java:177)
at sun.rmi.transport.Transport$1.run(Transport.java:174)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:556)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:811)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:670)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
更多资料请参考:
http://www.liquibase.org/