Use of Hibernate when constraints are not defined - spring

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.

Related

How does jpa annotation sequenceGenerator works and how to avoid duplicate entries?

#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqGen")
#SequenceGenerator(name = "seqGen", sequenceName = "seq", initialValue = 1)
private Long id;
Basically, i have three entities (fetched at the same times) that share the same sequenceName in my database.
I know for performance purpose, it's better to use the same tab for every entities with a SequenceGenerator.
When fetching a lot of datas, sometimes, i have a duplicate entries error, and basically changing the next_val of my sequence fix the problem temporarly.
I want to know how the SequenceGenerator work better and if there is a work around this issue ?
EDIT : could you explain me better what exactly is the purpose of a sequence and how his algorithm works ?
EDIT 2 : in a post, i saw this comment :
"Assume that you create a sequence object that has the CACHE option
enabled in Microsoft SQL Server 2012 or SQL Server 2014. When the
instance is under memory pressure, and multiple concurrent connections
request sequence values from the same sequence object, duplicate
sequence values may be generated. In addition, a unique or primary key
(PK) violation error occurs when the duplicate sequence value is
inserted into a table."
Is there a link with my current issue ?
There is no algorithm for sequence, it is +1 similar to autoincrement, also it has nothing to do with fetching your data that has duplicate entries.
Duplicate entries only mean that your database or code has a bad structure which leads to allowing data to be duplicated in the first place.
GenerationType. IDENTITY − In identity, the database is responsible to auto-generate the primary key. Insert a row without specifying a value for the ID and after inserting the row, ask the database for the last generated ID. Oracle 11g does not support an identity key generator. This feature is supported in Oracle 12c.
GenerationType. SEQUENCE − In sequence, we first ask the database for the next set of the sequence then we insert a row with return sequence id.
https://www.baeldung.com/hibernate-identifiers

Panache: Insert or ignore child

I want to persist an entity that has a #OneToMany relationship to a child entity. I'm using Quarkus 1.13.1 with Quarkus Panache.
Example
public class User {
private List<Item> items;
#OneToMany(cascade = CascadeType.ALL)
public List<Item> getItems()...
}
If I want to persist a user (user.persist()) with a few items that already exist in the item table, then I get of course a "duplicate key" exception. So far so good.
But I was wondering if there is a descent way to skip/ignore an insert if an item already exists in the table items.
Of course, I could query the database to check if the child value exists, but this seems somehow tedious and bloats the code with data checks, so I was wondering if there was some annotation or other shortcut to handle this.
A persist operation should be used exclusively to create (store) new objects in the database, and makes the Java objects managed by Hibernate until the Session is closed.
It's really important that you know which objects are managed, and which are not, and distinguish wich ones are newly made persistent rather than just represent an existing object in the database.
To this end, it would indeed be better to load the existing Items first; if you know for sure which ones are already existing in the DB you can use a lazy proxy to represent them and put those in the list before persisting the User.
If you don't know which Items already exist in the database, then you should indeed have to query the database first. There is no shortcut for this operation; I guess we could explore some improvements but generally automating such things is tricky.
I would suggest implement the checks explicitly so you have full control over the strategy. It might be a good idea to make Item a cached entity so you can implement safe validations without performance drawbacks.

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

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.

Spring data. How can I update an entity, having the id modified?

Spring data jpa + postgres.
Have an entity
class Entity {
#Id
#GeneratedValue// generator from here https://stackoverflow.com/questions/60989691/how-to-manually-set-a-value-for-generatedvalue/61007375#61007375
private int id;
private String value;
}
And what I wish to do is to UPDATE an existing entity, setting a different id (be it a bad practice or not) value.
By default it of course is treated as a new entity and is attempted to be INSERTed.
Going by the flow of #Modifying seems to do the job right, but currently struggling to find if I can pass the whole entity instead of pinpointing every field:
update Entity e set e.id=?1, e.value=?2 where...
to
update Entity e set e=?1
So the questions here would be:
1. Is there a way to gracefully do an "UPDATE" with modified id in terms or regular spring-data-jpa flow?
2. If not, is there a way to provide the full entity to be consumed by the #Query?
Is there a way to gracefully do an "UPDATE" with modified id in terms or regular spring-data-jpa flow?
If you are using GenerationType.AUTO strategy for #GeneratedValue (AUTO is the default strategy), then you can set id of Entity to null before calling save. It will insert a new record with rest of the fields being the same as original. id of new record will be generated automatically by database engine.
If you are using GenerationType.AUTO strategy for #GeneratedValue
If not, is there a way to provide the full entity to be consumed by the #Query?
You can chose not use #Query. A far simpler approach can be using default save method provided by JPA repositories to directly pass an Entity object.

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.

Resources