Race Condition in Postgres SQL using Spring data JpaRepository - spring

I am facing a wierd issue in my implementation where I am persisitng data to a PostgresSQL DB using Spring data JpaRepository
In my Entity class I have the below columns:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private int id;
#Column(name = "field1", nullable = false, length = 16)
private String field1;
#Column(name = "field2", nullable = false, length = 16)
private String field2;
#Column(name = "field3", nullable = false, length = 16)
private String field3;
I initially avoided declaring the fields above as composite since there were many fields to be dealt with as composite keys. I thought the java code check would do the trick in all scenarios
So basically, I have to maintain the uniqueness of each row based on field1,field2 and field3. That was the basic requirement for which I had checks in my java code that if any entry exists in the DB for the combination of field1,field2 and field3 then I used to throw java exceptions
No two rows can have these values repeating. All was good until the application was tested under some errorneous business scenarios which would never happen in production but got run by mistake
Whats happening now is that if 2 requests are triggered at the exact same instance with the exact same 3 fields above (through a script) then they both enter into the Database since both get the entry check as false
Would declaring all of them as one composite key resolve the situation?

You should define the unique constraint in your database in addition of JPA constraint.
#Entity
#Table(uniqueConstraints={
#UniqueConstraint(columnNames = {"field1", "field2", "field3"})
})
public class MyEntity {
...
}

Related

Postgresql Spring Data #GeneratedValue without #Id return null value

im working in spring boot project i want to map a property with serial (using my sequence) column in my table and this column is not the ID.
i found this solution :
#Column(name = "DEMANDE_NUMBER", insertable = false, updatable = false, columnDefinition = "serial")
private Integer demandeNumber;
(because #GeneratedValue persist null and doesn't use the sequence)
this solution works fine and the field is persisted in my DB and the value uses the sequence but when i get my object after saving using my repository the demandeNumber is null
Demande savedDemande= demandeRepository.save(demandeToSave);
//demandeObj .getDemandeNumber() return null
any suggestion to resolve this issue please ?
Thanks.
according to this answer How to use a sequence generator for a non ID field?
i added the following annotation on my property
#Generated(GenerationTime.INSERT)
#Column(name = "column_name", columnDefinition = "serial", updatable = false)
you should import the package from hibernate and not javax.persistence.
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
i hope this can help other people in the future. note this solution is for spring data with postgresql.

Best design pattern for Spring Boot CRUD REST API with OneToMany relationships

I'm struggling to find what feels like a good design for a Spring Boot CRUD REST API app that involves several OneToMany relationships w/ join tables. For example, consider this DB structure in MySQL which allows one "Recipe" to be associated with several "Recipe Categories":
create table recipes
(
id int auto_increment primary key,
name varchar(255)
);
create table recipe_categories
(
id int auto_increment primary key,
name varchar(64) not null
);
create table recipe_category_associations
(
id int auto_increment primary key,
recipe_category_id int not null,
recipe_id int not null,
constraint recipe_category_associations_recipe_categories_id_fk
foreign key (recipe_category_id) references recipe_categories (id)
on update cascade on delete cascade,
constraint recipe_category_associations_recipes_id_fk
foreign key (recipe_id) references recipes (id)
on update cascade on delete cascade
);
On the Java side, I'm representing the structures as JPA entities:
#Entity
#Table(name = "recipes")
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
#JsonManagedReference
private Set<RecipeCategoryAssociation> recipeCategoryAssociations;
// ... setter/getters ...
}
#Entity
#Table(name = "recipe_categories")
public class RecipeCategory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "name", nullable = false)
private String name;
// ... setter/getters ...
}
#Entity
#Table(name = "recipe_category_associations")
public class RecipeCategoryAssociation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "recipe_category_id", nullable = false)
private RecipeCategory recipeCategory;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "recipe_id", nullable = false)
#JsonBackReference
private Recipe recipe;
// ... setter/getters ...
}
This works OK, but my hang-up is that to persist/save a new Recipe via REST JSON API, the caller needs to know about the join table recipe_category_associations. For example a PUT request w/ this payload could add a new Recipe to the DB associating it with the "category foo" recipe category:
{
"name": "Chicken soup",
"recipeCategoryAssociations": [{
"recipeCategory": {
"id": 123,
"name": "category foo"
}
}]
}
Using this in the controller:
#PutMapping(path = PATH, produces = "application/json")
#Transactional
public #ResponseBody Recipe addNewRecipe(#RequestBody Recipe recipe) {
return recipeRepository.save(recipe);
}
To me, the inclusion of "recipeCategoryAssocations" key in the JSON payload feels weird. From the client POV, it doesn't really need to know the mechanism creating this association is a join table. Really, it just wants to set a list of recipe category ids like:
{
"name": "Chicken soup",
"recipeCategories": [123, 456, ...]
}
Any tips how best to accomplish this in nice way? It'd be nice if I can keep the REST implementation super clean (e.g., like I have now with one recipeRepository.save(recipe); call). Thanks in advance.
When writing software we expect requirement to change. Therefore we want to make sure our code will be flexible and easy to evolve.
Coupling our server response with our DB structure makes our code very rigid. If a client needs a new field or if we want to arrange the DB schema differently everything will change.
There are several different approaches to designing your software correctly. A common approach is called "Clean Architecture" and is outlined in a book by this title by the great Uncle Bob. The Book itself outlines the approach in high level but there are many example projects online to see what it means in action.
For example this article by my favourite Java blog:
[baeldung.com/spring-boot-clean-architecture][1]
If you are looking for something simpler, you can follow the ["3-Tier Architecture"][2] (not really an architecture in my mind). Separate your code in to 3 layer:
Controller/Resource/Client
Service/BusinessLogic
Repository/DataAccess
Each layer will use a different data object. the business logic layer will have the object in it's purest form without constraints regarding who will want to read it and where it is stored and will be mapped/converted to the objects in the other layers as needed.
So in your case you might have 3 (or more) different objects:
RecipeDTO
Recipe
model.Recipe (and model.RecipeCategoryAssociation etc.)
Make sure that the Business level object only have fields that makes sense from a business logic. The code in each layer will use the objects that are relevant to that layer. When a rest controller class for example calls the business logic server it will need to convert the DTO object to the Business level object for example. Very important to maintain this separation between layers

Spring Data + View with Union return duplicate rows

i'm using Spring Boot 2.4.2 and Data module for JPA implementation.
Now, i'm using an Oracle View, mapped by this JPA Entity:
#Entity
#Immutable
#Table(name = "ORDER_EXPORT_V")
#ToString
#Data
#NoArgsConstructor
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class OrderExportView implements Serializable {
private static final long serialVersionUID = -4417678438840201704L;
#Id
#Column(name = "ID", nullable = false)
#EqualsAndHashCode.Include
private Long id;
....
The view uses an UNION which allows me to obtain two different attributes of the same parent entity, so for one same parent entity (A) with this UNION I get the attribute B in row 1 and attribute C in row 2: this means that the rows will be different from each other.
If I run the query with an Oracle client, I get the result set I expect: same parent entity with 2 different rows containing the different attributes.
Now the issue: when I run the query with Spring Data (JPA), I get the wrong result set: two lines but duplicate.
In debug, I check the query that perform Spring Data and it's correct; if I run the same query, the result set is correct, but from Java/Spring Data not. Why??
Thanks for your support!
I got it! I was wrong in the ID field.
The two rows have the same parent id, which is not good for JPA, which instead expects a unique value for each line.
So, now I introduced a UUID field into the view:
sys_guid() AS uuid
and in JPA Entity:
#Id
#Column(name = "UUID", nullable = false)
#EqualsAndHashCode.Include
private UUID uuid;
#Column(name = "ID")
private Long id;
and now everything works fine, as the new field has a unique value for each row.

Manage String id sequence in spring boot entity

I'm working on oracle database to manage a JPA entity with a String Primary key.
I cannot modify the type on the PK to a Long or int in the database, so i want to know how to configure the pk sequence in my JPA entity,
i've tried this :
#Id
#SequenceGenerator(name="SEQ_ID", sequenceName = "SEQ_ID" )
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_ID")
#Column(name="SEQ_ID",unique=true, nullable = false,updatable = false)
private String id;
but when persisting a new entity i got the error : Unknown integral data type for ids : java.lang.String
someone can help me please ?
Try removing #GeneratedValue and #SequenceGenerator
Also, a remark, #Id will automatically set unique=true, nullable = false,updatable = false so you can remove them from #Column.
Otherwise, you can check this article for more details about creating a custom string generator https://vladmihalcea.com/how-to-implement-a-custom-string-based-sequence-identifier-generator-with-hibernate/

oracle with spring boot not fetching by primary key

I have written a spring boot app with oracle db.
Below is my entiry class.
#Entity
public class SystemTypeLookup{
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Type(type = "uuid-char")
#Column(name = "ID", updatable = false, nullable = false)
protected UUID id;
#Column(name = "CODE")
private String code;
}
And in passing my own UUID as primary key value.
In oracle db ID is considered as RAW and the UUID stored in oracle is differently.
There is no - separation in oracle and all the UUID chars are in upper case.
When i try to find the entity using primary key it is not fetching the row with id. I'm always getting null.
#Resource(name = "coreRepository")
private ErpEntityRepository coreRepositoryBase;
SystemTypeLookup systemTypeLookup = coreRepositoryBase.findOne("WHERE o.id='"+id+"'", SystemTypeLookup.class);
when is pass 76c03cd9-3d96-40c5-8df9-aad8f2369453 as id value then the oracle will insert the id without '-' and all chars will be in upper case.
So how to solve this issue?
First of all you should use parameters in your query and second make sure that the id you are passing is of type UUID and not String.

Resources