What is a good workflow for database migration in Grails? - grails

I want to use the database-migration grails plugin for database migration. When I start my Grails app the first time all the database tables are created automatically. The production setting in my DataSource.groovy is:
production {
dataSource {
dbCreate = "update"
url = "jdbc:mysql://localhost/myapp?useUnicode=yes&characterEncoding=UTF-8"
username = "test"
password = "test"
dialect = org.hibernate.dialect.MySQL5InnoDBDialect
properties {
validationQuery = "select 1"
testWhileIdle = true
timeBetweenEvictionRunsMillis = 60000
}
}
}
In my config.groovy I set:
grails.plugin.databasemigration.updateOnStart = true
grails.plugin.databasemigration.updateOnStartFileNames = ['changelog.groovy']
When I add properties to my domain classes I need to adjust the changelog file.
What is the best way to do database migration in this case? What are the steps I have to do when I add or remove columns?

As you're probably aware, the dbcreate directive is not recommended for production use:
You can also remove the dbCreate setting completely, which is recommended once your schema is relatively stable and definitely when your application and database are deployed in production.
So keep in mind that you will need to remove this (or set to 'none').
Initial Baseline Workflow
Define current state
Create database from change log or mark as up-to-date
Set config options
The first step is to get the changelog to reflect the current state. If you've got an existing database, you want to use that to define the baseline. Otherwise, use GORM to define the tables.
These commands will generate a baseline for your database. Also I choose to use the groovy DSL format rather than liquibase XML, because readability.
Existing Database
If you've got a production database with data already, its a little bit tricky. You will need to access the database or a copy of it from your grails environment. If you manipulate a copy, you will need to apply the updates back to your production (and potentially manage it as a planned outage).
The command is:
grails [environment] dbm-generate-changelog changelog.groovy
...where environment optionally specifies the dev/test/prod/custom environment the database is defined as.
Following that, mark the database as 'up-to-date' with regards to the changelog:
grails [environment] dbm-changelog-sync
Then reapply the database to production, if neccesary.
New Database
If you don't have an existing database (or don't care):
grails dbm-generate-gorm-changelog changelog.groovy
Then, to create the database from the changelog:
grails [environment] dbm-update
Configuration
You've already correctly got the options set:
grails.plugin.databasemigration.updateOnStart = true
grails.plugin.databasemigration.updateOnStartFileNames = ['changelog.groovy']
These options simply mean that the plugin will attempt to apply the changes to the database when the application starts.
Development Workflow
Make changes to domains
Generate changelog identifying differences
(Backup and) Update the database
So now you've got a database up-to-date, and you're smashing out changes to the domain classes, adding new ones and changing validation properties.
Each time you want to record your changes, you want to compare your GORM classes to what exists in the database, and create a new changelog file to record the difference:
grails [environment] dbm-gorm-diff [meaningful name].groovy --add
Here environment is the database you are comparing against, and meaningful name should reflect in some way the change being applied (perhaps a JIRA issue key, or a version number, or a description).
The --add flag will insert an include statement in changelog.groovy.
If you've configured updateOnStart, then you're done! Otherwise, to manually process the update, reuse the command:
grails [environment] dbm-update
RTFM
Plugin documentation - Getting Started
Plugin documentation - General Usage
Confile's answer above points to a good tutorial that goes into detail about manual changes to changelogs
Liquibase documentation - Changesets (Uses the XML format, but useful for understanding concepts)

The approach that I would use is to migrate every table to a Grails domain with the mapping (very important!) properly set.
Then leave Grails to create the database the first time and then populate it with a previous backup of the database you want to migrate.
After this set Grails config to update the database every time it starts.
I know it seems a little bit messy but if I´ve to do it I would´ve do it this way.
Hope it helps :)

I found a very good tutorial, which explains the solution to my problem:
Grails Db Migration Tutorial

Workflow consists of following steps:
1) Install the plugin using the command grails install-plugin database-migration
2) After setting up the plugin run the command:
grails dbm-generate-gorm-changelog changelog.groovy or changelog.xml
By default it will generate a file on location grails-app/migrations/changelog.groovy or .xml
3) set dataSource dbcreate='none'
3) Now, run
grails dbm-changelog-sync
this will create a table name databasechangelog and will insert entries according to your existing schema.
Thats it.

Related

Grails database migration gorm diff yields no changes

Background
I have a relatively new Grails project using 3.0.14. I am looking to integrate liquibase for database migrations via the Database Migration plugin (2.0.0.RC4).
I have a large enough domain model so far that I have used the plugin to 'seed' an initial changelog. This is straight from the docs, and works as intended:
grails dbm-generate-gorm-changelog changelog.groovy
What I am now trying to test/get working is the dbm-gorm-diff command, which will take changes to the domain model and create a changelog that can be applied. This is where I am running into issues.
The Grails documentation suggest removing the dbCreate block from the datasource to ensure that Hibernate doesn't do the updating, and Liquibase can take over. Great, exactly what I want.
The Issue
When I remove dbCreate, Grails/hibernate still seems to update the database before the Database Migration plugin has a chance to do the diff. When doing the diff, it is already too late to see changes, so the changelogs do not contain the right data.
Config
dataSource:
pooled: true
jmxExport: true
driverClassName: org.h2.Driver
username: sa
password:
environments:
development:
dataSource:
dbCreate: verify
driverClassName: org.postgresql.Driver
dialect: org.hibernate.dialect.PostgreSQLDialect
url: jdbc:postgresql://127.0.0.1:5432/liquibase_test
username: dbuser
password: dbuser
logSql: false
formatSql: true
(I am aware that the dbCreate is set to verify. More on this later)
Steps Taken
Create a new postgres database - dbcreate -U dbuser liquibase_test
Run the initial changelog on the new database - grails dbm-update
Verify that the database is now up to date, and check that select * from databasechangelog equals the number of changes in changelog.groovy
Add a new simple domain class:
class TestDomain {
int testInt
}
Run the plugin to get the diff - grails dbm-gorm-diff add-simple-domain.groovy. The command fails with an exception:
:DataModel:dbmGormDiff
Command execution error: liquibase.command.CommandExecutionException: java.lang.NullPointerException
DataModel:dbmGormDiff FAILED
Now, remove the config dbCreate: verify from above, and run again
This completes successfully without exception, but there are issues:
the command created add-simple-domain.groovy, but it has no mention of the new domain class I just created. (It has index/sequences, but I think this is a known issue)
the new domain class has been added to the database(!?) (checked in PgAdmin)
the table databasechangelog still has the original row count, and even when interrogated no reference to the new domain class
So, I'm at a loss to explain what is going on. I can deal with the extra create/drop indexes & sequences, but I can't seem to get the liquibase stuff working. Can anyone shed some light on this for me?
Edit
I did some more digging into the NullPointer, and it seems to come from the class liquibase/ext/hibernate/snapshot/ForeignKeySnapshotGenerator.java:45, where the plugin is trying to construct a foreign key to the inherited table id field (using tablePerHierarchy false for this inheritance). I couldn't find anything that seemed related to this error after a decent search.
Edit #2
I have found an issue on Github for the tablePerHierarchy NPE: https://github.com/grails-plugins/grails-database-migration/issues/68
Update your application.yml (or application.groovy) configuration for your datasource:
dataSource:
dbCreate: none
Setting to "none" is not the same thing as removing dbCreate entirely - you need to set it explicitly to overwrite any defaults that are being set elsewhere.
"none" seems not to work for me when using JNDI datasources and still causes the ddl to run. I set it to "ignore" to be able to use db-migrations with JNDI datasources in Grails 3.0.x
I ended up getting this to work by setting hibernate.hbm2ddl.auto = 'none' in my application.groovy. Interestingly, when I tried to instead put this same config in my application.yml it had no effect.
I suspect there may be other forces at play here as I tried replicating the behaviour on a fresh Grails project without issue.
For the time being I have settled on using the hibernate property in the groovy file, though I am still curious as to why I couldn't get the config to work for me like a vanilla project.

MVC 4 Server error

I am new to mvc in asp.net, so I have started with movie application in mvc with code first approach. My problem is when I change or add new field in movie class, then I got an error like
The model backing the 'MovieDBContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).
So anyone knows the solution for it with code first approach after changing or add any new field ...
using three command its solve which are list below.....which we have to write in package manager console
1)Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext
2)add-migration Initial
3)update-database
but why this error occurs and is there any other solution for it without the above three commands in mvc 4?
You have changed the Code First models since applying them to your database, so you need to reupdate, to apply these changes. First you add a new migration. Do this by typying this into the package manager console:
add-migration migrationName
migrationName should be unique and descriptive of the changes. After running this command, you should have a migration file, which you can apply by running:
update-database
Given that the changes are valid, they should now be applied to your database.
You have to do this every time you make changes to your CF models.

doctrine:generate-migrations-diff does not generate any new migration

After some changes I've made today in my schema.yml, each of one followed by the diff, migrate, build commands, the diff operation stopped working.
The last successful migration was the 243rd.
Now, every new change I make, when I give the diff command, the result is always the same:
/usr/bin/php /.../symfony --color doctrine:generate-migrations-diff
>> doctrine generating migration diff
>> file+ /tmp/doctrine_schema_92228.yml
Done.
No new file is created in lib/migration/doctrine, so I cannot use the migrate command to commit the changes to the db.
I tried to clear the cache, clean model files, build all classes, and also reboot.
Any ideas?
This is the best way I have come across to make migrations and success everytime. Cost me a lot to guess but works perfectly and works with multiple databases. I can be wrong in some sentences so feel free to add or correct me in anything you see.
MIGRATIONS, the safest way :)
If you need to work Migrations for multiple databases apply these patches and clear symfony cache, they work perfectly:
doctrine_core.r7687
doctrine_manager.r7657
A. BACKUP PROJECT AND DATABASE:
Save Symfony project Files. (optional but safe way).
Save Database Table Schemas only.
Save Database Table Data only.
Save Database Table Schema with Data.
B. HOW TO MAKE CHANGES TO .yml FILES:
.yml files cannot contain strange symbols, like ñ, ´, ```... or non UTF characters..
Always shows spaces and tabs in Notepad++ or Sublime. There cannot be tabs!!
You CANT have two modules with the same name, even in different databases. Never set two modules with same name or you will have a lot of problems.
If you want to work with multiple databases, you must specify the connection attribute at the beginning of your schema.yml file:
connection: doctrine_master
Working with multiple databases, again you must set the binding connection for the module with the right connection:
Tbtest001:
connection: doctrine_master
tableName: tb_test001
Setting the right variable value and type in schema.yml:
Schema Files
Variables, models and types
Working with multiple databases, take care and modify only one schema.yml for only one database each time!
If you are adding a new table with relations to another table, its recommended to do it in two steps, two migrations. First only add the table and migrate. Then Add the relation and Migrate again. It is the saftest way.
You can have different schemas.yml in different places.
C. MIGRATING THE CHANGES:
Install this plugin, because it has fixes and improvements for checking changes:
idlDoctrineMigrationPlugin
Make a new table for each database for your project. Needed for the plugin to work:
name: migration_version , column: version (int (11)). (autoincrement=false).
In version column, set its value to the lastest migration version you have now. You must do this step for every database where you have the table migration_version:
UPDATE databasetest.migration_version SET databasetest.migration_version.version='31';
UPDATE databasetest2.migration_version SET databasetest2.migration_version.version='31';
Clear Symfony cache:
symfony cc
Make the migration difference (you need the plugin above and version tables created)
symfony model:diff > migratediff.log
Check if the lastest generated changes are right in the following files:
.\lib\migration\doctrine\XXXXXX_versionXXX.php
.\data\migration\history\XXXXXXXXXX.yml
Proceed with the migration UP by specifing a number!, NEVER make migrate UP!. Also take in mind the new parameter --connection. It works now if you applied the above patches and it will migrate only the right databases:
symfony doctrine:migrate 32 --connection=doctrine_master > migrateUP.log
Rebuild models, Forms, Filters, Delete old models..
symfony doctrine:build-model
symfony doctrine:build-forms
symfony doctrine:build-filters
symfony doctrine:clean-model-files
symfony cc
Set all databases to the lastest migration number in their table migration_version:
UPDATE databasetest.migration_version SET databasetest.migration_version.version='32';
UPDATE databasetest2.migration_version SET databasetest2.migration_version.version='32';
Optional step, if you want to know the lastest SQL query send to the database after the migration:
symfony doctrine:build-sql [--application[="..."]] [--env="..."]
D. LINKS AND FILES:
Correct way to do a migrations diff
Doctrine migrations fallback
http://trac.symfony-project.org/ticket/7272
http://trac.symfony-project.org/ticket/7689
http://trac.symfony-project.org/ticket/8604
http://php-opensource-help.blogspot.com/2010/07/how-to-get-connection-from-doctrine-to.html
http://www.doctrine-project.org/documentation/manual/1_1/en/connections
http://forum.symfony-project.org/viewtopic.php?t=29361&p=104098
Main Files involved in migrations:
Migration.php, Builder.php, sfTaskMigration.php

Rake - how to use to modify database.yml config prior to invoking task?

I have a database with a public schema where we keep login/account information and multiple client schemas for client data. I've successfully used yaml_db to dump/load test data into a client schema, but this requires constantly modifying the schema_search_path between the public schema and client specific one each time I want to reload data.
I'm trying to build a rake task to automate this, but am unsure how to modify schema_search_path in prior to invoking the db:data:load task.
I can view the current values in database.yml by doing this:
env = "#{RAILS_ENV}"
config = YAML::load(File.open('config/database.yml'))
puts config[env]["schema_search_path"]
But I'm unclear how to modify that value to be used when i call
Rake::Task['db:data:load'].invoke
I've tried just assigning a new value to the
config[env]["schema_search_path"] = "test_data_schema"
but that doesn't appear to work
Figured this out - just needed to establish the ActiveRecord::Base connection with modified config prior to invoking the Rake task:
ActiveRecord::Base.establish_connection(config[env])
Rake::Task['db:data:load'].invoke()

Ruby on Rails Migration - Create New Database Schema

I have a migration that runs an SQL script to create a new Postgres schema. When creating a new database in Postgres by default it creates a schema called 'public', which is the main schema we use. The migration to create the new database schema seems to be working fine, however the problem occurs after the migration has run, when rails tries to update the 'schema_info' table that it relies on it says that it does not exist, as if it is looking for it in the new database schema and not the default 'public' schema where the table actually is.
Does anybody know how I can tell rails to look at the 'public' schema for this table?
Example of SQL being executed: ~
CREATE SCHEMA new_schema;
COMMENT ON SCHEMA new_schema IS 'this is the new Postgres database schema to sit along side the "public" schema';
-- various tables, triggers and functions created in new_schema
Error being thrown: ~
RuntimeError: ERROR C42P01 Mrelation "schema_info" does not exist
L221 RRangeVarGetRelid: UPDATE schema_info SET version = ??
Thanks for your help
Chris Knight
Well that depends what your migration looks like, what your database.yml looks like and what exactly you are trying to attempt. Anyway more information is needed change the names if you have to and post an example database.yml and the migration. does the migration change the search_path for the adapter for example ?
But know that in general rails and postgresql schemas don't work well together (yet?).
There are a few places which have problems. Try and build and app that uses only one pg database with 2 non-default schemas one for dev and one for test and tell me about it. (from thefollowing I can already tell you that you will get burned)
Maybe it was fixed since the last time I played with it but when I see http://rails.lighthouseapp.com/projects/8994/tickets/390-postgres-adapter-quotes-table-name-breaks-when-non-default-schema-is-used or this http://rails.lighthouseapp.com/projects/8994/tickets/918-postgresql-tables-not-generating-correct-schema-list or this in postgresql_adapter.rb
# Drops a PostgreSQL database
#
# Example:
# drop_database 'matt_development'
def drop_database(name) #:nodoc:
execute "DROP DATABASE IF EXISTS #{name}"
end
(yes this is wrong if you use the same database with different schemas for both dev and test, this would drop both databases each time you run the unit tests !)
I actually started writing patches. the first one was for the indexes methods in the adapter which didn't care about the search_path ending up with duplicated indexes in some conditions, then I started getting hurt by the rest and ended up abandonning the idea of using schemas: I wanted to get my app done and I didn't have the extra time needed to fix the problems I had using schemas.
I'm not sure I understand what you're asking exactly, but, rake will be expecting to update the version of the Rails schema into the schema_info table. Check your database.yml config file, this is where rake will be looking to find the table to update.
Is it a possibility that you are migrating to a new Postgres schema and rake is still pointing to the old one? I'm not sure then that a standard Rails migration is what you need. It might be best to create your own rake task instead.
Edit: If you're referencing two different databases or Postgres schemas, Rails doesn't support this in standard migrations. Rails assumes one database, so migrations from one database to another is usually not possible. When you run "rake db:migrate" it actually looks at the RAILS_ENV environment variable to find the correct entry in database.yml. If rake starts the migration looking at the "development" environment and database config from database.yml, it will expect to update to this environment at the end of the migration.
So, you'll probably need to do this from outside the Rails stack as you can't reference two databases at the same time within Rails. There are attempts at plugins to allow this, but they're majorly hacky and don't work properly.
You can use pg_power. It provides additional DSL for migration to create PostgreSQL schemas and not only.

Resources