Switch between databases in spring/ hibernate - spring

In my project I use Spring and hibernate. I use MySql and use auto increment for Ids. But now I need to support multiple database types. (separate installations). Say, MySql, Oracle (11g), Postgresql, etc.
My current idea is to use uuid for primary keys since I can switch to any database without much worrying about database layer. But since I have used Integer for auto_increment I have to modify my code base.
Is there a way to preserve Integer id? or shall I proceed with uuid ?
Current implementation
#Id
#GeneratedValue
#Column(name = "id", nullable = false, updatable = false)
private Integer id;
Or this, (or any other solution)
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
#Column
#Id
private String id;

I found a way to do this.
Idea is to add annotation configuration and override it using xml.
XML metadata may be used as an alternative to these annotations, or to override or augment annotations
Here is a good tutorial for this,
https://vladmihalcea.com/how-to-replace-the-table-identifier-generator-with-either-sequence-or-identity-in-a-portable-way/

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.

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.

data version dosen't increase when we delete or add an child entity in spring data?

I'm using #version annotation in spring data so I have a parent entity, and it has list of child entity. when I delete an element from child list the parent version doesn't increase. can anyone clarify for me this #version alternative,
why the versing in this case doesn't increase, is it a good way to manage versioning or should I use trasaction "lock".
in the documentation i read that the version update only on updating a row
in the databse but in my case i put version on parent entity and i want
note: i searched a lot in the internet but i didnt find a clear solution, can any one help me.
I assume you are using Hibernate. Lets say that the "UnderlyingPerTradingAccount" table has a column called "trading_account_id", which is a foreign key to the TradingAccount table. In order to achieve the behavior you described, you need to change the mapping. Can you try this:
public class TradingAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="trading_account_id", referencedColumnName = "trading_account_id", insertable = false, updatable = false)
private List<UnderlyingPerTradingAccount> underlyingPerTradingAccounts;
#Version
private Long version;
}
and
public class UnderlyingPerTradingAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name="trading_account_id", nullable = false)
private TradingAccount tradingAccount;
private Boolean enableBuy;
private Boolean enableSell;
}
This should mark the parent entity as "dirty" when the child entity is updated and trigger the version increment.
However, I would think of some other method to track "version" changes of the parent entity as this would just cause an additional overhead and update statements to the parent.

Has Spring-boot changed the way auto-increment of ids works through #GeneratedValue?

Spring-Boot 2.0.0 seems to have modified the way Hibernate is auto configured.
Let's suppose two simple and independent JPA entities:
#Entity
class Car {
#Id
#GeneratedValue
private long id;
//....
}
#Entity
class Airplane {
#Id
#GeneratedValue
private long id;
//....
}
Prior, using Spring-Boot 1.5.10, I was able to generate separate sequences of auto-increments, meaning that I can get a Car with 1 as primary key and an Airplane with 1 as primary key too.
No correlation between them, e.g no shared sequence.
Now, with 2.0.0, when I sequentially create a very first Car then a very first Airplane, the car gets 1 as id and airplane gets 2.
It seems that he has to deal with the GeneratedType.AUTO, that is the "used by default" specified within the #GeneratedValue annotation source.
However, my reasoning seems to stop here since GeneratedType.AUTO was also set as default with the 1.5.10.
A simple workaround to fulfil my expectation is to specify the IDENTITY strategy type of generation like so:
#Entity
class Car {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
//....
}
#Entity
class Airplane {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
//....
}
I can't figure out an explanation of this behavior.
What has Spring-boot 2.0.0 changed, explaining this scenario?
Spring Boot 2.0 uses Hibernate 5.2 (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes).
Hibernate changes its GeneratedType.AUTO strategy since 5.2. Any database that does not support sequences natively (e.g. MySQL), they use the TABLE generator instead of IDENTITY. (https://hibernate.atlassian.net/browse/HHH-11014)
That's why GeneratedType.AUTO does not work as you expected.
You can use
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
to use MySQL autoincrement.
If you are in need for a quick, not future-proof solution to prevent this issue from happening:
spring.jpa.hibernate.use-new-id-generator-mappings=false, as from the Spring Boot 2 docs:
spring.jpa.hibernate.use-new-id-generator-mappings= # Whether to use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE.
This will prevent from using the new generators and keep the old functionality included in Spring boot 1.x.x.
Please note that this is probably not the best solution, but it is very helpful on short term
As Andrew has pointed out in the comment, if you don't want the id to be incremented while values are created in other tables, you can specify your ID like this:
#Id
#GeneratedValue(
strategy= GenerationType.AUTO,
generator="native"
)
#GenericGenerator(
name = "native",
strategy = "native"
)
private Long id;
Doing this will make each table has its unique id beginning with 1,2,3 ... and so on.
By default spring-boot uses the auto and increment the value based on the order the objects are saved.
To provide unique id based on each object, use the following
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

Implementing multi tenancy on Spring Data JPA

I am planning to write a multi tenant aware application using Spring Data JPA as the persistent layer. However I'm not planning to keep separate databases per tenant rather all the data is in a single database. Following beans represent the Project entity and Tenant entity that I'm planning to implement. When fetching a project or fetching the list of all projects, I should be able to filter them according to a specific tenant. I know that I can easily do that by writing methods like bindByIdAndTenant_Id(int id, int id) but I'm worrying about the scalability of the solution. What I need is to have a method like findById(int id) and content will be automatically filtered by the tenant id that is fetched from a context. Is there a way I can do that in Spring Data JPA?
Project Entity
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "TENANT_ID", referencedColumnName = "id", foreignKey = #ForeignKey(name = "FK_TENANT_IN_PROJ"))
private Tenant tenant;}
Tenant Entity
public class Tenant {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
int id;
#Column(name = "NAME", unique = true)
private String name;}
There is (afaik) no such "out-of-the-box" mechanism. How should Spring know how a tenant is identified and what is important to separate the data. Maybe there is even some shared data between tenants so things get more complicated and it's on your own to do that.
I assume your data is accessed using a web layer and you have to identify the tenant on every call. A "simple" path parameter or a header is (without any further validation) too easy to manipulate so maybe a token (JWT) is a good alternative to store request based tenant information. Here is a nice article about exactly that topic. Another one is talking about pros and cons, especially related to differences in storage architecture.
Personally, I would tend to the "different databases" approach. It's easy to scale out and user data is separated properly. Also backup and restore mechanisms are possible for each tenant without affecting the other users. At least use different schemata for each tenant, but maybe that depends on the database and what exactly is meant by "schema".

Resources