Spring Boot Webflux CRUD Tutorial - Using Spring Data R2DBC & PostgreSQL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everyone and welcome to this video in this one we take a look on how to create a spring boot with flex Crut service with postgresdb we will create a webflux service and Implement create read update and delete methods on a postgres database so spring boot webflux is a reactive web framework and as a warm-up introduction let's point out the differences between spring Boot non-reactive and reactive web applications for the non-reactive setup we will choose the spring web starter dependency usually the non-reactive app applications are threat heavy there are many threats up to hundreds of threads which are receiving and processing incoming HTTP requests in the processing you often do blocking IO that is every time you do access a file or a database or call another service and blocking IO means the thread is blocked and waits until it receives the response and if the web application is running on a let's say 8 core CPU the threads are not really parallely executed and the CPU has a lot of context switch overhead and for their reactive web applications such as with spring boot weblux it is different only a few threads are processing the incoming requests for example for each CPU core one thread and the threads are not allowed to be blocked that means you must use reactive libraries to do IO operations and non-blocking means when the thread does a request it then returns to do another processing instead of waiting and when the response is ready then a thread picks up the response and continuous processing so such as the case with spring boot web flux and accessing a database so let us take a look on how we can do this let us create a new spring boot application with the spring initializer so I am gonna choose Maven as a build tool and I choose Java as the language 3.1 is the spring boot version which I want and I choose the Java 20 version and I do not care about the project meta data right now and the first dependency which we obviously choose is webflux and the next dependency which we gonna add is spring data R2 DBC which provides reactive SQL database access if you want to use a relational database such as MySQL or postgres this is the one which you want and the next dependency which we need is the postgres driver dependency this one spring data needs a driver dependency depending on the database which you want to use for us it is postgres and also I am going to add the Flyway dependency and this one is required if you want to do database Evolution on your SQL database it enables us to execute database schema scripts all versions and keeps a history of all executed schema Scripts so this one enables us to create and update our database schema and keeps track of the history and also I am going to add the lombok dependency and this one is simply eliminating the need to write boilerplate codes for example Getters and Setters and Constructors and so on so this one you will find in any Enterprise spring boot project so now we are prepared and generate and download the project so after downloading and opening the project with IntelliJ or whatever you want to use let us open the pom.xml file and in here we have specified the dependencies which we all gonna need this is the data R2 GBC and webflux and Flyway core and also the postgres driver dependencies and lombok and now we are all prepared and of course we want to have a postgres database I can do that with Docker compose so let me create a new file and I name it Docker compose yaml and in this file I simply paste this here the docker compose yaml file defines a stack of services here I have only one service with the name DB and I use the postgres docker image and I configure it with some environment variables one variable for the user it is postgres one variable for the password also postgres and I expose the default postgres Port having that in place and opening a new terminal in the folder where I have the docker compose file located I can type in Docker compose up and wait until my postgres database has successfully started and of course I can use a database tool like PG admin to check whether my postgres database is really started I can just connect to the local instance with these settings and the password is postgres as defined at the docker compose yaml then I can simply connect and I see all the tables and I see that there are no tables not yet but we can change that and to create a database schema we will use Flyway so Flyway expects that we create SQL scripts in a certain folder with a certain name syntax these files must be located in the resource folder in the folder DB migration in here we create a new file and we use the prefix V1 to indicate that this is the version one and then we put two underscores and it is important that these are two underscores it does not work with one so watch out for that and then just give it a name like create car table SQL and if we want to evolve the database schema in the future we would add more files here with higher version numbers in this file I paste a create table script here we create a new table with the name car it has a Serial numeric ID it has a brand for example Ford or Mercedes or whatever kilowatt this is where we want to add update delete and get cars via crud operations in order for Flyway to work properly we must configure it in the application properties file in here I set the spring Flyway enable flag to true this indicates that Flyway will be executed on service startup I also provide the database URL to Flyway so this is the URL to my locally running postgres database also I provide the credentials for the database the username and the password also we need to take care of the database connection settings for spring data r2dbc if we do not provide these database connection settings the spring boot service will not start so let me paste these settings here they are pretty self-explanatory obviously this is the URL it must have the prefix r2dbc then postgres SQL to indicate that this is a postgres database and then the URL with localhost port and database name and of course the username and the password so having that set up I can simply start the service by starting the main class and after a while we see the logs of the service we see that it's successfully started listening on the port 8080 and we also see some logs of Flyway we see that FlyAway is has been executed it successfully executed one migration script so it executed the script version 1 create table in PG admin we can verify that we have the stable now let me refresh here and I see we have two tables the card table what we have created with the with the database schema script and also Flyway has created a table for its own the Flyway schema history and if I do a select on the Flyway schema history table I see that there is one entry and this entry is for the version one create table script so Flyway remembers that it has executed the script and it will not execute it again on the next server startup and of course making a select on the card table we see that it is completely empty it has these three columns but no data inside now we can finally start to write the code for that I will apply a best practice for spring boot the three layer architecture this three-layer architecture has a controller layer a service layer and a repository layer the controller layer will have the spring boot controllers and the data transfer objects or short dtos the controller layer specifies which HTTP endpoints we have and what is the input and output the controller layer will delegate each request to the service layer in the service layer we will write the actual business logic and the service layer will use repositories from the repository layer to access the database with that we will have a clean layout code base which is easy to extend and easy to test with the unit tests for that let me create three packages the first one is the controller package the next one is the service package and the third one is the repository package the first thing I'm gonna do is create a data transfer object for a car so in the controller package I create a new package and name it dto and in that package I'm creating a Java record and name it car dto and the car has an ID of type integer it has a brand of type string and uh in Field named kilowatt of type integer and also create the car controller class so in this controller class we Define the HTTP endpoints regardless whether we use spring boot with flux or regular spring web starter this will look the same except that with web Flags we will wrap the responses in mono objects but I will get to that later so at first let's add some class annotations so in this controller let us create a get mapping and in that get mapping we Define a subpath and this path will contain a path variable and it will be named car ID so that means we will have an endpoint which will be reachable via slash car and then slash sum ID and in here we will return a mono which will contain a car dto so GitHub co-pilots kinda knows what I want to do here so I just auto complete and this method we will get a car ID I need to annotate it with a path variable and the name of it is car ID we will get the car ID and with that car ID we will create a new car dto uh some brand and some kilowatts or whatever so this mono wrapper class is very usual for this uh webflux reactive programming pattern mono means a single that means that this mono can have one response and it's very similar to promises in JavaScript it indicates that sometime in the future you will get a car dto it can also have an empty response that means no response or it can also throw an error and with mono just we create a mono which will create a which will have an hard-coded car dto object later we will forward that request to the service layer here and also I want to have a post mapping to be able to create a car and no subpath and here I will also return a mono of type car dto and GitHub Co pilot already knows I want to create a car I will receive a car dto in the request body and yeah just simply answer with a with a mirror of that car dto so nothing special yet and also I would like to add a put mapping and GitHub copilot knows I will I want to have a car ID in the subpart and in here I also auto complete use GitHub co-pilot it says update car I will receive a car ID in the path and a car dto in the request body and here I also mock with a fake response and also at last I will need to have a delete mapping to be able to delete a car and I will do it by a car ID and in here I can also take that what GitHub co-pilot is proposing me I will return a moon or void that means this mono will always be empty and I will receive a car ID in the path and here I just returned with an empty mono so these four methods are our crud operations create reads update delete and for testing purpose I can start that web service and try it out with Postman or something like that I have prepared four Postman requests get delete put post and I briefly check if they indeed return without throwing an error so at first the get and you see I get the fake or mock card dto object and delete it just returns to hundreds puts puts also returns this all this car and post also returns with 200 and with this car now we need to delegate these requests to the service layer it is best practice to Define interfaces for the service classes and then write implementations of these interfaces this is done to separate the service definition from the actual implementation and you will find it often in Enterprise spring boot projects so let us create a new interface in the service package it is an interface and let's name it car service foreign a method for each crud operation the first one is get a car and it will return a mono with car dto get car by car ID and the next one is to post a card or to create a car and it also returns a car dto and GitHub co-pilot already knows what I want create a car and also update a car GitHub co-pilot knows what I want update the car by car ID and car dto and then of course delete a car and these methods they match the controller operations get a car by ID create a car with a car dto update a car by car ID and car dto and delete a car by car ID and then I Define an implementation of that car interface so I create a new sub package within service package and call it impulse and here a new Java class and call it car service impulse and it implements the interface car service and then I tell IntelliJ to implement these methods IntelliJ will automatically generate me these methods and for now get car returns now I can simply do it like that just copy it from from the car controller and return this and for create a car also just copy this and for updated car update return copy paste the response for update car from the controller and the same for the elite car so now we want to delegate the controller calls to this service here and we can do that by injecting that service into the controller and before we can do that we should add a class annotation on the car service input and it is the service annotation and that service annotation makes sure that spring knows that it is in an injectable service and it is able to create an instance of that service and inject it where it is needed and we want to have that in the car controller so here we Define a field it will be a private final car service and I use the interface and not the impul here and a private final field must be initialized in a Constructor for that I can utilize lombok I just add a class annotation and it is the required ax Constructor the required ax Constructor from lombok and that annotation makes sure that lombok will generate a Constructor which will require these private finder Fields as an input and what I'm doing here is called Constructor injection that means I have defined a Constructor which requires inputs and spring boot detects that there is a Constructor with required fields and it will try to populate the fields with a by instantiating um yeah instances of that class here and because we have defined an instance of that car service we have annotated this with a service annotation spring will create an instance of that car service impul and inject it here via Constructor injection and now I can just delegate all the calls to the service like that and also the create car with car dto and also update car ID car dto and also delete a car with car ID so the final thing what we need is the repository layer and in the repository layer we Define entity class for the car and a reactive repository for the car an entity class is mapped onto a table and represents a table entry and a repository is responsible for storing and querying entities on the database so let us create a sub package called entity and in here I create a new class and call it car entity at first I will add here some class annotations some from lombok that is the getter annotation to so lombok will generate me getter methods and also set your annotation to generate Setter methods also I would like to use the Builder pattern and then I need to have a spring data annotation the table annotation and in here I must provide the name of the table and this is the table car and I need to populate the car with the fields remember our car has three Fields it has an ID as an integer and I need also to annotate it with the ID annotation and this is important without that it would not work and also it has a field of type string this is the brand and also an integer field kilowatts so these three Fields will be automatically mapped onto columns within the card table So based on those values and annotations and field names spring data will generate SQL queries to select or insert data and so that thing is called or orm object relational mapping and the whole mapping magic mapping fields to to queries it's all done by Spring and then next we create a repository class and we will do it in the repository package so let's create a new class and actually it will be an interface Repository and I will name it car Repository and in here the only thing what we need to do is extend and we will extend from the R2 GBC Repository and here we must provide um The Entity class and the ID type here in the generic parenthesis and this one must be car entity so we'll the repository is storing car entities and this is the ID type and it has a our car has an ID of type integer and that's it there's nothing more to do in the car Repository and just by extending from this parent class the R2 DBC repository class we are able to do all the crot operations and if we check out the parent class you see that it also has nothing but it extends from a reactive crotch repository and in that reactive craft repository you see that it has all the methods what we want we can save and entity that is an absurd save or update and we can find an entity by IDE we can delete one by an ID so everything what we need is already provided to us so now what we need to do is delegate the service calls to the car repository and we can also do that by just simply injecting it I just copy paste the name here and go to the car service impul and Define a private final field of type car Repository and again I will do Constructor injection that means I will add The lombok annotation required arcs Constructor and spring will automatically inject me an instance of that car Repository so for example for the get car method I can just call the car Repository find by ID and provide the car ID and then I will get an instance of the of the car entity it returns a mono of the car entity and once the mono has been resolved that means I will have a result I will have to map the car entity to the car dto and I did it by just Auto completing using GitHub co-pilot it knows that I want to map the car entity to the car dto that means it creates an instance of the car.to and sets the ID brand in kilowatt so I can delegate the store operation to the repository and then here I will just call the repository and then save and here I must provide a car entity and I need to map that car dto to a car entity to to this class here so that means I can use car entity and I have enabled the Builder pattern use Builder and um then I can use GitHub co-pilot to code auto complete it I will set the brand the kilowatt and then call the builds method and um I need to change it to this get the brand kilowatts so using the Builder pattern I create an instance of the car entity I set the brand I set the kilowatt and then I execute the Builder pattern and save it and the save method will return me another car entity and that car entity I can map to a car that you then I will have the car entity ID brand kilowatt and then simply return this mono I will have a mono containing a car dto and return it from this create car service method and I need to insert a parenthesis here so to avoid compiler errors and that should work and then I can do a similar thing for the updates car operation just call the car repository and I also do the do call the save method this save method doesn't absurd and um I will create a car entity I will set the car ID which is provided here I will set the brand getting it from the car dto setting the kilowatt getting it also from the car dto and put it to the save method and I will after the save has been completed I will get a car entity and map it to a car dto and provide it as a response for this method here and at last for the delete car operation I can also call the car repository here and call the delete by ID and provide the car ID here and just simply execute it and it will return a mono of type void that is okay I can just simply return it like that and I'm done so I have completed the three layer architecture all the crud operations are delegated to the database and now I will start the service and try it out with Postman so I have prepared the postman request at the first request I want to try out is the post request I want to create a car I will give it a brand Ford and kilowatts this number here and let me execute it and check whether that works so I get a response and I really get an ID now and I should be able to find that entry in the database so let's check it out so I am here in PG admin and I will do a select on the card table and I indeed see that I have a car of brand fours and with this ID here let me get another one foreign I would like to add a car of brand Mercedes so let me try it out and checking it in PG admin I see that I have two car entries right here and in Postman I can try to get a car let me go to the get operation and let me get the car with id2 and that should be the Mercedes car and indeed it is the Mercedes car and if I use id1 then I should get the Ford car so let me try to update the car it is the put operation and with the car ID I would like I would like to change it to a I don't know BMW with this kilowatt so let me execute it and then when I get the car with car id1 right here before it was effort with this kilowatt and now it should be a BMW and indeed it is a different car brand now and let me check PG admin so in PJ admin I have Mercedes BMW that all looks alright and At Last I would like to try out the delete operation so I would like to delete um a car with id1 and then I delete a car with id2 and that means we should have an empty car table and checking out the card table in PG admin indeed the car table is now completely empty so now we have a fully functional crutch service but maybe we can extend it a bit more maybe we would like to get a list of all cars within the database so let's create another get mapping in the controller class and I will add the path slash all because I want to get all cars and it will this time it will not return a mono but it will return a flux of car dtos and the difference between flux and mono is if Lux can contain multiple entries while a mono can contain only one entry and that means a flux is basically like a stream of objects or a list of objects or whatever you want to call it so this is the basic difference between them and uh I call the method get all cars so GitHub co-pilot already knows what I want to do and I want to get all cars in the service so let me go to the service definition and let me Define a method here it returns a flux of car dtos and we call it get all cars and of course in the implementation we are missing a method we will generate this method using IntelliJ and um we will have to delegate the get all cars method to the repository and we can just easily do that just call the car repository and use the find all methods and this will automatically return all car entities and you see it returns car entities and we must map them all to a car dto and that means called map function of the flux and in here we have a car entity and um we will just map it to a car dto and using GitHub co-pilot I can auto complete it so every car entity within the flux will be mapped to a car dto inserting the ID brand and kilowatts and this completes the get all cars method and we can try it out with Postman and before we can get all cards we must create new cars so let me just go ahead and create again this Mercedes car and you see it will get a ID3 because the ID 1 and 2 were already provided to the previous cars and unfortunately we have deleted them so we need to create new cars and this one here like that now we should have again two cars and I need to call the get all car operation so I have prepared the get all cars Postman request just call a car all with the get operation and when I do that I get a list of all the car dtos so now the video is concluded we now know how to create a fully functional spring boot webflux scratch service on a postgres database all reactive and non-blocking and applying best practices which are widely used in Enterprise projects there is of course more to learn like pagination and filtering but this is another complex story so take care and goodbye
Info
Channel: Genka
Views: 4,090
Rating: undefined out of 5
Keywords: springboot, r2dbc, postgresql, postgresdb, postgres, webflux
Id: 3LxtmlaX3Y0
Channel Id: undefined
Length: 36min 44sec (2204 seconds)
Published: Wed Jun 21 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.