Good day, I'm new to laravel I was doing a migration rollback and it was successfully done
Rolled back: 2018_02_22_172102_adding_fk_constrains_products_to_product_types_and_service_sub_types_table
But when I try to re-migrate I encountered this error below. BTW I don't want to drop the column because it already existed and I don't want to lose the existing data in that column. I only want to add constraint between those tables
[Illuminate\Database\QueryException]
SQLSTATE[23000]: Integrity constraint violation: 1022 Can't write; duplicate key in table '#sql-2fc8_17c' (SQL: alter table products add constraint products_product_type_id_foreign
foreign key (product_type_id) references product_types (id) on update cascade)
[PDOException]
SQLSTATE[23000]: Integrity constraint violation: 1022 Can't write; duplicate key in table '#sql-2fc8_17c'
Table Non_unique Key_name Seq_in_index Column_name
-------- ---------- ---------------------------------- ------------ -------------------
products 0 PRIMARY 1 id
products 1 products_product_type_id_index 1 product_type_id
products 1 products_service_sub_type_id_index 1 service_sub_type_id
this my migration code
public function up()
{
Schema::table('products',function (Blueprint $table){
$table->integer('product_type_id')->unsigned()->index()->change();
$table->foreign('product_type_id')->references('id')->on('product_types')->onUpdate('cascade');
$table->integer('service_sub_type_id')->nullable()->unsigned()->index()->change();
$table->foreign('service_sub_type_id')->references('id')->on('service_sub_types')->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('products', function(Blueprint $table){
$table->dropForeign(['product_type_id']);
$table->dropForeign(['service_sub_type_id']);
});
}
I just remove the following code in my migration
$table->integer('product_type_id')->unsigned()->index()->change();
$table->integer('service_sub_type_id')->nullable()->unsigned()->index()->change();
public function up()
{
Schema::table('products',function (Blueprint $table){
//$table->integer('product_type_id')->unsigned()->index()->change();
$table->foreign('product_type_id')->references('id')->on('product_types')->onUpdate('cascade');
//$table->integer('service_sub_type_id')->nullable()->unsigned()->index()->change();
$table->foreign('service_sub_type_id')->references('id')->on('service_sub_types')->onUpdate('cascade');
});
}
when I re migrate it was successfully migrated..
For those still looking for a solution, add this code to the down() method.
public function down()
{
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
Schema::table('products', function(Blueprint $table){
$table->dropForeign(['product_type_id']);
$table->dropForeign(['service_sub_type_id']);
});
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}
Related
I am working with the below code, which worked fine with typeorm 0.2.x. I am trying to upgrade my packages to 0.3.6. It could be that the problem is somehow Mac-specific. Yet, I am not sure.
The script is below:
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CleanSlate1654889719399 implements MigrationInterface {
name = 'CleanSlate1654889719399';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('IF EXISTS DROP TABLE "onetime_viewer_token" CASCADE');
...
await queryRunner.query(`CREATE TABLE IF NOT EXISTS "client_society_user" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "email" text NOT NULL, "tokenVersion" integer NOT NULL DEFAULT '0', "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "role" "public"."client_society_user_role_enum" NOT NULL, "isInternalAdmin" boolean NOT NULL DEFAULT false, "clientId" uuid, CONSTRAINT "PK_599c2dd9d3dc21c54f7df5d9c7e" PRIMARY KEY ("id"))`);
...
await queryRunner.query(`ALTER TABLE "client_user" ADD CONSTRAINT "FK_eb3e491fab0ea63cd9f9ffba47d" FOREIGN KEY ("clientId") REFERENCES "client"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "discord_role" DROP CONSTRAINT "FK_cc3204010e82bba2a8cdafb30fc"`);
await queryRunner.query(`IF EXISTS DROP TABLE "onetime_transfer_token"`);
...
await queryRunner.query(`IF EXISTS DROP TABLE "temp_token"`);
}
}
When I am trying to run this migration with typeorm 0.3.x, it results in an error: error: syntax error at or near "IF".
Is it still possible to use QueryRunner with raw SQL? MigrationInterface only supports QueryRunner...
Please advise.
The below works fine [on both Mac and Linux] with typeorm 0.3.6:
await queryRunner.manager.query(...)
I have three table ; users , products and transactions
the id fields is auto increment in laravel.
products :
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('title')->nullable();
$table->unsignedBigInteger('user_id')->index();
$table->integer('price')->nullable();
$table->text('description')->nullable();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
transactions :
Schema::create('transactions', function (Blueprint $table) {
$table->id()->from('1000');
$table->unsignedBigInteger('user_id');
$table->integer('code')->nullable();
$table->string('token')->nullable();
$table->bigInteger('amount');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users');
});
users :
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->bigInteger('phone');
$table->timestamp('last_seen')->nullable();
$table->rememberToken();
$table->timestamps();
});
when I insert new row on products or transactions it give me this error :
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (sabbabecom_DB.products, CONSTRAINT products_user_id_foreign FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE)
insert into products (user_id, updated_at, created_at) values (2, 2022-06-25 09:38:52, 2022-06-25 09:38:52)
or :
insert into transactions (user_id, amount, updated_at, created_at) values (2, 56000, 2022-06-25 09:50:14, 2022-06-25 09:50:14)
But in the users table , a user with id 2 is exist !!!
what is wrong in my database?
thanks
Why is ON DELETE SET NULL failing when deleting a row via the application code, but it behaves correctly when manually executing an SQL statement?
I have a todo table and a category table. The todo table has a category_id foreign key that references id in the category table, and it was created with the "ON DELETE SET NULL" action.
create table `category` (
`id` integer not null primary key autoincrement,
`name` varchar(255) not null
);
create table `todo` (
`id` integer not null primary key autoincrement,
`title` varchar(255) not null,
`complete` boolean not null default '0',
`category_id` integer,
foreign key(`category_id`) references `category`(`id`) on delete SET NULL on update CASCADE
);
I also have an endpoint in my application that allows users to delete a category.
categoryRouter.delete('/:id', async (req, res) => {
const { id } = req.params
await req.context.models.Category.delete(id)
return res.status(204).json()
})
This route successfully deletes categories, but the problem is that related todo items are not getting their category_id property set to null, so they end up with a category id that no longer exists. Strangely though, if I open up my database GUI and manually execute the query to delete a category... DELETE FROM category WHERE id=1... the "ON DELETE SET NULL" hook is successfully firing. Any todo item that had category_id=1 is now set to null.
Full application source can be found here.
Figured it out, thanks to MikeT.
So apparently SQLite by default has foreign key support turned off. WTF!
To enable FKs, I had to change my code from this...
const knex = Knex(knexConfig.development)
Model.knex(knex)
to this...
const knex = Knex(knexConfig.development)
knex.client.pool.on('createSuccess', (eventId, resource) => {
resource.run('PRAGMA foreign_keys = ON', () => {})
})
Model.knex(knex)
Alternatively, I could have done this inside of the knexfile.js...
module.exports = {
development: {
client: 'sqlite3',
connection: {
filename: './db.sqlite3'
},
pool: {
afterCreate: (conn, cb) => {
conn.run('PRAGMA foreign_keys = ON', cb)
}
}
},
staging: {},
production: {}
}
FYI and other people who stumbled across a similar problem, you need PRAGMA foreign_keys = ON not only for the child table but also for the parent table.
When I set PRAGMA foreign_keys = ON only for a program which handles the child table, ON UPDATE CASCADE was enabled but ON DELETE SET NULL was still disabled. At last I found out that I forgot PRAGMA foreign_keys = ON for another program which handles the parent table.
I am creating a new project and using Spring Data JPA to create some REST endpoints.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
I am able to put and persist to my primary class (customer), which works as long as the json file does not have any oneToMany data. However, when posting to customer, if there is oneToMany data I am getting errors.
The errors relate to the foreign key being null when trying to persist. I am not sure how Spring Data JPA should be using the annotation to let hibernate know what the value of the foreign key should be.
I have looked at numerous bi-directional OneToMany examples, as well as examples for creating foreign keys and have tried a number of modifications without success.
I also tried to use the spring.jpa.hibernate.ddl-auto=update to help create and update the database schema without any luck.
The customer
#Entity
#Table(name="customer")
#EntityListeners(AuditingEntityListener.class)
public class Customer extends Auditable<String> {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#OneToMany(fetch=FetchType.LAZY, mappedBy="customer", cascade={CascadeType.ALL})
private List<EmailAddress> emailAddresses;
.......
The emails
#Table(name="email_address")
#EntityListeners(AuditingEntityListener.class)
public class EmailAddress extends Auditable<String> {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="email_type")
private byte emailType;
#Column(name="email")
private String email;
#ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
#JoinColumn(name="customer_id")
#JsonIgnore
private Customer customer;
.....
The postman json test
{
"id": 1,
"firstName": "Bobby",
"lastName": "Smith",
"emailAddresses": [
{
"id": 1,
"emailType": 1,
"email": "bobby#bobby.com",
},
{
"id": 2,
"emailType": 1,
"email": "bobby#gmail.com",
}
]
}
BTW, I have confirmed that within the customer controller, that the emails are included in the request body of customer.
The customer controller
#PutMapping("/customers")
public Customer updateCustomer(#RequestBody Customer theCustomer) {
System.out.println("****email count "+theCustomer.getEmailAddresses().size());
for(EmailAddress index: theCustomer.getEmailAddresses()) {
System.out.println(index.toString());
}
customerService.save(theCustomer);
return theCustomer;
}
The customer service
#Override
public void save(Customer theCustomer) {
//Validate the input
if(theCustomer == null) {
throw new CustomerNotFoundException("Did not find the Customer, was null...");
}
customerRepository.save(theCustomer);
}
MySQL Script
--
-- Table structure for table `customer`
--
DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(24) COLLATE utf8_bin NOT NULL,
`last_name` varchar(24) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Primary Customer Table';
--
-- Table structure for table `email_address`
--
DROP TABLE IF EXISTS `email_address`;
CREATE TABLE `email_address` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email_type` tinyint(4) unsigned NOT NULL COMMENT 'email type',
`email` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'email address',
`customer_id` int(11) NOT NULL COMMENT 'foreign key',
INDEX par_ind (customer_id),
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
KEY FK_EMAIL_CUSTOMER_idx (customer_id),
CONSTRAINT FK_EMAIL_CUSTOMER FOREIGN KEY (customer_id) REFERENCES customer (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='email addresses';
Postman Complaint
{
"status": 400,
"message": "could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
"timeStamp": 1566840491483
}
Console Complaint
****email count 2
EmailAddress [id=1, type=1, email=bobby#bobby.com]
EmailAddress [id=2, type=1, email=bobby#gmail.com]
2019-08-28 17:33:07.625 WARN 8669 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1048, SQLState: 23000
2019-08-28 17:33:07.626 ERROR 8669 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'customer_id' cannot be null
2019-08-28 17:33:07.629 ERROR 8669 --- [nio-8080-exec-2] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
2019-08-28 17:33:07.735 WARN 8669 --- [nio-8080-exec-2] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement]
Therefore, with a post or put, I am not sure why the Spring Data JPA save does not satisfy the foreign key constraint for entities with oneToMany relationships. I am guessing it is either some missing annotations or something wrong with my sql script. Not sure why the update data does not persist to the email_address table. Does the emailAddress entity require some type of getter/setter for customer_id?
public class Customer extends Auditable<String> {
#OneToMany(fetch=FetchType.LAZY, mappedBy="customer", cascade={CascadeType.ALL})
private List<EmailAddress> emailAddresses;
}
public class EmailAddress extends Auditable<String> {
#ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
#JoinColumn(name="customer_id")
private Customer customer;
}
The mappedBy here means that the relationship between Customer and EmailAddress (i.e. the value of customer_id in customer table ) are determined by EmailAdress#cutomer but not Customer#emailAdresses.
What you are trying to show it just the content of Customer#emailAddress which will be ignored by Hibernate when deciding which DB values to be updated/inserted for this relationship. So you have to make sure EmailAddress#customer are set correctly.
For example , you can have the following method to add an email address to a Customer
public class Customer {
#OneToMany(fetch=FetchType.LAZY, mappedBy="customer", cascade={CascadeType.ALL})
private List<EmailAddress> emailAddresses;
public void addEmailAddress(EmailAddress email){
//As said Hibernate will ignore it when persist this relationship.
//Add it mainly for the consistency of this relationship for both side in the Java instance
this.emailAddresses.add(email);
email.setCustomer(this);
}
}
And always call addEmailAddress() to add an email for a customer. You can apply the same idea for updating an email address for a customer.
This is the error I am thrown when I try to delete my categories entry with products under that parent category:
Illuminate \ Database \ QueryException
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`store`.`products`, CONSTRAINT `products_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`)) (SQL: delete from `categories` where `id` = 1)
After I did some research I do know that you cannot delete a parent with existing children
I am not sure how to join the products with my category id when I delete the category id. This way I can delete all products with the associated category id.
Here is my function for deleting:
public function postDestroy() {
$category = Category::find(Input::get('id'));
if ($category) {
$category->delete();
return Redirect::to('admin/categories/index')
->with('message', 'Category Deleted');
}
return Redirect::to('admin/categories/index')
->with('message', 'Something went wrong, please try again');
}
If you want to delete any products that have the same category, I would change your category class that extends eloquent to something like this:
class Category extends Eloquent
{
// ... all your existing code...
public function delete()
{
// Delete all of the products that have the same ids...
Products::where("category_id", $this->id)->delete();
// Finally, delete this category...
return parent::delete();
}
}
Now calling $category->delete() will delete all the products that have the same category_id, as well as the category itself.