Implementing multi tenancy on Spring Data JPA - spring

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".

Related

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 MVP Forms overwriting Many to Many ArrayList when updating an object

I have a simple project that has a User model, Sports team model and a Many To Many table where a user can "like" the sports team.
User
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "likes",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "team_id")
)
private List<Team> teamsLiked;
Team
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(min=2, max=30)
private String teamName;
#NotBlank
private String city;
private String sport;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "likes",
joinColumns = #JoinColumn(name = "team_id"),
inverseJoinColumns = #JoinColumn(name = "user_id")
)
private List<User> likers;
My problem is, when I'm using Spring MVC forms for a user to edit a team, upon submission it completely wipes out existing likes on the Team object under likers. On the edit page, I am using #ModelAttribute and pre populating the existing team object, and have tried to put the likers as a hidden attribute so the data will persist, but that throws an error. I've tried on the #PostMapping backend, to set the origin list of likers before re-saving the DB and that's not working either. Besides using Normal HTML forms to update an object, is there a way I can have the list of users who liked a team persist after updating? Thanks in advance.
What you need here is a DTO and map that onto an existing entity. I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Team.class)
#UpdatableEntityView
public interface TeamDto {
#IdMapping
Long getId();
String getTeamName();
void setTeamName(String teamName);
String getCity();
void setCity(String city);
String getSport();
void setSport(String sport);
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TeamDto a = entityViewManager.find(entityManager, TeamDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<TeamDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
And in your case of saving data, you can use the Spring WebMvc integration
that would look something like the following:
#Transactional
#PostMapping("/teams")
void save(#RequestBody TeamDto dto){
repository.save(dto);
}

architecture microservice spring boot

I am working with Spring cloud (microservices) and I have implemented security with JWT token.
in my security application, I have entities like User, Role and UserRole.
so Every request first comes to the ZOOL service and it calls Authentication service and Authentication service creates/returns JWT token.
Also, I have another microservice-rest application (Questions-app) that needs JWT token.
in the Questions-app I have a Question entity that contains authorId field.
#Entity
#Table(name="QUESTION")
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", updatable = false, nullable = false)
private long id;
#Column(name = "AUTHOR")
private long authorId;
#Column(name = "TITLE")
private String title;
}
Now, it is not clear for me, is it right to set authorId long type or I should create User, Role, UserRole entities (just simple copy from AUTH project) in the questions-app and set "AUTHOR" column like that
#OneToOne
#JoinColumn(name="AUTHOR")
private User user;
I know that in the first option when I need show question and user's name on the webpage, then I should call 2 services (one from question-app (fetch question) and another from auth service (fetch user information by author id)
I would like to know what is the best practises?
If you have common database for all of these microservices and you need User related information based on question id.
Then instead of doing another database call for user, you can directly do #OneToOne to User.
From your question it is better to go for 2 option.

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;

Switch between databases in spring/ hibernate

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/

Resources