Grials update fails on unique constraint - validation

I have a user domain in which I have a unique constraint on it's employee number
class User {
Integer employeeNumber
String employeeLogin
String firstName
String middleName
String lastName
String nickname
Date birthday
static mapping = {
table 'user'
id generator: 'assigned', name: 'employeeNumber', type: 'int'
employeeNumber column: 'employee_number'
version false
sort 'lastName'
}
static constraints = {
employeeNumber(blank: false, nullable: false, unique: true)
employeeLogin(blank: false, nullable: false, unique: true)
firstName(blank: false, nullable: false)
middleName(blank: true, nullable: true)
lastName(blank: false, nullable: false)
nickname(blank: true, nullable: true)
birthday(blank: true, nullable: true)
}
}
and I am trying to update the user with
class UserController {
...
def saveUser() {
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
if (params.userId) { // handles user update for existing employeeNumber
def user = User.findByEmployeeNumber(params.userId) // sent in hidden field name userId with value employeeNumber
user.employeeNumber = Integer.parseInt(params.employeeNumber)
user.employeeLogin = params.employeeLogin
user.firstName = params.firstName
user.middleName = params.middleName
user.lastName = params.lastName
user.nickname = params.nickname
try {
user.birthday = formatter.parse(params.birthday)
}
catch (Exception ignore) {
user.birthday = null
}
if (user.validate()) {
user.save(flush:true, failOnError:true)
redirect(action:'profile', id:user.employeeNumber)
} else {
render(view:'editUser', model:[user:user])
}
} else { // handles new user
... // this part works
}
}
...
}
but it is catching on if (user.validate()) { ... } due to the unique constraints on employeeNumber and employeeLogin.
When creating a new user I want the username and id to be unique, but upon update I'd obviously like to update an existing user, however this unique constraint is blocking me from doing so. Any ideas on how to solve this problem?

You are getting this error because you have made the employeeNumber field as your primary key
For Hibernate/Grails, Primary Key is the identifier value for an object. It is something that will never change during the life of the object
During update you are trying to modify the identifier which is causing the issue.
You can definitely modify a unique field, so in your case you can modify the employeeLogin. But you can't modify the Primary Key.
Also employeeNumber is already a primary key so don't declare it as a unique key. This will remove the unique key error that you are facing.

Related

The properties of the file for records generated by jooq are always set to Optional

I have created the User table and generateJooq generates UserRecord.kt, but always the properties of the UserRecord class are optional.
I would like to know how to avoid sharing optional properties.
By the way, the schema of the User table is Not Null.
DB schema
CREATE TABLE user
(
id varchar(256) UNIQUE NOT NULL,
email varchar(256) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
build.gradle.kts
:
jooq {
configurations {
create("main") {
jooqConfiguration.apply {
jdbc.apply {
url = System.getenv("DB_URL")
user = System.getenv("DB_USER")
password = System.getenv("DB_PASS")
}
generator.apply {
name = "org.jooq.codegen.KotlinGenerator"
database.apply {
name = "org.jooq.meta.mysql.MySQLDatabase"
inputSchema = System.getenv("DB_NAME")
excludes = "flyway_schema_history"
}
generate.apply {
isDeprecated = false
isTables = true
isComments = true
}
target.apply {
packageName = "com.example.infra.jooq"
directory = "${buildDir}/generated/source/jooq/main"
}
}
}
}
}
}
Generated UserRecord.kt
:
/**
* This class is generated by jOOQ.
*/
#Suppress("UNCHECKED_CAST")
open class UserRecord() : UpdatableRecordImpl<UserRecord>(User.USER), Record2<String?, String?> {
var id: String?
set(value): Unit = set(0, value)
get(): String? = get(0) as String?
var email: String?
set(value): Unit = set(1, value)
get(): String? = get(1) as String?
:
}
Technologies
JOOQ: 3.16.4
Gradle
Spring Boot: 3.0.1
This feature will ship with jOOQ 3.18, see:
https://github.com/jOOQ/jOOQ/issues/10212
You can configure:
<kotlinNotNullPojoAttributes>true</kotlinNotNullPojoAttributes>
<kotlinNotNullRecordAttributes>true</kotlinNotNullRecordAttributes>
<kotlinNotNullInterfaceAttributes>true</kotlinNotNullInterfaceAttributes>
Of course, beware that as documented in the above issue, and throughout jOOQ's github issues, Stack Overflow, etc., the guarantee of non-nullability in this case will be a "lie." When jOOQ creates a new UserRecord for you, then those valuse will still be null:
// Contains nulls!
val user: UserRecord = ctx.newRecord(USER)

Get data from secondary table in Prisma2

I have a User table that contains user data. I also have a Relationship table that contains a parent_id and a child_id as well as the incrementing id. Each Parent can have many Children and I am trying to retrieve the array of children using the parent_id. I am currently getting an array, but the array only contains the Relationship table data.
How do I get the data from the User table? I was under the impression that because there is a relationship the following should work, but it doesnt.
query {
getChildren {
user{
id
name
email
}
}
}
The relevant code is as follows:
Query:
export const getChildren = queryField('getChildren', {
type: 'User',
list: true,
resolve: async (_parent, {}, ctx) => {
const userId = getUserId(ctx);
if (!userId) {
// TODO -think I might need to throw an error here
return;
}
const children = await ctx.prisma.relationship.findMany({
where: {
parent_id: userId,
},
});
return children;
},
});
schema.prisma:
model Relationship {
child_id Int
id Int #default(autoincrement()) #id
parent_id Int
child User #relation("Relationship_child_idToUser", fields: [child_id], references: [id])
parent User #relation("Relationship_parent_idToUser", fields: [parent_id], references: [id])
}
model User {
created_at DateTime #default(now())
email String? #unique
id Int #default(autoincrement()) #id
ischild Boolean #default(false)
name String?
password String
children Relationship[] #relation("Relationship_child_idToUser")
parents Relationship[] #relation("Relationship_parent_idToUser")
}
The query below should do it
prisma.relationship.findMany({
where: {
parent_id: user_id,
},
include: {
child: true,
},
})
You need to specify the include param explicitly to include the relationship in the output.

Issue with enum data type in create function

I have created "blogs" table with id(primary key), title ,created at ,updated at , status (with enum type having values "active,inactive,hidden" with default value "active")
create function on Blog Model is working fine when giving status value from above mentioned set of values and empty value
const Blog = sequelize.define('blog', { id: { type:
Sequelize.INTEGER,
primaryKey: true, autoIncrement: true }, text: Sequelize.STRING,
status : { type : Sequelize.ENUM, allowNull : false, values :
['Inactive','Active','Hidden'], defaultValue : 'Active' } });
Blog.create({"title" :"hello","status":"abc"}).then(result => {
console.log(result);
});
The above code inserted a new record in blogs table with status of empty value.but result object having status of "abc".How can I get newly inserted record ?
You should define ENUM like this:
status : {
type : Sequelize.ENUM('Inactive','Active','Hidden'),
allowNull : false,
defaultValue : 'Active',
validate: {
isIn: {
args: [['Inactive','Active','Hidden']],
msg: "Wrong status"
}
}
}
In order to validate enum values you can use per attribute validation

grails domain object unexpectedly saved during validation

Considering the following domain classes :
class EnrichmentConfig {
String name
String description
String concept
List fields = []
static hasMany = [fields: FieldConfig]
static constraints = {
name(maxSize: 60, blank: false, nullable: false, unique: true)
concept(maxSize: 255, blank: false, nullable: false)
description(nullable: true, blank: true)
fields(nullable: false, validator: { fields, enrichmentConfig ->
if (fields?.isEmpty()) {
return ['empty']
} else {
return true
}
})
}
static mapping = {
description(type: 'text')
fields(cascade: "all-delete-orphan")
sort('name')
}
}
and
class FieldConfig {
List providers = []
static hasMany = [providers: String]
static belongsTo = [mainConfig: EnrichmentConfig]
static constraints = {
providers(nullable: false, validator: { providers, fieldConfig ->
// some custom validation
})
}
static mapping = {
providers(cascade: 'all-delete-orphan', lazy: false)
}
}
Here the code I use to update an EnrichmentConfig instance in the associated controller:
def update = {
def enrichmentConfig = EnrichmentConfig.get(params.long('id'))
if (enrichmentConfig) {
enrichmentConfig.properties = params
if (enrichmentConfig.validate()) {
if (enrichmentConfig.save(flush: true, failOnError: true)) {
flash.message = "${message(code: 'enrichmentConfig.updated.message', args: [enrichmentConfig.name])}"
redirect(controller: 'enrichment')
}
} else {
// re-validation to attach an error object to each eroneous fieldConfig
enrichmentConfig.fields?.each { it.validate() }
}
render(view: 'fields', model: getFieldsModel(enrichmentConfig))
return
} else {
flash.message = "${message(code: 'enrichmentConfig.not.found.message', args: [params.id])}"
redirect(controller: 'enrichment')
}
}
I've noticed that when I validate an instance of EnrichmentConfig to be updated, associated FieldConfig instances are unexpectedly saved in the database even though they are invalid.
In fact, in debug ste-by-step mode, while enrichmentConfig.validate() is executed, the following appears in the console:
Hibernate:
update
field_config_providers
set
providers_string=?
where
field_config_id=?
and providers_idx=?
How can this be happening? What am I doing wrong?
I should specify that I use grails 1.3.7.
Thanks in advance for your help.
This is just a guess but possibly someplace to start. I don't pretend to understand when Hibernate decides to flush sessions and partially save data and the like. But what I do know is that putting all write related calls in a service saves me a ton of grief over time.
Try moving some of your update method to a service and see if you have better luck. My hunch is that possibly, hibernate needs to persist some of the data to do other stuff and if it were in a transactional service, that write would rollback once the RuntimeException is thrown.
I suggest using a service to save your objects. First, check all the objects for validity using the validate() method.
Then, save the objects in the order in which they depend or in the hierarchy they follow.

get name of failed constraints

Assume I have a User class that has a username property and there are several constraints defined on this field.
class User {
String username
static constraints = {
username blank: false, unique: true, email: true
}
}
If I call
user.save()
I can then figure out if any of the constraints on the username field failed via
user.errors['username'] != null
But is there a way I can figure out which of the constraints failed?
The value user.errors['username'].codes will contain a number of keys used for looking up validation messages in messages.properties. You can use these to figure out which constraints broke.
For example, user.errors['username'].codes[-1] will contain the constraint part of the messages.properties key:
assert user.errors['username'].codes[-1] == 'blank' // if blank constraint fails
assert user.errors['username'].codes[-1] == 'unique' // if unique constraint fails
Yeap, you can check the error code with code property on the error object:
def user = new User(email: '')
user.validate()
assert user.errors['email'].code == 'blank'
user.email = 'asdasd'
user.validate()
assert user.errors['email'].code == 'email.invalid'
If you may have more than one error in a property, the only way i found to get all the errors for that property is to iterate the allErrors property:
class Foo {
String bar, baz
static constraints = {
bar blank: false
baz email: true, notEqual: 'foobar'
}
}
def foo = new Foo(bar: '', baz: 'foobar')
foo.validate()
foo.errors.allErrors.each {
println "$it.field: $it.code"
}
Should output something like:
bar: blank
baz: notEqual
baz: email.invalid

Resources