Java JPA Hibernate
Exemplo JPA com Hibernate.
Ingredientes
- Docker
- Docker Compose
- Maven
- Open JDK 11
- Hibernate
- PostgresSQL
-
H2 Database
- vscode
- vscode language support for Java(TM) by Red Hat
- vscode extension pack for Java (Microsoft)
- vscode debugger for Java (Microsoft)
- vscode maven for Java (Microsoft)
- vscode project management for Java (Microsoft)
- vscode test runner for Java (Microsoft)
- vscode Java code generators (Microsoft)
- vscode XML language support by Red Hat
Preparo:
- No vscode:
ctrl + shift + p
- Selecione
Java: Create Java Project
- Selecione
Maven
- Selecione
maven-archetype-quickstart
Docker
Dockerfiles
maven:
FROM maven:3.8.3-openjdk-17-slim
RUN apt-get update && apt-get install -y netcat
ARG USER_HOME_DIR="/root"
ENV MAVEN_HOME /usr/share/maven
ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"
VOLUME "$USER_HOME_DIR/.m2"
WORKDIR /app
COPY src /app/src
COPY pom.xml /app
# Add entrypoint
COPY scripts/entrypoint.sh /app/scripts/entrypoint.sh
CMD ["chmod", "+x", "/app/scripts/entrypoint.sh"]
ENTRYPOINT ["/app/scripts/entrypoint.sh"]
maven test:
FROM maven:3.8.3-openjdk-17-slim
RUN apt-get update && apt-get install -y netcat
WORKDIR /app
COPY src /app/src
COPY pom.xml /app
Entrypoint
Produção:
#!/bin/bash
# --------------------------------------------------------------------------- #
# Wait until database starts.
# --------------------------------------------------------------------------- #
if [ "$DATABASE" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $SQL_HOST $SQL_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
# --------------------------------------------------------------------------- #
# Compile application.
# --------------------------------------------------------------------------- #
mvn -T 2C clean package \
-Dmaven.test.skip -DskipTests -f /app && \
# --------------------------------------------------------------------------- #
# Run application main class.
# --------------------------------------------------------------------------- #
mvn -e exec:java \
-Dexec.cleanupDaemonThreads=false \
-Dexec.mainClass="com.dmenezesgabriel.jpa.view.ProfileView"
Docker Compose
# # hibernate/docker-compose.yml
version: "3.9"
services:
# ------------------------------------------------------------------------- #
# Production Database
# ------------------------------------------------------------------------- #
postgres:
container_name: postgres
image: postgres:14.0-alpine
# volumes:
# - ./postgres_data:/var/lib/postgresql/data/
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: postgres
networks:
- prod
# ------------------------------------------------------------------------- #
# Database Tool
# ------------------------------------------------------------------------- #
pgadmin:
container_name: pgadmin
image: dpage/pgadmin4
ports:
- 8087:80
- 4443:443
environment:
PGADMIN_DEFAULT_EMAIL: dmenezes.gabriel@gmail.com
PGADMIN_DEFAULT_PASSWORD: postgres
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: "True"
PGADMIN_CONFIG_CONSOLE_LOG_LEVEL: 10
depends_on:
- postgres
networks:
- prod
# ------------------------------------------------------------------------- #
# Production Maven
# ------------------------------------------------------------------------- #
maven:
build:
context: .
dockerfile: dockerfiles/Dockerfile-maven
volumes:
- ./.m2:/root/.m2
- $PWD/src:/app/src
networks:
- prod
depends_on:
- postgres
environment:
# entrypoint.sh database health check variables
SQL_HOST: postgres
SQL_PORT: 5432
DATABASE: postgres
# ------------------------------------------------------------------------- #
# Maven Tests
# ------------------------------------------------------------------------- #
maven-test:
build:
context: .
dockerfile: dockerfiles/Dockerfile-maven-test
command: /bin/bash -c 'mvn -e clean test'
volumes:
- ./.m2:/root/.m2
- $PWD/src:/app/src
networks:
- test
# -------------------------------------------------------------------------- #
# Docker networks
# -------------------------------------------------------------------------- #
networks:
prod:
test:
Código fonte
Entidades
Usuário:
// hibernate/src/main/java/com/dmenezesgabriel/jpa/domain/User.java
package com.dmenezesgabriel.jpa.domain.entity;
/**
* User entity
*
* @author Gabriel Menezes
* @version 1.0
*/
import java.util.Calendar;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.util.Objects;
@Entity
@Table(name = "tbl_usuario")
// Each class has it's own table and subclass tables are mapped to superclass
@Inheritance(strategy = InheritanceType.JOINED)
public class User implements Serializable {
protected static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name = "tbl_usuario_cd_usuario_seq",
sequenceName = "tbl_usuario_cd_usuario_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "tbl_usuario_cd_usuario_seq")
@Column(name = "cd_usuario", updatable = false)
// The naming tablename_columname_seq is the PostgreSQL default sequence
// naming
// for SERIAL
// The allocationSize=1 is important if you need Hibernate to co-operate
// with
// other clients
// Note that this sequence will have "gaps" in it if transactions roll back.
// Never assume that for any id n there is an id n-1 or n+1.
private int id;
@Column(name = "nm_usuario", nullable = false, length = 60)
private String name;
@Column(name = "ds_email", nullable = false, unique = true, length = 60)
private String email;
@Column(name = "ds_senha", nullable = false, length = 60)
private String password;
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "dt_criacao", columnDefinition = "TIMESTAMP WITH TIME ZONE",
updatable = false)
private Calendar createdAt;
@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "dt_atualizacao",
columnDefinition = "TIMESTAMP WITH TIME ZONE")
private Calendar updatedAt;
// Constructor - getters and setters
public User() {}
public User(int id, String name, String email, String password,
Calendar createdAt, Calendar updatedAt) {
this.id = id;
this.name = name;
this.email = email;
this.password = password;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public Calendar getCreatedAt() {
return this.createdAt;
}
public void setCreatedAt(Calendar createdAt) {
this.createdAt = createdAt;
}
public Calendar getUpdatedAt() {
return this.updatedAt;
}
public void setUpdatedAt(Calendar updatedAt) {
this.updatedAt = updatedAt;
}
public User id(int id) {
setId(id);
return this;
}
public User name(String name) {
setName(name);
return this;
}
public User email(String email) {
setEmail(email);
return this;
}
public User password(String password) {
setPassword(password);
return this;
}
public User createdAt(Calendar createdAt) {
setCreatedAt(createdAt);
return this;
}
public User updatedAt(Calendar updatedAt) {
setUpdatedAt(updatedAt);
return this;
}
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof User)) {
return false;
}
User user = (User) o;
return id == user.id && Objects.equals(name, user.name)
&& Objects.equals(email, user.email)
&& Objects.equals(password, user.password)
&& Objects.equals(createdAt, user.createdAt)
&& Objects.equals(updatedAt, user.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(id, name, email, password, createdAt, updatedAt);
}
@Override
public String toString() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return "{" + " id='" + getId() + "'" + ", name='" + getName() + "'"
+ ", email='" + getEmail() + "'" + ", password='"
+ getPassword() + "'" + ", createdAt='"
+ sdf.format(createdAt.getTime()) + "'" + ", updatedAt='"
+ sdf.format(updatedAt.getTime()) + "'" + "}";
}
}
Gênero Enum:
// hibernate/src/main/java/com/dmenezesgabriel/jpa/domain/Gender.java
package com.dmenezesgabriel.jpa.domain.entity;
public enum Gender {
MALE("masculino"), FEMALE("feminino");
private String description;
Gender(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
Testes
Entidades:
// hibernate/src/test/java/com/dmenezesgabriel/jpa/User.java
package com.dmenezesgabriel.jpa.domain.entity;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class TestUser {
@Test
@DisplayName("Should instance User object correctly.")
public void shouldInstanceObjectCorrectly() throws Exception {
User user = new User();
user.setName("Gabriel");
user.setEmail("gabriel@example.com");
user.setPassword("123");
assertEquals(user.getName(), "Gabriel");
assertEquals(user.getEmail(), "gabriel@example.com");
assertEquals(user.getPassword(), "123");
}
}
persistence.xml
Produção:
<!-- src/main/resources/META-INF/persistence.xml -->
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1">
<!-- transaction-type -->
<!-- - RESOURCE_LOCAL -->
<!-- - JTA -->
<persistence-unit name="com.dmenezesgabriel.jpa" transaction-type="RESOURCE_LOCAL">
<!-- Framework used for JPA especification -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.dmenezesgabriel.jpa.domain.entity.User</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Phone</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Address</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Customer</class>
<class>com.dmenezesgabriel.jpa.domain.entity.EstablishmentResponsible</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Document</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Establishment</class>
<class>com.dmenezesgabriel.jpa.domain.entity.EstablishmentSegment</class>
<class>com.dmenezesgabriel.jpa.domain.entity.EstablishmentEvaluation</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Product</class>
<class>com.dmenezesgabriel.jpa.domain.entity.ProductCategory</class>
<class>com.dmenezesgabriel.jpa.domain.entity.ProductType</class>
<class>com.dmenezesgabriel.jpa.domain.entity.GuestCheckPad</class>
<properties>
<!-- Database Driver -->
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
<!-- Database connection uri -->
<!-- "jdbc:sgdb_name://host_address:port_number/database_name" -->
<!-- - When using docker, host addres is the container name -->
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://postgres:5432/postgres" />
<!-- Database User -->
<property name="javax.persistence.jdbc.user" value="postgres" />
<!-- Database Password -->
<property name="javax.persistence.jdbc.password" value="postgres" />
<!-- Database Dialect -->
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<!-- create / create-drop / update -->
<!-- - create: creates database from scratch using definitions -->
<!-- - update: try to update new attributes -->
<!-- - validate: verify if the entities are compatible with db tables -->
<property name="hibernate.hbm2ddl.auto" value="update" />
<!-- Show SQL in console -->
<!-- Should be disabled in production -->
<property name="hibernate.show_sql" value="false" />
<!-- Show SQL formatted -->
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
Testes:
- H2 Database: banco em memória.
<!-- src/test/resources/META-INF/persistence.xml -->
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1">
<!-- transaction-type -->
<!-- - RESOURCE_LOCAL -->
<!-- - JTA -->
<persistence-unit name="com.dmenezesgabriel.jpa" transaction-type="RESOURCE_LOCAL">
<!-- Framework used for JPA especification -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.dmenezesgabriel.jpa.domain.entity.User</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Phone</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Address</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Customer</class>
<class>com.dmenezesgabriel.jpa.domain.entity.EstablishmentResponsible</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Document</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Establishment</class>
<class>com.dmenezesgabriel.jpa.domain.entity.EstablishmentSegment</class>
<class>com.dmenezesgabriel.jpa.domain.entity.EstablishmentEvaluation</class>
<class>com.dmenezesgabriel.jpa.domain.entity.Product</class>
<class>com.dmenezesgabriel.jpa.domain.entity.ProductCategory</class>
<class>com.dmenezesgabriel.jpa.domain.entity.ProductType</class>
<class>com.dmenezesgabriel.jpa.domain.entity.GuestCheckPad</class>
<properties>
<!-- Database Driver -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<!-- Database connection uri -->
<!-- "jdbc:sgdb_name://host_address:port_number/database_name" -->
<!-- - When using docker, host addres is the container name -->
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test" />
<!-- Database User -->
<property name="javax.persistence.jdbc.user" value="" />
<!-- Database Password -->
<property name="javax.persistence.jdbc.password" value="" />
<!-- Database Dialect -->
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<!-- create / create-drop / update -->
<!-- - create: creates database from scratch using definitions -->
<!-- - update: try to update new attributes -->
<!-- - validate: verify if the entities are compatible with db tables -->
<property name="hibernate.hbm2ddl.auto" value="create" />
<!-- Show SQL in console -->
<!-- Should be disabled in production -->
<property name="hibernate.show_sql" value="false" />
<!-- Show SQL formatted -->
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
pom.xml
<!-- hibernate/pom.xml -->
<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>
<!-- ==================================================================== -->
<!-- Organization -->
<!-- ==================================================================== -->
<!-- organization/application url on the contrary br.com.domain -->
<groupId>com.dmenezesgabriel</groupId>
<!-- ==================================================================== -->
<!-- Project -->
<!-- ==================================================================== -->
<!-- Artifacty ID must be unique within the group -->
<artifactId>jpa</artifactId>
<version>1.0-SNAPSHOT</version>
<name>jpa</name>
<!-- ==================================================================== -->
<!-- Project's Website -->
<!-- ==================================================================== -->
<url>dmenezesgabriel.github.io</url>
<!-- ==================================================================== -->
<!-- Projects properties -->
<!-- ==================================================================== -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!-- ==================================================================== -->
<!-- Project's dependencies -->
<!-- ==================================================================== -->
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- end::junit[] -->
<!-- tag::hibernate[] -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.24.Final</version>
</dependency>
<!-- end::hibernate[] -->
<!-- tag::oracle[] -->
<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc8 -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>21.1.0.0</version>
</dependency>
<!-- end::oracle[] -->
<!-- tag::postgres[] -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.3</version>
</dependency>
<!-- end::postgres[] -->
<!-- tag::H2[] -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.210</version>
</dependency>
<!-- end::H2[] -->
<!-- tag::log4j[] -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.1</version>
</dependency>
<!-- end::log4j[] -->
</dependencies>
<!-- ==================================================================== -->
<!-- Project's build -->
<!-- ==================================================================== -->
<build>
<pluginManagement>
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Compilar e executar
Instruções:
Description
General Sales Management System
Requirements
- Openjdk 17
- Hibernate
- Docker
- Docker Compose
Usage
Run:
# Optional
docker-compose up -d pgadmin
# Run
docker-compose run maven --rm
Run tests:
# Run
docker-compose run --rm maven-test
Recompile and Run:
# Run
docker-compose run maven --rm
Bring down with volumes:
# Destroy database storage
docker-compose down -v