Two Almost Identical Methods Where One Works and One Returns Stack Overflow Error
In this instructor, you are going to learn more about PostgreSQL you bet to integrate it with a Spring Boot diligence. You will learn how to install a simple PostgreSQL instance using Stevedore and how to connect a Spring Boot application thereto. After that, you'll create a simple database outline and total some information thereto. Next, I'll show you how to make SQL files to deliver database changes, which are more suitable for enterprise applications. To finish, you will learn how to use PostgreSQL JSONB data structure and use PostgreSQL as a NoSQL database.
Let's dig in!
Get rolling with PostgreSQL via Dock worker
Initially proposed in the 70s, the RDBMS (Relational Database Management System) has mature in popularity through the years as computing processing power and computer memory capacity has augmented. A critical characteristic of RDBMS is the support for ACID transactions (Atomicity, Consistence, Isolation, Durability) which guarantee data body even in a concurrent environment without the developer needing to be fully aware.
PostgreSQL is combined of the most famous RDBMS roughly. Technically speaking IT is also one of the most ripe relational database systems available. Why is that? Postgres means Post Ingres operating theatre the successor of Ingres, an older database that paved the way to the more renowned Microsoft SQL Server and else products.
You will necessitate PostgreSQL installed to finish the instructor. To put in and trial run PostgreSQL, I recommend using Dock worker:
docker pull postgres:11 longshoreman run --diagnose dev-postgres -p 5432:5432 -e POSTGRES_PASSWORD =mysecretpassword -d postgres:11 # CREATE decibel coursedb docker EXEC dev-postgres psql -U postgres -c "CREATE DATABASE coursedb" postgres
NOTE: You will necessitate Docker installed for these commands to work.
The command above should run in whatsoever Linux, Windows, or MacOS distribution that has an instance of Docker installed. The first line pulls PostgreSQL version 11; the second line initiates a new case of information technology with the name dev-postgres,
running happening port 5432
. The terminal line executes a DDL command to create the database coursedb
into the illustration. Sustain in mind, you are not creating a volume for the storage data if the container is deleted, all its data will beryllium deleted equally well.
Create a Reverberate Boot App with PostgreSQL
Get-go, you need to create a new project with the required dependencies. You can use Spring Initialzr for this.
Configure your project as shown in the image above:
- Externalise Type: Maven Project
- Grouping: com.okta.developer
- Artifact: postgresql
- Dependencies: Web, JPA, PostgreSQL
Download the file and unzip it. Then, simply run the bid below:
NOTE: Depending on your operating system, you might need to change ./mvnw
to mvnw
.
Running mvnw
will download Maven, all dependencies, and run the application goal (spring-boot:run over
). It volition likely fail because you come non have a PostgreSQL database organized. Let's set this.
Add Database Configuration for PostgreSQL
You now possess a system of rules that has database dependencies but does not know where to connect. First, change your main class (plausibly com.okta.developer.postgresql.PostgresqlApplication
) and add the annotation @EnableTransactionManagement
to IT, like this:
package com.okta.developer.postgresql ; importation org.springframework.boot.SpringApplication ; import org.springframework.boot.autoconfigure.SpringBootApplication ; significance org.springframework.transaction.annotation.EnableTransactionManagement ; @SpringBootApplication @EnableTransactionManagement national class PostgresqlApplication { public atmospheric static null intense ( String [] args ) { SpringApplication . extend to ( PostgresqlApplication . class , args ); } }
Configuration annotations in Spring earmark you to set up your application using type secure code. This means if something becomes inconsistent with your configuration, it will show compilation errors. The feature is a nice phylogenesis from the older XML-based constellation Spring used to have. Previously, it was possible to get runtime errors since XML and your code was non linked.
Update src/main/resources/diligence.properties
to define your database connection properties:
spring.datasource.url = jdbc:postgresql://192.168.99.100:5432/coursedb outflow.datasource.username = postgres bound.datasource.password = mysecretpassword spring.jpa.properties.hole up.dialect = org.hibernate.dialect.PostgreSQLDialect spring.jpa.hole up.ddl-auto = create
Here's a quick explanation of each property:
-
spring.datasource.universal resource locator
- describes the JDBC connection URL. Each RDBMS (like PostgreSQL, MySQL, Oracle, etc.) has its format. The IP192.168.99.100
is the assigned aside Docker to the host auto in Windows or MacOS machines. If you are running on Linux or Mac, you must change to127.0.0.1
every bit the Docker Innkeeper is your machine. -
spring.datasource.username
- the username you will connect to the database. You are active to use the master exploiter for this tutorial. For yield, you should create a limited user for each application. -
spring.datasource.password
- The password you position when creating PostgreSQL docker illustrate. -
spring.jpa.properties.hibernate.dialect
- Although SQL is a standard, all database has some specific syntax that is addressed by hibernate dialect. -
spring.jpa.hole up.ddl-auto
- Define if Hibernate tooshie produce, blue-pencil and create, update or validate the current Database Schema. Presently, you should set this property tocreate
so Hibernate can handle all Database Schema.
What happens if you embark on the application again?
Now the application starts and you should Be able to assimilative http://localhost:8080
in your browser. Nix nice happens equally you haven't cursive any encode for the UI yet. You may see an mistake stack on initialization: don't worry, it is simply Hibernate telling the JDBC driver does not support a feature (method createClob
).
Create JPA Entities and Data Access Objects
Oklahoma, now let's do some real work.
First of all, let's look at this tutorial's schema. Information technology has two entities:
- Teacher
- Columns: name, picture, email
- Course of instruction
- Columns: name, workload, charge per unit
- Relationship: teacher
Before changing the cipher, add cardinal new dependencies in your pom.xml
file in:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependance> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency>
Project Lombok adds a serial of helpful features that simplify a developer's life the likes of getter/setter and builder autogeneration. If you are using an IDE to develop this tutorial, you must install a plugin to avoid compilation problems.
Recoil Data REST is a accessible dependency that creates an HTTP interface for your repositories. You are going to use the HTTP interface to test your code.
To help your entities, you'll create a superclass that handles the primary winder in the same way for every entity we create. PostgreSQL has support to UUID, so you are going to use it instead of the commonly in use auto-increment integer. UUID is reformatory to avoid a typical attack in which the hacker tries to increment operating theatre decrease an entity ID to find hot information.
Make the class EntityWithUUID
in package com.okta.developer.postgresql.entities
:
package com.okta.developer.postgresql.entities ; import org.hibernate.annotations.Character ; implication javax.perseverance.Id ; import javax.persistence.MappedSuperclass ; import java.util.UUID ; @MappedSuperclass public social class EntityWithUUID { @Id @Type ( type = "pg-uuid" ) private UUID id ; public EntityWithUUID () { this . ID = UUID . randomUUID (); } }
Thither are some excellent annotations Here:
-
@MappedSuperclass
says to JPA this class is a superclass of an entity and should have its attributes mapped into the entity. -
@Id
says the impute is a primary key -
@Type
specifies what type is used on the database. You motive to specify since PostgreSQL has its own type for UUIDs:pg-uuid
.
Now, create the two entities in the com.okta.developer.postgresql.entities
package:
Teacher.Java
package com.okta.developer.postgresql.entities ; moment lombok.AllArgsConstructor ; import lombok.Information ; importation lombok.NoArgsConstructor ; import javax.persistence.Entity ; @Entity @Data @AllArgsConstructor @NoArgsConstructor public separate Teacher extends EntityWithUUID { private String name ; private String pictureURL ; close Cosmic string e-mail ; }
Course.Java
package com.okta.developer.postgresql.entities ; importee lombok.AllArgsConstructor ; import lombok.Data ; moment lombok.NoArgsConstructor ; import javax.persistence.* ; @Entity @Data @AllArgsConstructor @NoArgsConstructor unrestricted classify Course of action extends EntityWithUUID { private Strand name ; private int workload ; private short rate ; @ManyToOne @JoinColumn ( foreignKey = @ForeignKey ( identify = "fk_course_teacher" )) private Instructor teacher ; }
The annotations get a significant influence in these classes:
-
@Data
- It's a Lombok annotation and tells Lombok to create getter and setter for all attributes. -
@AllArgsConstructor
- Tells Lombok to create a constructor with all year attributes -
@NoArgsConstructor
- Tells Lombok to create some other constructor with no arguments. It is useful for JPA. -
@Entity
- Indicates this class represents a persistent entity. Information technology is interpreted as a table by JPA, and the table should take the same class name unless you vary it. -
@ManyToOne
and@JoinColumn
- Indicates in that respect is a Many to One family relationship whose relationship uses of a Join Column. It is possible to set the extrinsic key if Hibernate is going to create the DDL.
So far-off, so good. Like a sho, make a DAO (Data Access Object, also called a Repository by Spring) class for each entity in the com.okta.developer.postgresql.dao
package:
CourseDAO.java
bundle com.okta.developer.postgresql.dao ; import com.okta.developer.postgresql.entities.Course ; implication org.springframework.information.secretary.CrudRepository ; import java.util.UUID ; public user interface CourseDAO extends CrudRepository < Run , UUID > {}
TeacherDAO.java
packet com.okta.developer.postgresql.dao ; spell com.okta.developer.postgresql.entities.Teacher ; import org.springframework.data.repository.CrudRepository ; import java.util.UUID ; public interface TeacherDAO extends CrudRepository < Teacher , UUID > {}
You instantly have DAOs for each entity. You may be asking, where is the implementation for them? Spring Data has some intelligent ways to handle this. Since your interfaces extend CrudRepository
, Take a hop automatically generates the practical class with all CRUD-related operations ready!
To fill some data, create a radical service class named DataFillerService
. It testament follow responsible for inserting data in the database as soon as the diligence starts.
DataFillerService.java
package com.okta.developer.postgresql.service ; import com.okta.developer.postgresql.dao.CourseDAO ; import com.okta.developer.postgresql.dao.TeacherDAO ; import com.okta.developer.postgresql.entities.Course ; import com.okta.developer.postgresql.entities.Teacher ; import org.springframework.stereotype.Service ; signification org.springframework.transaction.note.Transactional ; moment javax.annotation.PostConstruct ; @Service public class DataFillerService { private final CourseDAO courseDAO ; private final TeacherDAO teacherDAO ; semipublic DataFillerService ( CourseDAO courseDAO , TeacherDAO teacherDAO ) { this . courseDAO = courseDAO ; this . teacherDAO = teacherDAO ; } @PostConstruct @Transactional public void fillData (){ Teacher pj = new Instructor ( "Professor Jirafales" , "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Ruben2017.jpg/245px-Ruben2017.jpg" , "jirafales@yahoo_.com" ); Teacher px = new Teacher ( "Professor X" , "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS9uI1Cb-nQ2uJOph4_t96KRvLSMjczAKnHLJYi1nqWXagvqWc4" , "director@xproject_.com" ); teacherDAO . save ( pj ); teacherDAO . salve ( post exchange ); // create courses Row math = new Course ( "Mathematics" , 20 , ( momentaneous ) 10 , pj ); Course spanish = sunrise Course ( "Spanish" , 20 , ( short ) 10 , pj ); Course dealingWithUnknown = new Course ( "Dealing with nameless" , 10 , ( curtal ) 100 , pj ); Course handlingYourMentalPower = new Course ( "Handling your mental power" , 50 , ( forgetful ) 100 , pj ); Course introductionToPsychology = new Course ( "Presentation to psychological science" , 90 , ( short ) 100 , pj ); courseDAO . save ( math ); courseDAO . save ( European nation ); courseDAO . save ( dealingWithUnknown ); courseDAO . save ( handlingYourMentalPower ); courseDAO . save ( introductionToPsychology ); } }
The method acting fillData
is automatically named by Outflow Context as soon the practical application circumstance finishes loading. IT uses the @Transaction
annotations to indicate the smooth method acting moldiness run interior a transaction. That elbow room, if some instruction fails, the whole method will be trilled back. A you tush take in, CourseDAO
and TeacherDAO
has some CRUD methods available.
Now, you can test if your practical application is working:
The lotion creates several REST endpoints to access your DAO's method. Essay some commands:
draw in http://localhost:8080/courses curl http://localhost:8080/teachers
Good Spring Data REST with OAuth 2.0
You shouldn't expose your database social organization without proper authentication. Net ball's solve this aside creating an OAuth 2.0 Resource Server. A resource server is a service temporary in the infrastructure that has zero login page, and it is used for server-to-server communication theory. In strange words: it needs credentials but does non handle how they are acquired.
First, you'll need to add a dependency on the Okta Spring Boot starter:
<colony> <groupId>com.okta.spring</groupId> <artifactId>okta-spring-boot-starter</artifactId> <version>1.1.0</version> </dependency>
Then add a WebSecurityConfigurerAdapter
to PostgresqlApplication
:
package com.okta.developer.postgresql ; import org.springframework.iron heel.SpringApplication ; import org.springframework.boot.autoconfigure.SpringBootApplication ; significance org.springframework.context.annotation.Configuration ; importee org.springframework.security.config.annotation.network.builders.HttpSecurity ; importee org.springframework.surety.config.annotating.web.shape.WebSecurityConfigurerAdapter ; signification org.springframework.transaction.annotation.EnableTransactionManagement ; @SpringBootApplication @EnableTransactionManagement public assort PostgresqlApplication { public static void main ( Twine [] args ) { SpringApplication . run ( PostgresqlApplication . class , args ); } @Configuration atmospheric static class OktaOAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override fortified nullif configure ( HttpSecurity http ) throws Exception { http . authorizeRequests (). anyRequest (). authenticated () . and () . oauth2ResourceServer (). jwt (); } } }
The OktaOAuth2WebSecurityConfigurerAdapter
class defines the protection form.
-
.authorizeRequests().anyRequest().authenticated()
- tells Spring Security every petition must exist authenticated. -
.oauth2ResourceServer().jwt()
- configures the covering as a Imagination Server that will allows access tokens for authorization.
Finally, summate 2 new properties in your covering.properties
file:
okta.oauth2.issuer = https://{yourOktaDomain} /oauth2/default okta.oauth2.clientId = {yourClientId}
These properties are leaving to tell Spring and Okta where to see the OAuth 2.0 issuer and describe the client ID for this application…
… Issuer, node Idaho, what am I talking about?
In front you start, you'll need a free Okta developer account. Put in the Okta CLI and run okta register
to sign on for a new account. If you already have an account, run okta login
. Then, run okta apps create service of process
. Select the default app discover, or change it every bit you see in condition.
What does the Okta Command line interface do?
The Okta CLI will create an OAuth 2.0 Service App in your Okta Org. You will find output like the following when it's finished:
Okta application configuration has been written to: /path/to/app/.okta.env
Lam cat .okta.env
(OR character .okta.env
happening Windows) to see the issuer and credentials for your app.
export OKTA_OAUTH2_ISSUER = "https://dev-133337.okta.com/oauth2/default" export OKTA_OAUTH2_CLIENT_ID = "0oab8eb55Kb9jdMIr5d6" export OKTA_OAUTH2_CLIENT_SECRET = "NEVER-SHOW-SECRETS"
Your Okta domain is the first part of your issuer, before /oauth2/default
.
NOTE: You can also use the Okta Admin Console to create your app. Assure Create a Service App for more information.
Copy and paste the Guest ID in to your application.properties
file.
Now, start your application every bit before:
Run curl
(the -v
attribute will return the response headers):
> curl up -v http://localhost:8080/courses < HTTP/1.1 401 < Set-Cookie: JSESSIONID =6AA0A042FB1D69CC1CB9747820FDF5E1; Course =/; HttpOnly < WWW-Authenticate: Carrier < X-Content-Type-Options: nosniff < X-XSS-Protection: 1; mode =block < Lay away-Control: none-hoard, no-store, max-age=0, must-revalidate < Pragma: nary-cache < Expires: 0 < X-Frame-Options: DENY < Content-Length: 0 < Date: Thu, 20 Dec 2018 04:46:21 GMT
The server returns a 401 HTTP code, which means you are unauthorized. Now, let's create a valid token. An light way to attain a token is to generate extraordinary exploitation OpenID Connect
First, you'll need to create a new web application in Okta to use this servicing:
Run okta apps create
. Select the default app constitute, operating theatre change it as you see meet. Select Web and press Enter.
Select Other. Then, change the Airt URI to https://oidcdebugger.com/debug
and use https://oidcdebugger.com
for the Logout Redirect URI.
What does the Okta CLI do?
The Okta CLI will create an OIDC Web App in your Okta Org. It testament append the airt URIs you specified and grant access to the Everyone grouping. You volition see turnout like the following when it's finished:
Okta applications programme configuration has been written to: /path/to/app/.okta.env
Function cat .okta.env
(or type .okta.env
on Windows) to see the issuer and credentials for your app.
export OKTA_OAUTH2_ISSUER = "https://dev-133337.okta.com/oauth2/default" export OKTA_OAUTH2_CLIENT_ID = "0oab8eb55Kb9jdMIr5d6" export OKTA_OAUTH2_CLIENT_SECRET = "NEVER-SHOW-SECRETS"
Your Okta sphere is the first part of your issuer, before /oauth2/nonremittal
.
Bank note: You can also manipulation the Okta Admin Solace to create your app. See Create a Web App for more information.
An easy way to get an access token is to generate one using OpenID Connect Debugger. First, you mustiness configure your application on Okta to use OpenID Connect's implicit flow.
Run okta login
and open the ensuant URL in your web browser. Run low to the Applications section and quality the application you just created. Edit its General Settings and minimal brain dysfunction Implicit (Hybrid) as an allowed grant eccentric, with access token enabled. Then, make sure it has https://oidcdebugger.com/debug
in its Login redirect URIs. Click Save and copy the client ID for the next step.
Instantly, navigate to the OpenID Join Debugger website. Substitute your customer I.D., and use https://{yourOktaDomain} /oauth2/default/v1/authorize
for the Authorize URI. The say
field must be filled but can contain any characters. Select token for the response type.
Submit the form to start the authentication process. You'll pick up an Okta login form if you are not logged in or you'll meet the screen below with your custom token.
The token leave be valid for one hour and then you can do a great deal of testing with your API. IT's simple to enjoyment the token, just copy it and modify the curl command to use information technology every bit follows:
export Minimum ={YOUR_TOKEN} curl -v -H "Dominance: Bearer ${ TOKEN } " http://localhost:8080/teachers
Pretty sweet, eh?!
Versioning Scheme Changes in PostgreSQL with Flyway
Hole up DDL creation is a nice characteristic for PoCs or minute projects. For more significant projects that possess a complex deployment workflow and features like version rollback in case of a significant issue, the solution is non sufficient.
In that respect are various tools to handle database migrations, and matchless of the most democratic is Flyway, which works flawlessly with Spring Boot. Briefly, Migration route looks for SQL scripts on your project's resource path and runs all scripts not antecedently dead in a definite order. Flyway stores what files were executed into a particular prorogue called SCHEMA_VERSION
.
Initial, add Flyway as a dependency in your pom.xml
. When Spring Boot detects Migration route on the classpath, information technology will run it on startup:
<dependency> <groupId>org.flywaydb</groupId> <artifactId>migration route-core</artifactId> </dependency>
By default, Flyway looks at files in the format V$X__$DESCRIPTION.sql
, where $X is the migration version name, in folder src/main/resources/db/migration
. Create two files: one for the DDL and another for sample information:
V1__ddl.sql
CREATE TABLE course ( id UUID Non NULL , NAME VARCHAR ( 255 ), rate INT2 Non NULL , workload INT4 NOT NULL , teacher_id UUID , PRIMARY KEY ( id ) ); CREATE TABLE teacher ( id UUID Non NULL , email VARCHAR ( 255 ), Nominate VARCHAR ( 255 ), pictureurl VARCHAR ( 255 ), PRIMARY KEY ( id ) ); ALTER TABLE course ADD Restraint fk_course_teacher FOREIGN KEY ( teacher_id ) REFERENCES teacher ;
V2__data.sql
Cut-in INTO teacher ( id , electronic mail , NAME , pictureurl ) VALUES ( '531e4cdd-bb78-4769-a0c7-cb948a9f1238' , 'jirafales@yahoo_.com' , 'Profesor Jirafales' , 'https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Ruben2017.jpg/245px-Ruben2017.jpg' ); INSERT INTO instructor ( id , email , NAME , pictureurl ) VALUES ( '6924b3ad-a7e7-4a9a-8773-58f89ef88509' , 'director@xproject_.com' , 'Professor X' , 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS9uI1Cb-nQ2uJOph4_t96KRvLSMjczAKnHLJYi1nqWXagvqWc4' ); INSERT INTO course ( id , NAME , rate , teacher_id , workload ) VALUES ( 'aeebbc96-52be-43fa-8c01-61b9fbca8fd7' , 'Mathematics' , 10 , '531e4cdd-bb78-4769-a0c7-cb948a9f1238' , 20 ); INSERT INTO course ( id , Call , value , teacher_id , workload ) VALUES ( 'a6e54dad-a5a6-46c6-92b0-d61a78abb142' , 'European nation' , 10 , '531e4cdd-bb78-4769-a0c7-cb948a9f1238' , 1 ) ; INSERT INTO course ( Gem State , NAME , rate , teacher_id , workload ) VALUES ( '13710917-7469-4bd7-91cc-af8df36213c9' , 'Dealing with unknown' , 100 , '6924b3ad-a7e7-4a9a-8773-58f89ef88509' , 10 ) ; INSERT INTO course ( id , Discover , rate , teacher_id , workload ) VALUES ( 'c5e24451-86d3-4f20-ae06-55810c3cf350' , 'Handling your psychogenic power' , 50 , '6924b3ad-a7e7-4a9a-8773-58f89ef88509' , 1000 ) ; INSERT INTO track ( id , NAME , rate , teacher_id , workload ) VALUES ( 'd5063295-f1f6-44d3-8e3a-5ba5d8fb46eb' , 'Introduction to psychology' , 100 , '6924b3ad-a7e7-4a9a-8773-58f89ef88509' , 90 );
Ahead protrusive the project again, delete the class DataFillerService
since these SQL files will now import all data.
In lotion.properties
change the ddl-motorcar
conformation to validate
:
spring.jpa.hole up.ddl-auto = validate
This causes Hibernate to validate the schema to see if it matches with what's defined in Java. If no match is found, the application will not start.
Delete and make up a recent PostgreSQL database instance, since the first instance was created using JPA.
stevedore rm -f dev-postgres docker run --name dev-postgres -p 5432:5432 -e POSTGRES_PASSWORD =mysecretpassword -d postgres:11 dock worker White House dev-postgres psql -U postgres -c "CREATE DATABASE coursedb" postgres
Now, start Spring Boot once again:
You will go steady the same data open, but if you get a load into the logs, will placard Flyway runs the database migration:
2018-11-25 23:50:12.941 Information 30256 --- [ main] o.f.core.intrinsical.command.DbValidate : Successfully valid 2 migrations (execution of instrument time 00:00.037s) 2018-11-25 23:50:12.960 INFO 30256 --- [ principal] o.f.c.i.s.JdbcTableSchemaHistory : Creating Schema History table: "public"."flyway_schema_history" 2018-11-25 23:50:13.010 INFO 30256 --- [ main] o.f.nitty-gritty.internal.control.DbMigrate : Contemporary variant of schema "public": << Barren Outline >> 2018-11-25 23:50:13.013 INFO 30256 --- [ of import] o.f.core group.internal.command.DbMigrate : Migrating schema "populace" to translation 1 - ddl 2018-11-25 23:50:13.046 INFO 30256 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema "public" to version 2 - data 2018-11-25 23:50:13.066 INFO 30256 --- [ main] o.f.core.internal.instruction.DbMigrate : Successfully applied 2 migrations to schema "public" (execution time 00:00.109s)
Scratch the NoSQL Show u with Spring Boot and PostgreSQL's JSONB Information Construction
NoSQL databases are, as the distinguish says, databases that do not store their data with relationships. There are many types of NoSQL databases: graph databases, key-value stores, document, etc. Generally talking, one of the most significant advantages of this sort of database is the lack of outline enforcement (you can conflate a different sort of information), different levels of data body and better performance in just about cases.
PostgreSQL adopted approximately data types to handle JSON data exclusive its information structures. This datatype is called JSONB, and this part shows how information technology is thinkable to economic consumption IT with Spring Boot and without changing your current schema.
First, create a recently migration file V3__teacher_reviews.sql
.
Vary Defer instructor ADD COLUMN reviews jsonb
To properly exploit with this custom datatype, you need to add a habituation that right handles information technology. Add the undermentioned to pom.xml
:
<addiction> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>2.3.4</version> </dependency>
The column reviews
stores a JSON raiment with all reviews a instructor received. First, you need to switch the superclass to define the new datatype correctly:
EntityWithUUID.java
import com.vladmihalcea.hole up.type.json.JsonBinaryType ; import org.hibernate.annotations.Type ; import org.hibernate.annotations.TypeDef ; signification org.hibernate.annotations.TypeDefs ; signification javax.persistence.Id ; import javax.persistence.MappedSuperclass ; importee java.util.UUID ; @MappedSuperclass @TypeDefs ({ @TypeDef ( name = "jsonb" , typeClass = JsonBinaryType . class ) }) public class EntityWithUUID { @I.D. @Type ( type = "pg-uuid" ) confidential UUID id ; public EntityWithUUID () { this . id = UUID . randomUUID (); } }
Now, create a division called Review
in the entities
package:
software program com.okta.developer.postgresql.entities ; import lombok.AllArgsConstructor ; import lombok.Data ; import lombok.NoArgsConstructor ; import java.io.Serializable ; import java.fourth dimension.LocalDate ; @Data @AllArgsConstructor @NoArgsConstructor public class Review implements Serializable { private Drawstring author ; private String limited review ; private LocalDate date ; }
This class needs to implement Serializable
As it is stored in JSON format. Right away, change Teacher
class to have a reviews property:
import lombok.AllArgsConstructor ; import lombok.Information ; import lombok.NoArgsConstructor ; import org.hibernate.annotations.Eccentric ; importation javax.persistence.Underlying ; import javax.perseverance.Column ; signification javax.persistence.Entity ; import javax.persistence.FetchType ; import java.util.List ; @Entity @Data @AllArgsConstructor @NoArgsConstructor in the public eye class Teacher extends EntityWithUUID { private String along name ; close String pictureURL ; head-to-head Thread electronic mail ; @Type ( eccentric = "jsonb" ) @Column ( columnDefinition = "jsonb" ) @Basic ( fetch = FetchType . LAZY ) toffee-nosed List < Refresh > reviews ; }
Now you have set a name of information type jsonb
but also added a fetch = LAZY
property. The property tells Hole up NOT to retrieve the attribute value unless asked. Without the property, every call to a instructor instance will appendage JSON and create review objects. Hibernate necessarily a Maven plugin to work with lazy attributes properly. Edit the pommy.xml
file and hyperkinetic syndrome the following snippet in the <plugins>
section.
<plugin> <groupId>org.hibernate.orm.tooling</groupId> <artifactId>hole up-heighten-maven-plugin</artifactId> <version>5.4.1.Final</version> <executions> <execution> <configuration> <failOnError>true</failOnError> <enableLazyInitialization>honorable</enableLazyInitialization> </configuration> <goals> <goal>enhance</goal> </goals> </murder> </executions> </plugin>
OK, you are almost in that location. You just pauperism to add some methods to add reviews to a teacher. I distinct to take a Thomas More MVC approach here and make up a Service and a Controller.
TeacherService.Java
computer software com.okta.developer.postgresql.service ; import com.okta.developer.postgresql.entities.Review ; signification javax.validation.constraints.NotNull ; public interface TeacherService { /** * * @param teacherID * @param review * @throws javax.persistence.EntityNotFoundException */ void addReview ( @NotNull String teacherID , @NotNull Review reappraisal ); }
SimpleTeacherService.java
software com.okta.developer.postgresql.service ; import com.okta.developer.postgresql.dao.TeacherDAO ; signification com.okta.developer.postgresql.entities.Review ; import com.okta.developer.postgresql.entities.Teacher ; moment org.springframework.stereotype.Service ; import org.springframework.dealing.annotation.Isolation ; import org.springframework.transaction.annotation.Transactional ; import javax.doggedness.EntityNotFoundException ; import Java.time.LocalDate ; import java.util.ArrayList ; import java.util.Objects ; import java.util.UUID ; @Serving public classify SimpleTeacherService implements TeacherService { snobbish final TeacherDAO teacherDAO ; public SimpleTeacherService ( TeacherDAO teacherDAO ) { this . teacherDAO = teacherDAO ; } @Override @Transactional ( isolation = Closing off . SERIALIZABLE ) public invalidate addReview ( String teacherID , Brush up survey ) { Objects . requireNonNull ( teacherID ); Objects . requireNonNull ( review ); Teacher instructor = teacherDAO . findById ( UUID . fromString ( teacherID )) . orElseThrow (() -> new EntityNotFoundException ( teacherID )); reexaminatio . setDate ( LocalDate . now ()); if ( teacher . getReviews () == null ){ instructor . setReviews ( new ArrayList <>()); } teacher . getReviews (). add ( review ); teacherDAO . save ( instructor ); } }
TeacherController.coffee
software system com.okta.developer.postgresql.controllers ; import com.okta.developer.postgresql.entities.Review ; import com.okta.developer.postgresql.service.TeacherService ; import org.springframework.HTTP.ResponseEntity ; import org.springframework.vane.bind.annotation.* ; import javax.perseverance.EntityNotFoundException ; @RestController unexclusive sort out TeacherController { individual final TeacherService teacherService ; public TeacherController ( TeacherService teacherService ) { this . teacherService = teacherService ; } @PostMapping ( "/teachers/{id}/review" ) public ResponseEntity addReview ( @RequestBody Review review , @PathVariable ( "id" ) String teacherID ){ sample { teacherService . addReview ( teacherID , recap ); restitution ResponseEntity . ok (). build (); } catch ( EntityNotFoundException e ){ return ResponseEntity . notFound (). build (); } } }
In SimpleTeacherService
, note that there is an annotation @Transactional(isolation = Isolation.SERIALIZABLE)
. This means every call to this method runs in the most covert transaction level. In separate actor's line, once the code reads the reviews
JSON value, no some other code can read operating room publish on the reviews
column (it is blocked) until the transaction finishes. Why's that? Since you are manipulating JSON happy if a co-occurrent dealings updates the pillar, one of them will have consistency problems.
You can now start your app:
And test the review upload away running Gyre:
curl -X POST \ -H "Authorization: Bearer $TOKEN " \ HTTP://localhost:8080/teachers/531e4cdd-bb78-4769-a0c7-cb948a9f1238/review \ -H 'Content-Case: application/json' \ -d '{ "author" : "Raphael", "review" : "Test" }'
Learn Many almost Leap Boot, PostgreSQL, Hibernate, JPA, and Spring Data REST
In this tutorial, you lettered how to mix Spring Boot with PostgreSQL and expend some civilised technologies like Migration route and JSONB. You can find the source code for this blog Emily Price Post on GitHub.
There is plenty more to learn some JPA, Hole up and PostgreSQL. Delight refer to the following posts if you'rhenium fascinated in more knowledge:
- Produce a Secure Leaping REST API
- Build a Basic App with Spring Boot and JPA using PostgreSQL
- Use React and Spring Boot to Build a Shield-shaped CRUD App
- Build a Basic CRUD App with Angular 7.0 and Recoil Boot 2.1
- NoSQL Options for Coffee Developers
Like what you learned today? Comply us on Chitter, and subscribe to our YouTube conduct for more awesome content!
Two Almost Identical Methods Where One Works and One Returns Stack Overflow Error
Source: https://developer.okta.com/blog/2019/02/20/spring-boot-with-postgresql-flyway-jsonb
0 Response to "Two Almost Identical Methods Where One Works and One Returns Stack Overflow Error"
Post a Comment