April 16, 2019

Vlad Mihalcea High-Performance Hibernate Tutorial

Introduction

Below is a short summary of the most important things when designing JPA. For complete tutorial see https://vladmihalcea.com/tutorials/hibernate/.

@Id - Database-generated identifiers (Primary Key, PK)

The most common way to generate PK, is to let the DB generate it.


@Id
@GeneratedValue
protected Long id;

@javax.persistence.GeneratedValue(strategy = GenerationType) has 4 generation strategies:

  • AUTO (Default. Do not use for MySQL, see https://vladmihalcea.com/why-should-not-use-the-auto-jpa-generationtype-with-mysql-and-hibernate/)
  • TABLE (Which you should NOT use, see https://vladmihalcea.com/why-you-should-never-use-the-table-identifier-generator-with-jpa-and-hibernate/)
  • SEQUENCE
  • IDENTITY

Best choice is SEQUENCE, but is only supported by:

  • Oracle
  • SQL Server 2012
  • PostgreSQL
  • DB2
  • HSQLDB

Next best choice is IDENTITY, which is supported by:

  • Oracle 12c
  • SQL Server
  • MySQL (AUTO_INCREMENT)
  • DB2
  • HSQLDB

@OneToOne

https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/

Reuse Parent PK as PK and FK in "Child", with @MapsId


@Entity
public class Parent {

    @Id
    @GeneratedValue
    private Long id;
    
    @OneToOne(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
    private ParentDetails details;    
}

@Entity 
public class ParentDetails {

    @Id
    // @GeneratedValue No generated value, with @MapsId pk is fk
    private Long id;
    
    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private Parent parent;
}

@OneToMany

https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/

Best Unidirectional @ManyToOne On the Child.


@Entity
public class Parent {

    @Id
    @GeneratedValue
    private Long id;    
}

@Entity 
public class Child {

    @Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Parent parent;
}

List comments = entityManager.createQuery(
        "SELECT c " +
        "from Child c " +
        "where c.parent.id = :parentId", Child.class)
    .setParameter( "parentId", 1L )
    .getResultList();

Next best Bidirectional @OneToMany.


@Entity
public class Parent {

    @Id
    @GeneratedValue
    private Long id;    
    
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
    private List children = new ArrayList<>();
    
    public void addChild(Child chilld) {
        children.add(child);
        child.setParent(this);
    }
 
    public void removeComment(Child child) {
        children.remove(child);
        child.setParent(this);
    }
}

@Entity 
public class Child {

    @Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Parent parent;

    // Note equals and hashCode are important to implement when working with detached objects.
    // https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Child )) return false;
        return id != null && id.equals(((Child) o).getId());
    }
    
    // Note equals and hashCode are important to implement when working with detached objects.
    @Override
    public int hashCode() {
        // Database-generated identifiers
        // https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
        return 31;
    }
}

@ManyToMany

https://vladmihalcea.com/the-best-way-to-use-the-manytomany-annotation-with-jpa-and-hibernate/

Best Bidirectional @ManyToMany with Set instead of List. Note that relationship is managed by Post, so to add or remove Tag, you need to load Post.


@Entity
public class Post {
 
    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany(cascade = {
        CascadeType.PERSIST,
        CascadeType.MERGE
    })
    @JoinTable(name = "post_tag",
        joinColumns = @JoinColumn(name = "post_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private Set tags = new HashSet<>();
 
    public void addTag(Tag tag) {
        tags.add(tag);
        tag.getPosts().add(this);
    }
 
    public void removeTag(Tag tag) {
        tags.remove(tag);
        tag.getPosts().remove(this);
    }
 
    // https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Post)) return false;
        return id != null && id.equals(((Post) o).getId());
    }
 
    @Override
    public int hashCode() {
        // Database-generated identifiers
        return 31;
    }
}

@Entity
public class Tag {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @NaturalId
    private String name;
 
    @ManyToMany(mappedBy = "tags")
    private Set posts = new HashSet<>();

    // https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Tag tag = (Tag) o;
        return Objects.equals(name, tag.name);
    }
 
    @Override
    public int hashCode() {
        // Natural identifiers
        return Objects.hash(name);
    }
}

Read also "The best way to map a many-to-many association with extra columns when using JPA and Hibernate" https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/

Fetching Strategy

https://vladmihalcea.com/hibernate-facts-the-importance-of-fetch-strategy/

Always use 'fetch = FetchType.LAZY' on JPA mapped relationship, UNLESS you knew that you will always need relationship objects.

IF you have EAGER relationship fetch THOSE objects with EntityManager#find(Class entityClass, Object primaryKey) NOT JPQL. Using JPQL overrides EAGER relationship, which makes sense sense JPA is doing what it is told.

When using JPQL use INNER or LEFT JOIN FETCH for fetching relationship.

See https://www.w3schools.com/sql/sql_join.asp

JavaFaker - Generating Test Data

See https://magnus-k-karlsson.blogspot.com/2019/04/generating-test-data-javafaker.html for generating test data very easily.

April 12, 2019

Generating Test Data - JavaFaker

JavaFaker is good library for generating localized faked test data.


<dependency>
    <groupId>com.github.javafaker</groupId>
    <artifactId>javafaker</artifactId>
    <version>0.17.2</version>
    <scope>test</scope>
</dependency>

Faker faker = new Faker(new Locale("sv-SE")); // default Locale("en", "")

faker.name().name()         // optional prefix, a firstname and a lastname
faker.name().nameWithMiddle()   // optional prefix, a given and family name, another 
                                // 'firstname' for the middle name and 
                                // an optional suffix such as Jr. 
faker.name().title()
faker.name().prefix()       // e.g. Mr., Mrs., Ms.
faker.name().firstName()
faker.name().lastName()
faker.name().suffix()       // e.g. Jr., Sr., I, II
// and more username, fullName, ...

faker.address().streetAddress()
faker.address().secondaryAddress()
faker.address().zipCode()
faker.address().city()
// and more city, state, county, country, ...

faker.number().numberBetween(0, 99999999999999L)
faker.number().randomDigitNotZero()
// and some more double randomDouble, long randomNumber(), ...

faker.date().between(from, to)
// and more past, future, ...

faker.idNumber().validSvSeSsn(); // swedish social security number in format "######-####", "######+####"

And if that is not enough, you create custom


FakeValuesService fakeValuesService = new FakeValuesService(new Locale("en-GB"), new RandomService());
fakeValuesService.regexify("[a-z1-9]{10}");
fakeValuesService.letterify("12??34", boolean isUpper)  // Returns a string with the '?' characters 
                                                        // in the parameter replaced with random 
                                                        // alphabeticcharacters.

// numberify - Returns a string with the '#' characters in the parameter 
// replaced with random digits between 0-9 inclusive.

// bothify(String string, boolean isUpper) - applies both a numerify(String) and a letterify(String, boolean)

See also https://www.baeldung.com/java-faker.

April 5, 2019

Getting Started with JSF 2.3 and Bootstrap 4

Introduction

The most popular CSS framework today is Bootstrap.

We are now going to add Bootstrap to my previous getting started JSF 2.3 (Java EE 8).

Starting Files

  • pom.xml
  • src/main/java/se/magnuskkarlsson/jsf23/boundary/PersonBacking.java
  • src/main/webapp/welcome-bootstrap.xhtml (Copy of welcome.xhtml so far)
  • src/main/webapp/WEB-INF/web.xml
  • src/main/webapp/WEB-INF/beans.xml

Download Bootstrap

Open https://getbootstrap.com/ and click Download and then download "Compiled CSS and JS".

Unzip bootstrap-4.3.1-dist.zip to src/main/webapp/ so you have:

  • src/main/webapp/bootstrap-4.3.1-dist/css/
  • src/main/webapp/bootstrap-4.3.1-dist/js/

Bootstrap Template

Then go back to https://getbootstrap.com/ and click Get Started, there you will see a Bootstrap getting started template. Lets add that to our welcome-bootstrap.xhtml.

NOTE Since we are using xhtml, we need to close elements and have a well formatted file.


<!DOCTYPE html>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

    <h:head>
        <!-- Required meta tags -->
        <meta charset="utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>

        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="bootstrap-4.3.1-dist/css/bootstrap.min.css"/>

        <title>Hello JSF 2.3 and Bootstrap 4</title>
    </h:head>
    <h:body>
        <h:form>
            <h:outputLabel for="name" value="Who do you want to greet?"/>
            <h:inputText id="name" value="#{personBacking.name}" />
            <h:commandButton value="Submit" action="#{personBacking.submit()}">
                <f:ajax execute="@form" render=":message"/>
            </h:commandButton>
        </h:form>
        <br/>
        <h:outputText id="message" value="#{personBacking.message}"/>  

        <!-- Optional JavaScript -->
        <!-- jQuery first, then Popper.js, then Bootstrap JS -->
        <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
        <script src="bootstrap-4.3.1-dist/js/bootstrap.min.js"></script>
    </h:body>
</html>

Lets Use Bootstrap 4

Now lets use some bootstrap CSS classes.

Form: we need to add class="form-group" for each input for a vertical layout and for each input field add class="form-control".

Button: for our submit button we add class="btn btn-primary".

Overall layout: to add some space to the side lets add main container class.

Card: Which was panel in Bootstrap 3, we add class="card", class="card-body" and class="card-title".


<!DOCTYPE html>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

    <h:head>
        <!-- Required meta tags -->
        <meta charset="utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>

        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="bootstrap-4.3.1-dist/css/bootstrap.min.css"/>

        <title>Hello JSF 2.3 and Bootstrap 4</title>
    </h:head>
    <h:body>
        <div class="container">
            <div class="card">
                <div class="card-body">
                    <h5 class="card-title">
                        <h:outputText id="message" value="#{personBacking.message}"/>  
                    </h5>
                    <h:form>
                        <div class="form-group">
                            <h:outputLabel for="name" value="Who do you want to greet?"/>
                            <h:inputText id="name" value="#{personBacking.name}" class="form-control" />
                        </div>
                        <h:commandButton value="Submit" action="#{personBacking.submit()}" class="btn btn-primary">
                            <f:ajax execute="@form" render=":message"/>
                        </h:commandButton>
                    </h:form>
                </div>
            </div>
        </div>

        <!-- Optional JavaScript -->
        <!-- jQuery first, then Popper.js, then Bootstrap JS -->
        <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
        <script src="bootstrap-4.3.1-dist/js/bootstrap.min.js"></script>
    </h:body>
</html>

Test

Lets build, deploy and test it and then open for (JBoss EAP 7.2) http://localhost:8080/example-jsf23/welcome-bootstrap.xhtml.

Getting Started with JSF 2.3 (Java EE 8)

Introduction

JSF 2.3 comes with Java EE 8.

Maven

Add Java EE 8 dependency and Java 11 (latest Java LTS version).


<?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>se.magnuskkarlsson</groupId>
    <artifactId>example-jsf23</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.build.outputEncoding>UTF-8</project.build.outputEncoding>
        <maven.compiler.release>11</maven.compiler.release>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0</version>
            <scope>provided</scope>
        </dependency>
 
        <!-- Test Support -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
 
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.10.19</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <finalName>${project.artifactId}</finalName>
    </build>
</project> 

Facelet/JSF Page

In JSF you use Facelet, which you are xhtml files. Earlier we used HTML 4.01, but now we use HTML 5.

<h:head> and <h:body> are mandatory to make JSF work.

Lets write a simple Facelet src/main/webapp/welcome.xhtml.


<!DOCTYPE html>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

    <h:head>
        <title>Welcome JSF 2.3</title>
    </h:head>
    <h:body>
        <h:form>
            <h:outputLabel for="name" value="Who do you want to greet?"/>
            <h:inputText id="name" value="#{personBacking.name}" />
            <h:commandButton value="Submit" action="#{personBacking.submit()}">
                <f:ajax execute="@form" render=":message"/>
            </h:commandButton>
        </h:form>
        <br/>
        <h:outputText id="message" value="#{personBacking.message}"/>  
    </h:body>
</html>

web.xml

web.xml is no longer necessary, but to hide the source code of your xhtml files we explicitly set Facelet servlet to react on xhtml file extension.

src/main/webapp/WEB-INF/web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee webapp_4_0.xsd"
         version="4.0">
    
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>welcome.xhtml</welcome-file>
    </welcome-file-list>
</web-app>

beans.xml

To make CDI work we need to add an empty src/main/webapp/WEB-INF/beans.xml.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
       bean-discovery-mode="all"
       version="2.0">

</beans>

Backing Bean

In Java EE 8 the JSF Backing Bean or Managed Bean are deprecated, so now we use standard CDI.


package se.magnuskkarlsson.jsf23.boundary;

import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class PersonBacking {

    private String name;
    private String message;

    // ----------------------- Logic Methods -----------------------
    
    public void submit() {
        message = "Greeting " + name;
    }

    // ----------------------- Helper Methods -----------------------
    
    // ----------------------- Get and Set Methods -----------------------
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMessage() {
        return message;
    }

}

Test

Build and deploy to an Java EE 8 container such as JBoss EAP 7.2, then open http://localhost:8080/example-jsf23/welcome.xhtml.