I just created a new grails domain class on a project I just started working on. I know the datasources are setup correctly since we already have a bunch of domain classes that are updating to the database just fine.
The error I get:
Caused by BatchUpdateException: ORA-00942: table or view does not exist
I tried running DBMUpdate but that also did not create the table.
Is there something I'm missing with creating domain classes? Do I need to change something in the changelog ? Any advise would be helpful!
The easiest thing would be to add dbCreate = "update" to your DataSource.groovy. A better thing would be to use the database-migrations plugin for your app.
Regarding manually creating tables, the convention grails following by default is to underscore camelcase. For example, given the following domains:
class User {
String firstName
String lastName
static hasMany = [addresses: Address]
}
class Address {
static belongsTo = [user: User]
}
You would end up with the following tables:
user
---------
id
version
first_name
last_name
---------
address
---------
id
version
user_id
---------
Try running...
grails export-schema
This will uses Hibernate's SchemaExport tool to generate DDL or export the schema for the entire database. From that file, you can grab the lines for the missing table.
Check out the Grails Quick Reference for more details on the command.
Related
I am following the spring.io blog here: https://spring.io/blog/2018/09/24/spring-data-jdbc-references-and-aggregates and have a sample SpringBoot app with Book and Author entities here: https://github.com/b-sridhar/spring-data-jdbc-example.
I get the following error: No value supplied for the SQL parameter 'demobook': No value registered for key 'demobook'
And while debugging noticed the SQL query that is executed is: INSERT INTO demo.book_author (author, demo.book) VALUES (:author, :demobook) which should have been INSERT INTO demo.book_author (author, book) VALUES (:author, :book). This happens because I have the #Table("demo.book") as the annotation for the Book entity. If I remove the demo schema in the table name then the tests in BookAndAuthorsTests go through fine.
How to set the schema for the table Book?
This is a bug in the 1.0.x release. It is fixed for 1.1.
If you upgrade your spring-boot-starter-parent to 2.2.0.M5 you'll get the spring-data-jdbc version 1.1.0.RC2 which contains the fix.
With that fix table names in #Table annotations do work as well as the approach of having the NamingStrategy return a schema.
Note: that your configuration then needs to extend AbstractJdbcConfiguration instead of JdbcConfiguration.
Another note: Your example project then chokes because the AuthorRef needs an explicit mapping to the table BOOK_AUTHOR.
I'll leave a PR in a second.
I am trying to understand why something works. I have a user model and within it I state that a User can have one Project e.g.
public function project()
{
return $this->hasOne('App\Project', 'userId');
}
Then within my Project model I define a user belongs to a Project
public function user()
{
return $this->belongsTo('App\User', 'userId');
}
So the way I understand this, a User should be allowed only one project? So within my Projects controller, I have my store function. I wont go over all of it but I essentially do the following
$newProject = new Project();
$newProject->projectName = Input::get('projectName');
$newProject->projectValue = Input::get('projectValue');
$newProject->userId = Input::get('user');
$newProject->save();
Now where I get the input user, this is always the logged in users id. So say I log into the system and I create a new project. This project then has my userID. This works fine.
My question is why does it allow me to create a second project with my same ID? So if I am logged into the system, I can essentially create as many projects as I want under my name. Does this not go against what my relatiohsips are defined as? According to the relationship, I should only be allowed to create one Project.
I am really just looking for information as to why this is allowed?
Thanks
In One to One,
You're linking a record to another record in another table.
That means, You're checking for single record rather than entire table.
So in your example, As you defined it's a correct One - to - One relationship.
Why it's adding new more than one project?
As i said before, We are checking for a single record.
How do i restrict that?
use UNIQUE constraint for Foreign key
User table
----------
`id` int,
`project_id` UNIQUE,
Project table
----------
`id` int,
`user_id` UNIQUE,
Example without Unique Constraint http://sqlfiddle.com/#!9/f2b07/1/0
(Try to put same values it will fail to insert)
Example with Unique Constraint http://sqlfiddle.com/#!9/dda24/1
I am working in a project that has a large postgreSQL database. The previous project was developed in Java from scratch. We are now developing that in Laravel. The previous system had user management system similar to Zizaco/entrust. So, we used in our system as well. The previous table had module table instead of permission table used in entrust. We have already configured that by changing the table name in config/entrust.php. However, the previous system has permission_name instead of name field used in entrust. How do I config entrust to use the unique permission_name instead of name field.
I am looking for a solution, so that we don't have to change in the sources of entrust because then upgrading it would break the system. Can it be configured in the model?
The Entrust package is hardcoded to use the name attribute, so there is no configuration value or anything to change that. However, one thing you can attempt is to define an accessor and mutator for the name attribute.
In your App\Permission model, define the following functions:
class Permission extends Model {
// accessor
public function getNameAttribute($value) {
return $this->permission_name;
}
// mutator
public function setNameAttribute($value) {
$this->attributes['permission_name'] = $value;
}
}
Documentation for accessors and mutators: http://laravel.com/docs/5.0/eloquent#accessors-and-mutators
Which one to choose either ObjectContext or DbContext, when the requirements are,
Data Modeling done by Modeler and provides the DEV team a sql
script. Due to this we have opted Model First. Is this a correct
choice ?
Existing denormalized db will be migrated to new db created by
modeler
Need to maintain audit log for all the updates, at the field level,
from the UI
Each table has CreatedBy, CreatedOn, ModifiedBy, ModifiedOn. These
fields should be automatically filled by during
context.SaveChanges().
If you're starting a new app, just use DbContext. You can always drill down to ObjectContext if you need to.
If you prefer no designer, you can use Code First with Migrations and create a SQL Script via update-database -script as well.
Sounds like a task for the DBAs?
field by field changes..If this is a disconnected app, you'll be better off handling that outside of EF (IMHO)
you can easily override SaveChanges for this. You said in a tweet that you have the dbcontext book. There's an example of this where we do this using a base class. However if you are going to use model first, be sure to avoid this problem : http://msdn.microsoft.com/en-us/magazine/jj553510.aspx
Thanks a lot Julie for your super quick response. You are The-EF-Angel.
I have read your MSDN article on Logging in EF.
To your reponse:
1. As a mandate, We need to use sql scripts provided by our Modeler to create our db. Also these scripts will be keep changing(With addition of new tables & update of exising schema) for each sprints. Hope DataFirst Model is fine. Whenever new we get new sql scripts, we plan to recreate the DB and update our EDMX. Do you see any issues with this approach ?
2. Ya we have a migration specialist for this task. I justed pointed that in question as an FYI.
3. We use MVC app and for field by field changes in audit log table, we planned to let EF decide what fields have changed(using change tracking ideas from your book) and capture that info into a DTO(we borrow your repository ideas from the course "EF in enterprise course" you authored in PS). And push that DTO into our messaging infrastructure and that will insert the audit logs to the DB.
Is this fine ? Do you foresee any issues ?
4. As you pointed out, we could change our interface for our needs by referring to your MSDN article and there "Figure 3 Setting the Interface’s DateCreated Property During SaveChanges"
I plan to use,
public interface ITheBaseType
{
DateTime DateCreated { get; set; }
DateTime DateModified { get; set; }
string CreatedBy { get; set; }
string ModifiedBy { get; set; }
}
I trying to use Grails Scaffolding to throw a quick CRUD application together around some legacy database tables. It is an Oracle database, and the primary key value is intended to be populated by Oracle's GUID() function.
Based on this earlier StackOverflow question, I tried specifying "guid" as the Hibernate generator for this column in my Grails domain class:
...
static mapping = {
table name: "OWNER"
version false
columns {
id column: "OWNER_OID", generator: "guid"
name column: "NAME"
...
}
}
...
When I run my Grails app, viewing and even editing records works just fine. However, when I try to create a new record, things blow up with the Oracle error message "ORA-02289: sequence does not exist".
I enabled SQL logging for my datasource, and see Grails/Hibernate trying to execute the following during a save operation:
select hibernate_sequence.nextval from dual
This doesn't look right at all, and doesn't match the generated SQL from that earlier StackOverflow question linked above. Does anyone see something I am missing here, or otherwise know how to make Grails/Hibernate populate a primary key column with Oracle GUID values?
Whew... after another day of wrestling with this, I think I have my arms around the thing. This answer covers a bit more ground than the original question description, but that's because I found yet more problems after getting past the Hibernate generator issue.
Issue #1: Getting an Oracle GUID() value
As covered by Adam Hawkes' answer, the "guid" Hibernate generator is unmaintained and only works for older versions of the Oracle dialect.
However, if you use the Hibernate generator "assigned" (meaning that you want to set primary keys manually rather than have Hibernate auto-generate them), then you can insert values pulled from an Oracle SYS_GUID() call.
Even though Hibernate's newer Oracle dialects don't support "guid" seamlessly, they still understand the SQL necessary to generate these values. If you are inside of a Controller, you can fetch that SQL query with the following:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
If you are inside of a domain class instead, you can still do this... but you will need to first inject a reference to grailsApplication. You probably want to do this in a Controller, though... more on this below.
If you're curious, the actual String returned here (for Oracle) is:
select rawtohex(sys_guid()) from dual
You can execute this SQL and fetch the generated ID value like this:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Issue #2: Actually using this value in a Grails domain object
To actually use this GUID value in your Grails domain class, you need to use the Hibernate generator "assigned". As mentioned earlier, this declares that you want to set your own ID's manually, rather than letting Grails/GORM/Hibernate generate them automatically. Compare this modified code snippet to the one in my original question above:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
In my domain class, I changed "guid" to "assigned". I also found that I needed to eliminate the "columns {}" grouping block, and move all my column information up a level (weird).
Now, in whichever Controller is creating these domain objects... generate a GUID as described above, and plug it into the object's "id" field. In a Controller generated automatically by Grails Scaffolding, the function will be "save()":
def save() {
def ownerInstance = new Owner(params)
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
ownerInstance.id = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
if (!ownerInstance.save(flush: true, insert: true)) {
render(view: "create", model: [ownerInstance: ownerInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'owner.label', default: 'Owner'), ownerInstance.id])
redirect(action: "show", id: ownerInstance.id)
}
You might think to try putting this logic directly inside the domain object, in a "beforeInsert()" function. That would definitely be cleaner and more elegant, but there are some known bugs with Grails that prevent ID's from being set in "beforeInsert()" properly. Sadly, you'll have to keep this logic at the Controller level.
Issue #3: Make Grails/GORM/Hibernate store this properly
The plain truth is that Grails is primarily intended for virgin-new applications, and its support for legacy databases is pretty spotty (in fairness, though, it's a bit less spotty than other "dynamic" frameworks I've tried). Even if you use the "assigned" generator, Grails sometimes gets confused when it goes to persist the domain object.
One such problem is that a ".save()" call sometimes tries to do an UPDATE when it should be doing an INSERT. Notice that in the Controller snippet above, I have added "insert: true" as a parameter to the ".save()" call. This tells Grails/GORM/Hibernate explicitly to attempt an INSERT operation rather than an UPDATE one.
All of the stars and planets must be in alignment for this to work right. If your domain class "static mapping {}" block does not set the Hibernate generator to "assigned", and also set "version false", then Grails/GORM/Hibernate will still get confused and try to issue an UPDATE rather than an INSERT.
If you are using auto-generated Grails Scaffolding controllers, then it is safe to use "insert: true" in the Controller's "save()" function, because that function in only called when saving a new object for the first time. When a user edits an existing object, the Controller's "update()" function is used instead. However, if you are doing your own thing in your own custom code somewhere... it will be important to check on whether a domain object is already in the the database before you make a ".save()" call, and only pass the "insert: true" parameter if it really is a first-time insert.
Issue #4: Using natural keys with Grails/GORM/Hibernate
One final note, not having to do with Oracle GUID values, but related to these Grails issues in general. Let's say that in a legacy database (such as the one I've been dealing with), some of your tables use a natural key as their primary key. Say you have an OWNER_TYPE table, containing all the possible "types" of OWNER, and the NAME column is both the human-readable identifier as well as the primary key.
You'll have to do a couple of other things to make this work with Grails Scaffolding. For one thing, the auto-generated Views do not show the ID field on the screen when users are creating new objects. You will have to insert some HTML to the relevant View to add a field for the ID. If you give the field a name of "id", then the auto-generated Controller's "save()" function will receive this value as "params.id".
Secondly, you have to make sure that the auto-generated Controller's "save()" function properly inserts the ID value. When first generated, a "save()" starts off by instantiating a domain object from the CGI parameters passed by the View:
def ownerTypeInstance = new OwnerType.get( params )
However, this does not handle the ID field you added to your View. You will still need to set that manually. If on the View you gave the HTML field a name of "id", then it will be available in "save()" as "params.id":
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
Piece of cake, huh? Perhaps "Issue #5" is figuring out why you put yourself through all this pain, rather than just writing your CRUD interface by hand with Spring Web MVC (or even vanilla JSP's) in the first place! :)
Support for using SYS_GUID() is dependent upon the Oracle dialect that you are using. Looking at the hibernate source on GitHub it appears that the dialect was only setup to use the Oracle-generated guid in Oracle9Dialect.java and Oracle8iDialect.java. Therefore, it won't work with the 9i or 10g dialects.
You should submit a patch to hibernate which will add the required function(s) to enable the same functionality as the other dialects.