Как исправить «операцию отката базы данных после теста JUnit» в Hibernate (без Spring)? - PullRequest
2 голосов
/ 15 мая 2019

Я работаю с проектом Maven, использующим Hibernate (hibernate-entitymanager), MySQL-коннектор (mysql-connector-java) и JUnit (junit-jupiter-engine), и я хочу сделать операцию отката базы данных после Тест JUnit

Ну, я уже пробовал с:

  • Поместите @Transactional аннотацию в тестовый класс и тестовый набор
  • Создать новый метод savePerson(Person p, boolean isRollback) передать дополнительный параметр isRollback и поставить несколько if условий.
  • Создайте класс обслуживания PersonServiceImpl и поместите аннотацию @Transactional в класс верхнего уровня.

К сожалению, Это не сработало.

У меня есть 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>
    <groupId>com.example</groupId>
    <artifactId>entity-manager-07</artifactId>
    <version>1.0</version>
    <name>entity-manager-07</name>
    <description>entity-manager-07</description>

    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.2.Final</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.2.Final</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.5.0-M1</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.5.0-M1</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

An Person Сущность

package com.example.entities;

import javax.persistence.*;

@Entity
@Table(name = "Person")
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true)
    private Long id;
    @Column(name = "name")
    private String name;
    @Column(name = "lastname")
    private String lastname;

    public Person() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("Person [id=");
        builder.append(id);
        builder.append(", name=");
        builder.append(name);
        builder.append(", lastname=");
        builder.append(lastname);
        builder.append("]");
        return builder.toString();
    }

}

Интерфейс DAO Dao<T>

package com.example.dao;

public interface Dao<T> {

    T save(T t) throws Exception;

    T save(T t, boolean isRollback) throws Exception;
}

Также, класс DAO PersonDAOImpl

package com.example.dao;

import javax.persistence.EntityManager;

import com.example.entities.Person;

public class PersonDAOImpl implements Dao<Person> {

    private EntityManager entityManager;

    public PersonDAOImpl(EntityManager em) {
        this.entityManager = em;
    }

    @Override
    public Person save(Person person) throws Exception {

        try {
            entityManager.getTransaction().begin();

            if (person.getId() == null) {
                entityManager.persist(person);
            }

            entityManager.persist(person);
            entityManager.getTransaction().commit();
        } catch (Exception e) {
            if (entityManager.getTransaction() != null && entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            throw e;
        } finally {
            entityManager.close();
        }

        return person;
    }

    @Override
    public Person save(Person person, boolean isRollback) throws Exception {

        try {
            entityManager.getTransaction().begin();

            if (isRollback){
                entityManager.getTransaction().setRollbackOnly();
            }

            if (person.getId() == null) {
                entityManager.persist(person);
            }

            if (entityManager.getTransaction().getRollbackOnly()){
                System.out.println("Rollback...");
                entityManager.getTransaction().rollback();
            } else {
                entityManager.getTransaction().commit();
            }

        } catch (Exception e) {
            if (entityManager.getTransaction() != null && entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            throw e;
        } finally {
            entityManager.close();
        }

        return person;
    }
}

Сервисный интерфейс PersonService

package com.example.services;

import com.example.entities.Person;

public interface PersonService {
    Person savePerson(Person p) throws Exception;
    Person savePerson(Person p, boolean isRollback) throws Exception;
}

Класс обслуживания PersonServiceImpl

package com.example.services;

import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import com.example.dao.PersonDAOImpl;
import com.example.entities.Person;

@Transactional
public class PersonServiceImpl implements PersonService{

    private PersonDAOImpl dao;

    public PersonServiceImpl(EntityManager em) {
        // DAO
        this.dao = new PersonDAOImpl(em);
    }

    @Override
    public Person savePerson(Person p) throws Exception {
        return this.dao.save(p);
    }

    @Override
    public Person savePerson(Person p, boolean isRollback) throws Exception {
        return this.dao.save(p, isRollback);
    }
}

Настройки постоянства

src/test/resources/hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://192.168.1.5:3306/entitymanager-test-001</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <!-- JDBC Connection Pool Property : -->
        <property name="hibernate.connection.pool_size">2</property>
        <!-- Echo all executed SQL to stdout -->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.use_sql_comments">true</property>
        <!-- SQL Dialect Property -->
        <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <!-- Drop and re-create the database schema on startup -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- Set the current session context -->
        <property name="current_session_context_class">thread</property>
        <!-- Entities  -->
        <mapping class="com.example.entities.Person"  />
    </session-factory>
</hibernate-configuration>

src/test/resources/META-INF/persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/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">
    <persistence-unit name="my-persistence-unit"
        transaction-type="RESOURCE_LOCAL">
        <!-- Persistence provider -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="hibernate.ejb.cfgfile"
                value="./hibernate.cfg.xml" />
            <property name="hibernate.session.events.log" value="true" />
        </properties>
    </persistence-unit>
</persistence>

Наконец, тест JUnit PersonServiceImplTest

package services;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

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

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import com.example.entities.Person;
import com.example.services.PersonServiceImpl;

@Transactional
class PersonServiceImplTest {

    private EntityManagerFactory emf;
    private EntityManager em;

    @BeforeEach
    void setUp() {
        this.emf = Persistence.createEntityManagerFactory("my-persistence-unit");
    }

    @AfterEach
    void tearDown() {
        emf.close();
    }

    @Test
    @Transactional
    void savePersonRollback() {
        this.em = emf.createEntityManager();

        // Operation
        PersonServiceImpl personService = new PersonServiceImpl(this.em);

        // New person
        Person person = new Person();
        person.setName("Michael");
        person.setLastname("Fisher");

        Person pe = null;
        try {
            pe = personService.savePerson(person, true);
            assertNotNull(pe);
            assertTrue(pe.getId() > 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Новая попытка решения (Добавлено: 17 мая 2019 г.)

  • Добавить новую недвижимость person в PersonDAOImpl
  • Создание нового метода savePersonDoWork (с использованием Savepoint, PreparedStatement, Work by Hibernate)
package com.example.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;

import javax.persistence.EntityManager;

import org.hibernate.Session;
import org.hibernate.jdbc.Work;

import com.example.entities.Person;

public class PersonDAOImpl implements Dao<Person> {

    private EntityManager entityManager;
    private Person person;

    public PersonDAOImpl(EntityManager em) {
        this.entityManager = em;
    }

    // ... more code

    @Override
    public Person savePersonDoWork(Person personToSave, boolean isRollback) throws Exception {

        try {
            entityManager.getTransaction().begin();

            if (isRollback) {
                System.out.println("Rollback if...");
                entityManager.getTransaction().setRollbackOnly();
            }

            Session session = entityManager.unwrap(Session.class);
            session.doWork(new Work() {

                @Override
                public void execute(Connection con) throws SQLException {
                    // do something useful
                    con.setAutoCommit(false);

                    // Savepoint
                    Savepoint saveBeforeInsert = con.setSavepoint();

                    // INSERT SQL
                    String sql = "INSERT INTO Person (lastname, name) VALUES (?, ?)";
                    con.prepareStatement(sql);

                    PreparedStatement st = con.prepareStatement(sql);
                    st.setString(1, personToSave.getName());
                    st.setString(2, personToSave.getLastname());

                    // execute the preparedstatement insert
                    Long idPerson = new Long(st.executeUpdate());

                    if (isRollback) {
                        System.out.println("Rollback execute()...");
                        con.rollback(saveBeforeInsert);
                    }

                    // Close
                    st.close();

                    // Set data
                    person = new Person();
                    person.setId(idPerson);
                    person.setName(personToSave.getName());
                    person.setLastname(personToSave.getLastname());

                }
            });

        } catch (Exception e) {
            if (entityManager.getTransaction() != null && entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            throw e;
        } finally {
            entityManager.close();
        }

        return this.person;
    }
}

К сожалению, операция отката базы данных пока не работает .

Ожидаемые результаты

Я ожидаю, что в базе данных нет никаких изменений , ни новых строк, ни новых данных, потому что моя база данных заполняется тестовыми данными, я этого не хочу.

  • Перед запуском теста : SELECT * FROM Person (5 строк)
  • После запуска теста : SELECT * FROM Person (5 строк)

Не могли бы вы мне помочь, пожалуйста? Буду признателен за ваши советы, рекомендации или поддержку. Спасибо.

1 Ответ

0 голосов
/ 15 мая 2019

Вы звоните entityManager.getTransaction().commit(); в DAO.Таким образом, транзакция уже зафиксирована и больше не может быть отменена.

Если вы хотите иметь возможность откатить транзакцию, вы извлекаете обработку транзакции.

Используете ли вы Spring или Java EE или делаететы просто нам обычный Hibernate без каких-либо других фреймворков?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...