org.hibernate.search.test.envers.SearchAndEnversIntegrationTest.java Source code

Java tutorial

Introduction

Here is the source code for org.hibernate.search.test.envers.SearchAndEnversIntegrationTest.java

Source

/*
 * Hibernate Search, full-text search for your domain model
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.search.test.envers;

import java.util.List;

import org.apache.lucene.analysis.core.StopAnalyzer;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.hibernate.Transaction;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;
import org.hibernate.envers.query.AuditEntity;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.test.SearchTestBase;
import org.hibernate.search.testsupport.TestForIssue;
import org.hibernate.testing.SkipForDialect;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

/**
 * Unit test covering proper behavior and integration between Hibernate Search and Envers.
 * As well as it verifies that the index is in sync with the latest transaction state.
 *
 * @author Davide Di Somma <davide.disomma@gmail.com>
 */
@SkipForDialect(jiraKey = "HSEARCH-1943", value = PostgreSQL81Dialect.class)
public class SearchAndEnversIntegrationTest extends SearchTestBase {

    private Person harryPotter;
    private Person hermioneGranger;
    private Address privetDrive;
    private Address grimmauldPlace;

    /**
     * This test case aims to verify that insertion, updating and deleting operations work correctly
     * for both Hibernate Search and Hibernate Envers
     */
    @TestForIssue(jiraKey = "HSEARCH-1293")
    @Test
    public void testHibernateSearchAndEnversIntegration() {
        atRevision1();
        atRevision2();
        atRevision3();
        atRevision4();
    }

    /**
     * It verifies that insertion operation works correctly
     */
    private void atRevision1() {
        // Objects creation
        privetDrive = new Address("Privet Drive", 121);
        privetDrive.setFlatNumber(2);
        harryPotter = new Person("Harry", "Potter", privetDrive);
        grimmauldPlace = new Address("Grimmauld Place", 12);
        hermioneGranger = new Person("Hermione", "Granger", grimmauldPlace);

        {
            FullTextSession session = Search.getFullTextSession(openSession());
            try {
                Transaction tx = session.beginTransaction();
                session.save(privetDrive);
                session.save(grimmauldPlace);
                session.save(harryPotter);
                session.save(hermioneGranger);
                tx.commit();
            } finally {
                session.close();
            }
        }

        {
            FullTextSession session = Search.getFullTextSession(openSession());
            try {
                Transaction tx = session.beginTransaction();

                //Let's assert that Hibernate Envers has audited correctly
                AuditReader auditReader = AuditReaderFactory.get(session);
                assertEquals(1, findLastRevisionForEntity(auditReader, Person.class));
                assertEquals(1, findLastRevisionForEntity(auditReader, Address.class));
                assertEquals(2, howManyEntitiesChangedAtRevisionNumber(auditReader, Person.class, 1));
                assertEquals(2, howManyEntitiesChangedAtRevisionNumber(auditReader, Address.class, 1));
                assertEquals(2, howManyAuditedObjectsSoFar(auditReader, Person.class));
                assertEquals(2, howManyAuditedObjectsSoFar(auditReader, Address.class));
                //Let's compares that entities from Hibernate Search and last revision entities from Hibernate Envers are equals
                Person hermioneFromHibSearch = findPersonFromIndexBySurname(session, "Granger");
                Person hermioneAtRevision1 = findPersonFromAuditBySurname(auditReader, "Granger");
                assertEquals(hermioneFromHibSearch, hermioneAtRevision1);
                Person harryFromHibSearch = findPersonFromIndexBySurname(session, "Potter");
                Person harryAtRevision1 = findPersonFromAuditBySurname(auditReader, "Potter");
                assertEquals(harryFromHibSearch, harryAtRevision1);

                tx.commit();
            } finally {
                session.close();
            }
        }
    }

    /**
     * It verifies that updating operation on Address entity works correctly
     */
    private void atRevision2() {
        // Changing the address's house number and flat number
        {
            FullTextSession session = Search.getFullTextSession(openSession());
            try {
                Transaction tx = session.beginTransaction();

                privetDrive = (Address) session.merge(privetDrive);
                privetDrive.setHouseNumber(5);
                privetDrive.setFlatNumber(null);

                tx.commit();
            } finally {
                session.close();
            }
        }

        {
            FullTextSession session = Search.getFullTextSession(openSession());
            try {
                Transaction tx = session.beginTransaction();
                AuditReader auditReader = AuditReaderFactory.get(session);

                //Let's assert that Hibernate Envers has audited everything correctly
                assertEquals(1, findLastRevisionForEntity(auditReader, Person.class));
                assertEquals(2, findLastRevisionForEntity(auditReader, Address.class));
                assertEquals(0, howManyEntitiesChangedAtRevisionNumber(auditReader, Person.class, 2));
                assertEquals(1, howManyEntitiesChangedAtRevisionNumber(auditReader, Address.class, 2));
                assertEquals(2, howManyAuditedObjectsSoFar(auditReader, Person.class));
                assertEquals(3, howManyAuditedObjectsSoFar(auditReader, Address.class));
                @SuppressWarnings("unchecked")
                List<Address> houseNumberAddressChangedAtRevision2 = auditReader.createQuery()
                        .forEntitiesModifiedAtRevision(Address.class, 2)
                        .add(AuditEntity.property("houseNumber").hasChanged())
                        .add(AuditEntity.property("flatNumber").hasChanged())
                        .add(AuditEntity.property("streetName").hasNotChanged()).getResultList();
                assertEquals(1, houseNumberAddressChangedAtRevision2.size());

                //Let's assert that Hibernate Search has indexed everything correctly
                List<Person> peopleLivingInPrivetDriveFromHibSearch = findPeopleFromIndexByStreetName(session,
                        "Privet");
                assertEquals(1, peopleLivingInPrivetDriveFromHibSearch.size());
                //Let's compare that entities from Hibernate Search and last revision entities from Hibernate Envers are equals
                Person harryFromHibSearch = peopleLivingInPrivetDriveFromHibSearch.get(0);
                Person harryAtRevision2 = findPersonFromAuditBySurname(auditReader, "Potter");
                assertEquals(harryFromHibSearch, harryAtRevision2);

                tx.commit();
            } finally {
                session.close();
            }
        }
    }

    /**
     * It verifies that updating operation on Person entity works correctly
     */
    private void atRevision3() {
        // Moving Hermione to Harry
        {
            FullTextSession session = Search.getFullTextSession(openSession());
            try {
                Transaction tx = session.beginTransaction();

                hermioneGranger = (Person) session.merge(hermioneGranger);
                hermioneGranger.setAddress(privetDrive);

                tx.commit();
            } finally {
                session.close();
            }
        }

        {
            FullTextSession session = Search.getFullTextSession(openSession());
            try {
                Transaction tx = session.beginTransaction();
                AuditReader auditReader = AuditReaderFactory.get(session);

                //Let's assert that Hibernate Envers has audited everything correctly
                @SuppressWarnings("unchecked")
                List<Person> peopleWhoHasMovedHouseAtRevision3 = auditReader.createQuery()
                        .forEntitiesModifiedAtRevision(Person.class, 3)
                        .add(AuditEntity.property("address").hasChanged()).getResultList();
                assertEquals(1, peopleWhoHasMovedHouseAtRevision3.size());
                assertEquals(3, findLastRevisionForEntity(auditReader, Person.class));
                assertEquals(3, findLastRevisionForEntity(auditReader, Address.class));
                assertEquals(1, howManyEntitiesChangedAtRevisionNumber(auditReader, Person.class, 3));
                assertEquals(2, howManyEntitiesChangedAtRevisionNumber(auditReader, Address.class, 3));
                assertEquals(3, howManyAuditedObjectsSoFar(auditReader, Person.class));
                assertEquals(5, howManyAuditedObjectsSoFar(auditReader, Address.class));
                //Let's assert that Hibernate Search has indexed everything correctly
                List<Person> peopleLivingInPrivetDriveFromHibSearch = findPeopleFromIndexByStreetName(session,
                        "Privet");
                assertEquals(2, peopleLivingInPrivetDriveFromHibSearch.size());

                tx.commit();
            } finally {
                session.close();
            }
        }
    }

    /**
     * It verifies that deleting operation on Person entity works correctly
     */
    private void atRevision4() {
        // Now let's clean up everything
        {
            FullTextSession session = Search.getFullTextSession(openSession());
            try {
                Transaction tx = session.beginTransaction();
                session.delete(harryPotter);
                session.delete(hermioneGranger);
                session.delete(grimmauldPlace);
                session.delete(privetDrive);
                tx.commit();
            } finally {
                session.close();
            }
        }

        {
            FullTextSession session = Search.getFullTextSession(openSession());
            try {
                Transaction tx = session.beginTransaction();
                AuditReader auditReader = AuditReaderFactory.get(session);

                //Let's assert that Hibernate Envers has audited everything correctly
                assertEquals(4, findLastRevisionForEntity(auditReader, Person.class));
                assertEquals(4, findLastRevisionForEntity(auditReader, Address.class));
                assertEquals(2, howManyEntitiesChangedAtRevisionNumber(auditReader, Person.class, 4));
                assertEquals(2, howManyEntitiesChangedAtRevisionNumber(auditReader, Address.class, 4));
                assertEquals(7, howManyAuditedObjectsSoFar(auditReader, Address.class));
                assertEquals(5, howManyAuditedObjectsSoFar(auditReader, Person.class));
                //Let's assert that Hibernate Search has indexed everything correctly
                assertNull(findPersonFromIndexBySurname(session, "Potter"));
                assertNull(findPersonFromIndexBySurname(session, "Granger"));
                assertEquals(0, findPeopleFromIndexByStreetName(session, "Privet").size());
                assertEquals(0, findPeopleFromIndexByStreetName(session, "Guillaume").size());

                tx.commit();
            } finally {
                session.close();
            }
        }
    }

    /**
     * It returns how many entities are modified for a specific class and number revision.
     */
    private int howManyEntitiesChangedAtRevisionNumber(AuditReader auditReader, Class<?> clazz, Number revision) {
        return ((Long) auditReader.createQuery().forEntitiesModifiedAtRevision(clazz, revision)
                .addProjection(AuditEntity.id().count()).getSingleResult()).intValue();
    }

    /**
     * It returns how many audited objects are there globally for a specific class.
     */
    private int howManyAuditedObjectsSoFar(AuditReader auditReader, Class<?> clazz) {
        return auditReader.createQuery().forRevisionsOfEntity(clazz, true, true).getResultList().size();
    }

    /**
     * It returns the last revision for a specific class.
     */
    private Number findLastRevisionForEntity(AuditReader auditReader, Class<?> clazz) {
        return (Number) auditReader.createQuery().forRevisionsOfEntity(clazz, false, true)
                .addProjection(AuditEntity.revisionNumber().max()).getSingleResult();
    }

    private Person findPersonFromAuditBySurname(AuditReader auditReader, String value) {
        return (Person) auditReader.createQuery().forEntitiesAtRevision(Person.class, 1)
                .add(AuditEntity.property("surname").eq(value)).getSingleResult();
    }

    @SuppressWarnings("unchecked")
    private List<Person> findPeopleFromIndex(FullTextSession session, String term, String value) {
        Query luceneQuery = createLuceneQuery(term, value);
        return session.createFullTextQuery(luceneQuery, Person.class)
                .setSort(new Sort(new SortField("surname", SortField.Type.STRING))).list();
    }

    private Person findPersonFromIndexBySurname(FullTextSession session, String surname) {
        List<Person> people = findPeopleFromIndex(session, "surname", surname);
        if (people.isEmpty()) {
            return null;
        } else if (people.size() > 1) {
            throw new RuntimeException("I've found too many people!!!");
        }
        return people.get(0);
    }

    private List<Person> findPeopleFromIndexByStreetName(FullTextSession session, String streetName) {
        return findPeopleFromIndex(session, "address.streetName", streetName);
    }

    private Query createLuceneQuery(String term, String value) {
        String searchQuery = term + ":" + value;
        QueryParser parser = new QueryParser(term, new StopAnalyzer());
        Query luceneQuery;
        try {
            luceneQuery = parser.parse(searchQuery);
        } catch (ParseException e) {
            throw new RuntimeException("Unable to parse query", e);
        }
        return luceneQuery;
    }

    @Override
    public Class<?>[] getAnnotatedClasses() {
        return new Class[] { Person.class, Address.class };
    }
}