I have simple domain:
package app
class Customers {
String CUSTOMER
String NOTE
static mapping = {
version false
id column: 'ID_CUSTOMER', type: 'long',
generator: 'sequence',
params: [sequence: 'CUSTOMER_SEQ']
}
static constraints = {
CUSTOMER(nullable: false, blank: false, unique: ['NOTE'])
NOTE(nullable: true, blank: true)
}
}
Columns and constraints are created properly in database (I want constraint on customer and note together), but instead of getting:
grails.validation.**ValidationException**
I am getting:
Message: Hibernate operation: could not execute statement; SQL [n/a]; ORA-00001: unique constraint (TEST2.UNIQUE_CUSTOMER_NOTE) violated
; nested exception is java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (TEST2.UNIQUE_CUSTOMER_NOTE) violated
Shouldn't I get ValidationException when constrains are violated?
Controller code is standard auto code generated by the generate-all script.
You don't show any of the controller code - are you checking the return value of validate() and save() calls (or using hasErrors()) and redirecting back so the user can fix the mistakes?
What you're seeing will happen if you change one or both values and they're no longer unique, but you don't call validate() or save(). That's because at the end of the request, any modified instances will be updated in the database because the session gets flushed, and Hibernate uses its cached copy of the original values to detect modified/dirty instances that haven't been saved and flushed yet.
If you change a value but don't plan on saving the change (e.g. if you set values only for rendering the GSP) be sure to evict the instance from the session by calling discard() - Hibernate won't try to push those changes if the instance is no longer attached to the session.
Related
Is it possible to write an entire object to Dexie without knowing the schema?
I just want to do this:
var db = new Dexie(gameDataLocalStorageName);
db.version(1).stores({
myData: "gameData"
});
db.myData.put(gameData);
console.log(db.myData.get('gameData'));
But I'm getting the following error:
Unhandled rejection: DataError: Failed to execute 'put' on 'IDBObjectStore': Evaluating the object store's key path did not yield a value.
DataError: Failed to execute 'put' on 'IDBObjectStore': Evaluating the object store's key path did not yield a value.
The error is because you've specified the schema to use inbound key "gameData", i.e. require each object to have the property "gameData" as its primary key.
If you don't need to have the primary key within the objects, you can declare the schema as {myData: ""} instead of {myData: "gameData"}. By doing so, you will need to provide the primary key separate from the object in calls to db.myData.put().
See docs for inbound vs non-inbound keys and detailed schema syntax
var db = new Dexie(gameDataLocalStorageName);
db.version(1).stores({
myData: ""
});
Promise.resolve().then(async () => {
await db.myData.put(gameData, 'gameData'); // 'gameData' is key.
console.log(await db.myData.get('gameData'));
}).catch(console.error);
Since we're changing the primary key here, you will need to delete the database in devtools before this would work.
I've managed to create number of readonly Web Api OData services following the tutorials here: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api. I'm therefore employing the ODataConventionModel builder to create the model from a set of entities (incidentally coming from a Telerik ORM). This all seems to work fine and I can happily issue queries, view the metadata and so forth on the service.
I've now tried to turn my attention to the other CRUD operations - firstly Create and have stumbled into a problem! Namely, the Post method fires correctly (CreateEntity) but the entity parameter is null - by doing a check against the ModelState.IsValid, it shows that the problem is a null ID (key) value. This is unsurprising because the database uses a Database Generated Identity for the ID column and therefore the ID would be created when the entity is saved into the database context.
I've therefore tried all sorts of ways of marking the ID column as database generated, but haven't managed to find anything. Strangely, I can't seem to find even one post of someone asking for this - surely I can't be the only one?!
I noted that when looking at the EF modelbuilder (for example here: http://forums.asp.net/t/1848984.aspx/1) there appears to be a means of affecting the model builder with a .HasDatabaseGeneratedOption property, but no similar option exists in the System.Web.Http.OData equivalent.
So the questions therefore are:
Is there a means of altering the model builder (or something else) so that the controller will accept the object and deserialize the entity even with a null key value?
If so, how can I do this?
If not, any suggestions as to other options?
I realise that I could potentially just populate the object with an (in this case) integer value from the client request, but this seems a) semantically wrong and b) won't necessarilly always be possible as a result of the client toolkit that might be used.
All help gratefully received!
Many thanks,
J.
You need to create a viewmodel for insert which does not contain the ID parameter. Use Automapper to map the properties of the incoming insert-model to your data entities.
The problem that you're having is that ID is a required attribute in your data model because it is your PK, except during insert, where it shouldn't be specified.
In my case, my database-generated key is a Guid.
As a work-around, in my TypeScript client code, I submit (via http POST) the object with an empty Guid like this: Note: ErrorId is the key column.
let elmahEntry: ELMAH_Error = {
Application: 'PTUnconvCost',
Host: this.serviceConfig.url,
Message: message,
User: that.userService.currentUserEmail,
AllXml: `<info><![CDATA[\r\n\r\n${JSON.stringify(info || {})}\r\n\r\n]]></info>`,
Sequence: 1,
Source: source,
StatusCode: 0,
TimeUtc: new Date(Date.now()),
Type: '',
ErrorId: '00000000-0000-0000-0000-000000000000'
};
Then, in my WebApi OData controller, I check to see if the key is the empty guid, and if so, I replace it with a new Guid, like this:
// POST: odata/ELMAH_Error
public IHttpActionResult Post(ELMAH_Error eLMAH_Error)
{
if (eLMAH_Error.ErrorId == Guid.Empty)
{
eLMAH_Error.ErrorId = Guid.NewGuid();
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.ELMAH_Error.Add(eLMAH_Error);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (ELMAH_ErrorExists(eLMAH_Error.ErrorId))
{
return Conflict();
}
else
{
throw;
}
}
return Created(eLMAH_Error);
}
For some reason I am getting the following error at the db.SaveChanges(); instruction:
Cannot insert the value NULL into column 'UserId', table 'XXXXXXXXX_Dev.dbo.Portfolios'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Controller code:
[HttpPost]
[Authorize]
public ActionResult Create(Portfolio portfolio)
{
if (ModelState.IsValid)
{
portfolio.UserId = (Guid)Membership.GetUser().ProviderUserKey;
db.AddToPortfolios(portfolio);
db.SaveChanges();
}
return View("MyPortfolios");
}
I have stepped through the debugger and confirmed that UserID is being populated.
Update:
I have tried changing db.AddToPortfolios(portfolio); to db.Portfolios.AddObject(portfolio); but it is still having the same problem.
Portfolios is an ObjectSet, should I use the Attach() method?
I know this exception from only one situation, that is: UserId is not an identity column in your database but in the EF model the corresponding property is flagged as such - which means it is either explicitely attributed with DatabaseGeneratedOption.Identity or implicitely by conventions.
The problem is that in this case EF won't sent the property value to the Db (no matter if it's set or not) because it assumes that the DB will do the work to create a column value. But the Db doesn't, hence the exception.
Just a guess.
Edit:
To solve the problem you must flag UserId with DatabaseGeneratedOption.None.
I am integrated with a legacy Oracle database which uses assigned VARCHAR2 values for primary keys. I am creating a one-to-many relationship with this existing table. The legacy table is called Applications (which I may not alter) and the new table is called Projects. Many projects may be assigned to one application.
When GORM creates the Project table it is creating a NUMBER column for the foreign key, application_id, even though this is a VARCHAR2 field in the Applications table.
class Application {
static hasMany = [projects : Project]; // does not fix problem
String application_id;
...
static mapping = {
table 'applications'
version false
id (column:'application_id')
}
static constraints = {
application_id(maxSize:16,blank:false,unique:true,type:"string",generator:"assigned")
}
...
}
class Project {
Application application;
...
}
When I compile the app I get warnings like this:
Unsuccessful: alter table project add constraint FKED904B1956694CB5 foreign key (application_id)
ORA-02267: column type incompatible with referenced column type
When I run the app and click on Application controller I get this error:
SQL state [99999]; error code [17059]; Fail to convert to internal representation; nested exception is java.sql.SQLException: Fail to convert to internal representation
When I click on Project | create I get this error:
Fail to convert to internal representation; nested exception is java.sql.SQLException: Fail to convert to internal representation at /project/create:172
So how can I set the Project class to expect a VARCHAR2 foreign key for the Application?
Thanks for any help!
Look at this site. Maybe it will help you.
Here is the correction in the Application class... in case anyone else searches for this:
class Application {
static hasMany = [projects : Project];
String application_id;
String id // <--- part of solution
static mapping = {
id column:'application_id',generator:'assigned' // <--- part of solution
}
static constraints = {
application_id(maxSize:16,blank:false,unique:true) // <--- part of solution
columns { // <--- part of solution
id type:'text'
application_id type:'text'
}
}
I have a groovy system configured using tomcat and Oracle 10g.
I have a groovy class which defines an follows: (reduced version)
class ChangeTicket {
static constraints = {
chngNr(nullable:false)
}
String chngNr
}
My controller has defined a save method:
if (changeTicketInstance.validate() && !changeTicketInstance.hasErrors() && changeTicketInstance.save()) {
flash.message = "changeTicket.created"
...
}
As far as I know the save method calls by default the validate method in order to
know if the constraints are fullfilled or not therefore the validate method call is redundant. Anyway, when the save is performed an exception will be thrown if the field chngNr is NULL.
In fact the field cannot be empty (NULL) because I've defined the constraint (nullable:false).
What am I doing wrong here?
Thanks in advance,
Luis
The validate call should fail if chngNr is NULL. Some databases do not consider an empty string ("") null (HSQL). If you are binding chngNr to changeTicketInstance using params from a form it is getting assigned an empty string as a value and in that case you would want your constraint to be:
chngNr(blank:false)
Also, save() wont throw an Exception unless you use save(flush:true). Hibernate queues up the changes and, unless you flush, wont throw an actual exception.
try this:
chngName(blank:false,nullable:false)
:-)