I have an abstract Event class which holds properties shared by all Event types and beneath it I have two subclasses with one table per class (MasterEvent and ParentEvent).
The problem is that in MasterEvent the id column is of type "number" and in the ParentEvent it is of type "varchar2".
This means that when I try to run the app I get :
Caused by HibernateException: Wrong column type in * for column event_id. Found: number, expected: varchar2(255 char).
Note that this is a legacy database, so there's no chance in changing the column types on the database level.
The sample code below should help in understanding better:
Event.groovy
package a
abstract class Event {
String id
static mapping = {
tablePerHierarchy "false"
id column: "id"
}
}
ParentEvent.groovy
package a
class ParentEvent extends Event {
static mapping = {
id column: "id"
}
}
MasterEvent.groovy
package a
class MasterEvent extends Event {
static mapping = {
id column: "id", type: "number"
}
}
I have tried putting type: number all sorts of combinations, but it's always giving me the same error.
Is there anyway to either cast the sqlType directly to String or have grails ignore this validation?
type: 'number' does not work because it is not a hibernate basic type
Try changing it to type: 'long' (or whatever fits your legacy DB)
Also see the docs: Link
Edit after your comment
I don't know what you are doing in your abstract class, but perhaps you can do something like that and use the getter in your abstract class (not tested!):
class MasterEvent extends Event {
Long longId
static mapping = {
longId column: "id"
}
#Override
String getId() {
longId?.toString()
}
}
Did you try to define id field in subclasses? "String id" in Parent event and "long id" in Master event?
You could try a custom Hibernate UserType which takes the number id column and converts it to a String. This would then go in the MasterEvent class:
static mapping = {
id column: "id", type: NumberToStringType
}
Related
I have a MS SQL DB (which I can't change) with 3 tables :
contact
id, name, number
c_group
id,name,email
contact_group
id, contact_id, group_id
In Grails I have 3 domain classes :
class Cgroup {
String name
String email
static constraints = {
}
static mapping = {
table name: "c_group"
version false
}
}
class Contact {
String name
String number
static constraints = {
name nullable:false
}
static mapping = {
version false
}
}
class Contact_group {
Cgroup cgroup
Contact contact
static constraints = {
}
static mapping = {
version false
cgroup_id column: "c_group_id", sqlType: "int"
}
}
I'm trying to create a list using :-
def contactInstance = Contact.get(id) //I have previously got the id
List userGroupList = Contact_group.createCriteria().list(params) {
eq("contact", contactInstance)
}
And it is throwing an error
Invalid column name 'cgroup_id'.. Stacktrace follows:
As you can see, I'm trying to map the correct column name (in the Contact_group domain class) as the table has been renamed from group to c_group in the DB, and just to complicate matters for some reason I decided to call the domain class Cgroup in my Grails app. (I can change this if needs be)
So, I'm now in a bit of a muddle about how best to create the list. Any suggestions or pointers?
In your mapping you should point to the class property (cgroup_id should be cgroup, since that is what you named it in your domain.)
Also, your column name in your table is id not c_group_id so you should set the column as id.
static mapping = {
version false
table name: "contact_group"
cgroup column: "id", sqlType: "int"
}
I need to map Domain classes and subclasses of a legacy database.
The model thath I need to recreate with Grails is to this tables:
Tables structure
CARD_PAYMET and CHEQUE_PAYMENT is subclasses of PAYMENT and share a composite key of two field: OrderId and PaymentId.
I try some ex scenarios, but I can´t arrive to solution. None of then recreate the same model data, and I can´t change this model.
Can any one help me?
Thanks.
Your database looks like a good fit for table-per-subclass inheritance. First, since you're using a composite primary key, your domain classes need to implement the Serializable interface. Then it's a matter of mapping each table column to a property.
import groovy.transform.EqualsAndHashCode
#EqualsAndHashCode(includes=['orderId', 'paymentId'])
class Payment implements Serializable {
int orderId
int paymentId
float amount
static mapping = {
version false
tablePerHierarchy false
id composite: ['orderId', 'paymentId']
orderId column: 'OrderId'
paymentId column: 'PaymentId'
/* Assuming case-insensitive db, so I left out 'Amount'. */
}
}
class CardPayment extends Payment {
String cardType
static mapping = {
version false
cardType column: 'CardType'
}
}
class ChequePayment extends Payment {
int checkNumber
static mapping = {
version false
checkNumber column: 'CheckNumber'
}
}
Note: In this example I used Groovy's EqualsAndHashCode AST transformation to implement Serializable.
With the domain classes in place you'll be able to do GORM polymorphic queries:
def payments = Payment.list() // All Payments (Payment, CardPayment, and ChequePayment).
def cardPayments = CardPayment.list() // Only CardPayments.
...
def nicePayments = Payment.where { amount > 1000 }.list()
Neo4j needs an id field to work which is of type Long. This works well with Spring data neo4j. I would like to have another field of type UUID and have T findOne(T id) to work with my UUID not neo generated Ids.
As I am using Spring Data Rest, I don't want to expose neo's id in the URL.
http://localhost:8080/resource/{neoId}
to
http://localhost:8080/resource/{uuid}
Any ideas if this is possible?
UPDATED
{
name: "Root",
resourceId: "00671e1a-4053-4a68-9c59-f870915e3257",
_links: {
self: {
href: "http://localhost:8080/resource/9750"
},
parents: {
href: "http://localhost:8080/resource/9750/parents"
},
children: {
href: "http://localhost:8080/resource/9750/children"
}
}
}
When it comes to the finder method in your repository you are at liberty to either override the one provided in the CrudRepository in your own interface, or provide an alternative which may be T findByUuid(Long uuid).
Depending on how you are modelling your classes you can rely on the derived query from your method name, or you can annotate with a query, something like:
#Query(value = "MATCH (n:YourNodeType{uuid:{0}) RETURN n")
If you are going to be using a specific UUID class then you will need to tell Neo how to persist the UUID value. If you are will store it as a String (as seems sensible) then I believe there is no need to annotate the field, anything else then you will need a GraphProperty annotation:
#GraphProperty(propertyType = Long.class)
Again depending on the class of UUID you may need to register a conversion class with Spring which implements the org.springframework.core.convert.converter.Converter interface and converts between your domain class type (UUID) and the stored type (String).
Or, just convert the UUID to a string and store it yourself and don't worry about all the conversion.
Whatever you do, make sure that your new uuid is indexed and presumably unique.
You can add a String attribute to your entities, calling it uuid, and simply declare a E findByUuid(String uuid) in your Repository for E, Spring Data will automatically generate code for it.
For example:
#NodeEntity
public class Entity {
...
#Indexed
private String uuid;
...
public String getUuid() {
return uuid;
}
void setUuid(String uuid) {
this.uuid = uuid;
}
...
}
public interface EntityRepository extends GraphRepository<Entity> {
...
Entity findByUuid(String uuid);
...
}
My Grails 2.0 app has a User domain object defined:
class User {
static mapping = {
table "dt_user"
columns {
id column:'user_id', generator:'assigned', type:'string'
}
}
When I try to save a new User in my BootStrap file like so:
def user = new User(id: "smith").save(failOnError:true)
I get the following error:
| Error 2012-01-13 10:09:42,659 [main] ERROR property.BasicPropertyAccessor - IllegalArgumentException in class: User, setter method of property: id
| Error 2012-01-13 10:09:42,660 [main] ERROR property.BasicPropertyAccessor - expected type: java.lang.Long, actual value: java.lang.String
I also tried changing the User class to this:
class User {
static mapping = {
table "dt_user"
columns {
id column:'user_id', generator:'assigned', type:'string', name:'id'
}
}
String id
}
which made the above errors go away. However I found that this resulted in ids being generated automatically, completely ignoring the generator: 'assigned' clause.
What am I doing wrong here?
Looks like wrapping it in the columns block is the culprit. This may have been required at some point (before my time) but it's been optional as long as I've used Grails and apparently is now broken. But you can just declare column mappings directly:
class User {
String id
static mapping = {
table "dt_user"
id column: 'user_id', generator: 'assigned'
}
}
As long as the field is declared as a String and it's configured as assigned it will work; there's no need to tell GORM it's a String, it can figure that out.
Here is the domain class I have defined:
package mypackage
public enum UFModeType {
I(0),
O(1),
R(3)
Integer mode
public UserFileModeType(Integer mode) {
this.mode = mode;
}
static list() {
[I, O, R]
}
}
This is a property of another domain Parent where it is as follows:
package mypackage
class Parent {
String name
... ... ...
UFModeType uFMode
static mapping = {
table 'parent_table_with_ufMode_col_as_number'
version false
tablePerHierarchy false
id generator:'sequence', params:[sequence:'myseq']
columns {
id column:'parentid'
uFMode column: 'UFMODE'
}
}
static constraints = {
userFileMode(nullable: true)
}
}
The gsp call for this looks like this:
g:select name="uFMode" from="${mypackage.UFModeType?.list()}" value="${parentInstance?.uFMode?.name()}" /
I have tried a lot of variants of the above in the gsp call but I am getting error that the db insert fails saying the entry of ufmode is invalid number, thus this is not being passed as a number. I printed the params in the controllers save and it shows this:
Params in save=[uFMode:I ...
I am sure I may be missing some minor thing in syntax, but I have tried a lot of things without much success, so any inputs will be greatly appreciated.
Thanks!
Try changing
value="${parentInstance?.uFMode?.name()}
to
value="${parentInstance?.uFMode?.mode()}
From the definition of UFModeType you give you do not have a name attribute.