Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

[Projeto] Persistência em Java In-Memory

Olá, tudo bem?

Eu gostaria de uma luz de como é a dinâmica de fazer uma persistência em java com JUinit em in-memory, estou tendo dificuldade em fazer o arquivo persistence.xml e fazer com que crie as tabelas e realizar teste de integração, teria algum curso sobre o assunto? algum exemplo que eu possa me basear?

6 respostas

Olá, Samay, tudo bem?

Você pode utilizar o H2 Database, que é um banco de dados in-memory amplamente utilizado para testes em Java. Veja um exemplo básico de como configurar o persistence.xml para usar o H2:

<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">
    <persistence-unit name="testPU" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <class>com.seu.pacote.SuaClasse</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
            <property name="javax.persistence.jdbc.user" value="sa"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

Neste arquivo, o hibernate.hbm2ddl.auto está configurado como update, o que significa que ele criará ou atualizará as tabelas automaticamente com base nas suas entidades. O DB_CLOSE_DELAY=-1 é uma configuração específica do H2 para manter o banco de dados em memória ativo durante toda a execução dos testes.

Para os testes de integração com JUnit, você pode criar um teste simples para verificar se a conexão e as operações básicas estão funcionando:

import static org.junit.Assert.assertNotNull;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class JpaTest {

    private EntityManagerFactory emf;
    private EntityManager em;

    @Before
    public void setUp() {
        emf = Persistence.createEntityManagerFactory("testPU");
        em = emf.createEntityManager();
    }

    @After
    public void tearDown() {
        em.close();
        emf.close();
    }

    @Test
    public void testEntityManager() {
        assertNotNull(em);
    }
}

Este exemplo configura um EntityManager para ser usado nos testes e garante que ele está sendo criado corretamente.

Espero ter ajudado.

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.Bons Estudos!

eu preciso configurar algo no application.properties?

E a forma que estou montando o projeto é com, de exemplo, um Customer para regra de negócios e ser a entidade, um CustomerModel para ser a classe de persistência, no momento da persistência eu passaria um objeto do Customer, e também fiz um Repository que implementa a persistência com métodos de create, find, update e findAll, meu teste é justamente chamar o Repository passando um Customer e a persistência ocorrer, o exemplo que você deu acima funcionou, mas quando testo chamar o Repository dar um erro:

java.lang.IllegalArgumentException: Unknown entity: br.com.desafio.infrastructure.customer.repository.hibernate.CustomerModel

    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:723)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:706)
    at br.com.desafio.infrastructure.customer.repository.hibernate.CustomerRepository.create(CustomerRepository.java:21)
    at br.com.desafio.infrastructure.customer.repository.hibernate.CustomerRepositoryTest.shouldCreateCustomer(CustomerRepositoryTest.java:50)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

Meu teste:

  @Test
    public void shouldCreateCustomer() {
        Customer customer = new Customer(UUID.randomUUID().toString(),"Samn");
        Address address = new Address("Rua 2", 10, "64851-452", "Fortal");
        customer.changeAddress(address);
        customerRepository = new CustomerRepository();
        customerRepository.create(customer);

    CompletableFuture<Customer> foundCustomer = customerRepository.find(customer.getId());

    assertNotNull(foundCustomer);
    assertEquals(foundCustomer.join(), customer);
}

E meu repository:

public class CustomerRepository implements CustomerRepositoryInterface {

    private final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("testPU");

    @Override
    public CompletableFuture<Void> create(Customer entity) {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        CustomerModel customerModel = new CustomerModel(entity.getId(),entity.getName(), entity.getAddress().getStreet(), entity.getAddress().getNumber(), entity.getAddress().getZip(), entity.getAddress().getCity(), entity.isActive(), entity.getRewardPoints());
        entityManager.persist(customerModel);
        entityManager.getTransaction().commit();
        entityManager.close();
        return CompletableFuture.completedFuture(null);
    }

Coloquei o caminho correto no arquivo persistence.xml desse meu Model

Esse erro ocorre porque o Hibernate não reconhece a classe CustomerModel como uma entidade válida. Isso geralmente está relacionado a configurações incorretas no persistence.xml ou à falta de anotações de entidade. Faz o seguinte para resolver o problema:

1. Verifique a Anotação da Classe CustomerModel

Certifique-se de que a classe CustomerModel esteja anotada como uma entidade:

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "customers")
public class CustomerModel {
    
    @Id
    private String id;
    private String name;
    private String street;
    private int number;
    private String zip;
    private String city;
    private boolean active;
    private int rewardPoints;

    // Construtor, getters e setters...
}

2. Verifique o Caminho da Entidade no persistence.xml

No seu persistence.xml, você precisa registrar explicitamente a entidade CustomerModel. Confirme que a tag <class> está correta:

<persistence-unit name="testPU" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <class>br.com.desafio.infrastructure.customer.repository.hibernate.CustomerModel</class>
    <properties>
        <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
        <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
        <property name="javax.persistence.jdbc.user" value="sa"/>
        <property name="javax.persistence.jdbc.password" value=""/>
        <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
        <property name="hibernate.hbm2ddl.auto" value="update"/>
        <property name="hibernate.show_sql" value="true"/>
    </properties>
</persistence-unit>

3. Ajustes no Teste e no Repositório

Você está criando um CustomerRepository dentro do teste manualmente, mas ele pode não estar configurado corretamente. Uma abordagem melhor seria injetar o EntityManagerFactory para evitar a criação repetida da fábrica em cada teste. Segue uma sugestão de ajuste:

public class CustomerRepository implements CustomerRepositoryInterface {

    private final EntityManagerFactory entityManagerFactory;

    public CustomerRepository(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    @Override
    public CompletableFuture<Void> create(Customer entity) {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        
        CustomerModel customerModel = new CustomerModel(
            entity.getId(), entity.getName(), 
            entity.getAddress().getStreet(), entity.getAddress().getNumber(), 
            entity.getAddress().getZip(), entity.getAddress().getCity(), 
            entity.isActive(), entity.getRewardPoints()
        );

        entityManager.persist(customerModel);
        entityManager.getTransaction().commit();
        entityManager.close();
        return CompletableFuture.completedFuture(null);
    }
}

E o teste pode ser ajustado para:

public class CustomerRepositoryTest {

    private EntityManagerFactory entityManagerFactory;
    private CustomerRepository customerRepository;

    @Before
    public void setUp() {
        entityManagerFactory = Persistence.createEntityManagerFactory("testPU");
        customerRepository = new CustomerRepository(entityManagerFactory);
    }

    @After
    public void tearDown() {
        entityManagerFactory.close();
    }

    @Test
    public void shouldCreateCustomer() {
        Customer customer = new Customer(UUID.randomUUID().toString(), "Samn");
        Address address = new Address("Rua 2", 10, "64851-452", "Fortal");
        customer.changeAddress(address);

        customerRepository.create(customer);

        CompletableFuture<Customer> foundCustomer = customerRepository.find(customer.getId());

        assertNotNull(foundCustomer);
        assertEquals(foundCustomer.join(), customer);
    }
}

4. Sobre o application.properties

Como você está utilizando persistence.xml para a configuração de persistência, não é necessário adicionar configurações de banco no application.properties. Porém, se você decidir usar o Spring Boot futuramente, poderá mover essas configurações para o application.properties. Um exemplo:

spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

Deu certooo!! Precisei colocar Autowired no CustomerRepository do test mesmo inicializando ele, é correto?

solução!

Fico feliz que você conseguiu fazer funcionar! Parabéns! Sobre sua dúvida, se o seu projeto está começando a utilizar o Spring, o uso de @Autowired faz sentido. Caso contrário, manter a configuração manual é aceitável. O mais importante é garantir que as dependências estão bem gerenciadas e que seus testes continuam funcionando conforme esperado.

Qualquer outra dúvida é só chamar! Bons estudos.