I tried cascade remove on the 'file' entity that keeps my 'expanse' entity from removing. But this doesn't work.
The error:
Cannot delete or update a parent row: a foreign key constraint fails (zioo.files, CONSTRAINT FK_6354059F395DB7B FOREIGN KEY (expense_id) REFERENCES expenses (id))
The file entity code:
/**
* #ORM\ManyToOne(targetEntity="Expense", inversedBy="files", cascade={"remove"})
* #ORM\JoinColumn(name="expense_id", referencedColumnName="id")
*/
private $expense;
The expanse entity code:
/**
* #ORM\OneToOne(targetEntity="File", cascade={"persist"})
* #ORM\JoinColumn(name="file_id", referencedColumnName="id")
*/
private $file = null;
/**
* #ORM\OneToMany(targetEntity="File", mappedBy="expense", cascade={"remove"})
*/
protected $files;
If a expanse gets deleted the file associated with it should be deleted too.
Using cascade={"remove"} the entity won't be deleted if it is owned by something else. The issue seems to be caused by doctrine, as the expanse entity has 2 relations to file entity and this causes doctrine to "think" that your file entity is owned by something else and not send a delete to database for it, before trying to delete the expanse.
As a result when it tries to delete the expanse this error is thrown.
To test it, remove private $file = null;relation and will see that it will work.
To overcome this, I suggest to use onDelete="CASCADE" on the owning side:
/**
* #ORM\ManyToOne(targetEntity="Expense", inversedBy="files", cascade={"remove"})
* #ORM\JoinColumn(name="expense_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $expense;
In this case, you no longer need cascade={"remove"}:
/**
* #ORM\OneToMany(targetEntity="File", mappedBy="expense")
*/
protected $files;
Doctrine delete relation options
Related
As our project scaled we decided that every single data should belong to companies that created them. Therefore I'm to add a column "data_owner_company_id" that points to the company that owns given record. Yes it's possible to generate migration to add this column to each model but that is not really feasible since there is 120+ tables & models. How can i tackle this with minimum effort ?
For the model part i figured i can easily apply it to all models by inheritance, but not sure about migration.
TL;DR
How to add int column to all tables by migration ?
Database: MySQL v8
Framework: Laravel 8, PHP 7.3
It's simple if you find all the tables' names in your database, you have to loop and create columns for each and every table.
Try creating columns using queues as it will be a heavy job for 120 tables.
Check the following code:
class CreateDataOwnerCompanyIdtoEachTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up ()
{
$columns = 'Tables_in_' . env('DB_DATABASE');//This is just to read the object by its key, DB_DATABASE is database name.
$tables = DB::select('SHOW TABLES');
foreach ( $tables as $table ) {
//todo add it to laravel jobs, process it will queue as it will take time.
Schema::table($table->$columns, function (Blueprint $table) {
$table->unsignedInteger('data_owner_company_id');
});
}
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down ()
{
$columns = 'Tables_in_' . env('DB_DATABASE');//This is just to read the object by its key, DB_DATABASE is database name.
$tables = DB::select('SHOW TABLES');
foreach ( $tables as $table ) {
//todo add it to laravel jobs, process it will queue as it will take time.
Schema::table($table->$columns, function (Blueprint $table) {
$table->dropColumn('data_owner_company_id');
});
}
}
}
I'm not 100% sure that it's going to work, but here it goes:
Create class that extends Illuminate\Database\Schema\Blueprint;
In constructor call parent construntor and then
$this->unsignedBigInteger('data_owner_company_id')->nullable();
Use your new class in migrations instead of default Blueprint
I'm having trouble with Laravel database migration. I have entered foreign key constraint in my database migration file, but when I try to migrate the file it shows this error message.
Illuminate\Database\QueryException : SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ') on delete cascade on update cascade' at line 1 (SQL: alter table education_qualifications add constraint education_qualifications_teacher_id_foreign foreign key (teacher_id) references teachers () on delete cascade on update cascade)at E:\XAMPP\htdocs\ViduresaApp\vendor\laravel\framework\src\Illuminate\Database\Connection.php:664
660| // If an exception occurs when attempting to run a query, we'll format the error
661| // message to include the bindings with SQL, which will make this exception a
662| // lot more helpful to the developer instead of just the database's errors.
663| catch (Exception $e) {
> 664| throw new QueryException(
665| $query, $this->prepareBindings($bindings), $e
666| );
667| }
668|
Exception trace:
1 PDOException::("SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ') on delete cascade on update cascade' at line 1")
E:\XAMPP\htdocs\ViduresaApp\vendor\laravel\framework\src\Illuminate\Database\Connection.php:452
2 PDO::prepare("alter table education_qualifications add constraint education_qualifications_teacher_id_foreign foreign key (teacher_id) references teachers () on delete cascade on update cascade")
E:\XAMPP\htdocs\ViduresaApp\vendor\laravel\framework\src\Illuminate\Database\Connection.php:452
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateEducationQualificationsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('education_qualifications', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('teacher_id')->nullable();
$table->unsignedInteger('student_id')->nullable();
$table->string('institute_name');
$table->string('user_degree');
$table->string('field_of_study');
$table->string('user_grade');
$table->date('from_date')->nullable();
$table->date('to_date')->nullable();
$table->text('edu_description');
$table->timestamps();
$table->foreign('teacher_id')->references('id')->on('teachers')->onUpdate('cascade')->onDelete('cascade');
$table->foreign('student_id')->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade');
$table->primary(['teacher_id', 'student_id']);
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('education_qualifications');
}
}
You can use both onupdate and ondelete at the same line
For example:
$table->foreign(’author’)->references(’id’)->on(’users’)->onUpdate(’cascade’);
Can anyone recommend a way to perform inserts in a database table via spring data jpa (in a clustered environment) only when the entry doesn't already exist in the database?
As an example situation, take a database with message and email_address tables. When a new message addressed to 'user#so.com' is added to the message table, a check will be done in the email_address table whether the email 'user#so.com' exists in the email_address and if it doesn't, it gets added to the database. Afterwards the email address entity field is set on the message entity and subsequently the message entity is saved.
#Entity
public class Message {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private EmailAddress emailAddress;
private String content;
}
#Entity
public class EmailAddress {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
// a unique constraint exists on this field on the database
private String email;
}
What should there be done with the spring data jpa EmailAddressRepository to avoid database exceptions that can occur when concurrently trying to insert entities with the same email address?
You could run a check inside a #PrePersist annotated method in your entity. Other than that, I don't think JPA or spring-data provide something out of the box for that.
I have three entities in my project.
First Entity:
class Entity1
{
/**
* #ORM\ManyToMany(targetEntity="App\MyBundle\Entity\Entity2", inversedBy="i")
*
*/
protected $j;
}
Second Entity:
class Entity2
{
/**
* #ORM\ManyToMany(targetEntity="App\MyBundle\Entity\Entity1", mappedBy="j")
*/
protected $i;
}
Now i have a manyToMany connection between entity 1 and entity 2, the table look like this.
Tablename: entity1_entity2
Fields: entity1_id, entity2_id
I would like to create a third entity and connect this to the related table entity1_entity2 with a oneToMany relation? How can i do this? Is this use case possible?
/**
* Returns the property key with the given name. If automatic type making is enabled, it will make the property key
* using the configured default type maker if a key with the given name does not exist.
*
* #param name name of the property key to return
* #return the property key with the given name
* #throws IllegalArgumentException if a property key with the given name does not exist or if the
* type with the given name is not a property key
* #see PropertyKey
*/
public PropertyKey getPropertyKey(String name);
getPropertyKey method will always return a prpertyKey.
To check for if a key exists in the database use:
mgmt.containsRelationType(keyToCheck)
In case of the latest version of Titan (1.0.0) you can use com.thinkaurelius.titan.core.TitanTransaction to check if property key exists in Titan DB schema. http://thinkaurelius.github.io/titan/javadoc/1.0.0/com/thinkaurelius/titan/core/TitanTransaction.html
Example:
TitanTransaction tt = TitanGraph.newTransaction();
boolean exists = tt.containsPropertyKey("keyName");