#Column annotation doesn't change nullability when I change nullable property from false to true - spring

By mistake I sit the #Column annotation property to false while my intention was to set it to true.
#Column(nullable = false)
private String title;
I tried to change the property to true after the table was created but It doesn't get changed in my table. Even though I have the following lines in my application.properties file:
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
Now when I try to create a new record I gett this error:
SQL Error: 1048, SQLState: 23000
Column 'title' cannot be null
I know that I can change nullability manually through phpMyAdmin but I'm wondering why the column annotation property doesn't work after the table is created.

From: How does spring.jpa.hibernate.ddl-auto property exactly work in Spring?
The update operation for example will attempt to add new columns, constraints, etc but will never remove a column or constraint that may have existed previously but no longer does as part of the object model from a prior run.
In general, the schema generation tool is mostly for prototyping, testing and validation. Incremental scripts using dedicated tools are a better solution for production.
There is also a note about this in the documentation:
Although the automatic schema generation is very useful for testing and prototyping purposes, in a production environment, it’s much more flexible to manage the schema using incremental migration scripts.
I'm not overly familiar with these type of tools but, to give you an example, Liquibase is just the first one that comes to mind.

Related

Spring-data-jdbc - How to set schema in many-to-many relationship

I am following the spring.io blog here: https://spring.io/blog/2018/09/24/spring-data-jdbc-references-and-aggregates and have a sample SpringBoot app with Book and Author entities here: https://github.com/b-sridhar/spring-data-jdbc-example.
I get the following error: No value supplied for the SQL parameter 'demobook': No value registered for key 'demobook'
And while debugging noticed the SQL query that is executed is: INSERT INTO demo.book_author (author, demo.book) VALUES (:author, :demobook) which should have been INSERT INTO demo.book_author (author, book) VALUES (:author, :book). This happens because I have the #Table("demo.book") as the annotation for the Book entity. If I remove the demo schema in the table name then the tests in BookAndAuthorsTests go through fine.
How to set the schema for the table Book?
This is a bug in the 1.0.x release. It is fixed for 1.1.
If you upgrade your spring-boot-starter-parent to 2.2.0.M5 you'll get the spring-data-jdbc version 1.1.0.RC2 which contains the fix.
With that fix table names in #Table annotations do work as well as the approach of having the NamingStrategy return a schema.
Note: that your configuration then needs to extend AbstractJdbcConfiguration instead of JdbcConfiguration.
Another note: Your example project then chokes because the AuthorRef needs an explicit mapping to the table BOOK_AUTHOR.
I'll leave a PR in a second.

Use of Hibernate when constraints are not defined

We have a requirement to redesign one application but without doing any modification on the database.
The current code uses plain jdbc to query data. We are planning to implement hibernate instead as part of the redesign.
The problem is the current database doesn't have any constraints present at the database level.
For example if there are two entities Course and Review. The current database doesn't have any mapping between them.
Now in this case if I implement hibernate and try to have #OneToMany between Course and Review, without having those constraints in the database, will that work out fine?
1 To stop hibernate from changing you scheme set parameter
spring.jpa.hibernate.ddl-auto = none
81.2 Initialize a Database Using Hibernate
2 Set GeneratedValue strategies to use existing sequences
#SequenceGenerator(name="my_seq", sequenceName="my_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq")
private Long id;
This way Hibernate will change nothing in your schema. It will not create any sequences, tables, foreign keys, constraints e.t.c. Just use existing structure and fall if something wrong with it )
If there is no cascading from Course to Reviews and you delete Course the delete will go through without any complaints. With constraint present, DB would have complained about existing child records(Review in this case).
So special care needs to be taken specifying the cascade.

How to manage unique Ids with Hibernate and Flyway?

Here is a snippet of my entity class
#Entity
public class User {
#Id
#GeneratedValue
private long id;
private String firstName;
private String lastName;
When using (Spring Boot + Hibernate) Spring Boot setups schema automatically including sequences like one below
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
But I am using Flyway 5.0.7 to setup my schema. And in this case I get the error below, which means sequence is not getting created.
Sequence "HIBERNATE_SEQUENCE" not found; SQL statement
I was able to fix this by creating sequence using flyway script like below
create sequence HIBERNATE_SEQUENCE start with 1001;
But now this sequence is used to generate Ids for all entities which I do not want. I want each entity to have its separate sequence.
Is it possible to create sequences using Hibernate when using Flyway? Otherwise it is not practical to manually create sequences for all entities which can be in hundreds.
Any alternative approach to handle this?
Flyway is a DB migration tool, and it does not know of any DDL/DML changes unless you tell it so (via new scripts in the locations property).
If Hibernate handles some of these changes (the sequences in your case) Flyway won't know about it and will use whatever sequence it already has knowledge about.
The normal thing to do is letting Flyway know of your changes, which includes a new sequence for a new entity for instance, just like you would do for the schema itself of your entity. My personal advice is to manage all your schema changes in one place, so if you are using Flyway, then let it be in charge of all of it.

Hibernate's saveOrUpdate ignores not-null column property

I have a mapping file that specifies a given column as not-null="true". This is a mistake since the table's column is set to NULL on the Oracle database. But we did not notice that until now, more than an year after creating the mapping file, because Hibernate have been "ignoring" this. Is that possible?
To make it clearer. On the database:
CREATE TABLE db.my_table
(...)
my_column NUMBER(10,0) NULL,
(...)
On the mapping file:
<column name="MY_COLUMN" precision="10" scale="0" not-null="true">
Then on the Java code:
getHibernateTemplate().saveOrUpdate(myEntity);
getHibernateTemplate().flush();
This code is WORKING on our environment. Has always been. But some clients have had issues of ot-null property references a null or transient value and when I debugged the code it didn't make any sense. This code should never been able to run, as far as I know.
Of course it's simple to solve the clients problems, I just have to correct the mapping file so it represents my entity properly. But the real issue here is why haven't Hibernate complained about it?
I've asked some other engineers here with more experience on Hibernate but none of them have ever seen this.
So, can anyone gimme a hint?
EDIT: Just want to stress that both our test environment and my client's are running the exact same code, and in both cases the myEntity object has the myColumn property set to NULL. So, what puzzles me is why it does not yield any exception here on our environment.
It is absolutely correct behaviour.
The not-null attribute has two meanings:
support schema exporting tools
check the Entity in runtime (i.e. do not check database column setting)
See: 5.1. Mapping declaration, extract:
The mapping document also contains some extra optional attributes and
elements that affect the database schemas exported by the schema
export tool (for example, the not-null attribute).
And 5.1.11. Property, extract:
not-null (optional): enables the DDL generation of a nullability
constraint for the columns.
So, if your clients run some code, which tries:
getHibernateTemplate().saveOrUpdate(myEntity);
while the myEntity is missing some property set as not-null="true" it is correct in throwing a runtime exception. While in your test environment you most likely always set the property to some not null value.
And there is an advantage even. The DB and App are loosely coupled. So if needed, you can make more constraints on App side, while not touching the DB (e.g. you are not allowed)
I had exactly the same problem:
In my hibernate mapping file I had set not-null="true" for a special column.
On my development machine I could persist null values without any exceptions. On the customers machine we always received a PropertyValueException / DataIntegrityViolationException.
This was a dangerous behavior. Manual and automatic tests would not fail.
Solution:
On my development machine I hat to set the property hibernate.check_nullability to true. Only then I also got the exceptions on my development system.
Conclusion
This strange behavior could come from adding the hibernate validator to the classpath. This turns check_nullability to false.
See this related question: How to Enable Spring Validation
Somehow this only applies on my dev-system and not in production. This might root from a different dependency loading within different application servers.

GeneratedValue with oracle and hibernate does not work

I am facing an issue with the GeneratedValue annotation in my project with Oracle XE database. The value simply does not get generated - I keep getting the error when trying to insert rows in the table with the autogenerated field.
"Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("SYSTEM"."T_VAPP"."VAPPID")"
The exact same code works with Derby or MySQL but I need to make it work with Oracle and it seems to ignore whatever I put in the generated field definition. I tried using GenerationType.TABLE, GenerationType.SEQUENCE but the same error persists. I also verified using Oracle SQL developer that none of the sequences defined in the code are being created. What do I miss here?
#Entity
#Table(name = "T_vApp")
#RooJavaBean
#RooToString
#RooJpaActiveRecord(identifierColumn = "vAppId", identifierField = "id", table = "T_vApp")
public class VLabApplication {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="my_entity_seq_gen")
#SequenceGenerator(name="my_entity_seq_gen", sequenceName="MY_ENTITY_SEQ")
#Column(name = "vAppId" )
private Long id;
GenerationType.AUTO is correct --- this will ensure that your code works for oracle as well as mysql (without the need to change your code). I use it like this and never had any problems.
Try removing the #Roo magic, generate setters/getters and see if this helps. BTW, I usually annotate the getters. I don't think this is the problem, but you can always try if everything else fails.
My guess is some problem between roo and hibernate (I've never used nor read about roo, so it is really a guess).
I suspect it may be getting confused because you're telling it in two places what the identifier field is. Plus you're telling it both with a #Table and the argument inside the #RooJpaActiveRecord annotation. You should probably remove all the arguments from the #RooJpaActiveRecord annotation on the class. Leave the #Id and #Column annotations on the class "id" member and the #Table annotation on the class.
Alternately, you may need to switch to a custom generator class that does the right thing based on which database.
You used GenerationType.AUTO instead of GenerationType.SEQUENCE

Resources