Manually setting annotation on generated migration using EF and MySql - mysql

I have a property that is in my class that is not the primary key that I want to auto increment. The primary key is a GUID so I can still use the auto increment function on another column in the table. Also I can't change the primary key to int as the GUID key is defined in a base class. I can manually add the .Annotation("MySQL:AutoIncrement", true) to the property in the generated migration but I'm concern about editing the migration causing future issues. I found what would be the answer via the .AddAnnotation(,) method but it doesn't created the desired results.
Also [DatabaseGenerated(DatabaseGeneratedOption.Identity)] doesn't produce the desired result.
I was hoping this:
builder.Entity<Editor>().Property(p => p.CreatorId).ValueGeneratedOnAdd().Metadata.AfterSaveBehavior = PropertySaveBehavior.Throw;
builder.Entity<Editor>().Property(p => p.CreatorId).Metadata.AddAnnotation("MySQL:AutoIncrement", true);
Would make this:
migrationBuilder.CreateTable(
name: "editor",
columns: table => new
{
CreatorId = table.Column<int>(nullable: false).Annotation("MySQL:AutoIncrement", true)
...
MySql.Data.EntityFrameworkCore: 8.0.18.0
Microsoft.EntityFrameworkCore: 2.2.4

After using DotPeek to look at the code for MySql.Data.EntityFramework it seems to not be possible to reach the desired affect with fluentApi or attributes as it will only add the annotation if it's the primary key. Regardless of if it's possible in the database.

Related

Perl DBIx::Class encounterd Object Json

I'm new to Perl and DBIx::Class.
This is how I get my meaning_ids from the table translation where language = 5:
my $translations = $schema -> resultset('Translation')->search({ language => '5'});
After it I'm trying to push my data from the database into my array data:
while ( my $translation =$translations->next ) {
push #{ $data }, {
meaning_id => $translation-> meaning
};
}
$self->body(encode_json $data );
If I do it like this, I get the following error:
encountered object
'TranslationDB::Schema::Result::Language=HASH(0x9707158)', but neither
allow_blessed , convert_blessed nor allow_tags settings are enabled
(or TO_JSON/FREEZE method missing)
But if I do it like that:
while ( my $translation =$translations->next ) {
push #{ $data }, {
meaning_id => 0+ $translation-> meaning
};
}
$self->body(encode_json $data );
I don't get the error anymore, but the meaning is not the number out of the database. It's way too big (something like 17789000, but only numbers till 7000 are valid).
Is there an easy way to tell Perl that meaning_id is an INT and not a string?
It's a bit hard without knowing your schema classes, but #choroba is right. The error message says $translation->meaning is an instance of TranslationDB::Schema::Result::Language. That's explained in DBIx::Class::Manual::ResultClass on CPAN.
I believe there is a relationship to a table called meaning, and when you call $translation->meaning what you get is a new result class. Instead you need to call $translation->meaning_id. Actually that would only happen in a join, but your code doesn't look like it does that.
It seems $translation->meaning returns an object. Using 0+ just returns its address (that's why the numbers are so high).
It looks like there's a relationship between your translation and meaning tables. Probably, the translation table contains a foreign key to the meaning table. If you look in the Result class for your translation class then you will see that relationship defined - it will be called "meaning".
As you have that relationship, then DBIC has added a meaning method to your class which retrieves the meaning object that is associated with your translation.
But it appears that the foreign key column in your translation table is also called "meaning", so you expect calling the "meaning" method gives you the value of the foreign key rather than the associated object. Unfortunately it doesn't work like that. The relationship method overrides the column method.
This is a result of bad naming practices. I recommend that you call the primary key for every table id and the foreign key that links to another table <table_name>_id - so the column in your translation table would be called meaning_id. That way you can distinguish between the value of the key ($translation->meaning_id) and the associated meaning object ($translation->meaning).
A work-around you can use if you can't rename columns, is to use the get_column method - $translation->get_column('meaning').

Fluent NHibernate Schema output with errors when using list

I have two tables which are Many-To-One mapped. However, it is important to maintain the order of the second table, so when I use automapping, Fluent automapper creates a bag. I changed this to force a list by using this command:
.Override(Of ingredients)(Function(map) map.HasMany(Function(x) x.PolygonData).AsList())
(VB.NET syntax)
So I say "AsList" and instead of using a bag, the mapping xml which gets generated contains a list now. Fine so far. However,
the statement generated cannot be handled by MySQL. I use MySQL55Dialect to create the statements and I use exactly that version. But it creates the following create:
create table `ingredients` (
Id INTEGER NOT NULL AUTO_INCREMENT,
Name FLOAT,
Amout FLOAT,
Soup_id INTEGER,
Index INTEGER,
primary key (Id)
)
It crashes because of the line "Index INTEGER," but I don't know what to do here. Any ideas?
Thanks!!
Best,
Chris
I would suspect that Index could be a keyword for MySQL. To avoid such conflict, we can define different Index column name (sorry for C# notation)
HasMany(x => x.PolygonData)
.AsList(idx => idx.Column("indexColumnName").Type<int>())

Update empty string to NULL in a html form

I'm building a site in Laravel.
I have foreign key constraints set up among InnoDB tables.
My problem is that if i don't select a value in a, say, select box, the framework tries to insert or update a record in a table with '' (empty string). Which causes a MySQL error as it cannot find the equivalent foreign key value in the subtables.
Is there some elegant way to force the insertion of NULL in the foreign key fields other than checking out every single field? Or to force MySQL to accept '' as a "null" foreign key reference?
In other words: I have a, say, SELECT field with first OPTION blank. I leave the blank OPTION chosen. When I submit, an empty string '' is passed. In MySQL apparently I can do UPDATE table SET foreignKey=NULL but not UPDATE table SET foreignKey=''. It does not "convert" to NULL. I could check the fields one by one but and convert '' to NULL for every foreign key, maybe specifying all of them in an array, I was wondering if there's a more streamlined way to do this.
Maybe have to change my ON UPDATE action (which is not set) in my DB schema?
Edit: the columns DO accept the NULL value, the problem is in how the framework or MySQL handle the "empty value" coming from the HTML. I'm not suggesting MySQL "does it wrong", it is also logical, the problem is that you can't set a "NULL" value in HTML, and I would like to know if there's an elegant way to manage this problem in MySQL or Laravel.
In other words, do I have to specify manually the foreign keys and construct my query accordingly or is there another robust and elegant way?
My code so far for the model MyModel:
$obj = new MyModel;
$obj->fill(Input::all())); // can be all() or a subset of the request fields
$obj->save();
At least since v4 of Laravel (and Eloquent models), you can use mutators (aka setters) to check if a value is empty and transform it to null, and that logic is nicely put in the model :
class Anything extends \Eloquent {
// ...
public function setFooBarAttribute($value) {
$this->attributes['foo_bar'] = empty($value)?null:$value;
}
}
You can check out the doc on mutators.
I've been oriented by this github issue (not exactly related but still).
Instead of using
$obj = new MyModel;
$obj->fill(Input::all())); // can be all() or a subset of the request fields
$obj->save();
Use
$obj = new MyModel;
$obj->fieldName1 = Input::get('formField1');
$obj->fieldName2 = Input::has('formField2') && Input::get('formField2') == 'someValue' ? Input::get('formField2') : null;
// ...
$obj->save();
And make sure your database field accepts null values. Also, you can set a default value as null from the database/phpmyadmin.
You must remove the "not null" attribute from the field that maps your foreign key.
In the model add below function.
public function setFooBarAttribute($value)
{
$this->attributes['foo_bar'] = $value?:null;
}

Taking a datetime field into primary key throws fatal error

I would like to use the combination of two foreign keys plus the datetime field as my combined primary key.
But I get a
Catchable Fatal Error: Object of class DateTime could not be converted
to string in
C:\development\xampp\htdocs\happyfaces\vendor\doctrine\orm\lib\Doctrine\ORM\UnitOfWork.php
line 1337
when I do so. As soon as I remove the id: true from my YML entity declaration everything works fine again.
What is the problem that occurs here? It seems to be rather a Symfony2 or a Doctrine2 bug to me, because the datetime is set fine in the database if I don't declare the datetime column to be part of the primary key.
Can anyone help or advise?
Its not possible and not recommended. For primary key focus on primitive data types such as Integer or String. The most RDMS System prefer Integer as primary key for maximum performance.
Take look: http://doctrine-orm.readthedocs.org/en/2.1/tutorials/composite-primary-keys.html
Maybe a workaround could work by adding a new Doctrine data type. With a __toString() function, but I think Doctrine will force you to use primitive data types only.
class Foo
{
private $bar = 'test';
public function __toString()
{
return $this->bar;
}
}
echo new Foo();
Your error means in general DateTime has no __toString() function or is not string compatible. I never tested it to use a custom data type as primary key. So you've to try it yourself.
Take a look: http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html
Another try is use String as Primary key and set your id with
$entity->setId(new \DateTime()->format('yyyy/mm/dd'));
Here is a similar question: Symfony/Doctrine: DateTime as primary key

Naming a multi-column constraint using JPA

The name attribute of #UniqueConstraint seems to have no effect.
#Entity
#Table(name = "TAG", uniqueConstraints = #UniqueConstraint(columnNames = {
"TAG_NAME", "USERS_ID" }, name="UQ_TAG_USER"))
public class Tag extends BaseEntity {
}
I'm usning SQL Server 2008, JPA 2.0 with Hibernate 3.6.
On the DB side an index, UQ__TAG__6EF57B66 is created instead of UQ_TAG_USER.
What am I missing? is there no way to enforce a given name from java side? and one must resort to editing schema files? we are a small shop without a DBA and I try to make do as much as I can by the help of hibernate schema facilities.
I assume you are using hibernate because you have it in the tags for this question. It's a bug/missing feature in hibernate:
https://hibernate.onjira.com/browse/HB-1245
It will simply ignore the unique constraint name when the dialect supports creating the constraint in the same statement as create table.
I've checked SqlServer and Oracle dialects and they both support this way of creating the constraint, that will cause the bug you are experiencing.
There are two ways to workaround this bug:
1. The quick way:
Just extend the dialect and return false for supportsUniqueConstraintInCreateAlterTable() method:
public static class SQLServerDialectImproved extends SQLServerDialect {
#Override
public boolean supportsUniqueConstraintInCreateAlterTable() {
return false;
}
}
And set this class as your dialect in hibernate.dialect property of the persistence unit configuration (persistence.xml).
2. The right way:
Fix the hibernate code and recompile:
The bug is at org.hibernate.mapping.UniqueKey class, the method sqlConstraintString() will return unique (TAG_NAME, USERS_ID) for all dialects, even if they support constraint UQ_TAG_USER unique (TAG_NAME, USERS_ID).
But that is probably a larger change (need to support all kinds of dialects, etc.)
Under the hood:
If you use the original dialect, it will cause the following sql statement to be executed to create the table (added id column):
create table TAG (
id bigint not null,
TAG_NAME varchar(255),
USERS_ID varchar(255),
primary key (id),
unique (TAG_NAME, USERS_ID)
)
And after you apply the fix as stated in first option the following sql statements will be executed:
create table TAG (
id numeric(19,0) not null,
TAG_NAME varchar(255),
USERS_ID varchar(255),
primary key (id)
)
create unique index UQ_TAG_USER on TAG (TAG_NAME, USERS_ID)
which include the creation of the unique constraint with the chosen name (UQ_TAG_USER) in a separate statement.