hbm2ddl.auto update and new boolean property added to an existing table with records - jpa-2.0

I have a mapped entity with JPA in a PostgreSQL database.
The table exists and I have some records in it, now I want to add a simple new boolean (not Boolean) property.
In logs I can correctly see the alter table using not null because I chose boolean and not Boolean, it is all right but....
without seeing any errors, the database isn't being updated.
Trying to execute the alter table directly in my SQL client finally I can see the problem, that is the column that I'm just adding contains null values...
Obviously already existent records will have null values as soon as that column will be created.
That said, what could I do if I want to create a not null property in a table having already at least one record?
Thanks

From what i understood from your question, you are getting issues with your earlier added table rows when you update your table by adding a new boolean column.
Your previous table rows gets null values for the newly added column.
You can set the default values on your JPA entity properties using columnDefinition provided as an attribute in #Column annotation.
If you want to add a new boolean property in your JPA entity you can try this
#Column(name = "is_active", columnDefinition="tinyint(1) default 1")
private boolean isActive;
And since you are using hbm2ddl, this will not only create a fresh boolean column for you but also give it a default of true. It will also set true for all the previously added rows in that table.
Hope it helps!

Related

Doctrine Migration from string to Entity

I've an apparently simple task to perform, i have to convert several tables column from a string to a new entity (integer FOREIGN KEY) value.
I have DB 10 tables with a column called "app_version" which atm are VARCHAR columns type. Since i'm going to have a little project refactor i'd like to convert those VARCHAR columns to a new column which contains an ID representing the newly mapped value so:
V1 -> ID: 1
V2 -> ID: 2
and so on
I've prepared a Doctrine Migration (i'm using symfony 3.4) which performs the conversion by DROPPING the old column and adding the new id column for the AppVersion table.
Of course i need to preserve my current existing data.
I know about preUp and postUp but i can't figure how to do it w/o hitting the DB performance too much. I can collect the data via SELECT in the preUp, store them in some PHP vars to use later on inside postUp to write new values to DB but since i have 10 tables with many rows this become a disaster real fast.
Do you guys have any suggestion i could apply to make this smooth and easy?
Please do not ask why i have to do this refactor now and i didn't setup the DB correctly in the first time. :D
Keywords for ideas: transaction? bulk query? avoid php vars storage? write sql file? everything can be good
I feel dumb but the solution was much more simple, i created a custom migration with all the "ALTER TABLE [table_name] DROP app_version" to be executed AFTER one that simply does:
UPDATE [table_name] SET app_version_id = 1 WHERE app_version = "V1"

QSqlTableModel::removeRow() does not delete row in SQLite database

I use Windows, C++ and Qt 5.11.1. I have a simple single-table SQLite database. This table "templates" contains primary key: id INTEGER PRIMARY KEY.When I try to remove a single row in "templates" (using QSqlTableModel), m_model.removeRow(0) returns true and submitAll() returns true. But the table still owns that row. I've found that Qt generated the following SQL:
DELETE FROM templates WHERE "templates"."id" IS NULL.
I suppose that the problem is linked with QSqlRecord::isGenerated. How can I fix this error?
As QSqlTableModel::removeRows documentation :
Deletions are submitted immediately to the database. The model retains a blank row for successfully deleted row until refreshed with select().
Sorry, the reason was in overriden data() method in QSqlTableModel descendant. record() method required data(index, Qt::EditRole).

Doctrine Nothing to update after adding onDelete="CASCADE"

I want to add the option onDelete="CASCADE" on one of my attributes via the #JoinColumn annotation:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Product",mappedBy="category",fetch="EAGER")
* #ORM\JoinColumn(onDelete="CASCADE")
*/
private $products;
But when I try to update with php bin/console doctrine:schema:update --force , I always get:
nothing to uptade - database already sync.
I tried to add some other attributes and I got the same issue. However, if I intentionally add a mistake I get an error as expected.
How can I fix this?
The #OneToMany annotation is the one you use on the inverse side of your many-to-one association. The table storing the entities on this side of the association does not hold any foreign key pointing to the table storing your Product entities, thus there is no "join column" there.
The documentation states the following about #JoinColumn:
This annotation is used in the context of relations in #ManyToOne, #OneToOne fields and in the Context of #JoinTable nested inside a #ManyToMany.
In your case, the annotation does not apply to any column at all and consequently, your database does not need to be updated.
If you wish to have Product entities related to a given Category removed through cascade operations by your database, you have to add a #JoinColumn(onDelete="CASCADE") on the owning side of the association, next to the #ManyToOne annotation of the category attribute of Product.

Update Else Insert without Update Strategy

I'm inserting from a source table to the target table. If the source record already exists in the target, then it will update else insert. I have done this without using Update strategy. In session properties, I have set as treat rows as 'Update' and in the mapping target properties, I selected 'Insert' and 'Update Else Insert' checkbox. Also, I have chosen a primary key in the target table as well. But while running the session every time, it is always inserting the same rows again and again. I.e. duplicate rows are inserting instead of update the record. am I doing anything wrong?
The database table should have primary keys defined for this to work.

Cannot make #ManyToOne relationship nullable

I have a many-to-one relationship that I want to be nullable:
#ManyToOne(optional = true)
#JoinColumn(name = "customer_id", nullable = true)
private Customer customer;
Unfortunately, JPA keeps setting the column in my database as NOT NULL. Can anyone explain this? Is there a way to make it work? Note that I use JBoss 7, JPA 2.0 with Hibernate as persistence provider and a PostgreSQL 9.1 database.
EDIT:
I found the cause of my problem. Apparently it is due to the way I defined the primary key in the referenced entity Customer:
#Entity
#Table
public class Customer {
#Id
#GeneratedValue
#Column(columnDefinition="serial")
private int id;
}
It seems that using #Column(columnDefinition="serial") for the primary key automatically sets the foreign keys referencing it to NOT NULL in the database. Is that really the expected behavior when specifying the column type as serial? Is there a workaround for enabling nullable foreign keys in this case?
Thank you in advance.
I found the solution to my problem. The way the primary key is defined in entity Customer is fine, the problem resides in the foreign key declaration. It should be declared like this:
#ManyToOne
#JoinColumn(columnDefinition="integer", name="customer_id")
private Customer customer;
Indeed, if the attribute columnDefinition="integer" is omitted the foreign key will by default be set as the source column: a not-null serial with its own sequence. That is of course not what we want as we just want the to reference the auto-incremented ID, not to create a new one.
Besides, it seems that the attribute name=customer_id is also required as I observed when performing some testing. Otherwise the foreign key column will still be set as the source column. This is a strange behavior in my opinion. Comments or additional information to clarify this are welcome!
Finally, the advantage of this solution is that the ID is generated by the database (not by JPA) and thus we do not have to worry about it when inserting data manually or through scripts which often happens in data migration or maintenance.
I came across this problem but I was able to solve it this way:
#ManyToOne
#JoinColumn(nullable = true)
private Customer customer;
Maybe the problem emerged from declaring #ManyToOne(optional = true)
That is very weird.
In JPA nullable parameter is true by default. I use this kind of configuration all the time and it works fine. If you try to save entity it should be successful.
Did you try to delete table that is created for this relationship? Maybe you have legacy table with that column?
Or maybe you should try to find solution on other chunks of code, because this is proper configuration.
Note: I have tried this configuration on PostgreSQL with JPA2 and Hibernate.
EDIT
In that case maybe you can try a little bit different definition of primary key.
For example you can use definition like this:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column()
private Long id;
and postgresql will generate
id bigint NOT NULL
-- with constraint
CONSTRAINT some_table_pkey PRIMARY KEY (id)
If this is good enough you can try this solution.
within transaction but before the save operation, explicitly set the foreign key column value as null. By this hibernate ,never perform select queries for this foreign key related table and don't throw the exception "save the transient instance before flushing". if you want to set "null value " conditionally, then perform 1. fetch & set the value using repo call get/ find 2. then check the fetched value for the condition and set it to null accordingly .pasted the code below which is tested and found working
// Transaction Start
Optional<Customer> customerObject = customerRepository.findByCustomerId(customer.getCustomerId())
if(customerObject.isPresent())yourEnclosingEntityObject.setCustomer(customerObject)}
else {yourEnclosingEntityObject.setCustomer(null)}
yourEnclosingEntityObjectRepository.save(yourEnclosingEntityObject)
// Transaction End