Java Spring Boot applikation utveckling

Java Utveckling för webben med hjälp av Spring Boot gör många av Java tekniker enklare att använda på en webbapplikation. Exemplar består av databasanvändning (Data source), säkerhet (Security), och Sessions. I det här inlägget beskriver jag olika färdiga tekniker genom sina egna projekt. Varje projekt som länkas till är en färdig projekt som jag använder som en exempel på tekniken, förklaras kort på varje film.

 

App 1

Här är Spring Boot projektet som använder Spring Boot Security med en databas för inloggningar samt en NoOpPasswordEncoder.
Här finns projektet för att ladda ner: 1.SpringBootSecurityWithDataSource,NoOpPasswordEncoder

App 2

Här är Spring Boot Security appen med BCrypt som password encoder:
Här finns projektet för att ladda ner:

2.SpringBootSecurityWithBCrypt

App 3

Här är Spring Boot Security appen som använder sig av Sessions för att spara användaresinformation som till exempel användarnamn på sessions.
Här finns projektet för att ladda ner:  Spring Boot Security och Session från databasinformation

App 4

Här är två appar som samarbetar: en Java Spring Boot applikation och en Angular applikation. En fungerar på backend och serverar JSON objekter och en presenterar en “drag and drop” gränssnitt för interaktioner på JSON slutpunkter. Gränssnittet använder JSON Objekter för att göra interaktioner möjligt i samband med resurs och klient hantering. Nedan finns en demo film. För att köra Java Spring Boot applikationen så behövs det att du har en Redis Server och MySQL databas på plats. En exempel på hur den databasen kan se ut finns i Java appens källkatalog.
Koden på de två appar finns för nedladdning: TwoApps–backend with JSON Rest (using Java), frontend with drag and drop (using Angular)

Interacting with data via Spring Boot

These are links to resources that describe how to access data via queries in a Spring Boot application. The Spring repository to work with will extend the Crud repository and will include methods that have annotations such as @Query and @Modifying. The queries can use JPQL or be native SQL.

Baeldung’s Tutorial on Spring JPA Data @Query:
https://www.baeldung.com/spring-data-jpa-query

App developer’s blog on the @Query annotation
http://www.appsdeveloperblog.com/spring-data-jpa-native-sql-query/

StackOverflow’s Inserting into Spring data
How to insert into db in spring-data?

Working with Thymeleaf and Templating issues for front end development

Here are starting points to work with Thymeleaf templates and some issues revolving the front end development


Thymeleaf — A Templating alternative with Spring Boot:
https://www.thymeleaf.org/

To include HTML or dynamic HTML parts into a Thymeleaf template, Thymeleaf uses the concept and term: “fragments”, which assists with inserting or replacing parts of your pages with other parts. Here is a tutorial:
https://www.baeldung.com/spring-thymeleaf-fragments

Validating The Form entries that will be sent to the Spring Boot application:
https://stackoverflow.com/questions/22658572/spring-annotations-modelattribute-and-valid

Using Constraints with form input
https://javaee.github.io/tutorial/bean-validation002.html

Other two suggested links:
https://docs.jboss.org/hibernate/validator/5.3/reference/en-US/html_single/#validator-gettingstarted-uel

https://www.baeldung.com/spring-mvc-custom-validator

Spring Boot + Thymeleaf HTML Form Handling Tutorial
https://medium.com/@grokwich/spring-boot-thymeleaf-html-form-handling-part-2-b4c9e83a189c

Spring Boot + Thymeleaf CRUD Example
https://www.dariawan.com/tutorials/spring/spring-boot-thymeleaf-crud-example/

Descriptions and Links for Tools while working with Windows and the application server

Guide to Windows Batch Scripting
http://steve-jansen.github.io/guides/windows-batch-scripting/

On this site you will find all kinds of (administrative) scripting related information:

https://www.robvanderwoude.com/

Pin batch file to Start Menu or Taskbar: https://www.askvg.com/windows-tip-pin-batch-bat-files-to-taskbar-and-start-menu/

Creating database tables having many to many field relations

There are cases in designing a database where the value of one particular field (say, Id field) of a first table holds association with many possible values of a field (say, Id) in a second table, and vise-versa, that is, the second table also has this same field (Id) associated with many possible Ids of the first table. An example is a database that has two tables: Users, and Roles: One user can have many Roles, and one Role can be associated with many users. Another example is the example of a subscription database: one subscriber can have association with several magazines and one magazine has association with many subscribers. A third example would be a movies and categories database: the movies table can be included in many categories and one category would include many movies. This type of table design in a database is what is called as designing a many-to-many SQL relationship.

Below is a review on preparing such tables and their relationship and to insert and retrieve records accordingly.

An example where the designer uses JPA, Hibernate and Spring Boot to utilize a many-to-many relationship with Users and Roles is here.

Another example where the many-to-many relationship is discussed and how one can possibly have a third table which has a compound primary key to hold this relationship is discussed here.

Here is the SQL queries for preparing two tables, a movie table and a category table, and a conjunction table for the many-to-many relationship:

create table Movie (Movie_ID int not null auto_increment, Name varchar(255), primary key(Movie_ID));
create table Category (Category_ID int not null auto_increment, Name varchar(255), primary key (Category_ID));
create table movie_category_junction (
movie_id int,
category_id int,
CONSTRAINT movie_cat_pk PRIMARY KEY (movie_id, category_id),
CONSTRAINT FK_movie FOREIGN KEY (movie_id) REFERENCES movie (movie_id), CONSTRAINT FK_category FOREIGN KEY (category_id) REFERENCES category (category_id) );

Now we can insert records to the first and second table, as well as the junction table that holds the relationships between the two tables:

INSERT INTO movie (movie_id, name) VALUES (8, "Movie on SQL development");
INSERT INTO movie (movie_id, name) VALUES (9, "Movie on Java EE application development");


INSERT INTO category (category_id, name) VALUES (10, "SQL Development");
INSERT INTO category (category_id, name) VALUES (11, "Java EE Development");




INSERT INTO movie_category_junction (movie_id, category_id) VALUES (8,10);
INSERT INTO movie_category_junction (movie_id, category_id) VALUES (8,11);

INSERT INTO movie_category_junction (movie_id, category_id) VALUES (9,10);
INSERT INTO movie_category_junction (movie_id, category_id) VALUES (9,11);

Another example would be: https://stackoverflow.com/questions/19714308/mysql-how-to-insert-into-table-that-has-many-to-many-relationship

And to select records from the two table we would use LEFT JOIN.

To get a movie with a given Id (say, movie with Id 8:

SELECT m.*, c.* FROM movie as m
LEFT JOIN movie_category_junction as mc ON m.movie_id = mc.movie_id
LEFT JOIN category AS c on mc.category_id = c.category_id
WHERE m.movie_id = 8;

To get a movie with a given Id 9:

SELECT m.*, c.* FROM movie as m
LEFT JOIN movie_category_junction as mc ON m.movie_id = mc.movie_id
LEFT JOIN category AS c on mc.category_id = c.category_id
WHERE m.movie_id = 9;

And to get the category with a given category Id, say category 11, we would do:

SELECT m.*, c.* FROM category as c 
LEFT JOIN movie_category_junction as mc ON c.category_id = mc.category_id
LEFT JOIN movie AS m on mc.movie_id = m.movie_id
WHERE c.category_id = 11;

Many to many relationships occur where two tables have fields that can be associated with more than one field in both ways. This is where a many to many relationship can be applied via SQL. In the above example, a third table was created to hold the relationship.

Setting up the basics of a Maven Java EE app on WildFly 18.0.1 with its MySQL database and database connector, and utilizing JPA Hibernate among other dependencies

A Java EE application should be designed with a structure that is easily understandable and makes a balanced use of Data Access Objects, Entities, REST controller, transactions and a database back end. To have the app hosted on WildFly 18.0.1 application server we would require a connection to the database on the server. Below are the basics for the preparation process in order to have a basic application run:

1.

Set up a database. In our example, we set up a database called “WorkSuggest1” and set up a table within the database called “Person”:

create database WorkSuggest1;

 CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) ,
  `email` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

2.

When the database is set up, start preparing the WildFly database connector and the data source that will be used in the application. A CLI script can be very useful to have this done. The script below (written by instructors at IT Högskolan Göteborg) helps to set up the module, given that you have the MySQL connector, so keep reading past the script below for more details. The link to the database is also needed. Be sure that the MySQL Connector is in c:\Users folder and the connector version is the one you have.

module add --name=com.mysql --resources=~/mysql-connector-java-8.0.19.jar --dependencies=javax.api,javax.transaction.api
/subsystem=datasources/jdbc-driver=mysql:add(driver-name="mysql",driver-module-name="com.mysql",driver-class-name=com.mysql.cj.jdbc.Driver)
data-source add --jndi-name=java:/worksuggest1 --name=worksuggest1 \
--connection-url="jdbc:mysql://localhost:3306/worksuggest1?allowPublicKeyRetrieval=true&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC" \
--driver-name=mysql --user-name=root --password=root --min-pool-size=2 --initial-pool-size=4 --max-pool-size=6
/subsystem=logging/file-handler=fh:add(level=INFO, file={"relative-to"=>"jboss.server.log.dir", "path"=>"worksuggest1.log"}, append=true, autoflush=true)
/subsystem=logging/logger=com.mygrouptasks.worksuggest1:add(use-parent-handlers=false,handlers=["fh"])

Normally, a MySQL connector is found in form of a Jar file such as the one found at Jar-download.com, which will accompany a “module.xml” file.
To complete the set up, follow a good tutorial on how to set up MySQL database connection on WildFly application server, such as this one.

In general, be sure that you have the database connector, the required module file, and the link to the database set up correctly as they are required for your Java EE application to interact with the database. Some side notes while you are setting them up are:

  • The connector and the module.xml file should be placed in a directory structure that you create under the “base” folder hierarchy in the WildFly 18 folder structure — more specifically in the folder: C:\wildfly-18.0.1.Final\modules\system\layers\base. Here you will create a “com” folder, add a “mysql” folder there, and in it create a “main” folder. In this main folder you place the mysql connector file as well as the module.xml file.
  • The module.xml file could have content similar to the one below.
<module name="com.mysql" xmlns="urn:jboss:module:1.5">
    <resources>
        <resource-root path="mysql-connector-java-8.0.18.jar">
    </resource-root></resources>
    <dependencies>
        <module name="javax.api">
        <module name="javax.transaction.api">
    </module></module></dependencies>
</module>

3.

Continue the set up of the database for use via the application server. Given that you will use the CLI script above, place the CLI script in the bin folder of the WildFly 18.0.1 folder, go to the bin folder, and run the script while the server is running. Assuming you are using Windows PowerShell it would be:

.\jboss-cli -c --file=worksuggest1.cli

4.

Upon receiving success message, proceed to setting up the IntelliJ project which should have a Maven.pom.xml file with dependencies as described in the below pom.xml file:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mygrouptasks.worksuggest1</groupId>
  <artifactId>worksuggest1</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>worksuggest1 Maven Webapp</name>
  <description>Work Suggest Application</description>


  <dependencies>

    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>8.0.1</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.wildfly</groupId>
      <artifactId>wildfly-feature-pack</artifactId>
      <version>18.0.0.Final</version>
      <type>pom</type>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>javax.ws.rs</groupId>
      <artifactId>javax.ws.rs-api</artifactId>
      <version>2.1.1</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.hibernate.javax.persistence</groupId>
      <artifactId>hibernate-jpa-2.1-api</artifactId>
      <version>1.0.2.Final</version>
    </dependency>


    <dependency>
      <groupId>org.jboss.spec.javax.ejb</groupId>
      <artifactId>jboss-ejb-api_3.2_spec</artifactId>
      <version>2.0.0.Final</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.5.2</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>mysql</artifactId>
      <version>${testcontainers.version}</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>testcontainers</artifactId>
      <version>${testcontainers.version}</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.json/json -->
    <dependency>
      <groupId>org.json</groupId>
      <artifactId>json</artifactId>
      <version>20190722</version>
    </dependency>


  </dependencies>

  <build>
    <finalName>worksuggest1</finalName>
    <sourceDirectory>src/main/java</sourceDirectory>

    <plugins>
      <plugin>
        <groupId>org.wildfly.plugins</groupId>
        <artifactId>wildfly-maven-plugin</artifactId>
        <version>2.0.1.Final</version>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>${maven.compiler.source}</source>
          <target>${maven.compiler.target}</target>
          <compilerArgument>-Xlint:unchecked</compilerArgument>
        </configuration>
      </plugin>
    </plugins>
  </build>


  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <testcontainers.version>1.12.2</testcontainers.version>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </properties>

</project>

5.

The persistence.xml file can be as follows:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="worksuggest1" transaction-type="JTA">
        <jta-data-source>java:/worksuggest1</jta-data-source>
        <properties>
            <property name="hibernate.archive.autodetection" value="class, hbm" />
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect" />
            <property name="hibernate.show_sql" value="true" />
            <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)" />
            <property name="openjpa.Log" value="SQL=TRACE" />
            <property name="openjpa.ConnectionFactoryProperties" value="printParameters=true" />
        </properties>
    </persistence-unit>
</persistence>

6.

A file structure such as the one shown below can help with the development of the project with respective Java EE concepts considered:

7.

The complete and working project (given that the MySQL database is available and the WildFly 18.0.1 application server are running) is included in the zip file below.

Notes while working with MySQL database

  • CDing into MySQL Server 8.0
cd "C:\Program Files\MySQL\MySQL Server 8.0\bin"
  • Logging into MySQL Server:
.\mysql -u root -p
  • Shutting down server after logging in:
shutdown;
  • Starting up the server again via DOS (given it is administrator account):
net start mysql80
  • Show databases available:
show databases;
  • Start working with one of the databases:
use <database name>
  • View all records in a given table:
select * from <table name>;
  • Delete a record from a given table:
delete from <table name> where <condition>;
  • Rename a table:
ALTER TABLE table_name RENAME TO new_table_name;
  • Create a table:
CREATE TABLE `student` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `forename` varchar(255) DEFAULT NULL,
  `lastname` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ìd` (`id`),
  UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
  • Insert into table:
INSERT INTO student (id, forename, lastname, email) VALUES (20,"Bob","L.","test@test.com");
INSERT IGNORE INTO student (id, forename, lastname, email) VALUES (20,"Bob","L.","test@test.com");
  • save the last insert Id:
SET @last_id = LAST_INSERT_ID();
  • get the last insert Id:
SELECT @last_id;
  • Alter a table, add a column:
ALTER TABLE table_name ADD column_name datatype;

eg:

 ALTER TABLE `resource` ADD `url` varchar(255);
  • Alter a table column, i.e.:
    ALTER TABLE table_name MODIFY COLUMN column_name datatype;
ALTER TABLE `users` MODIFY COLUMN id INT(11) NOT NULL;
  • Alter a table, drop a column:
ALTER TABLE `tableName` DROP COLUMN `columnName`;
  • Update a table column, i.e.:
    UPDATE table_name SET column1 = value1, column2 = value2, …
    WHERE condition;
UPDATE `users` SET id=6 WHERE username='user1';
  • Add a foreign key to an existing table:
ALTER TABLE`tablename` ADD CONSTRAINT `fk_namedescrip` FOREIGN KEY (`fieldinthistable`) REFERENCES referringtable(fieldinthereferringtable);
  • Update one or two tables that have foreign keys which make updating rise a foreign key constraint.
-- set foreign key checks to 0 first...
SET FOREIGN_KEY_CHECKS = 0;
-- do the update, eg:
update authorities set username='AdminBob' where username='bob1';
-- return the foreign key check to 1, i.e.:
SET FOREIGN_KEY_CHECKS = 1;

Setting up new WildFly18 configuration in IntelliJ IDEA Ultimate to deploy Java EE Maven app

Although the WildFly Maven Plugin “is used to deploy, undeploy, or run your application”, it suffices to set up a “Run Configuration” and to proceed with having the basics of your Java EE maven dependencies, if you want to work with a Maven Java EE app in IntelliJ IDEA Ultimate.

Below are images on how to set up a new local JBoss configuration for your Java EE application in IntelliJ IDEA Ultimate, given that you have downloaded and installed WildFly18 in a folder such as in C:\wildfly-18.0.0.Final.

  1. Open the dialog box to edit a run configuration:

2. Click on Edit Configuration to start setting up a new configuration:

3. Locate the JBoss Server configuration and add a local setup:

4. Select the installed application server folder by clicking on Configure:

5. Since the “Fix” button shows up in the right bottom corner, click the button and select the option that you wish, in this example, war exploded option

The “Deployment” tab now shows the war file being deployed:

6. You now have a JBoss configuration availabe which you can run your Java EE app on.

Other resources related to WildFly and Maven:
https://docs.jboss.org/wildfly/plugins/maven/latest/
https://wildfly.org/

Working with JSON in RESTful applications

There are many JSON libraries that make JSON works possible in a Java application. When responding HTTP response that should be in JSON format we should use the annotation:

@Produces(MediaType.APPLICATION_JSON)

With the Http Methods Get, Post, Delete, Put and Patch, we should consider the respective annotation, i.e. @GET, @POST, @DELETE, @PUT and @PATCH.

Since it is desirable that RESTful Java apps reply through JSON objects, we do responses in JSON format.

One way would be to write a String variable but use the curly brackets and backslashes as in

String var1 = "{\"Hello\":\"world\"}";

return Response.ok(var1).build();

Another way would be to use a library package like Gson. More reading can be found at: https://github.com/google/gson

            JSONObject obj = new JSONObject();
            obj.put("name", "foo");
            String json = new Gson().toJson(obj);
            return Response.ok(json).build();
            
            /* Returns:
            {
                "map": {
                "name": "foo"
            }
            }
            */

Its Maven dependency would be:

      <dependency>
          <groupId>com.google.code.gson</groupId>
          <artifactId>gson</artifactId>
          <version>2.8.6</version>
      </dependency>

Another technique is using the org.json’s JSONObject and to use its .toString at return time:

            JSONObject obj = new JSONObject();
            obj.put("name", "foo");
            return Response.ok(obj.toString()).build();

Its dependency is as follows and more reading can be found at ttps://www.tutorialspoint.com/json/json_java_example.htm

    <!-- https://mvnrepository.com/artifact/org.json/json -->
    <dependency>
      <groupId>org.json</groupId>
      <artifactId>json</artifactId>
      <version>20190722</version>
    </dependency>

Java applications using the Jersey implementation for RESTful Java apps can use the Jersey media dependency. More reading on this can be found at: https://code.i-harness.com/en/q/120cba9 where we see that the right Maven dependency can help in returning JSON objects from Java variables:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>{latest version}</version>
</dependency>

For example to return a List variable in JSON format using the above dependency, we can write code where we use the @POST annotation, prepare a list of persons and return via Response.ok(persons).build.

    @POST
    @Path("/all")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces({MediaType.APPLICATION_JSON})
    public Response all() {
        Person p1 = new Person (1, "Bob", "bob@test.com", "0123456789", "123 Main St.");
        Person p2 = new Person (2, "Bob2", "bob@example.com", "0987654321", "456 Main St.");
        Person p3 = new Person (3, "Bob3", "bob@company.com", "0700123456", "789 Main Street");
        List<Person> persons = new ArrayList<Person>();
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        
        //good resources:
        // https://stackoverflow.com/questions/41761082/convert-list-to-jsonobject-in-java-using-json-simple
        //https://code.i-harness.com/en/q/120cba9 //java - webservices - jersey return arraylist json

        System.out.println("List: " + persons);

        return Response.ok(persons).build(); //this works
        //return Response.ok().entity(persons).build(); //this works also
    }

}

A more complete example as an IntelliJ IDEA project, with a GET and a POST method and their JSON Response can be downloaded here.

Other resources: