Avoiding table changes when mapping legacy database tables in Grails? - grails

I have an applicaton that contains some tables that are auto-generated from Grails domain classes and one legacy table (say table legacy) that have been created outside of Grails but are being mapped by Grails domain classes. Mapping the columns in the legacy database is trivial, but I would like to disable the adding of extra fields and indexes that Grails tries to take care of for said table.
My question is: How do I instruct Grails not to make any table changes to the legacy table (changes such as adding indexes, foreign keys, version columns, etc.)?
Please note that I do not want to disable the automatic schema generation/updating for all tables, only for the mapped table legacy.

The only way I've been able to do stuff like this is a custom Configuration class:
package com.foo.bar;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
public class DdlFilterConfiguration extends GrailsAnnotationConfiguration {
private static final String[] IGNORE_NAMES = { "legacy" };
#Override
public String[] generateSchemaCreationScript(Dialect dialect) throws HibernateException {
return prune(super.generateSchemaCreationScript(dialect), dialect);
}
#Override
public String[] generateDropSchemaScript(Dialect dialect) throws HibernateException {
return prune(super.generateDropSchemaScript(dialect), dialect);
}
#Override
public String[] generateSchemaUpdateScript(Dialect dialect, DatabaseMetadata databaseMetadata) throws HibernateException {
return prune(super.generateSchemaUpdateScript(dialect, databaseMetadata), dialect);
}
private String[] prune(String[] script, Dialect dialect) {
if (dialect instanceof HSQLDialect) {
// do nothing for test env
return script;
}
List<String> pruned = new ArrayList<String>();
for (String command : script) {
if (!isIgnored(command)) {
pruned.add(command);
}
}
return pruned.toArray(new String[pruned.size()]);
}
private boolean isIgnored(String command) {
command = command.toLowerCase();
for (String table : IGNORED_NAMES) {
if (command.startsWith("create table " + table + " ") ||
command.startsWith("alter table " + table + " ") ||
command.startsWith("drop table " + table + " ")) {
return true;
}
}
return false;
}
}
Put this in src/java (it can't be written in Groovy because of a weird compilation error) and register it in DataSource.groovy using the 'configClass' attribute:
dataSource {
pooled = true
driverClassName = ...
username = ...
password = ...
dialect = ...
configClass = com.foo.bar.DdlFilterConfiguration
}

My solution was a bit simpler.
in the mapping section of the Domain class, I just set version false and I named the 'id' column.
class DomainClass {
static mapping = {
table 'legacyName'
version false
columns{
id column: 'legacy_id'
}
}
}

You can try using Hibernate annotations to specify things such as column name, table, etc instead of creating a normal domain class. For more info see the "Mapping with Hibernate Annotations" section of the following link.
http://www.grails.org/Hibernate+Integration

Related

Spring Data Neo4j 4 and dynamic properties

At my Neo4j/SDN 4 project I have a following entity:
#NodeEntity
public class Value extends BaseEntity {
#Index(unique = false)
private Object value;
private String description;
...
}
During the application run-time I want to be able to add a new dynamic properties to Value node, like for example value_en_US, value_fr_FR.
Right now I don't know what exact properties will be added to a particular Value node during application run-time so I can't define these properties at the code as a separate fields in Value.
Is there at SDN 4 any mechanisms to define these properties during the application run-time? I need something similar to DynamicProperties from SDN 3.
There is no such functionality in SDN 4, but it will be added in SDN 5 through a #Properties annotation on Map.
It will be available for testing in snapshot version very soon.
Check out this commit for more details
You might also want to look at this response to a similar question.
https://stackoverflow.com/a/42632709/5249743
Just beware that in that answer the function:
public void addAllFields(Class<?> type) {
for (Field field : type.getDeclaredFields()) {
blacklist.add(field.getName());
}
if (type.getSuperclass() != null) {
addAllFields(type.getSuperclass());
}
}
is not bullet proof. For one thing it doesn't look at #Property annotations. So if you want to go down that route keep your eyes open.
An 'improvement' is
public void addAllFields(Class<?> type) {
for (Field field : type.getDeclaredFields()) {
blacklist.add(findName(field));
}
if (type.getSuperclass() != null) {
addAllFields(type.getSuperclass());
}
}
private String findName(Field field) {
Property property = field.getAnnotation(Property.class);
if(property == null || "".equals(property.name())) {
return field.getName();
} else {
return property.name();
}
}
But this obviously doesn't look for the annotation on methods...

groovy script having access to domain classes

I would like to have a groovy script that can access my domain classes and extract all properties from there.
I have not written any groovy-scripts so far within my Grails application.
How do I do this?
I am thinking of something like
run-script <scriptname>
In the script I would like to
For all Domain classes
For all Fields
println (<database-table-name>.<database-field-name>)
What would be the easiest approach to achieve this.
Below I'm including a script code using which you can list down all the domain classes with their properties. This script generates a Map that contains the db mapping for domain and its properties. If you have a different requirement, you can achieve that using the same approach.
import org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass
import org.codehaus.groovy.grails.commons.DomainClassArtefactHandler
import org.codehaus.groovy.grails.orm.hibernate.persister.entity.GroovyAwareSingleTableEntityPersister as GASTEP
import org.hibernate.SessionFactory
//Include script dependencies required for task dependencies
includeTargets << grailsScript("Bootstrap")
target(grailsDomianMappings: "List down field details for all grails domain classes") {
//Task dependencies required for initialization of app. eg: initialization of sessionFactory bean
depends(compile, bootstrap)
System.out.println("Running script...")
//Fetch session factory from application context
SessionFactory sessionFactory = appCtx.getBean("sessionFactory")
//Fetch all domain classes
def domains = grailsApp.getArtefacts(DomainClassArtefactHandler.TYPE)
GASTEP persister
List<String> propertyMappings = []
Map<String, List<String>> mappings = [:]
//Iterate over domain classes
for (DefaultGrailsDomainClass domainClass in domains) {
//Get class meta data
persister = sessionFactory.getClassMetadata(domainClass.clazz) as GASTEP
propertyMappings = []
//fetch table name mapping
String mappedTable = persister.tableName
//fetch all properties for domain
String[] propertyNames = persister.propertyNames
propertyNames += persister.identifierPropertyName
//fetch column name mappings for properties
propertyNames.each {
propertyMappings += persister.getPropertyColumnNames(it).first()
}
mappings.put(mappedTable, propertyMappings)
}
//Print data
mappings.each { String table, List<String> properties ->
properties.each { String property ->
System.out.println("${table}.${property}")
}
System.out.println("++++++++++++++++++++++++++++++++++++++++++++++")
}
}
setDefaultTarget(grailsDomianMappings)

How to generate sql file from Liquibase without DATABASECHANGELOG inserts?

I have the following problem : I need to produce migration file for a database in production. Currently I'm using ant and following ant task :
<liquibase:updateDatabase changeLogFile=db.changelog-master.xml" databaseRef="oracle-database" outputFile="out_ora.sql" />
But my file includes insert statements for DATABASECHANGELOG table. How can I produce output file without this statements ? (I wouldn't like to delete this statements manually or by some script later).
You can use this extension: https://github.com/liquibase/liquibase-nochangelogupdate
Just add the jar to your classpath and liquibase will not output any databasechangelog SQL
If you want to filter the insert/update from ChangeSets irrespective of Liquibase Insert/update statements retaining only the Create, Alter scripts.
Define a property with list of classes to be excluded
sqlgenerator.exclude=liquibase.statement.core.InsertOrUpdateStatement,liquibase.statement.core.InsertStatement,liquibase.statement.core.UpdateStatement,liquibase.statement.core.GetNextChangeSetSequenceValueStatement,liquibase.statement.core.MarkChangeSetRanStatement,liquibase.statement.core.RemoveChangeSetRanStatusStatement,liquibase.statement.core.UpdateChangeSetChecksumStatement
Implement a SQL Generator class that filters all the SQL statements falls into the class category listed above.
Spring injects the property into the class. Be sure to create the class under the package "liquibase.sqlgenerator.ext".
#Component
public class FilteredSQLGenerator extends AbstractSqlGenerator<AbstractSqlStatement> {
private static final Logger LOGGER = Logger.getLogger(FilteredSQLGenerator.class);
private static String[] excludeArr = new String[0];
#Value("${sqlgenerator.exclude}")
private String exclude;
#PostConstruct
public void init() {
LOGGER.debug(" Exclude List set to : " + exclude);
if (StringUtils.isNotBlank(exclude)) {
excludeArr = StringUtils.split(exclude, ',');
}
}
#Override
public ValidationErrors validate(AbstractSqlStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
return sqlGeneratorChain.validate(statement, database);
}
#Override
public int getPriority() {
return 1000;
}
#Override
public Sql[] generateSql(AbstractSqlStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
String clazzName = statement.getClass().getName();
for (String exclude : excludeArr) {
if (exclude.equals(clazzName)) {
return new Sql[0];
}
}
return sqlGeneratorChain.generateSql(statement, database);
}
}

Need help creating Grails executeQuery based on domain and lookup table

I have two classes: sample, and parameter. I also have a sample_sample_parameter lookup table which exists to hold the sample id and the parameter id. This is mapped in my grails app.
I was able to write an sql query that works in squirrel:
select s.* from sample s, sample_sample_parameters sp where s.id = sp.sample_id and sp.sample_parameter_id = 41
where 41 would be replaced with the parameter.id variable passed from the gsp page to the action. I have also tried to make it work with executeQuery but it tells me that sample is not mapped.
How do I turn this query into a gorm recognizable form?
class Sample {
Date collectionDate // date the sample was collected
Date sampleReceivedDate // date the sample arrived on site
Date dateCreated
String sampleComments // details about the sample
String labAccessionID // internal reference
String sampleGender // is it from a male or female?
String sampleAge // the age of the animal the sample was taken from
String sampleBreed // the breed of animal
String sampleNameID // patient name
String filepath
String enteredby
String sg
String mosm
static searchable = true
static hasMany =[sampleParameters:SampleParameter, ficsruns:Ficsrun]//[tags:Tag]// a Sample can have many parameters
/* mappedBy allows two tables share a common table. It creates two join tables, one for each.
* SampleParameter is the table being shared by Sample and SampleType tables */
static mappedBy=[sampleParameters:"samples"]//[tags:"domainClass1s"]/*http://www.van-porten.de/2010/09/multiple-many-to-many-in-grails/*/
static belongsTo = [sampleType:SampleType, labFinding:LabFinding, sampleSource:SampleSource, species:SpeciesList] // creates dependencies
static constraints = {
sampleType(blank:false)
sampleNameID(blank:false)
collectionDate(blank:false)
sampleReceivedDate(blank:false)
sampleComments(nullable:true, maxSize:1000)
labAccessionID(nullable:true)
sampleGender(blank:false, inList:["M","F","NM","SF", "UNK"])
sampleAge(nullable: true)
sampleBreed(nullable:true)
sampleSource(blank:false)
species(blank:false)
labFinding(nullable:true)
filepath(nullable:true)
enteredby(nullable:true)
sg(nullable:true)
mosm(nullable:true)
dateCreated()
}
/* This section is for static mapping to the hematology database*/
static mapping = {
version false
id generator:'sequence', params:[sequence:'SHARED_SEQ']
}
String toString(){
"${sampleNameID}"
}
}
class SampleParameter implements Comparable{
String name
String value
static hasMany = [
samples:Sample, //domainClass1s: DomainClass1,
sampleTypes:SampleType //domainClass2s: DomainClass2
]
static mapping = {
version false
id generator:'sequence', params:[sequence:'SHARED_SEQ']
}
static mappedBy = [samples:"sampleParameters",sampleTypes:"sampleParameters"]//[domainClass1s: "tags", domainClass2s: "tags"]
static belongsTo =[Sample,SampleType] //[DomainClass1, DomainClass2]
static constraints = {
name(blank:false)
//value()
//value(unique:true)
value (unique: 'name')
}
#Override public String toString() {
return name + " " + value
}
#Override
public int compareTo(Object o) {
if (o == null || this == null) {
return 0;
} else {
return value.compareTo(o.value)
}
}
}
As a first suggestion, when you have the paramter's id, do the following.
Parameter p = Parameter.get(params.id) // or wherever your id is stored
List<Sample> samples = Sample.findAllByParameter(p) // this assumes, the parameter property is actually named 'parameter'
Of course there is no error handling in place right now, but you'll get the idea.
Welcome to GORM, welcome to Grails.
The problem is that you're not using a HQL query in your executeQuery method. Instead you're using native sql. From the manual:
The executeQuery method allows the execution of arbitrary HQL queries.
Take a look at the specification to see ways of doing that. Which, by the way, are way easier than native sql.
List<Sample> samples = Sample.findAllBySampleParameter(SampleParameter.get(variable))
Give it a try?

How To Use A Unique Constraint On Multiple Columns Instead Of Creating A Composite Key

The new system I'm starting has several domains that are just join tables for many-to-many relations. They are all like this example for Spring Security Core. In the case, of Spring Security, a "legacy" database is being mapped, so using a composit key is necessary.
My question is if it's possible to put a unique constraint on the combination of the fields instead of having to use a composite key and thus having to implement the get and other functions. I'd be quite happy to use the id as the key, but I do need to ensure the records are unique.
In other words, how could I add a unique constraint on secUser + secRole instead of creating a composite key in the following domain?
class SecUserSecRole implements Serializable {
SecUser secUser
SecRole secRole
boolean equals(other) {
if (!(other instanceof SecUserSecRole)) {
return false
}
other.secUser?.id == secUser?.id &&
other.secRole?.id == secRole?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (secUser) builder.append(secUser.id)
if (secRole) builder.append(secRole.id)
builder.toHashCode()
}
static SecUserSecRole get(long secUserId, long secRoleId) {
find 'from SecUserSecRole where secUser.id=:secUserId and secRole.id=:secRoleId',
[secUserId: secUserId, secRoleId: secRoleId]
}
static SecUserSecRole create(SecUser secUser, SecRole secRole, boolean flush = false) {
new SecUserSecRole(secUser: secUser, secRole: secRole).save(flush: flush, insert: true)
}
static boolean remove(SecUser secUser, SecRole secRole, boolean flush = false) {
SecUserSecRole instance = SecUserSecRole.findBySecUserAndSecRole(secUser, secRole)
if (!instance) {
return false
}
instance.delete(flush: flush)
true
}
static void removeAll(SecUser secUser) {
executeUpdate 'DELETE FROM SecUserSecRole WHERE secUser=:secUser', [secUser: secUser]
}
static void removeAll(SecRole secRole) {
executeUpdate 'DELETE FROM SecUserSecRole WHERE secRole=:secRole', [secRole: secRole]
}
static mapping = {
id composite: ['secRole', 'secUser']
version false
}
}
You could do that if you're allowed to add a new column that'll be the real primary key, e.g. an autoincrement or a sequence-based column. Then adding the unique constraint would be simple:
class SecUserSecRole {
SecUser secUser
SecRole secRole
static constraints = {
secUser(unique:'secRole')
}
}
But I doubt that's an option :)
Note that Grails 2.0 has support for Hibernate Bags, which addresses the collection performance problems that this SecUserSecRole approach works around.

Resources